Sunday 7 March 2010

RESTful Web Services with Jersey

I've been applying Jersey more and more often in projects at work so I thought this was a good time to write a tutorial on it..

Recently I posted a couple of articles on SOAP web services. Here, I discuss the Representational State Transfer (REST) architecture as an alternative to SOAP and WSDL. REST has gained widespread acceptance across the web as being an easier-to-use, resource-oriented model to expose your services.

Although there is room for overlap, SOAP is mostly useful for invoking behavior (as you might have already noticed on one of my previous posts) while REST is good for managing information. Probably the most interesting aspect of the REST vs. SOAP debate is the security topic, while SOAP sends remote procedure calls (RPC) through standard HTTP ports, REST calls go over HTTP or HTTPS. In general, sensitive data should not be sent as parameters in URIs, also large amounts of data such as in purchase orders can become cumbersome or even out of bounds within a URI. I will not engage in a SOAP vs REST discussion here (Its way beyond the scope of this article), instead I will go through the basics of creating a REST web service API using SUN's JAX-RS implementation for Java: Jersey. If you feel like doing some reading on this topic, I recommend the article on SOAP or REST? it's about your priorities! and also Giving SOAP a REST.

First of all, lets all be clear that Jersey is not the only JAX-RS implementation out there. Other implementations by different vendors include: RESTEasy (from JBoss), CXF (from Apache), Restlet, etc. You can find a detailed discussion on this topic at the SOA Community Blog

1. Background (in a nutshell):
JAX-RS is an annotation-based API for implementing RESTful web services. Jersey is the JAX-RS reference implementation from Sun. Because REST uses HTTP as the communication protocol, the REST style is constrained to a stateless client/server architecture.

2. Requirements:
• Java 5 or above (because of its heavy reliance on annotations).
• Jersey JARs. Download Jersey's archives from the Jersey Project site and add them to your web project library. Here, I'm using asm-3.1.jar, jersey-core.jar, jersey-server.jar, jsr-311-api-1.0.jar

3. Environment Set-Up:

Declare Jersey's framework Servlet inside the web.xml

