by Gautam Shah

Mustang: The fast track to Web services

news
Jul 3, 200616 mins

Java SE eases Web services development and consumption

If you haven’t been keeping up with what Sun Microsystems has been cooking up for the next release of Java, here is a wakeup call: For months now, Sun has been providing early releases of binaries, Javadocs, and source code for Java Platform, Standard Edition 6 (Java SE is Sun’s new name for J2SE), also known as Mustang. And it is not too late to hitch your wagon to this galloping horse.

An obvious question is, “Why should I care?” For the skeptic, Java SE 6 has improvements across the board, from opening programmatic access to the Java compiler, to system-tray and splash-screen components, to mixing scripting languages with your Java source code (with JavaScript supported out-of-box), to a dapper look and feel in Swing, to XML digital signatures, to the Smart Card I/O API, to JMX monitor threading improvements, to Web services annotations for service providers and simplified client access—to name just a few of the new features coming our way. (The java.net Website provides everything you wanted to know about this new release.)

In this article, we focus on the improvements to the Web Services Metadata specification and Java API for XML Web Services (JAX-WS) 2.0 in Java SE 6 that make both development and consumption of Web services quite easy. Using these new features, we create a Web service from a simple Java class by merely slapping on annotations; thereafter, we consume this service using JAX-WS 2.0. We even add a handler to the service that intercepts the service call and dumps the SOAP messages to System.out.

Actually, these features have been available as after-market downloads that implement Java Specification Request 181 (Web Services Metadata) and JSR 224 (JAX-WS). Having these features part of the standard Java release makes them more mainstream; expect to see proliferated support in IDEs soon.

JSR 181 and JSR 224 are also part of Java EE 5, which allows application vendors to supply hosting platforms for Web services based on the standard specification. Ideally, such functionality would allow inchoate Web service applications deployed on just the Java SE 6 Java Runtime Environment to scale to large workload deployments by using application servers that support the same specifications without codebase changes—reality aside, that is at least the hope.

Our Mustang Web service: Server and client

Before we let the horses out of stables, download the zip file that accompanies this article. It contains the following four files (the jar files are just for convenience):

  • mustangws.zip contains this article’s Web service server application source code, build file, and wsgen script file
  • mustangwsclient.zip contains the client application source code, build file, and wsimport script file
  • mustangws.jar contains the compiled application for our server
  • mustangwsclient.jar contains the compiled application for our client

Note: You’ll need Java SE 6 and Apache Ant to work with this article’s example.

After you unzip the files, you will have two folders, mustangws and mustangwsclient, with each corresponding to a server and client application. Both projects have the familiar src folder containing Java source files and Apache Ant’s build.xml under it. There are additional wsgenMustang.bat and wsimportMustang.bat files that contain console commands for generating Web service artifacts and stubs, which I cover later in this article.

Apache Ant’s build.xml file for both applications is located in the root folder of mustangws and mustangwsclient. This file has the familiar init, compile, dist, clean, and run tasks.

The root folder for our Web service’s server contains a folder named wsdl, which will be used to store the WSDL (Web Services Description Language) file generated by wsgen (more on this later).

Web Services Metadata

The Web Services Metadata specification uses annotations (also called metadata), which were introduced in Java SE 5. For those of you unfamiliar with the concept, annotations are tags that can be applied to source code; e.g., class definitions, functions, function parameters, or function return values. These annotations are directives or hints to compilers, JVMs, and application servers as to how they should treat annotated code. Annotations start with an “at” sign (@) and are followed by annotation text. For example, the @Deprecated annotation applied to a function definition generates a warning during code compilation using the function that is to be phased out. While the @Deprecated annotation resembles an @deprecated Javadoc annotation, the point is that annotations have now become first-class Java constructs. (Tarak Modi provides a good introduction to annotations in “Taming Tiger, Part 3” (JavaWorld, July 2004).)

When Web Services Metadata annotations are applied to class definitions and functions in Java source code, a deployable Web service is produced. The Web Services Metadata specification follows a “start with Java” development mode, which means you first define a Java class and functions, and then apply Web Services Metadata annotations to them. For Java developers, this is a more natural way of creating Web services than by starting with WSDL and creating corresponding Java classes. These annotations are hints for Web-service-enabler runtime engines as to how they might enable a Java class and its functions as a Web service and Web service operations. Java SE 6 comes bundled with such an engine, but since Web Services Metadata is also part of Java EE 5, you will see numerous vendors providing application servers that support these annotations.

Our Mustang Web service server

