Thursday, May 06, 2010

How-to: Building REST/JSON services with OSB 11g and JAX-RS

Last couple of weeks, I have read a lot of blog postings about REST services in combination with the Oracle Service Bus (like this posting). I've even made a first attempt to write a posting about this subject, but I got really constructive comments in return on that posting that I decided to write a complete new one. Thanks for the comments :)

I decided to play around with REST and OSB myself and choosed for JAX-RS (Jersey) as the Java technology to build my REST service and the brand new OSB 11g release for proxing this REST service. In this posting, I will show you how easy it is to build a REST/JSON service with the feature rich and highly flexible JAX-RS standard, deploy it to WebLogic 10.3.3 and proxy it with an OSB 11g service.

REST service in JAX-RS
Required libraries (can be downloaded from here):

  • jersey-bundle-1.1.5.1.jar

  • jsr113-api-1.1.jar

  • asm-3.1.jar


I build in JDeveloper a very simple REST product service with a single method to find a product by its id. The REST method returns a JSON representation of the product.

First you have to create a new web application project in JDeveloper, because the JAX-RS product service is deployed as a web application to the WebLogic Server. In the web.xml file the following things has to be specified:

  • the JAX-RS servlet which will handle all requests and forward them to the appropriate REST Service class.

  • The context path to access the servlet

  • The mime type application/json


The web.xml has the following content:



xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
web.xml for

10


html
text/html


json
application/json


txt
text/plain


JAX-RS Servlet
jersey-servlet
com.sun.jersey.spi.container.servlet.ServletContainer

com.sun.jersey.config.property.resourceConfigClass
com.sun.jersey.api.core.PackagesResourceConfig


com.sun.jersey.config.property.packages
nl.oracle.com.fmw.rest;nl.oracle.com.fmw.rest.model



jersey-servlet
/*



Now the web.xml file is in place, two Java classes have to be implemented:

  • ProductResource

  • Product


The ProductResource class will contain the method to find a product by its id:

package nl.oracle.com.fmw.rest;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import nl.oracle.com.fmw.rest.model.Product;

@Path("/products")
public class ProductResource {

@GET
@Path("{id}")
@Produces("application/json")
public Product getProductById(@PathParam("id") int id){
//Return a simple new product with the provided id
return new Product(id, "DummyProduct");
}
}


The code listing above shows that that getProductById method is only accessible through the HTTP GET method. Also the use of the @Path annotation on the class and method level makes it possible to set a specific relative-uri with which the service can be accessed. The {id} serves as a placeholder for the product id and can be accessed through @PathParam annotation

The Product class is shown in the following code listing and uses the JAXB XmlRootElement binding annotation to automatically map the class structure to a JSON structure. Isn't that cool :). More JSON serialization and deserialization options in JAX-RS can be found here

package nl.oracle.com.fmw.rest.model;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Product {

private int id;
private String name;

public Product(){

}

public Product(int id, String name) {
this.id=id;
this.name=name;
}

public void setId(int id) {
this.id = id;
}

public int getId() {
return id;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

Coding is completed and it should be clear now that the use of annotations makes it very easy and flexible to create REST services with JAX-RS. Create a WAR deployment descriptor and make sure you add the jersey libraries to the WAR file and set the JEE web context root to services. Finally, deploy the WAR file to the WebLogic 10.3.3 server. I target the application to the osb_server1 managed server, which hosts my OSB 11g installation.

After deployment you can test the REST service using this URL where I use 1 as the id:

http://localhost:port/services/products/1


OSB 11g proxy
I used the brand new OSB 11g installation for creating a proxy service for my REST service.

The implementation is fairly simple and straight forward and follows more or less the same steps used in this excellent posting:

Business Service
The business service invokes the REST product service. Make sure you use the messaging service type with the HTTP transport protocol. Also set the HTTP method to GET.

Proxy Service
Create a proxy service in Eclipse and use the messaging service type with the HTTP transport protocol. I used request type none and Response type text. In the message flow I added a routing action to invoke the business service.
I need to mention two important things about the my Proxy service implementation

  • Set the endpoint /osb-services/products. This enables you to add anything to this context path, for example /{id}. This makes it also possible to use the OSB service as a proxy for different types of REST calls (make sure you can switch between HTTP methods in the message flow) to the same product service

  • Use the transport request element from the $inbound variable to append the relative path after /osb-services/products to the REST service endpoint. I've used an insert operation for this:





Deploy the OSB service to the OSB 11g server and use the test console to test the service. Make sure you set the relative-uri attribute in the test console transport panel:



The $body element in the response message should look like this:



The OSB service just passes the JSON response forward to the client. It is fairly simple to convert the JSON output to XML and vice versa using JSON lib in a Java service callout. How to do this is described in this posting.

Also the JSON structure returned by the product service is very simple. In most cases you have to do more work with JAX-RS in order to construct the required JSON structure. The JAX-RS libraries contain options for configuring JSON

6 comments:

Unknown said...

Cool Sample - Thanks.
As I coming from a NetBeans background, I hope JDev will soon add support for creating REST based web service in a developer friendly way, "à la" NetBeans, i.e. a nice wizard and the plumbing code being generated for you. See http://netbeans.org/kb/docs/websvc/rest.html

It is time to cross-polenize !

Unknown said...

Nice, looks better this time.
The great thing about rest is that it's just http. So you could even implement your proxy using a reversed proxy like squid or apache. No need for an esb. And http has great caching support for the get method, so you get let squid cache all your data. Better for performance, might even help you avoid the need for a data grid.

Actually, on my current project (glassfish 3 + servicemix/camel) we're thinking about implementing all synchronous services (called by webpages) as rest services, so we get free caching for all read-only data.

Regards,
Andrej

Tom Hofte said...

Andrej,

an esb proxy will probably introduce more overhead compared to a plain http proxy server with caching enabled..

But how easy can it be scaled out in terms of data consistency when you want to access mutable data; how does the data synchronize with the back-end system in case of data changes?

Anyway thanks for your feedback! Success with with your current project. Sounds interesting.

Gerard Davison said...

Hey,

I own the JDeveloper REST functionality, it was in PS1 and updated in PS2 and I notice you didn't use any of it in your blog posting to generate the required deployment descriptors.

Did you have problems with the features? Did you try out the HTTP Analyzer against the REST services? Please get in touch.

Thanks

Gerard

Gerard.Davison@oracle.com

Anonymous said...

This is the best starting for OSB Proxy to invoke Rest Service..

Thank you so much

Anonymous said...

Good One...