Skip to Content

RESTful Web services emerged as an easy to use alternative to the SOAP and WSDL style Web services. Due to their simplicity RESTful Web services continue the gain high adoption among many Web 2.0 service providers.


SAP NetWeaver CE 7.20 does not provide out of the box support for REST services, but many 3dt party tools can be used for developing and deploying such services on SAP NetWeaver.


This article will show how to start developing RESTful Web services using SAP NetWeaver CE and Apache CXF.


Apache CXF is a popular open source services framework. Among the many other supported standards is JAX-RS (Java API for RESTful Web Services) that standardizes the way RESTful services can be developed in Java.


JAX-RS Software Component


Create a JAX-RS Software Component


To be able to develop and execute RESTful Web services you need to create a new Software Component that wraps the CXF JAX-RS implementation.

  1. Open the Development Infrastructure perspective, select LocalDevelopment in the Component Browser and create a new Software Component. Use JAX-RS as value for the Name and the Relative Root fields. On the second page of the wizard, select only SAP_BUILDT as a dependency and click Finish. The new SC will contain one “description” development component that you can ignore
  2. In the newly created SC, create the following two Development Components:
    • External Library: jax-rs/lib
    • Enterprise Application: jax-rs/ear
      • The External Library will contain all required CXF libraries, and the Enterprise Application will package the library for deployment and references
  3. Download the latest Apache CXF release and extract it to a folder of your choice. Since the CXF distribution comes with many libraries that are not required by the REST use-case, the libraries used in this article are just a subset that provides JAX-RS support.
    • Copy the following jar files from the modules folder of the CXF distribution into the libraries folder of the jax-rs/lib project (note that the versions strings may differ depending on the actual CXF version)
      • cxf-api-2.2.9.jar 0.2.
      • cxf-common-utilities-2.2.9.jar
      • cxf-rt-bindings-xml-2.2.9.jar
      • cxf-rt-core-2.2.9.jar
      • cxf-rt-frontend-jaxrs-2.2.9.jar
      • cxf-rt-transports-http-2.2.9.jar
    • Copy the following jars from the libfolder of the CXF distribution into the libraries folder of the jax-rs/lib project:
      • jettison-1.2.jar
      • jsr311-api-1.0.jar
      • wsdl4j-1.6.2.jar
      • wstx-asl-3.2.9.jar  
  4. The next step is to configure the public parts and the dependencies between the two development components.
    • Open the Component Properties view, select the jax-rs/lib project and configure the public parts’ contents:
      Public Part Entities
      api jsr311-api-1.0.jar
      archives *(all jar files)

    • Configure the jax-rs/lib DC permissions:
      • Authorized Component: JAX-RS
      • Entity Forwarding Allowed: Yes
    • Select the jax-rs/ear project and add jax-rs/lib to the required DCs
    • In jax-rs/ear create a compilation public part api that references the same public part from jax-rs/lib
    • Allow unrestricted access to the jax-rs/ear component
  5. Add dependency from the LocalDevelopment Software Component to JAX-RS

Export the JAX-RS Software Component for future reuse (optional)


This step is optional if you don’t plan to reuse the JAX-RS software component.

Open the Development Infrastructure perspective, select the JAX-RS SC and choose Export… from the context menu. Choose to build all three contained DCs and click Next. On the second page of the wizard select the target file name and location.

Import the JAX-RS Software Component (optional)

This step is optional if you are not reusing the JAX-RS software component.

Open the Development Infrastructure perspective, select your development configuration and choose Import SC from the context menu.

A sample RESTful service

Develop a RESTful service

After having the JAX-RS libraries in the workspace, the development of a RESTfull service is relatively easy.

Create a Web Application DC and a wrapping Enterprise Application DC. In both DCs add dependency to jax-rs/ear from the JAX-RS SC.Accept all proposed settings.

The application will contain two classes – one data object and one service. Bellow you can find the sample source code.

The data object:

package com.mycompany.rest;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "Customer")
public class CustomerData {
     private String id;
     private String name;
     public CustomerData() {
     }
     public CustomerData(String id, String name) {
          super();
          this.id = id;
          this.name = name;
     }
     public String getId() {
          return id;
     }
     public void setId(String id) {
          this.id = id;
     }
     public String getName() {
          return name;
     }
     public void setName(String name) {
          this.name = name;
     }
}

The service:

