Saturday, September 12, 2009

Webservices with Axis2/Java - the simple POJO way

There are many ways to create web services in Java; the most common implementation, nowadays, is using Apache Axis2/Java and a servlet container such as Apache Tomcat or Jetty.
On the Axis2 web site, there are a number of tutorials and explanations of the various concepts; yet, somehow, I'm missing a description of the "most easy way" of creating a web service. That's why I write it here. The tutorials are based on using the provided build.xml files instead of explaining how to do it. This makes it very hard to work in a different context.

A POJO in an .aar

Axis2 is able to "interpret" any Plain Old Java Object (POJO) as a web service, given the right hints. A Pojo is just a java class with some public method(s).

A POJO

package tecbites.ws;
public class Echo {
public String echo(String input) {
return input;
}
}

To tell Axis2 that this is a web service, you need additionally

  • a services.xml that defines this class as a POJO RPC web service
  • an echo.aar that contains (at least) the services.xml in the META-INF directory, and usually, the web service code (.class file(s)).

services.xml

<service name="Echo" scope="application" >
<description>
Tecbites Echo POJO web service
</description>
<messageReceivers>
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-only"
class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/>
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out"
class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</messageReceivers>
<parameter name="ServiceClass">tecbits.ws.Echo</parameter>
</service>

Put this file into META-INF. 

creating an .aar - jar

One way to create an .aar is just using the jar-tool that is part of the JDK. Using the following folder structure:
tecbites/ws/
Echo.class
META-INF/
services.xml

, a jar can just be created that is named echo.aar. Check that the jar/aar does not contain the files in a subdirectory named echo!.
javac tecbites/ws/Echo.java
jar cf echo.aar .


creating an .aar - build.xml

Another way of creating an .aar is using build.xml, the same as creating any other .jar. See the following example:

<?xml version="1.0" encoding="UTF-8"?>
<project name="tecbites.echo" default="aar" basedir="."> <target name="build">
<javac debug="true" srcdir="." destdir="." failonerror="true" />
</target> <target name="aar" depends="build">
<property name="aar" value="echo.aar"/>
<delete file="${aar}" failonerror="false"/>
<jar destfile="${aar}">
<manifest>
<attribute name="Built-By" value="Tecbites http://tecbites.blogspot.com/"/>
</manifest>
<fileset dir="." >
<include name="**/*.class" />
<include name="META-INF/**" />
</fileset>
</jar>
</target>
</project>

Just run ant. You have got ant installed, have you?

Deployment

The installation of an Axis2 webapp and the deployment of an .aar is explained quite well on the site. Just a short version here, based on Tomcat6:

  • download the Axis2 webapp
  • deploy (copy) the axis2.war into webapps/ of a servlet container (tomcat, jetty, jboss, glassfish, ...)
And start the servlet container. Then, copy the echo.aar into the webapps/axis2/WEB-INF/services directory (the servlet container should automatically unpack axis2.war into axis2/
If you need additional jars or java code you don't want to pack into the .aar itself, there is space in axis2/WEB-INF/lib (for the jars) and axis2/WEB-INF/classes (for any package/name/Class.class files).
Usually, Axis2 is configured for "hot deployment". This means that any .aar copied newly into the services directory of a running server is directly deployed as a new web service. There is also "hot update" which is usually disabled, but can be enabled in axis2/WEB-INF/conf/axis2.xml. Then, the .aars are automagically reloaded when replaced. Of course, any code in lib/ or classes/ is not.
By the way - it should also be possible to deploy the web service by copying the unjared folder structure into a subdirectory of the services dir, thus creating webapps/axis2/WEB-INF/services/Echo/META-INF/services.xml etc.

Testing

The web service should then be visible on http://localhost:8080/axis2/services/listServices (replace port 8080 with the correct one of your servlet container installation). There, you can see all deployed services and list their methods.
The WSDL can be accessed via http://localhost:8080/axis2/services/EchoService?wsdl.
Axis2 supports a part of REST, named POX - Plain Old XML documents over http. This allows to call the web service via http://localhost:8080/axis2/services/EchoService/echo?input=Good%20Morning. A web page should be returned reading

<ns:echoResponse xmlns:ns="http://ws.tecbites">
<ns:return>Good Morning</ns:return>
</ns:echoResponse>

By the way - the following error message when accessing http://localhost:8080/axis2/services/EchoService means the service is working correctly: ;)

<soapenv:Reason>
<soapenv:Text xml:lang="en-US">
The endpoint reference (EPR) for the Operation not found
is /axis2/services/EchoService and the WSA Action = null
</soapenv:Text>
</soapenv:Reason>



Direct POJO deployment

There is also the description of deploying the POJO .class file directly into a specially configured axis2 directory. In the axis2/WEB-INF/conf/axis2.xml, there is usually a line reading

<!--POJO deployer , this will alow users to drop .class 
file and make that into a service-->
<deployer extension=".class" directory="pojo"
class="org.apache.axis2.deployment.POJODeployer"/>

This should allow to deploy the ws by copying tecbites/ws/Echo.class into the pojo directory. In the context of the jetty servlet container inside the Global Sensor Networks (GSN) project, I was unable to get this to run.

JAXWS @WebService annotations

The JAXWS standard allows to "create" web services by annotating the class with the @WebService tag. In the axis2+service.xml context, this is not necessary, but supported.
Yet, this is helpful when the web service class contains public methods that should not be accessible as web service methods.

package tecbites.ws;
import javax.jws.WebService;
import javax.jws.WebMethod;

@WebService
public class Echo {
public String echo(String input) {
return input;
}
@WebMethod(exclude=true)
public /*static*/ String helper(String input) { return input; }
}


Side note

You might think I'm working for the Apache project, the way I propose their products/projects here. No, I'm not, it's just a fact that "they" are developing open source Java projects for many purposes, from development support (log4j, ant, ...) over utilities (commons, ...) to enterprise technologies (tomcat, axis2).

No comments: