Monday, July 18, 2011

JDK6 (aka Java SE 6) web services and Tomcat

Web services came a long way since first introduced in 2000. The latest edition 6 of standard Java provides decent WS remoting support for simple APIs.

To make a simple web service out of your existing API, not much is needed. Just annotate your class with @WebService, public methods with @WebMethod, and method params with @WebParam:
@WebService
public class SimpleWS {
@WebMethod
public String storeData(
@WebParam(name="data") String data
)
{
try {
BufferedWriter out = new BufferedWriter( new FileWriter("test.txt", true));
out.write(data+"\r\n");
out.close();
}
catch (IOException e) {
return "ERROR: "+e;
}
return "OK";
}
}

This is it. If you want to make it accessible via web, all you need to do is to call
Endpoint.publish("http://localhost:8080/simplews", new SimpleWS());
Java2 SE has a built-in lightweight HTTP server that will accept web service requests and call your methods. All you need to know is to point your client code generator (e.g. in Visual Studio) to http://localhost:8080/simplews?wsdl
If you need a Java client, the best way to generate it's code is to call the command line wsimport tool from Java's bin folder:
wsimport -p client -keep http://localhost:8080/simplews?wsdl

Of course, you can use a different, more appropriate host name and port to make WSDL usable from remote locations.

You can make this web service into Windows Service by using, e.g. Apache Commons Daemon Package - I had a very good experience using it recently. I also find daemons on Linux much simpler, but it is outside of this posts' scope.

One thing to bear in mind is, once you deployed this service into production, changes to the web service code and descriptors will change WSDL, which will require changing all the clients. Think very carefully before the first deployment, and plan upgrades with all parties involved. For serious projects it is still preferable to start with WSDL and use it as a common denominator.

It is often important for troubleshooting, what specs are supported by this J2SE 6 built-in framework for compatibility reasons. Here we go:
WSDL 1.1 (root element will be and port binding will include the full URL where you published it)
JAXWS 2.1 javax.xml.ws
JAXB 2.1 javax.xml.bind
SAAJ 1.3 javax.xml.soap
JSR 181 2.0 javax.jws

For more information, see J2SE & Web Services.

Often, you need to make your existing web application web-services enabled. In this case, it makes sense to re-use existing Tomcat HTTP server facility, so you should not call Endpoint.publish(). Instead, download JAX-WS RI 2.1.7 and place provided libraries into your WEB-INF/lib folder. I actually use the following subset:
activation.jar
FastInfoset.jar
jaxb-impl.jar
jaxb-xjc.jar
jaxws-rt.jar
jaxws-tools.jar
jsr173_api.jar
jsr250-api.jar
mimepull.jar
resolver.jar
saaj-api.jar
saaj-impl.jar
stax-ex.jar
streambuffer.jar
woodstox.jar
You will also need to provision servlet mappings in your web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<listener>
<listener-class>
com.sun.xml.ws.transport.http.servlet.WSServletContextListener
</listener-class>
</listener>

<servlet>
<servlet-name>SimpleService</servlet-name>
<servlet-class>
com.sun.xml.ws.transport.http.servlet.WSServlet
</servlet-class>
<load-on-startup>1>/load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>SimpleService</servlet-name>
<url-pattern>/simplews</url-pattern>
</servlet-mapping>
</web-app>

and put sun-jaxws.xml into the WEB-INF folder with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">
<endpoint name="SimpleService" implementation="com.mypackage.SimpleWS" url-pattern="/simplews" />
</endpoints>

As a result, your generated WSDL should be accessible via http://localhost:8080/[war file name]/simplews?wsdl