package com.mycompany.rest;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.core.Response.Status;
@Path("/customer")
public class CustomerService {
     private Map<String, CustomerData> customers;
     public CustomerService() {
          customers = new HashMap<String, CustomerData>();
     }
     @GET
     @Path("{id}")
     @Produces({"text/xml", "application/json"})
     public Response getCustomer(@PathParam("id") String id) {
          CustomerData customer = customers.get(id);
          if (customer != null) {
               return Response.ok().entity(customer).build();
          }
          return Response.status(Status.NOT_FOUND).entity("Customer with id " + id + " was not found!").build();
     }
     @POST
     @Consumes({"application/json", "text/xml"})
     public Response createCustomer(@Context UriInfo uriInfo, CustomerData customer) {
          String id = String.valueOf(System.currentTimeMillis());
          customer.setId(id);
          customers.put(id, customer);
          URI uri = uriInfo.getRequestUriBuilder().path(id).build();
          return Response.created(uri).build();
     }
     @PUT
     @Path("{id}")
     @Consumes({"application/json", "text/xml"})
     public Response updateCustomer(@PathParam("id") String id, CustomerData customer) {
          CustomerData existingCustomer = customers.get(id);
          if (existingCustomer != null) {
               existingCustomer.setName(customer.getName());
               return Response.noContent().build();
          }
          return Response.status(Status.NOT_FOUND).entity("Customer with id " + id + " was not found!").build();
     }
     @DELETE
     @Path("{id}")
     public Response deleteCustomer(@PathParam("id") String id) {
          CustomerData customer = customers.remove(id);
          if (customer != null) {
               return Response.noContent().build();
          }
          return Response.status(Status.NOT_FOUND).entity("Customer with id " + id + " was not found!").build();
     }
}


The last step before deployment is to configure the CXF servlet instance that will intercept all RESTfull calls to your application. Open the web deployment descriptor web.xml and configure Apache CXF for your application. Add the entries bellow after the welcome-file-list section.

<servlet>
     <servlet-name>RestfulApp</servlet-name>
     <servlet-class>org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet</servlet
     <!-- Specify here all REST services classes. Multiple entries can be separated with space.
     <init-param>
          <param-name>jaxrs.serviceClasses</param-name>
          <param-value>com.mycompany.rest.CustomerService</param-value>
     </init-param>
     <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
     <servlet-name>RestfulApp</servlet-name>
     <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

Deployment

Before deploying the RESTfull application ensure that jax-rs/ear is deployed on your server.

Execution

The application can be tested with any of the REST clients around today or you can even write your own.

There are even add-ons and plug-ins for some web browsers.

Bellow you can see screenshots from the execution using “Simple REST Client” (see Resources section).

A key point for any RESTful service is its’ resource URL – it uniquely identifies the underlying entity and is used for all entity operations except create. Let’s take a look at a sample resource URL:


http://myserver:50000/vendor~appname/rest/customer/123

Here http://myserver:50000/vendor~appname/+ is the URL of the web application and usually depends on the web development component vendor and name.

rest” is the path inside the application which is assigned to the REST servlet. This value was specified in the web.xml deployment descriptor above – see the servlet-name section

customer” is the name of the RESTful service as specified by the @Path annotation in the CustomerService class.

The remaining part of the URL constitutes the unique resource identifier.

Create a new entity

Entity creation in REST is mapped to HTTP POST. To create an entity instance, you specify POST for HTTP method, “Content-Type: application/json” and supply the actual message data (in this case in JSON format) – {Customer: {name: “John Smith”}}.

After a successful execution, the server will return HTTP status 201 CREATED and an HTTP header named location. The value of this header is a direct link to the entity – it is actually the POST method url with the addition of the entity key (the customer’s id in this case).

Create entity    

Read entity

Reading an entity in REST corresponds to the HTTP GET method. The location header value from the previous step is used to for reading the existing entity. The data format depends on the Accept HTTP header – in the example bellow text/xml is used and data is returned as xml. To use JSON instead, set the header value to application/json.

Read entity    

Update entity

HTTP PUT is used to update an existing entity. Set the “Content-Type” HTTP header to indicate the data format and fill in the data itself. Use HTTP GET to check the update operation results.

Update entity    

Delete entity

Deleting an entity is simple – just use HTTP DELETE

Delete entity

Disclaimer
  • SAP does support customer application containing 3rd-party / open source components.
  • SAP does not fix bugs in the actual 3rd-party / open source components.
  • SAP does care about the integration 3rd-party / open source components with NW / CE and will typically consider fixing integration bugs on the SAP side. However, this need to be evaluated case by case and the customer must accept workaround or limitations.

Tsvetan Stoyanov is a senior developer in the area of SOA Foundation.


To report this post you need to login first.

5 Comments

You must be Logged on to comment or reply to a post.

  1. Daniel Koller
    additionally it would be interesting to see a technical comparison w/ DJ Adams ( @qmacro’s approach written also here in SDN at A new REST handler / dispatcher for the ICF ).

    The most important difference seemed to be for me that DJ accesses directly SAP Business Objects from the ABAP stack: if I see it right, you would have to repeat the business object access logic in Java in order to be able to get them from the ABAP side.

    (For me it was also nice to see a description on how to include open source components to SAP CE deployment)

    Daniel

    (0) 
    1. Tsvetan Stoyanov Post author
      Hi Daniel,

      You are right – all the business logic is not part of the story. It should be repeated or written from scratch in Java.
      This is just a core REST provisioning for Java.

      Tsvetan

      (0) 
  2. Jan Penninkhof
    Thanks for the very useful blow. I thought I would give this a try and see if I can 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 passese the request to JAX-RS. Did anyone else spot the same behavior?

    (0) 
  3. Daniel Graversen

    Hi

    It seems like it still works. Though files have changed in the lib. I have added the following liberies. I did have some issues finding eh JSON provider but now it works.

    Screen Shot 2015-01-06 at 6.01.43 PM.png

    (0) 

Leave a Reply