Skip to main content

Web Services Made Easy with JAX-WS 2.0

June 13, 2006

{cs.r.title}







JAX-WS (Java API for XML Web Services) is a technology designed to simplify the construction of web services and web service clients in Java. The latest version, JAX-WS 2.0, which is part of the new Java EE 5 platform, provides powerful new tools and techniques to facilitate web service development.

Introducing SOA and Web Services

Service-oriented architecture (SOA) is one of the more promising concepts to have emerged in enterprise architecture circles of late. SOA involves the use of loosely coupled, independent application services made available across a network. These services communicate via a standardized, platform-independent protocol that hides the underlying implementation details of each service. So a .NET client can access a service implemented in Java, for example.

The use of a standardized interface language also means that the application publishing the service does not need to know anything about the calling application. This makes SOA particularly useful for enterprise and inter-enterprise architectures.

SOA can be implemented using several technologies; however the most common choice today is the use of web services.

Web services are published in a standard XML format called WDSL, which describes the services available on a given server. A client invokes a web service by sending a request to the server, using the XML-based SOAP or REST protocols. Messages are typically sent over an HTTP or HTTPS connection, so web services can easily be published over the internet (hence the name "web services").

The interest in SOA in general and in web services in particular is growing at an impressive rate. Many well-known internet-based companies such as Amazon.com, eBay, Google, Yahoo, and many others now provide web services as part of their internet business offerings, and many other companies are considering web services as a basis for their evolving SOA-based enterprise architecture plans.

What Is JAX-WS?

JAX-WS (formerly JAX-RPC) is Sun's answer to the question of how to develop web services easily in Java. JAX-WS 2.0 provides a library of annotations and a toolkit of Ant tasks and command-line utilities that hide the complexity of the underlying XML message protocol. This new release supports different protocols such as SOAP 1.1, SOAP 1.2, and REST, and uses JAXB 2.0, also new to Java EE 5, for XML data binding.

When writing a JAX-WS 2.0 web service, the developer uses annotations to define methods either in an interface or directly in an implementation class (the interface can be automatically generated). On the client side, the web service client simply creates a proxy object, and then invokes methods on this proxy. Neither the server nor the client needs to generate or parse SOAP (or REST) messages; the JAX-WS 2.0 API takes care of these tedious low-level tasks.

Let's look at these tasks in more detail.

How to Write a Web Service using JAX-WS

First, let's write a web service implementation, which we can deploy on a JAX-WS-compliant server. For this article, I used the JAX-WS reference implementation, which is part of the GlassFish project. The aim of the GlassFish project is to build an open source application server implementing the latest Java EE 5 technologies.

JAX-WS 2.0 uses a powerful library of annotations to ease web service development tasks. Annotations provide a convenient way to turn plain old Java classes into first-rate web services. Annotations let you associate supplementary information about a class or interface and its members. This "metadata" can then be processed by specialized tools to generate underlying boilerplate code and other dirty details.

In practice, the web service annotations generate much of the web service plumbing for you automatically, leaving you to concentrate on the business code.

There are several possible approaches. Using the JAX-WS toolkit, you can start from the WSDL file and generate interface and skeleton implementation classes, or you can start from an implementation class and generate both a WSDL file and a Java interface. For the sake of simplicity, we will describe the latter approach in the rest of this article.

Before you start, you will need to download JAX-WS 2.0 from the reference implementation download page (see Resources). The reference implementation comes bundled as a .jar file. To install, you need to run the .jar as shown here:

java -jar jaxws-2_0.jar

This will extract a directory called jaxws-ri, which contains the reference implementation. For this example, I extracted the jaxws-ri package into a directory called /usr/local/share.

Now we write a web service implementation class. In JAX-WS 2.0, a web service implementation simply takes the form of an ordinary Java class with some special annotations. You can also manually write the actual Java interface, though in our example we will let the JAX-WS toolkit generate it for us. The class is as follows:

   @WebService(serviceName="StockQuoteService", name="StockQuote")
   public class StockQuoteImpl {
      @WebMethod()
      public double getQuote(String code) {
         double result = 0.0;
         if (code.equals("SUN")) {
            result = 20.0;
         } else if (code.equals("IBM")){
            result = 83.0;
         } else if (code.equals("BEA")) {
            result = 12;
         }
         //...
         return result;
      }
   }

The @WebService annotation declares the class as a web service. The name property in the @WebService annotation lets you define the web service name (the wdsl:portType attribute in WDSL 1.1). The serviceName property lets you define the WDSL service name. These properties are optional; if undefined, sensible default values will be derived from the class name.

