Skip to Content
Author's profile photo Werner Steyn

Put SOAP to REST using CE

This blog aims to demonstrate how to create and run REST (Representational State Transfer) Web services using the SAP NetWeaver Composition Environment.

Contrary to popular believe getting REST to work with your code can be a bit tricky without the help from Jersey. Being a Sun product, it is developed as the open source Project Jersey and is the reference implementation of JSR 311 (“The Java API for RESTful Web Services”).  Jersey takes care of the lower level HTTP processing and provides a way to map a RESTful HTTP Request directly to methods in your Java code. My aim is to get you to create a REST Web service on the SAP NetWeaver Composition Environment with the help from the Jersey libraries and to show you how to implement the most commonly used method calls.

This blog assumes that you are using the SAP NetWeaver Composition Environment (7.1, 7.11 or 7.2) including the corresponding SAP NetWeaver Developer Studio. Also, it is assumed that you have downloaded the required Jersey libraries from https://jersey.dev.java.net/.

 

Dealing with the Jersey libraries

Using the SAP NetWeaver Developer studio create an External Library Development Component (DC) project called ‘rest/ext/lib’

image

 

Download and import the necessary Jersey jar files into the libraries folder of the external libraries project. At the time of writing this article the following jar files were packaged within a jersey-archive-1.1.4.zip file obtained from the aforementioned URL.

 

image

 

Define 2 public parts of type Assembly and Compilation on the External Library Project; and add the libraries to each public part. The Assembly public part will ensure that your libraries are assembled and deployed with your Enterprise Application Project. The Compilation public part will ensure that your Java code that references the REST libraries will be able to compile. 

 

image

 

Create a Java EE Project

Create a Java EE Web Module Development Component Project and a Java EE Enterprise Application Development Component Project; remember to reference the Java EE Web Module Project. Name the Web Module Project ‘rest/web’ and name the Enterprise Application Project ‘rest/ear’

 

image

 

Select the Web Module Project (rest/web) and assign a dependency to the (Compilation – API) public part of the External Library DC Project created in the first step.

 

image

 

Ensure that you select Build Time (on the API and ***) public part – this will ensure that you will be able to reference the Jersey API from within your web development component project.

 

image

 

In order to get Jersey to work you need to configure the Jersey servlet to a URL root. Also, you need to tell Jersey the location of the Java package to scan for REST annotations. This information is part of the web application’s web.xml file. The com.sun.jersey.spi.container.servlet.ServletContainer servlet is the main REST class and is part of the JAX-RS API runtime. Notice the url-pattern tag, it indicates all URL requests prefixed with ‘/rest’ relative to the context root of the war file needs to be forwarded to the Jersey REST servlet for processing. Note the param-value tag; this is where you specify your Java package that will contain your REST implementation Java files.

 

Enable the Jersey REST Servlet

 

image

 

 

Create the Java REST Web service code 

 

At this point you are finished with the setup and configuration and you can turn your attention over to your Java code. First create a package demo.sap.com.material and create 2 java files as shown in the image below.  

 

image

 

 

The Material java class will act as your POJO – add the fields and methods below to the class. Remember to add the XmlRootElement annotation before the class declaration. This will ensure that the material values will be represented as XML elements in an XML document that will be used within the HTTP response.

 

 

image

 

 

Start adding the Java code to the MaterialResouce Java file as shown below. This is your main Java class where the REST processing will occur. Note the annotations at the top of the class.

The Produces annotation is used to specify the MIME media types that will be returned back to the client. If the Produces annotation is applied at the class level, all the methods in a class can produce the specified MIME types by default. If it is applied at the method level, it overrides any Produces annotations applied at the class level. In our RESTful service example the material response information will be represented in XML format.

The Path annotation identifies the URI path to which the Java class responds, and is specified at the class level. The Path annotation’s value is a partial URI path relative to the base URI of the server on which the application is deployed, the context root of the WAR, and the URL pattern to which the Jersey helper servlet responds. Later on you will see where the ‘materials’ URI comes into play.

For simplicity sake we hard code the material list and added it to a Collection or type Material. The Material POJO should look familiar since we created this in one of the first steps.  

 

 

image

 

To return a complete list of all materials simply create the following method and add the GET annotation in front of the method declaration. Notice there is no Path annotation so therefore the class Path annotation materials will apply to this method.  Specifically this method will respond to both the HTTP GET method and the URI ‘/rest/materials’. Remember ‘/rest’ is defined within the web.xml file and ‘/materials’ are define at the class level; both will be inherited down to the method.

 

 