<servlet>
  <servlet-name>JerseyController</servlet-name>
    <servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer
    </servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>JerseyController</servlet-name >
  <url-pattern>/*</url-pattern>
</servlet-mapping>

Jersey uses a Servlet called Grizzly Servlet, and the servlet (com.sun.jersey.spi.container.servlet.ServletContainer) handles the requests to Grizzly.

4. Implementation:
In its most simplest form, Jersey requires no more than a couple of annotations to build a trivial service:

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

@Path ("/printname")
public class PrintNameResource {

 @GET
 @Produces ("text/plain")
 public String printName() {
  return "Hi Tulio Domingos";
 }
}

@Path
Indicates what URI path is mapped to the resource. For example, you would access this class using the following URL: http://localhost:8080/<context>/printname
@GET
JAX-RS provides a clear mapping between the HTTP protocol and the URIs through well-defined classes and interfaces. @GET, @POST, @DELETE, etc.
The type of HTTP method request dictates which request method designator is called. For example, if the request is from a HTTP GET request (above), the service automatically invokes a method annotated with @GET and provides the response.
@Produces
This annotation specifies the MIME type for the response (a representation that can be produced by a resource and sent back to the client)

Above, I illustrate the use of 3 basic JAX-RS annotations. In case you feel the need to have two or more methods handling HTTP GET requests, then make sure you provide a path (@Path annotation) for each method so they don't clash.

The above sample code, as you might have guessed, is useless as I have hard coded the response to client. Consider a scenario where the server constructs the response based on a URI query parameter 'name'.
i.e. http://localhost:8080/<context>/printname?name=tulio

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

@Path ("/printname")
public class PrintNameResource {

 @GET
 @Produces ("text/plain")
 public String printName(@QueryParam("name") String name) {
  return "Hi " + name;
 }
}

Alternatively, you can make use of relative URI paths. As discussed earlier, you identify a root resource class using the @Path annotation. The value of the annotation can have a relative URI path template between curly braces {,}, with the deployment context providing the reference to the base URI. Note as well you use it in conjunction with the @PathParam as opposed to the @QueryParam (above)In this case, the URI would look more like:
i.e. http://localhost:8080/<context>/printname/tulio

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

@Path ("/printname/{name}")
public class PrintNameResource {

 @GET
 @Produces ("text/plain")
 public String printName(@PathParam("name") String name) {
  return "Hi " + name;
 }
}

Change the @Produces annotations to specify a MIME type for the response that suits your needs (HTML, XML, JSON, Image, etc). Resource classes can produce or consume any type of MIME. Use annotations to specify the MIME media type for both request and response on a resource class or resource method.

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

@Path ("/printname")
public class PrintNameResource {

 @GET
 @Produces("text/xml")
 public String printName(@QueryParam("name") String name) {
   return "<msg>Hi " + name + "</msg>";
 }
}

One of the coolest features of JAX-RS/Jersey is the automatic creation of XML and JSON via JAXB annotations! That's right, no further coding is required, below is an example of how easily a POJO can be turned into a REST service, simply by adding a JAXB annotation:

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

@Path ("/message")
public class PrintNameResource {
 @GET
 @Produces("text/xml")
 public Message getMessage(@QueryParam("name") String name) {
   Message message = new Message();
   message.setContent("Hi " + name);
   return message;
 }
}

And the message bean would look something like:

@XmlRootElement
public class Message{
  private String content;
  public String getContent(){
   reuturn content;
  }
  public void setContent(String c){
   this.content = c;
  }
}

JAXB is often associated with web services. With JAXB you can generate XML from either an XSD or your own POJO, also you're free to manually marshal them may you find it more convenient.

5. HttpSessions in Jersey:
Feel the need to access the session object in Jersey? Below is an example on just how you can achieve this:

@GET
@Path("/getsession")
@Produces(MediaType.APPLICATION_JSON)
@Consumes("application/x-www-form-urlencoded")
public String getSession(@Context HttpServletRequest req){
  String jsessionid = req.getSession().getId();
  logger.info("JSESSIONID is: " + jsessionid);
  return jsessionid;
}

Alternatively, you can annotate your resource class with the @PerSession annotation so that a new instance of the resource class gets created for each session. This instance will remain servicing the client for as long as the session lives.

Refer to its javadoc for more information.

6. Jersey's support for JSON:
Jersey added support for JSON by including the library jettison-1.0.jar in its bundle. Add this library to the build path of your project and change the @Produces annotation to something like @Produces("application/json"). Make sure to use it in conjunction with JAXB annotation (above).

Note: If you need to append bytes to the response, whether its in JSON, XML, or any other text format, then use some form of encoding such as BASE64. Byte values would turn into illegal characters when converted to string. The same happens in a SOAP message.
According to the JSON documentation, JSON supports any UNICODE character except (1) control characters, (2) \ and (3) "

7. Jersey's support for Spring:
Follow the steps below in order to integrate Spring with Jersey:

• Download and add the Spring JARs to the build path of your project

• Include Jersey's Spring library: jersey-spring.jar

• Also add antlr version 3: antlr-3.0.jar. This is important! If you have the wrong antlr library, you might face errors of type: Context initialization failed. java.lang.NoSuchFieldError: ruleMemo

• Configure Jersey's Spring servlet in your web.xml along with its respective init-parameters: com.sun.jersey.config.property.resourceConfigClass and com.sun.jersey.api.core.PackagesResourceConfig

<servlet>
  <servlet-name>Jersey Spring</servlet-name>
    <servlet-class>
com.sun.jersey.spi.spring.container.servlet.SpringServlet
    </servlet-class>
    <init-param>
       <param-name>
com.sun.jersey.config.property.resourceConfigClass
       </param-name>
      <param-value>
com.sun.jersey.api.core.PackagesResourceConfig
      </param-value>
    </init-param>
    <init-param>
      <param-name>
com.sun.jersey.config.property.packages
      </param-name>
    <param-value>
com.tcm.appstore.ws.resources
    </param-value>
    </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>Jersey Spring</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping>


• Create the Spring application context file (spring-context.xml) as you would normally in Spring and specify it in the web.xml via a context parameter:

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/spring-context.xml</param-value>
</context-param>

• Last but not least, register the Spring context loader listener also in the web.xml:

<listener>
  <listener-class>
    org.springframework.web.context.ContextLoaderListener
  </listener-class>
</listener>

• An interesting feature of Jersey Spring, is its dependency injection mechanism through the use of annotations. This feature enables the injection of a Spring bean into you application using the @Inject annotation. Below is an example of such feature (assume the message bean is declared in the spring-context.xml):

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import com.sun.jersey.spi.inject.Inject;

@Path ("/message")
public class PrintNameResource {

 @Inject
 Message message;

 @GET
 @Produces("text/xml")
 public Message getMessage() {  
  return message;
 }
}

Please bear in mind that all code samples outlined in this article were not tested, as a result, syntax mistakes might have occurred at some point down the line. Any form of feedback is welcome.

6. Further Reading:
http://www.javaworld.com/javaworld/jw-10-2008/jw-10-rest-series-1.html
http://www.devx.com/Java/Article/42873/1954

Sunday 21 February 2010

BCLService - Accessing the web service with a generated client

On my previous post, I explained how you can expose your existing java API as a SOAP web sevice using Apache Axis2. I went through the steps of creating a web service called "BCLService"...Now here's the sequel, we are going to look into how we can interact with the service using a generated client.

This is a step by step guide to interacting with the BCLService using a client based on generated code. This guide uses Axis 2 as an implementation of the SOAP protocol. Axis is a framework for constructing SOAP clients and servers.

1. Requirements:
JDK 1.5 (or latest)
Axis 2 1.4 (or latest)

You can download the latest release of Axis2 from the Apache download site

Apache provides a tool in the Axis2 distribution (WSDL2Java) to generate client side stubs that interacts with the service (You can find WSD2Java in "org.apache.axis.wsdl.WSDL2Java").

2. Generating client stubs using the Apache WSDL2Java tool: Client programs need only to know the EPR and the operation name (both listed on the WSDL) in order to interact with your web service.

In order to generate the stubs, run the following command:

•[Linux]
$ sh WSDL2Java.sh -uri http://:/axis2/services/?wsdl -o /path/to/your/client/code/
•[Windows]
$ WSDL2Java.bat -uri http://:/axis2/services/?wsdl -o /path/to/your/client/code/
-uri Specifies the location of the WSDL file. In other terms, the Endpoint Reference (EPR) of the service with the “?wsdl” argument appended in the end.
-o Specifies the output path for the tool, in other words, the location where you want WSD2Java to generate the client code, Note that you need not to specify the source folder (/src) in the parameter as the source folder will already be added by WSDL2Java

The above command generates 2 java classes. Now, all you need to do is to create a simple class (i.e. Client.java) which uses the stub to call the service.


3. Calling the Web Service: The following is a sample client class code used to interact with the web service.
Make sure to add all the Axis JARs that the stubs refer to (JARs can be found under AXIS_HOME/axis2-1.4/lib).


public class Client {
  public static void main(String[] args)
                           throws RemoteException {
    MyServiceStub stub = new MyServiceStub();

    /* Invoking the GetCurrentPosition service */

    //Create the request
    MyServiceStub.GetCurrentPosition request =
                 new MyServiceStub.GetCurrentPosition ();

    /* if the method doesn’t take parameters, then just
     skip the request object and instantiate the reponse
     object directly by calling the method on the stub */
    request.setClubName("flamengo");

    GetCurrentPosition Response response =
                 stub.GetCurrentPosition(request);

    System.out.println("Web Service returned : " +
                               response.get_return());
  }
}