The @WebMethod annotation is used to indicate which methods are to be exposed by this web service. Again, this annotation takes optional parameters that can be used to customize the WDSL values, though the default values are often quite sufficient. Note again that, apart from the annotations, this class is a perfectly normal Java class.

The annotations are processed by the JDK annotation processing tool, or Apt. Although Apt can be used as a command-line tool, in general you should integrate it into your build process. To do this using Ant, you need to declare the Apt task, as follows. The class is provided in the jaxws-tools.jar file:

   <taskdef name="apt" classname="com.sun.tools.ws.ant.Apt">
      <classpath refid="jaxws.classpath"/>
   </taskdef>

Now you need to run Apt on all of your server classes to process the annotations we've seen in the StockQuoteImpl class. In our example, we compile the server-side classes (all of the server classes are in a nested package called webservices.server), and then run the Apt task on the compiled server classes. The Ant tasks that do this are shown here:

   <target depends="init" name="compile-server">
      <echo message="Compiling server classes"/>
      <javac destdir="target/classes"
         includes="**/webservices/server/**">
         <src path="src/main/java"/>
         <classpath refid="project.classpath"/>
      </javac>
   </target>

   <target name="apt" depends="compile-server">
      <apt   destdir="${build}"
         sourcedestdir="${generated}"
         sourcepath="${src}">
         <classpath refid="jaxws.classpath"/>
         <source dir="${src}">
         <include name="**/server/*.java"/>
         </source>
      </apt>
   </target>

This task will generate some low-level classes that will handle the SOAP encapsulation for us. In this case, the Apt task generates two heavily annotated classes, which encapsulate the request and the response to the getQuote() method, respectively.

  • GetQuote: Encapsulates the request coming from the client invoking the getQuote() method.
  • GetQuoteResponse: Encapsulates the response sent from the web service back to the client.

The next step is to generate deployable web service artifacts. To do this, you use the wsgen tool. The wsgen tool will generate the WDSL file and XSD schema for the web service, which are needed to publish our web service description to the outside world.

From Ant, declare and call the wsgen task as follows:

    <target name="wsgen" depends="apt">
        <taskdef name="wsgen" classname="com.sun.tools.ws.ant.WsGen">
            <classpath refid="jaxws.classpath"/>
        </taskdef>       
        <wsgen resourcedestdir="${wsdl.dir}"
            sei="com.wakaleo.tutorials.webservices.server.StockQuoteImpl"
            keep="true"
            sourcedestdir="${generated.server}"
            destdir="${build}"
            genwsdl="true">
            <classpath refid="project.classpath"/>
        </wsgen>
    </target>

In our example, this task will generate two files:

  • StockQuoteService_schema1.xsd: The web service schema definition.
  • StockQuoteService.wsdl: The web service WSDL file.

Your *.wsdl files should be placed in the WEB-INF/wsdl directory--that's what the resourcedestdir="${wsdl.dir}" in the wsgen target above is for.

This process may seem a little long at first, but there is a lot of automation going on. Once the different tools have been configured in your build process, basically all you have to do is concentrate on the web service implementation classes. Many modern IDEs like NetBeans and Eclipse now have built-in tools and plugins to help make the process even easier.

Deploying the Web Service

Now you are ready to deploy the web service to an application server. A web service is deployed in the same way as any other Java web application: in the form of a .war file. First you will need to configure the Sun web service servlet in your web.xml file. In our tutorial, our web.xml file looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
   xmlns="http://java.sun.com/xml/ns/j2ee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee " title="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
">http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
   <listener>
      <listener-class>
        com.sun.xml.ws.transport.http.servlet.WSServletContextListener
      </listener-class>
   </listener>
   <servlet>
      <servlet-name>stock_quote_service</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>stock_quote_service</servlet-name>
      <url-pattern>/stock_quote</url-pattern>
   </servlet-mapping>
   <session-config>
      <session-timeout>
         30
      </session-timeout>
   </session-config>
   <welcome-file-list>
      <welcome-file>
      index.jsp
      </welcome-file>
   </welcome-file-list>
</web-app>

You will also need to configure the sun-jaxws.xml file, which defines a set of "endpoints." Each endpoint represents a web particular published web service, and includes information about the implementation class, URL pattern, and (optionally) other technical details such as the WSDL service and port. You need to place this file in your WEB-INF directory. In our example, the sun-jaxws.xml file looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'>
   <endpoint
      name='StockQuote'
      implementation='com.wakaleo.tutorials.webservices.server.StockQuoteImpl'
      url-pattern='/stock_quote'/>
</endpoints>