image

 

 

Eventually after you have deployed your application you can simply execute the URL below in your browser. Since browser requests are HTTP GET’s by nature the above method will automatically get called and produce the XML output.

 

 

image

 

Let’s take the example a step further; a complete list of all the materials may be sufficient however what if you only are interested in one specific material? The obvious approach would be to pass the material id as a variable to a method.  The way that REST handles variables are denoted by curly brackets, for example – look at the Path annotation below; this method can then be executed using the HTTP GET URI ‘/rest/materials/A1001′. This is how the material id can get passed into the method using the PathParam annotation.

 

 

image

 

 

Since the ‘materials’ path URI parameter is defined at the class level you can simply append the material id. As a result the getMaterial method will get called passing in a single material id and the response will contain one instance of the Material class in XML format.

 

 

image

 

 

So far we have only looked at using REST to retrieve (READ) material information. The next sections will focus on modifying material data (ADD, UPDATE & DELETE). You might have found it interesting that the previous READ examples coincided nicely with the GET annotation and make use of the HTTP GET method. Similarly, to add data REST makes use of the standard HTTP POST method, to update information REST again makes use of the standard HTTP PUT method, and then finally to remove data REST makes use of the standard HTTP DELETE method.

Let’s start by looking at addMaterial method and specifically at the annotations. Notice that the POST annotation is used, meaning that this method will respond to HTTP POST method calls but only if the URI matches ‘/rest/materials/add’. Since the method returns a simple non-XML string the Produces annotation is set to ‘text/plain’. The Consumes annotation however is mandating that the material request be in XML format; therefore the calling client application needs to ensure that the material request information is wrapped within a XML document.

 

 

image

 

 

At this point things can get a bit tricky trying to test your REST code to respond to HTTP POST, PUT or DELETE methods; a browser can no longer be used to test these methods. Therefore depending on the day I alternate between either cURL (a simple command line utility for getting and sending information using URL syntax) or my favorite is POSTER (a Mozilla Firefox add-on)  to test REST services. After you have installed the add-on you will see Poster within the Tools menu.

 

 

image

 

 

Start POSTER and as a first step supply the URL (notice the suffix ‘/add’). Next make sure to select the POST action. Since the Consumes annotation mandates ‘application/xml’ make sure to also set the Content Type accordingly. Then finally add the XML material structure and press Submit.  

 

 

image

 

 

As a result you should see the response in plain text as returned by the REST method.

 

 

image

 

 

Note: Wonder why you are not seeing your newly added material when you try to execute the ‘/rest/materials’ URL? Since this is a simple test you can define the Singleton annotation class declaration.  This will ensure that there is one instance of the class. Please use this annotation with caution and for testing purposes only otherwise it will have ambiguous affects when you have multiple users accessing this REST web service.

 

 

Now that you have some familiarity with sending data let’s move on to the situation where you want to update data. As mentioned before REST uses the standard HTTP PUT method to accomplish updates. Notice the PUT annotation. Since the material id is critical to update the correct material you will notice the familiar looking {id} variable inside the Path annotation. In this case since the method returns void it’s not mandatory to use the Produces annotation. However, it is required to specify the mime type within the Consumes annotation.  As before to update any material the representative information needs to be passed as an XML document.

 

 

image

 

 

To test REST updates using POSTER, simply append the material id to the URL, select the PUT Action and finally assign the Content Type and add the material XML structure. In our example we are simply updating the material price.

 

 

image

 

The last method that we will be looking at is the Delete method. The process is similar to those described above; however notice the Delete annotation that will respond to any HTTP Delete methods assuming the URI matches ‘/rest/materials/?’

 

 

image

 

 

As before simply add the URL, select the Delete Action and Click Submit. You do not have to supply a XML document for the DELETE method; also notice there is no Consumes annotation.

 

 

image

 

 

Create the Java Client

 

Since cURL and POSTER are only really good for testing purposes I have included some information if you need to execute REST services using Java Code instead. Jersey not only provides capabilities to easily create server side RESTful services but you can also use the Jersey libraries to consume RESTful services. If you look closely at all the Jersey libraries you should see a jersey-client*.jar file; this file includes the Jersey Client files to help you to consume REST services. Notice the package and files that you need to add in your import statement.

 

 

image

 

 

Good luck and have fun building your REST services.

