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

4 comments:

Anonymous said...

I tested your code with tomcat 6.0.32 and 7.0.19 but it does not work !!
when I try to access the published wsdl I got file not found error page !!!

Anonymous said...

Great article !

Could you tell us how to access the published wsdl once the war is deployed to tomcat please ?

Thanks

Anonymous said...

I fixed the wsdl problem...
by the way, the required jar list is not enough to make the ws work. I got many class not found error and fixed them by adding the remaining jars which are available in the jaxws zip file


thanks again for your great and simple article!!

Exceeder said...

Thanks for all the comments! I added the link to the wsdl and will review the JARs issue. If you have "class not found" issues, there is no harm in adding all of the JARs. If I find a little time, I will post the sources for the example.