Without further ado, let’s look at Web Services Metadata in action by looking closer at the server portion of our Mustang Web service. Browse to the mustangws/src/com/techyatra/hellows folder. You will see the following files:

  • HelloServer
  • Bootstrap
  • TraceHandler
  • HelloException
  • Person

Open the HelloServer file; it contains a Java class enabled as a Web service by Web Services Metadata annotations. It is the service implementation bean:

 

package com.techyatra.hellows;

... @WebService(name="HelloServer", targetNamespace="http://mustangws.techyatra.com/", serviceName="HelloService") @SOAPBinding(style=SOAPBinding.Style.RPC) public class HelloServer {

... @WebMethod(operationName="hello", action="urn:hello") public @WebResult(partName="result")String ping(@WebParam (partName="person", mode=Mode.IN, targetNamespace="http://mustangws.techyatra.com/") Person person) throws HelloException { if (person == null) { System.out.println("function: hello(null)... throwing exception"); throw new HelloException("0001", "Person is null"); } else { System.out.println("function: hello(person.getTitle() + person.getName())"); return "Hello. " + person.getTitle() + person.getName() + "!"; } } }

In the snippet above, the @WebService annotation marks the MustangServer class as implementing a Web service, while @WebMethod identifies ping as being exposed as a Web service operation. ping does not do much other than return a greeting.