Assigned Tags

      11 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Paul Snyman
      Paul Snyman
      Hi Werner,

      Thanks for taking a step back and putting this together for us all, I like any simplification of form through function! Being fairly new to CE I'm looking forward to experimenting with this great info.

      Groete,
      Paul

      Author's profile photo Thomas Schnocklake
      Thomas Schnocklake
      Hi Werner,

      Thanks for this blog. I am certainally about to try it out soon.
      But the open question is the unavoidable Soap vs REST question.
      I did a blog on an Android client app using soap to connect to SAP, which is not directly supported by the android plattform, because REST seems to better in this context than SOAP. With your blog I realized how much easier is is to use Rest instead of SOAP.
      But on the other side SAP supports SOAP natively.
      So you are forced to use SOAP to connect directly or you have to implement a kind of middleware (like yours) for your specific project or for all Business Objects.
      Is there a intention at SAP to support REST directly?

      Thanks

      Thomas

      Author's profile photo Pierre Dominique
      Pierre Dominique
      Hi Thomas,

      Thanks to the ICF (Internet Communication Framework) you don't need necessarily need a middleware. DJ Adams wrote a couple of interesting blogs on the subject on SDN. This blog from Uwe Kunath is also very interesting : http://www.sdn.sap.com/irj/scn/weblogs;jsessionid=%28J2EE3417800%29ID1361435150DB10313724815857880283End?blog=/pub/wlg/14592

      I'll write a blog about Android and REST Web Services as soon as I have some time.

      Regards,

      Pierre

      Author's profile photo Thomas Schnocklake
      Thomas Schnocklake
      Hi Pierre,

      Yes, you don't have to have a middleware in means of installind/deploying another piece of software. But if I understand it right, you have to implement every REST Service in ABAP, so coding for each business object.
      So this means at the end, that you implement a custom service in the backend for each purpose/use case/client. So it is not so easy to use/reuse already existing services of the backend and just create a new client.

      Thomas

      Author's profile photo Pierre Dominique
      Pierre Dominique
      Hi Thomas,
      Yes, you have to implement each REST Web Service in ABAP. On the other hand, existing SOAP Web Services (Enterprise Services for instance) are usually way to complex to be used in mobile applications. So in the end, no matter the approach you choose, you'll probably have to develop you own Web Services.
      Regards,
      Pierre
      Author's profile photo Jan Penninkhof
      Jan Penninkhof
      Thanks for the very useful blog. I have given Jersey a try on my CE 7.2 system to see if I could build REST applications with it.

      For one of he resources I wanted to use MatrixParams to pass some extra parameters. It seems however, that the CE engine filters matrix parameters from the URL before it passes the request to Jersey/JAX-RS. Did anyone else spot the same behavior?

      Author's profile photo Former Member
      Former Member

      Hi,

      I am new to SAP CE, SAP, followed steps to build this service and invoke from client, have been consistently getting below warnings in Log Viewer:

      SC demo.sap.com MyComponents not known yet, can not get release level for vendor: 'demo.sap.com', name: 'rest/ear', scV: 'demo.sap.com', scN: 'MyComponents', location: 'localDevelopment', counter: '20130218154428', R: 'null', SP: 'null', PL: 'null', change number: 'null'
      Cannot delete the working directory for application [demo.sap.com/rest~ear] during update.
      Web Model Builder: C:\usr\sap\CE3\J00\j2ee\cluster\server0\temp\deploy\demo.sap.com~rest~ear.ear1361200435852\demo.sap.com~rest~web.extracted.war\WEB-INF\lib\jersey-server-1.6.jar#META-INF/taglib.tld(demo.sap.com~rest~web.war) is not Java EE 5 compatible, because of this annotations will not be processed.

      In Env:

       
      SAP NetWeaver Developer Studio

      SAP NetWeaver 7.3 SP07 PAT0007

      Tried with Jersey Archives of Versions 1.3, 1.4,1.5 1.6 1.17

      Request for help.

      Thaks and Regards,

      BS

      Author's profile photo David Raven
      David Raven

      Hi,

      I have 2 questions:

      1. I created a simple web service using EJB. I want to use it via ajax. Do I have to create the web service as restful?
      2.  Am I obliged to use CE ?

      If not, can you please tell me what are the steps to create a restful service?

      Thanks in adance.

      Regards.

      Author's profile photo David Raven
      David Raven

      Hi,

       

      I got this message error  when deploying "CSN component of deployment item is not available".

      Maybe you know what it means ?

      Thanks,

       

      Regards.

      Author's profile photo Marvin Mamaril
      Marvin Mamaril

      10 year later

      this document still useful.

       

      Thankyou

      Author's profile photo Lakshmi Narayana Kodavati
      Lakshmi Narayana Kodavati

      I tried this blog to build a REST api service for UME parameters return from Portal 7.3. service deployed with warning  but unable to call the service with URL.

      https://host/com.jnj~rest~umecontrolsweb/umecontrols

      how to find the correct URI for REST API.

      @Produces("application/xml")

      @Path("umecontrols")

      public class UMEControls {

      IUMParameters umeParm = UMFactory.getProperties();

      Properties prop = umeParm.getProperties();

      private ArrayList<UMEControlsData> umecontrols = new ArrayList<UMEControlsData>();

      public UMEControls(){

      UMEControlsData secData = new UMEControlsData();

      umecontrols.clear();

      secData.setAuto_unlock_time(prop.getProperty("ume.logon.security_policy.auto_unlock_time"));

      secData.setEnableInvokerServletGlobally(prop.getProperty("EnableInvokerServletGlobally" ));

      secData.setEnforce_policy_at_logon(prop.getProperty("ume.logon.security_policy.enforce_policy_at_logon"));

      secData.setHttponlycookie(prop.getProperty("ume.logon.httponlycookie"));

      secData.setLock_after_invalid_attempts(prop.getProperty("ume.logon.security_policy.lock_after_invalid_attempts"));

      secData.setOldpass_in_newpass_allowed(prop.getProperty("ume.logon.security_policy.oldpass_in_newpass_allowed"));

      secData.setPassword_alpha_numeric_required(prop.getProperty("ume.logon.security_policy.password_alpha_numeric_required"));

      secData.setPassword_change_allowed(prop.getProperty("ume.logon.security_policy.password_change_allowed"));

      secData.setPassword_expire_days(prop.getProperty("ume.logon.security_policy.password_expire_days"));

      secData.setPassword_history(prop.getProperty("ume.logon.security_policy.password_history"));

      secData.setPassword_impermissible(prop.getProperty("ume.logon.security_policy.password_impermissible"));

      secData.setPassword_last_change_date_default(prop.getProperty("ume.logon.security_policy.password_last_change_date_default"));

      secData.setPassword_max_idle_time(prop.getProperty("ume.logon.security_policy.password_max_idle_time"));

      secData.setPassword_max_length(prop.getProperty("ume.logon.security_policy.password_max_length"));

      secData.setPassword_min_length(prop.getProperty("ume.logon.security_policy.password_min_length"));

      secData.setPassword_mix_case_required(prop.getProperty("ume.logon.security_policy.password_mix_case_required"));

      secData.setPassword_special_char_required(prop.getProperty("ume.logon.security_policy.password_special_char_required"));

      secData.setPassword_successful_check_date_default(prop.getProperty("ume.logon.security_policy.password_successful_check_date_default"));

      secData.setUserid_digits(prop.getProperty("ume.logon.security_policy.userid_digits"));

      secData.setUserid_in_password_allowed(prop.getProperty("ume.logon.security_policy.userid_in_password_allowed"));

      secData.setUserid_lowercase(prop.getProperty("ume.logon.security_policy.userid_lowercase"));

      secData.setUseridmaxlength(prop.getProperty("ume.logon.security_policy.useridmaxlength"));

      secData.setUseridminlength(prop.getProperty("ume.logon.security_policy.useridminlength"));

      secData.setUserpwd_automatic_logon(prop.getProperty("ume.logon.userpwd_automatic_logon"));

      umecontrols.add(secData);

      }

      @GET

      public List<UMEControlsData> getUMEControls(){

      return umecontrols;

      }

      }

       

       

       

       

      WEB.xml

      <?xml version="1.0" encoding="UTF-8"?>

      <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">

      <display-name>LocalDevelopment~umecontrolsweb~com.jnj</display-name>

      <servlet>

      <servlet-name>Jersey REST Application</servlet-name>

      <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>

      <init-param>

      <param-name>com.sun.jersey.config.property.packages</param-name>

      <param-value>com.jnj</param-value>

      </init-param>

      <load-on-startup>1</load-on-startup>

      </servlet>

      <servlet-mapping>

      <servlet-name>Jersey REST Application</servlet-name>

      <url-pattern>/rest/*</url-pattern>

      </servlet-mapping>

      <welcome-file-list>

      <welcome-file>index.html</welcome-file>

      <welcome-file>index.htm</welcome-file>

      <welcome-file>index.jsp</welcome-file>

      <welcome-file>default.html</welcome-file>

      <welcome-file>default.htm</welcome-file>

      <welcome-file>default.jsp</welcome-file>

      </welcome-file-list>

      </web-app>