JIM HUNTER Computer Software & Web Application Development

Three different versions:

For a Web Service demo I decided to implement a few methods that could access the data that I use for my Message Editor application. The Message Editor application accesses text messages which are stored in a Java properties file. Other data used by the Message Editor is stored within an XML file. I decided that my Web Service demo could contain a method to retrieve a specific message by a key, a method to retrieve a list of message keys by category, and a method to retrieve a list of categories. These methods can be summarized by this Java interface:

public interface MessageDemo extends java.rmi.Remote {

    public String getMessage(String messageKey)
	    throws java.rmi.RemoteException;
    
    public String[] getMessageKeysByCategory(String categoryName) 
	    throws java.rmi.RemoteException;

    public String[] getCategoryNames()
	    throws java.rmi.RemoteException;
}

I have implemented my web service in two different ways, using two different languages, Perl and Java. The reason for the two different implementations is more the result of the development of server technology, than anything else. Originally, my web service demo was deployed on a server host which I had little control of. I could not deploy a JEE web application in the usual way using a web.xml descriptor under the usual WEB-INF directory, and I could not restart the server. I could only upload my files (HTML files, JSP files, Servlets, and other Java class files, etc.).

So the reason behind my Perl implementation was, then, out of necessity, a way around these problems of deployment. I discovered a Perl module which implements the SOAP specification which I could use to deploy a web service without having to restart the web server. The SOAP::Lite module, available from CPAN.org, enables the deployment of a web service by way of a CGI proxy. Perl CGI scripts are interpreted at runtime and do not need any configuration information to be loaded at server startup. So, as long as you can run Perl CGI scripts, you can deploy a web service. So the SOAP::Lite module gave me the work-around solution to deploy my web service.

Eventually, I moved my web site over to my current host, hostjava.net, which provides virtual hosting capabilities. In a virtual host environment, the user can deploy a JEE web application using a web.xml descriptor in the traditional convention, and even restart the server, if necessary. So, on hostjava.net I have been able to deploy a version of my web service using Axis2 - which is what I wanted to do originally.

So now I've use both Perl and Java in the creation of a web service. I'll briefly discuss both implementations here.


First, the Perl implementation:

The SOAP::Lite module does most of the heavy lifting. Consider this example:

#!/usr/bin/perl

use SOAP::Transport::HTTP;

SOAP::Transport::HTTP::CGI   
-> dispatch_to('demo')     
-> handle;

package demo;
    ... 
1;
These few lines of code allow a Web Service client to access the subroutines defined within the demo module using the SOAP protocol over HTTP. Of course the client needs to know what those subroutines look like, but the cool thing here is how simple and easy to use the SOAP::Lite module can be. My Web Service demo is named MessageDemo.cgi. The service address is http://hunterj.hostjava.net/cgi-bin/MessageDemo.cgi. MessageDemo.cgi serves as a proxy that can dispatch the client's calls to the demo module. No configuration or deployment descriptor file is needed. That's the gist of it.

Now to expose the Web Service methods to the world a WSDL file is needed. So here is the MessageDemoService.wsdl . To create the WSDL file I used Sun's wscompile tool that comes with the J2EE 1.4 SDK. I also used the wscompile tool to generate the Java client stub code to test the service.

A Perl client can access the service very easily using the SOAP::Lite module. For example:

#!/usr/bin/perl

use SOAP::Lite;

$categoryList = SOAP::Lite
    -> uri('http://hunterj.hostjava.net/demo')
    -> proxy('http://hunterj.hostjava.net/cgi-bin/MessageDemo.cgi')
    -> getCategoryNames()
    -> result;

foreach $category (@$categoryList) {
    print $category . "\n";
}
This Perl program will access the Web Service, call getCategoryNames(), and then display the category names.

I also created a Java test client with Apache Axis which you can download [here], and give it a try. You can check out the complete Perl source for the Web Service [here ].

For more information about the SOAP::Lite module you can go to http://search.cpan.org/~byrne/SOAP-Lite-0.60a/lib/SOAP/Lite.pm


Now for the Axis2 implementation:

I implemented the Axis2 version of my web service in the usual way using the code generation tools that come with the Axis2 distribution - java2wsdl.bat and wsdl2java.bat. After the code generation steps, I simply implemented the specific logic I needed within the service skeleton class. Once I had all the service-specific code completed I used the Apache Ant build script, provided by the Axis2 distribution, to archive the service into MessageEditorService.aar, which worked very nicely, packaging up the MessageEditorService.wsdl as well as the services.xml file.

Thanks to the virtual hosting on hostjava.net I was able to deploy the Axis2 web service in the usual configuration, as illustrated here:

Axis Deployment

Finally I generated a JUnit test client for the Axis2 version using wsdl2java.bat, which you can download [here].