A word of caution: You cannot willy-nilly slap these annotations on any Java class and functions to expose them as Web services and Web service operations, respectively. For a Java class to be a Web Services Metadata-enabled service implementation bean, it needs to follow these key musts and must-nots:

  • It must be public
  • It must not be final or abstract
  • It must have a default public constructor
  • It must not have a finalize() method
  • The functions in a service implementation bean must follow these musts:

    • It must be public.
    • Its parameters, return values, and exceptions can be XML-enabled as per JAX RPC 1.1’s Java to XML/WSDL mapping rules—e.g., parameters and return values are primitives, arrays, and so on; exception extends Exception; etc. Please refer to Java API for XML-based Remote Procedure Call 1.1, Section 5 for more information.

    Now that we have the rules of engagement out of the way, let’s look at Web services annotations in the above code a little closer.

    Note: This article does not describe all annotations and all members of the Web Services Metadata specification. Please refer to JSR 181 for complete details.

    There are various types of annotations. @WebService and @WebMethod are WSDL mapping annotations. These annotations associate Java source code to WSDL elements that represent the Web service. @SOAPBinding is a binding annotation that specifies the network protocol and format.

    The @WebService annotation’s name, serviceName, and targetNamespace members specify wsdl:portType, wsdl:service, and targetNameSpace for the Web service’s generated WSDL (more on this later).

    The @SOAPBinding is a binding annotation that specifies the protocol as RPC and format as SOAP.

    The @WebMethod annotation’s operationName member specifies wsdl:operation, and its action specifies the SOAPAction header in WSDL. This is the value clients must put in SOAPHeader, a caveat of SOAP 1.1

    The @WebParam annotation’s partName member specifies wsdl:part in the WSDL.

    The @WebResult annotation’s partName member specifies wsdl:part for the return value in the WSDL.

    The HelloException class is the exception class with basic error code and description properties, and Person is a simple value class with last name, first name properties. These classes remain unaffected by any annotations.

    While it’s tempting to use @WebFault on HelloException, don’t! This annotation is used by the wsgen utility on a generated exception bean. Interestingly, it is not part of JSR 181, but is part of JAX-RPC 1.1.

    Before we move on to wsgen, one nice feature that needs attention is how you might enable overloaded functions as Web service operations. Achieve this functionality by specifying a different value for @WebMethod‘s operationName. For HelloServer, the overloaded function hello() and hello(person) have different operationNames, namely hello and quickHello, respectively.

    Now run Apache Ant’s compile task for our server application. Doing so generates a build folder that contains compiled classes and a wsdl folder that contains the WSDL file generated by wsgen.

    wsgen

    The HelloServer class with its Web services annotations is not yet ready to be deployed because it uses additional HelloException classes that have no Web service enablement or Java XML bindings. If HelloServer lacked exceptions thrown by its functions, we would not need wsgen. However, for all practical applications, this scenario is highly unlikely; hence, using wsgen is a must for creating classes that enable Web services.

    wsgen is a command line utility that generates portable JAX-WS artifacts. It reads Web service endpoint class files, which in our case is com.techyatra.hellows.HelloServer, and generates all the source files and class files needed for Web service deployment. It optionally generates WSDL (in wsdl folder) and schemas corresponding to a HelloServer Web service. A complete list of options for wsgen is available from Resources.

    For sake of convenience, this article’s source code contains a wsgenHello.bat file in the mustangws folder, which contains the wsgen command with all parameters required for our server project.

    Executing mustangws/wsgenHello.bat at mustangws generates the HelloExceptionBean source and compiled class in the com.techyatra.hellows.jaxb folder. This class holds the Java-XML bindings.

    Now that we have everything needed to deploy a Web service, before we deploy the service, let’s tinker with adding processing to a Web service call sequence. This is quite handy in cases where common processing is required for all calls to the server. Such processing is available via handlers. Handlers allow applications to introduce custom and special processing outside the realm of core business requirements. Such handlers could perform security checks, gather usage statistics, encrypt or decrypt data, or any another function developers might want to whip up.

    Handlers

    A SOAP message sent by the client (service consumer) to the server (service provider) makes its way through Java SE 6’s JAX-WS processing pipeline. Java SE 6 provides interceptors called handlers in the javax.xml.ws.handler package that can be plugged into this processing pipeline to perform custom, application-specific processing of inbound and outbound messages. Two types of handlers are available: logical and SOAP. SOAP handlers can operate on an entire SOAP message, including the SOAP header and body, while logical handlers operate on just the payload contained in the SOAP body.

    Our server includes the com.techyatra.hello.TraceHandler SOAP handler. It intercepts inbound and outbound SOAP messages, and writes the content to System.out as shown in the snippet below:

     

    /* * TraceHandler.java * @author shahga */

    package com.techyatra.hellows; ... public class TraceHandler implements SOAPHandler<SOAPMessageContext> { ... public boolean handleMessage(SOAPMessageContext messageContext) { trace(messageContext); return true; } public boolean handleFault(SOAPMessageContext messageContext) { trace(messageContext); return true; } ... private void trace(SOAPMessageContext messageContext) { Boolean outMessageIndicator = (Boolean) messageContext.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY); if (outMessageIndicator.booleanValue()) { System.out.println("nOutbound SOAP:"); } else { System.out.println("nInbound SOAP:"); } SOAPMessage message = messageContext.getMessage(); try { message.writeTo(System.out); System.out.println(""); } catch (Exception exp) {

    System.out.println("Exception in TraceHandler:trace(messageContext) : " + exp); } } }

    The TraceHandler implements the javax.xml.ws.handler.SOAPHandler<c extends MessageContext> interface. The handleMessage() and handleFault() functions write out inbound and outbound SOAP messages to System.out. These functions are supplied with SOAPMessageContext, which is the grab bag for all things in the SOAP message. It contains various message properties and the SOAP message itself.

    Now that we have a Web service with a handler ready, how do we package and host such a service? The packaging of such a service is a jar file discussed below, but it’s the hosting that’s interesting. Enter the Bootstrap class.

    Boostrap

    In Java SE 6, you’ll find a javax.xml.ws package that includes an Endpoint class, which can be used for hosting Web services within the JVM. Yes, you heard that right! No application servers needed. Just create a server instance and publish it using Endpoint, as shown below in the Bootstrap class. Endpoint is useful for smaller applications, where the cost of application servers is not justifiable and for development purposes.

     

    package com.techyatra.hellows;

    ... public class Bootstrap {

    ... public static void main (String [] args) throws Exception { HelloServer server = new HelloServer(); Endpoint endpoint = Endpoint.publish("http://localhost:9090/HelloServer", server); Binding binding = endpoint.getBinding(); List<Handler> handlerChain = new LinkedList<Handler>(); handlerChain.add(new com.techyatra.hellows.TraceHandler()); binding.setHandlerChain(handlerChain); } }

    The Bootstrap class is a convenience class that brings various pieces of the HelloServer Web service together. It publishes the server at a URL and adds TraceHandler to the Web service processing pipeline.

    The service application jar file

    Run Apache Ant dist to create a compiled JAR. The dist task creates a MustangWS.jar file that marks the com.techyatra.hellows.Bootstrap class as the entry point class containing the main() function.

    Now run Apache Ant run. This will invoke main in Bootstrap, which publishes the Web service at http://localhost:9090/HelloServer.

    Point your browser to http://localhost:9090/HelloServer?WSDL to show your dynamically generated WSDL. Look closely at how the values supplied to various Web Services Metadata annotations make it to the WSDL.

    The Mustang Web service client

    Now that we have HelloServer deployed as a Web service, let’s look at how to consume it. While there are various approaches to this task, we will look at using JAX-WS 2.0 features in Java SE 6 to consume the HelloServer Web service.

    The client application is found in the mustangwsclient folder. In addition to the familiar src folder and build.xml file, mustangwsclient also contains the wsimportHello.bat file, which generates JAXB (Java Architecture for XML Binding) stubs. Clients, to conveniently consume Web services, require stubs. Java SE 6 comes bundled with the wsimport utility that generates these stubs. wsimportHello.bat uses this utility to generate stubs for HelloService.

    wsimport

    wsimport can inspect a WSDL file available at a specific URL to generate all classes that make client code clean and easier to understand.

    While some might want to work with SAAJ (SOAP with Attachments API for Java), JAXB, or straight-up XML to consume a Web service, for the cautious, wsimport-based stubs provide a convenient layer for accessing Web services. It generates JAX-WS portable artifacts, which means classes work across specification-compliant application servers and JVMs.

    When you run wsimportHello.bat found in the mustangwsclient folder, it creates a build folder for our Mustang Web service client project and then executes wsimport with some key parameters, which in turn generates stub files and places them under the com/techyatra/helloclient/jax folder. For more detail on these parameters, see Resources.

    Let’s look at the classes generated by wsimport from http://localhost:9090/HelloService?WSDL in the wsimportHello.bat file. These classes are at the client’s disposal for accessing the HelloServer Web service:

    • HelloServer is the service endpoint interface (SEI)
    • HelloService is the service
    • HelloException is the exception class mapped from wsdl:fault found in the WSDL
    • Person is the JAXB mapping of Java classes from the Person schema found in the WSDL

    These stubs are then used by the HelloService client application in com.techyatra.helloclient.JAXClient via com.techyatra.helloclient.Client.

    com.techyatra.helloclient.Client and com.techyatra.helloclient.JAXClient

    The main entry point to the client application is the com.techyatra.helloclient.Client class. It receives command line parameters, performs checks and overrides arguments if needed, creates and populates a Person object, and finally calls invoke(person) on JAXClient as shown in the snippet below:

     

    package com.techyatra.helloclient;

    public class Client { public static void main(String[] args) throws Exception { ... if (args[0].equals("jax")) { com.techyatra.helloclient.jax.Person person = new com.techyatra.helloclient.jax.Person(); person.setTitle(args[1]); person.setName(args[2]); JAXClient jaxClient = new JAXClient(); jaxClient.invoke(person); } } }

    The JAXClient is shown below.

     

    package com.techyatra.helloclient;

    import com.techyatra.helloclient.jax.*;

    public class JAXClient { ... public void invoke(Person person) throws Exception { HelloService service = new HelloService(); HelloServer server = (HelloServer) service.getHelloServerPort(); String ret = server.hello(person); System.out.println(ret); } }

    Now, executing Ant’s run task should give you following message:

     [java] No arguments specified. Overriding arguments to – jax Mr. Doe
    [java] Alive and well. Thank you and hello  Mr. Doe! 
    
    

    And there you have it!

    Although not discussed in this article, the code and WSDL2Java script file for an Apache Axis-based client is included in the attached download. The source code for the Axis-based client can be found in mustangwsclient/com/techyatra/hellowsclient/AxisClient.java, while the wsdl2javaHello.bat file, which contains the console command to create Axis-based client stubs, can be found in the application’s root mustangwsclient folder. To remove Axis references, delete the mustangwsclient/com/techyatra/helloclient/axis folder (generated by wsdl2javaHello.bat) and the mustangwsclient/com/techyatra/helloclient/AxisClient.java source file.

    Conclusion

    Let’s map out our speedy route to Web services:

    • Unzip the attached code.
    • For our server application:
      • Execute Ant’s compile task.
      • Execute wsgenMustang.bat to generate portable artifacts.
      • Execute Ant’s run task, which internally invokes the dist task to build the MustangWS.jar file.
    • You now have the HelloServer service running at http://localhost:9090/HelloServer. Point your browser to http://localhost:9090/HelloServer?WSDL to see the dynamically generated WSDL.
    • For the client application:
      • If not interested in using Axis, delete its references by deleting the mustangwsclient/com/techyatra/helloclient/axis folder and mustangwsclient/com/techyatra/helloclient/AxisClient.java file.
      • Execute wsImportHello.bat to generate stubs for the client.
      • Run Ant’s run task and enjoy!

    The inclusion of metadata and JAX-WS within Java SE has now made Web services a first class citizen and has reduced the ramp-up for developers interested in getting on the Web service bandwagon. You should fully expect to see IDEs and application servers providing value-adds in conjunction with these features in the near future.

Gautam Shah has been developing large, complex applications for more than a decade. Currently, as a senior architect for Unisys, he has been designing solutions that enable information sharing between agencies. He has varied expertise in Java EE, .Net, open source solutions, and various integration platforms such as BizTalk, WebMethods, and WebSphere Business Integration Server.