Next you need to package your classes into a .war file and deploy it to your web server. You can currently deploy and test your new JAXWP web service on any Java-WSDP-2.0-compliant web servers, which according to Sun at the time of writing include:

  • GlassFish Application Server, the open source application server project led by Sun
  • Sun Java System Application Server
  • Tomcat 5.0 for Java WSDP, a modified version of Jakarta Tomcat 5.0.18

Installing the GlassFish Application Server

The examples in this tutorial were developed using the GlassFish Application Server. The Sun Application Server, built on the same code base, is also very similar. For completeness, we will run through how to install and run this GlassFish Application Server. First, download the latest stable version (this article was written using Milestone 7), and run it as a Java application, as shown here:

   $ java -Xmx256m -jar glassfish-installer-9.0-b48.jar

This will create a new glassfish directory. In true open source tradition, this directory contains the GlassFish source code, rather than the final executable. So now you need to build the application. It's a Java application, so all you need are JDK 1.5 and Ant. Go to this directory and build the application using Ant, as follows:

   $ cd glassfish
   $ ant -f setup.xml

This will setup a default configuration with a domain called (imaginatively) domain1. Now all you need to do is to start up the server. Put the glassfish/bin directory on your system PATH variable, and start the application server using the asadmin command, as follows:

   $ asadmin start-domain domain1

You can deploy your .war either via the administration console, or directly by placing it in the autodeploy directory of your domain folder (the directory will be something along the lines of $GLASSFISH_HOME/domains/domain1/autodeploy, where $GLASSFISH_HOME is the GlassFish installation directory). The deploy target in the sample code does this automatically. After a few seconds, your .war will be deployed onto the application server. Now you can test your web service. First, to see if the web service has been correctly deployed, try the following URL in any browser:

http://localhost:8080/stockquotes/stock_quote?wsdl

This will display the WSDL configuration file for this web service (see Figure 1). If this file is displayed, your web service is correctly deployed.

Figure 1
Figure 1. The WDSL configuration file of the deployed web service (click for full-size image)

Writing the Client Code

Next we can write the client code. Normally, all of this would be done in a different application, but for the purposes of our example, we are just going to set up a unit test class that will invoke the deployed web service.

The client code is quite simple, and is shown here:

   package com.wakaleo.tutorials.webservices.client;

   import com.wakaleo.tutorials.webservices.server.StockQuote;
   import com.wakaleo.tutorials.webservices.server.StockQuoteService;
  
   public class StockQuoteClient {
     
      public static double getQuote(String quote) {
         StockQuoteService service = new StockQuoteService();
         StockQuote port = service.getStockQuotePort();
         return port.getQuote(quote);
      }
  
      public static void main(String[] args) {
            String code = "SUN";
   
            if (args.length > 0) {
                  code = args[0];
            }
            double result = getQuote(code);
            System.out.println(code + ": " + result);
      }     
   }

All of the interesting stuff happens in the getQuote() method. Here we create a web service port object for our web service, and invoke the getQuote() method on this object. All the tricky data encapsulation and SOAP encoding happens under the hood--all we have to worry about is calling the business method.

The astute reader will have noticed two new classes: StockQuote and StockQuoteService. StockQuote is the Java interface for the web service implementation class generated earlier on. This object is also known as the client endpoint. StockQuoteService implements a factory-type function (getStockQuotePort()) that provides the endpoint object. You generate these classes, and all of the other classes that the client needs, using the wsimport tool, as shown in the following Ant task:

 
<target name="wsimport" depends="init">

    <taskdef name="wsimport" classname="com.sun.tools.ws.ant.WsImport">
        <classpath refid="jaxws.classpath"/>
    </taskdef>

    <echo message="Running wsimport"/>
    <wsimport debug="true"
            keep="true"
            destdir="${generated.client}"
            package="com.wakaleo.tutorials.webservices.server"
            wsdl="http://localhost:8080/stockquotes/stock_quote?wsdl"/>
</target>

Now all you need to do is bundle the client classes up into a client package (don't forget the JAX-WS .jars as well; you still need them for the client distribution). If you want to do a quick test on the tutorial code, run the "test" target, as follows--this will call the client class:

$ ant test
Buildfile: build.xml

test:
     [java] SUN: 20.0

BUILD SUCCESSFUL

Conclusion

The new version of the Java API for XML Web Services, or JAX-WS 2.0, is an exciting part of the new Java EE 5 platform. Using a powerful combination of Java 5 annotations and Ant-compatible tools to mask the underlying complexity of the SOAP protocol, JAX-WS 2.0 greatly simplifies the development of web services and of web-service-based SOA architectures.

Resources

width="1" height="1" border="0" alt=" " />
John Ferguson Smart is a freelance consultant specialising in Enterprise Java, Web Development, and Open Source technologies, currently based in Wellington, New Zealand.
Related Topics >> Web Services and XML   |