Now you can compile and run the client code. Its is important to note that the classpath must have all the jars in the "lib" directory of the axis2 distribution when compiling and running the client.

Saturday 13 February 2010

SOAP Web Services made easy with Axis2

Apache Axis2 provides utilities for generating and deploying web service client and servers. I'm not going to get into the details of its architecture or its improvements compared to earlier versions of Axis. You can visit Axis2 home page for a detailed description.

This tutorial takes you through the necessary steps required to expose your existing classes as a web service using Axis2 for Java.

Requirements: First download Axis2 from the Apache website and install it according to the "installation.txt" found inside the package.

Set AXIS2_HOME to your environment variables and also "%AXIS2_HOME%/bin" (include the bin directory) to your PATH

Scenario: You have a simple bean that provides a service to a client application (i.e. The client gives the name of a football team in the Brazilian cup league and the server responds with the team's current position in the league) This is done by invoking the "getCurrentPosition(String team)" of the class "BCLService.java"

Exposing your class as a web service: Actually, what you expose are the methods of the class and not the class itself. You can expose the "getCurrentPosition(String team)" method to the client by simply making it available in a configuration file.

1. Configuration: Create a simple XML file "service.xml" which will inform Axis2 about its services. The following is a sample "service.xml" for our "BCLService" class:

<service>
  <parameter name="ServiceClass" locked="false">
    com.xyz.BCLService
  </parameter>
  <operation name="getCurrentPostion">
    <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver" />
  </operation>
</service>

Note: The value of the xml element <parameter> must be the full qualified name your java class.
All public methods of your declared class will be exposed by default. If you wish not to expose a method, then use the <excludeOperation> element.
The "locked" attribute of element <parameter> is set to false so that any child node parameter can override it.
The "name" attribute of element <operation> must be the name of the method you wish to expose in the web service (you can add as many as you want, however, Axis2 does not support overloaded methods! Operation names must be unique!)


2. Packaging: Axis2 expects its services to be packaged according to a specific format:
2.1 Create a "tmp" dir (just for packaging purpose)
2.2 Compile your "BCLService.java" class (javac BCLService.java) and move it to the "tmp" directory
2.3 Create a "META-INF" directory inside the "tmp" directory and place the "service.xml" config file inside it.
2.4 Finally, in order to package everything into an axis archive (aar), run the following command from the "tmp" dir level:

Jar cvf BCLService.aar .

Jar The jar command generates an archive file in the same directory
cvf The cvf option indicates that you want to create a JAR file in verbose mode and that the output should go to a file rather than to stdout. For more information on creating jar files, visit SUN's Tutorial "Packaging Programs in JAR"
BCLService.aar Is actually a jar, just renamed to "aar" (axis archive) to distinguish from other jars – note that this is optional
. Package everything in the current directory


3. Hosting the Service: Below are the 2 most popular ways of hosting the service:

* Using the Simple HTTP Server that comes in Axis2 distribution
* Use Axis2 to generate a WAR file and deploy it in a servlet container of your choice. (i.e. Tomcat)

The following steps will take you through the 2nd option (create WAR from AAR):

3.1 Place your newly created "BCLService.aar" in the following directory:
%AXIS2_HOME%/repository/services
3.2 Execute target "create.war" from the ant build file at:
%AXIS2_HOME%/webapps/build.xml
i.e. run command: ant create.war
3.3 Find the generated "axis2.war" inside:
%AXIS2_HOME%/dist
3.4 If you’re using Tomcat as your servlet container, drop the WAR file in the "webapps" dir of Tomcat, then start the server.

4. Running the web service application: To verify the deployment go to: http://<HOST>:<PORT>/axis2/
Follow the "services" link and check that the "getCurrentPosition(String name)" operation is available from the list of operations.

It will also list the endpoint reference URL (EPR) but you will not be able to access it from the browser since Axis2 doesn’t expect input from the browser.

Click on the "BCLService" service link to view its WSDL file. Clients can now simply look up your WSDL to find out what operations your service exposes in order to call them.

Note that Axis2 generated the WSDL file for you, however, you are also free to create one or your own, just drop your own WSDL file (i.e. BCLService.wsdl) in the META-INF directory of the Axis Archive (AAR).

Check out my post on BCLService - Accessing the web service with a generated client for steps on how you can interact with the web service.

-----------------------------------
Axis2 vs CXF

Both are web service frameworks under the Apache umbrella of but what is it that differentiate them from one another?

Axis2

1. Axis2 evolved from the well know Axis 1.
2. The Axis concept is more of a stand alone web service (separate webapp), independent of other applications with its own architecture.
3. Suitable for larger web services, most configuration done via XML files.
4. Packages its own archive: AAR
5. When you build a service in Axis2, it's default behaviour is that you can access it using SOAP or REST request. Axis2 will look at the request "content-type" header and figure out whether to process the request as SOAP or REST.

CXF

1. CXF is the result of a merge between Celtix and XFire.
2. You can easily embed CXF into your application, it has good Spring integration, no extra XML configuration files.
3. Packaged in a WAR archive.
4. You can use JAX-RS annotations to build RESTfull services just like Jersey but needs extra config file "cxf.xml".