Lastly, the RESTful version:

The RESTful version was probably the easiest to implement. I used the JAX-RS/Jersey API to create the service as well as a test client. I implemented the same methods (mentioned above) but with a variation in the return types.

Method URI
getCategoryNames() /rest/messages/categories
getKeysByCategory() /rest/messages/keys?cat={CATEGORY_NAME}
getMessage() /rest/messages/{KEY_NAME}

In the SOAP-based implementations, two of these methods return a String[]. A String[] would be marshalled and unmarshalled automatically within the SOAP layer without the need for any additional coding. With Jersey, however, it's not so simple. Jersey does not support the marshalling/unmarshalling of a bare String[] type, or a Collection<String> type, automatically right out of the box. With Jersey - for the present time, at least - you have to do a little more work.

One strategy would be to create a wrapper class to contain a Collection type:

@XmlRootElement()
public class ResponseList {

    @XmlElement()
    List data;
    
    public ResponseList() {}
    
    public ResponseList(List data) {
        this.data = data;
    }

    public List getData() {
        return data;
    }
}
The XML result from marshalling a ResponseList looks something like this:
<responseList>
  <data> xsi:type="xs:string">General/System></data>
  <data> xsi:type="xs:string">Consumer Signup></data>
  <data> xsi:type="xs:string">Small Business Signup></data>
  <data> xsi:type="xs:string">Initiate Payment Process></data>
  <data> xsi:type="xs:string">Claim Payment Process></data>
  <data> xsi:type="xs:string">EpicWare Related></data>
  <data> xsi:type="xs:string">Request Payment Process></data>
  <data> xsi:type="xs:string">Change Password></data>
  <data> xsi:type="xs:string">Lost Password></data>
  <data> xsi:type="xs:string">POS Transaction></data>
</responseList>

The advantage of this type of strategy is that the marshalling/unmarshalling can be handled, automatically, by Jersey's built-in support for JAXB. The @XmlRootElement annotation marks the class as one that JAXB can marshall into XML, and unmarhall XML back into. The annotated class can then be used as a return type for the Web Service methods, e.g.:

public ResponseList<String> getCategoryNames();
Indeed, this type of approach may work well for complex domain objects, like, an AccountInfo object, an EmployeeInfo object, a ContactInfo object, and so on. It also supports the production of JSON instead of XML whenever needed. The JAXB processor can automatically produce the supported media type requested by the client. JAXB can also handle collections, such as List<AccountInfo>, as long as the specified object (e.g., AccountInfo) is properly annotated.

But for built-in data types, like List<String>, it seems unnecessary to add an additional wrapper class. It seems like there should be a simpler way to support the handling of simple Java data types.

After several online searches I found a few suggestions using javax.ws.rs.core.Response, or com.sun.jersey.api.JReponse, as well as javax.ws.rs.core.GenericEntity, and javax.xml.bind.JAXBElement. None of the example solutions worked as advertised in my environment - which currently is Jersey 1.17 running on Tomcat 7 with Java 1.7 on Debian 6.

After some trial and error I finally got a successful response from the following:

@Path("/categories")
@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response getCategoryNames() {

    List<JAXBElement<String>> categories = new ArrayList<JAXBElement<String>>();

    // populate the list...

    GenericEntity<List<JAXBElement<String>>> entity = 
            new GenericEntity<List<JAXBElement<String>>>(categories){};        

    return Response.ok(entity).build();
}
Only by wrapping the individual strings in a JAXBElement, and wrapping the entire List in a GenericEntity did I get a successful result. An alternative implementation is to use com.sun.jersey.api.JResponse in place of javax.ws.rs.core.Response, and skip the GenericEntity wrapper. Since JResponse preserves the data types within the response, the GenericEntity is not required. Of course if JResponse is used, that means the application is coupled to the Jersey implementation, rather than the JAX-RS API. So, you would need to decide if the lack of flexibility, and portability, is worth it. I prefer to code to an interface as a general rule. The result of the operation is the same. The XML marshalled from the Response object above looks like this:
<strings>
  <category>General/System</category>
  <category>Consumer Signup</category>
  <category>Small Business Signup</category>
  <category>Initiate Payment Process</category>
  <category>Claim Payment Process</category>
  <category>EpicWare Related</category>
  <category>Request Payment Process</category>
  <category>Change Password</category>
  <category>Lost Password</category>
  <category>POS Transaction</category>
</strings>
On the client side, the response can be digested as XML, JSON, or as a JAXB object, since the JAXB processor, on the server, can produce the supported media type requested by the client.

I have created a simple client which demonstrates how to use the built-in JAXB support to digest the responses from each of the example methods above. You can download the demo client [here].

copyright © 2003 - 2014 James P Hunter