Skip to main content

Asynchronous Web Service Invocation with JAX-WS 2.0

September 19, 2006

{cs.r.title}






Given that web service invocations are always remote across the internet, developing rigorous and responsive web service client applications has always been a challenge for architects and developers working with SOA. JAX-WS 2.0 comes with one effective solution to this problem: asynchronous web service invocation, with which a web service client may interact with a web service in a non-blocking, asynchronous approach. In this article, we will provide an exposition of this technology with examples built upon the reference implementation.

Our examples utilize JDK 5.0, JAX-WS 2.0 reference implementation (RI), and Tomcat 5.5. JAX-WS 2.0 requires JAXP 1.3. To replace the JAXP 1.2 released with JDK 5.0 with this newer version, one approach is to download the JAXP 1.3 RI, and copy the endorsed directory under /lib of its installation home to %JAVA_HOME%/jre/lib. We need to copy the jaxp-api.jar of JAXP 1.3 RI to %JAVA_HOME%/jre/lib/endorsed as well. To make Tomcat 5.5 work with JAX-WS 2.0, readers need to copy all the .jar files under the /lib directory of the JAX-WS 2.0 RI installation to the %CATALINA_HOME%/shared/lib directory.

As of this writing (in addition to Tomcat 5.5), Sun Java System Application Server 9.0, GlassFish, and Celtix also support JAX-WS 2.0. xFire is in the process of completing its implementation of this specification.

Since asynchronous web service invocation in JAX-WS 2.0 is built upon the concurrent programming support in JDK 5.0 introduced with the java.util.concurrent package, we will start from there.

Asynchronous Computation with Future and Callback

Compared to a synchronous invocation, an asynchronous invocation of the program must return immediately without waiting for the actual computation to complete. For the calling client to retrieve the computation result, one approach is to return the client an instance of the java.util.concurrent.Future interface as a placeholder for the result. This interface is defined as follows:

public interface Future<V>{
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit) throws InterruptedException,ExecutionException,TimeoutException;
}

Here, V represents the computation result. get() allows the client to make a rendezvous with the Future. When called, it returns the result if ready; otherwise, it will block until the computation has completed. With get(long timeout, TimeUnit unit), the calling client can specify the maximum waiting time. Computation may be cancelled with the cancel() method, but this can only be done before the computation completes. The method isDone() is used to check the status of the computation.

The second approach in asynchronous interaction is to use callbacks. In this scenario, the calling client provides a callback handler to the program along with the invocation. The callback handler communicates instructions from the client to the program on how to process the result. When the result is ready, the program invokes the handler, and processes the result as instructed. Typically, the program will utilize the Executor framework in java.util.concurrent to provide a thread to execute the callback handler, and the process may be tuned through the Executor.

JAX-WS 2.0 adopts both approaches in its asynchronous web service invocation mechanism. In this case, the program is the client runtime. A web service client with asynchrony enabled for a particular operation may poll the invocation result using the generic interface javax.xml.ws.Response, which directly extends Future<T>. It may also define a callback handler implementing javax.xml.ws.AsyncHandler to receive results in an asynchronous callback.

Below is the core class for implementing the callback operation in JAX-WS 2.0 RI:

import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Response;
......
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;

public class AsyncHandlerService {

    private AsyncHandler _handler;
    ......
    private Executor _executor;
    private WSFuture wsfuture;
    private Response response;

    public AsyncHandlerService(AsyncHandler handler, Executor executor) {
        ......
        _handler = handler;
        _executor = executor;
    }
    ......

    public void executeWSFuture() {

        _executor.execute((Runnable) wsfuture);
    }

    public WSFuture<Object>setupAsyncCallback(
                       final Response<Object>result) {
        response = result;

        wsfuture = new WSFuture<Object>(new Callable<
                                        Object>() {

            public Object call() throws Exception {
                _handler.handleResponse(response);
                return null;
            }
        });
        return wsfuture;
    }
}

Here, WSFuture<T> is a FutureTask defined by the RI. The code is self-explanatory. From this class, we see clearly how Future, Callback, AsyncHandler, and Executor work together in JAX-WS 2.0 client runtime.

A Simple Web Service

Before we discuss how to invoke a web service asynchronously in JAX-WS 2.0, let us build a simple web service as the starting point. With JAX-WS 2.0, we can develop the service side of a web service from a service endpoint implementation class or from a WSDL file. Our example follows the first approach with the following implementation class:

public class StockQuoteImpl {
        public double getQuote(String ticker) {
                double result = 0.0;
                if (ticker.equals("MHP")) {
                        result = 50.0;
                } else if (ticker.equals("IBM")) {
                        result = 83.0;
                }
                return result;
        }
}

To expose a class as a web service, JAX-WS 2.0 utilizes the annotation mechanism of JDK 5.0, particularly uses those annotations defined by JSR 181, Web Service Metadata for the Java Platform. Our first step is to annotate the StockQuoteImpl class and its only operation, getQuote(), with @WebService() and @WebMethod(), respectively:

@WebService(name="StockQuote", serviceName="StockQuoteService")
public class StockQuoteImpl {
        @WebMethod(operationName="getQuote")
        public double getQuote(String code) {
        ......
        }
}

We can then use the apt tool of JDK 5.0 to process the annotated class and generate portable artifacts for service-side implementation of the web service. We use the Ant task version of apt that comes with the JAX-WS 2.0 RI:

<target name="build-server-java" depends="setup">
        <apt    fork="true"
                debug="${debug}"
                verbose="${verbose}"
                destdir="${build.classes.home}"
                sourcedestdir="${build.classes.home}"
                sourcepath="${basedir}/src">
            <classpath>
                <path refid="jaxws.classpath"/>
                <pathelement location="${basedir}/src"/>
            </classpath>
            <option key="r" value="${build.home}"/>
            <source dir="${basedir}/src">
                <include name="**/server/*.java"/>
            </source>
        </apt>
</target>

The build-server-java task produces those heavily annotated classes:

@XmlRootElement(name = "getQuote",
namespace = "http://server.stockquote.jaxws.company.com/")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "getQuote",
namespace = "http://server.stockquote.jaxws.company.com/")
public class GetQuote {
    @XmlElement(name = "arg0", namespace = "")
    private String arg0;
    public String getArg0() {
        return this.arg0;
    }
    public void setArg0(String arg0) {
        this.arg0 = arg0;
    }
}

and

@XmlRootElement(name = "getQuoteResponse",
namespace = "http://server.stockquote.jaxws.company.com/")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "getQuoteResponse",
namespace = "http://server.stockquote.jaxws.company.com/")
public class GetQuoteResponse {
    @XmlElement(name = "return", namespace = "")
    private double _return;
    public double get_return() {
        return this._return;
    }
    public void set_return(double _return) {
        this._return = _return;
    }
}

These are the JavaBeans required by JAXB 2.0 for marshalling and un-marshalling web service request and response messages in JAX/XML binding. To complete our web service development, we need to package the service-side artifacts into a .war archive so that it can be deployed into Tomcat. This is accomplished by the Ant task:

<target name="create-war">
   <war warfile="${build.war.home}/${ant.project.name}.war"
        webxml="etc/web.xml">
       <webinf dir="${basedir}/etc" includes="sun-jaxws.xml"/>
       <classes dir="${build.classes.home}" includes="**/*.class"/>
   </war>
</target>

The content in web.xml is specific to the implementations of Tomcat, GlassFish, and Sun Java System Application Server. sun-jaxws.xml is a proprietary artifact needed by those implementations.

Here is the structure of the .war file:

WEB-INF\web.xml
WEB-INF\sun-jaxws.xml
WEB-INF\classes\com\company\jaxws\stockquote\server\StokQuoteImpl.class
WEB-INF\classes\com\company\jaxws\stockquote\server\jaxws\GetQuote.class
WEB-INF\classes\com\company\jaxws\stockquote\server\jaxws\GetQuoteResponse.class

Note that there are no WSDL or XML schemas involved. JAX-WS 2.0 RI comes with a tool called wsgen for generating such artifacts. But it is recommended not to package them into the .war when web service development starts from an implementation class.

With Tomcat running, copy the .war archive to $CATALINA_HOME$\webapps, and the StockQuote web service is ready to serve client calls.

Client for Synchronous Invocation

To assist web service client development, JAX-WS 2.0 RI comes with a tool called wsimport for generating client-side artifacts.

We use the Ant task version of wsimport as follows:

<target name="generate-client" depends="setup">
        <wsimport debug="${debug}"
                verbose="${verbose}"
                keep="${keep}"
                extension="${extension}"
                destdir="${build.classes.home}"
                wsdl="${client.wsdl}">
<binding dir="${basedir}/etc" includes="${schema.binding}"/>
<binding dir="${basedir}/etc" includes="${client.binding}"/>
        </wsimport>
</target>

In the build.properties file, we specify the wsdl file at http://localhost:1970/stockquote/stockquote?wsdl. Therefore, to successfully carry out the above task, the web service .war must be properly deployed and Tomcat server must be running.

This Ant task generates the JAXB 2.0 binding classes GetQuote, GetQuoteResponse, package-info, and ObjectFactory. Furthermore, it creates the service endpoint interface StockQuote, which provides the client view of the service, and the class StockQuoteService, which allows a client to interact with the client runtime. A point to note here is that, even though the wsimport is from the RI, the artifacts it generates are portable.

Here is StockQuote:

@WebService(name = "StockQuote",
targetNamespace = "http://server.stockquote.jaxws.company.com/")
public interface StockQuote {
  @WebMethod
  @WebResult(targetNamespace = "")
  @RequestWrapper(localName = "getQuote",
   targetNamespace = "http://server.stockquote.jaxws.company.com/",
   className = "com.company.jaxws.stockquote.client.GetQuote")
  @ResponseWrapper(localName = "getQuoteResponse",
   targetNamespace = "http://server.stockquote.jaxws.company.com/",
   className = "com.company.jaxws.stockquote.client.GetQuoteResponse")
  public double getQuote(
        @WebParam(name = "arg0", targetNamespace = "")
        String arg0);
}
StockQuoteService uses this interface to create a port proxy when the getPort() methods are called, and we can then use the proxy in our client StockQuoteClient to call the web service:

StockQuote port = new StockQuoteService().getStockQuotePort();
String ticker="MHP";
......
double result = port.getQuote(ticker);

This approach was already defined in JAX-RPC, the predecessor of JAX-WS 2.0.

Asynchronous Invocation with a Port Proxy

With those preparations, we can now turn our attention to the main topic of this article.

Asynchronous invocation in JAX-WS 2.0 can be realized completely in the client runtime, and this is the approach JAX-WS 2.0 RI takes. The focus is in generating the service endpoint interface with operations for asynchronously invoking the web service. Then, based on the interface, the client runtime creates the dynamic port proxy, which will carry out the actual asynchronous invocation. In JAX-WS 2.0 RI, customizing the wsimport Ant task can generate this service endpoint interface.

Continuing with our example, this time we use the following to produce the client artifacts:

<target name="generate-client-async" depends="setup">
    <wsimport
            debug="${debug}"
            verbose="${verbose}"
            keep="${keep}"
            extension="${extension}"
            destdir="${build.classes.home}"
            wsdl="${client.wsdl}">
        <binding dir="${basedir}/etc" includes="${schema.binding}"/>
        <binding dir="${basedir}/etc" includes="${client.binding.async}"/>
    </wsimport>
</target>

The difference from our previous use of wsimport is the file for one of the binding subelements (${client.binding.async}). This element takes external binding files for customizing WSDL binding for JAX-WS 2.0 (the custom-schema.xml pointed by ${schema.binding} is for JAXB binding declaration, and we will not discuss it here). In the synchronous case, we use custom-client.xml (pointed by ${client.binding}) for WSDL binding customization:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bindings
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    wsdlLocation="http://localhost:1970/stockquote/stockquote?wsdl"
    xmlns="http://java.sun.com/xml/ns/jaxws">
    <bindings node="wsdl:definitions">
        <package name="com.company.jaxws.stockquote.client"/>
    </bindings>
</bindings>

In the asynchronous case, we add one more element to <bindings node="wsdl:definitions"> in the above file (and result in custom-client-async.xml)

<bindings node="wsdl:definitions">
    <package name="com.company.jaxws.stockquote.client"/>
    <enableAsyncMapping>true</enableAsyncMapping>
</bindings>

As a result, the service endpoint interface StockQuote becomes:

@WebService(name = "StockQuote",
targetNamespace = "http://server.stockquote.jaxws.company.com/")
public interface StockQuote {
    @WebMethod(operationName = "getQuote")
    @RequestWrapper(localName = "getQuote",
                targetNamespace = "http://server.stockquote.jaxws.company.com/",
                className = "com.company.jaxws.stockquote.client.GetQuote")
    @ResponseWrapper(localName = "getQuoteResponse",
                targetNamespace = "http://server.stockquote.jaxws.company.com/",
                className = "com.company.jaxws.stockquote.client.GetQuoteResponse")
    public Response<GetQuoteResponse>getQuoteAsync(
        @WebParam(name = "arg0", targetNamespace = "")
        String arg0);

    @WebMethod(operationName = "getQuote")
    @RequestWrapper(localName = "getQuote",
                targetNamespace = "http://server.stockquote.jaxws.company.com/",
                className = "com.company.jaxws.stockquote.client.GetQuote")
    @ResponseWrapper(localName = "getQuoteResponse",
                targetNamespace = "http://server.stockquote.jaxws.company.com/",
                className = "com.company.jaxws.stockquote.client.GetQuoteResponse")
    public Future<?>getQuoteAsync(
        @WebParam(name = "arg0", targetNamespace = "")
        String arg0,
        @WebParam(name = "asyncHandler", targetNamespace = "")
        AsyncHandler<GetQuoteResponse>asyncHandler);

    @WebMethod
    @WebResult(targetNamespace = "")
    @RequestWrapper(localName = "getQuote",
                targetNamespace = "http://server.stockquote.jaxws.company.com/",
                className = "com.company.jaxws.stockquote.client.GetQuote")
    @ResponseWrapper(localName = "getQuoteResponse",
                targetNamespace = "http://server.stockquote.jaxws.company.com/",
                className = "com.company.jaxws.stockquote.client.GetQuoteResponse")
    public double getQuote(
        @WebParam(name = "arg0", targetNamespace = "")
        String arg0);
}

With this interface, we can invoke the web service through the corresponding proxy asynchronously in two ways as mentioned before. One is pulling:

public Response<GetQuoteResponse>getQuoteAsync(String arg0);

And the other employs a callback mechanism through:

public Future<?>getQuoteAsync(String arg0, AsyncHandler<GetQuoteResponse>asyncHandler);

In our sample client, StockQuoteClientAsync, the pulling method is called as follows:

Response<GetQuoteResponse>resp = port.getQuoteAsync (code);
Thread.sleep (2000);
GetQuoteResponse output = resp.get();

In real applications, we may replace the call to Thread.sleep() with some useful client-side processes. Furthermore, we have the option to cancel the web service call with resp.cancel(), based on the results of those client side processes. In certain scenarios, we may also find the routine below useful:

Response<GetQuoteResponse>resp = port.getQuoteAsync (code);
while( ! resp.isDone()){
        //some client side processes
}
GetQuoteResponse output = resp.get();

In summary, all the features of the Future interface come under our employment with this asynchronous pulling method.

The callback mechanism requires a callback handler to process invocation result. In our example, it is defined as follows:

private class GetQuoteCallbackHandler
       implements AsyncHandler<GetQuoteResponse>{
  private GetQuoteResponse output;
  public void handleResponse (Response<GetQuoteResponse>response) {
     try {
        output = response.get ();
     } catch (ExecutionException e) {
        e.printStackTrace ();
     } catch (InterruptedException e) {
        e.printStackTrace ();
     }
  }
  GetQuoteResponse getResponse (){
        return output;
  }
}
handleResponse() is the only method required by the AsyncHandler interface. Our sample client uses this handler as follows:

double result;
......
StockQuote port = new StockQuoteService().getStockQuotePort();
GetQuoteCallbackHandler callbackHandler = new GetQuoteCallbackHandler ();
......
Future<?>responseCallback;
......
responseCallback = port.getQuoteAsync ("MHP", callbackHandler);
Thread.sleep (2000);
result = callbackHandler.getResponse().getReturn();

The wildcard interface Future<?> returned to the client may be used to check invocation status, and to cancel the invocation.

As a side note, if a web service operation is enabled for asynchronous invocation, JAX-WS 2.0 dictates that three methods be generated in the service endpoint interface. Besides the two asynchronous ones described above, we also have the usual synchronous method.

Exception Handling in Asynchronous Invocation

Exception handling in asynchronous invocation needs special attention. Let us add an exception to our web service implementation. NotAvailableException is thrown when our implementation does not have quote information for a stock.

On the client side, with wsimport, the WSDL fault element is mapped to an exception class NotAvailableException_Exception. (Not a particularly good name, indeed. We can customize the WSDL binding to generate a better one, but this is beyond the scope of this article.)

Looking at the newly generated StockQuote, the synchronous method throws this exception directly. However, this service specific exception NotAvailableException_Exception does not appear at the asynchronous methods. The immediate question is, how are service exceptions communicated to the web service client in those asynchronous cases?

The answer comes from the way java.util.concurrent.Future<T> works. With this interface, all response information associated with the invocation is returned when the get() method is called. In a successful call, the return T carries the result. On the other hand, if any exception is thrown as a result of the invocation, the exception will be wrapped into a java.util.concurrent.ExecutionException, which will be thrown to the client when it calls the get() method. The client then can retrieve the root exception with the getCause() method of ExecutionException.

In asynchronous invocation with callback operation, since the response is processed first in handleResponse() of the AsyncHandler interface, ExecutionException may be caught and handled in the callback handler, and therefore, service exceptions or any other invocation exceptions may be examined there.

Just as in any concurrent scenario, a web service client invoking asynchronous methods must also be prepared to catch and handle InterruptionException, but this is more a concern associated with any multi-thread client than a specific web service one.

Executor for Processing Callbacks

In this section, let us see how we can customize callback handling with an Executor in asynchronous web service invocation.

Have a closer look at the StockQuoteService class generated by wsimport. We already know it is a proxy factory that our client calls to get the port proxy for calling the web service operation(s). By extending javax.xml.ws.Service, this class is actually the single point for our client to communicate with JAX-WS 2.0 client runtime. Among others, it has the following methods as part of its API inherited from Service:

......
<T>T getPort(java.lang.Class<T>serviceEndpointInterface)
<T>T getPort (javax.xml.namespace.QName portName,
                   java.lang.Class<T>serviceEndpointInterface)
java.util.concurrent.Executor getExecutor();
void setExecutor(java.util.concurrent.Executor executor)
......

Through those methods, JAX-WS 2.0 allows a web service client to use the getExecutor() and setExecutor() to customize an Executor for handling callbacks. The specification requires a default Executor to be available in the client runtime, and barring an application-specific Executor, this default Executor will process the callbacks. An application may use setExecutor() to configure its own executor.

As an example, in our sample client StockQuoteClientAsyncWithExecutor, we define a TimingThreadExecutor:

private static class TimingThreadPool extends ThreadPoolExecutor {
   public TimingThreadPool(){
     super(1, 1, 0L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10));
   }
   private final ThreadLocal<Long>startTime = new ThreadLocal<Long>();
   private final AtomicLong numTasks = new AtomicLong();
   private final AtomicLong totalTime = new AtomicLong();
   protected void beforeExecute(Thread t, Runnable r) {
     super.beforeExecute(t, r);
     System.out.printf("Runnable %s starts in %s. \n", r, t);
     startTime.set(System.nanoTime());
   }
   protected void afterExecute(Runnable r, Throwable t) {
     try {
        long endTime = System.nanoTime();
        long taskTime = endTime - startTime.get();
        numTasks.incrementAndGet();
        totalTime.addAndGet(taskTime);
        System.out.printf("Runnable %s ends after %dns. \n", r, taskTime);
        if (t != null) {
                t.getCause().printStackTrace();
        }
     } finally {
        super.afterExecute(r, t);
     }
   }
   protected void terminated() {
     try {
        System.out.printf("Terminated: avg time=%dns. \n",totalTime.get() / numTasks.get());
     } finally {
        super.terminated();
     }
   }
}

This executor records the time spent for a task it executes, and reports the average time per task in its lifetime. The code is based on an example in Java Concurrency in Practice by Brian Goetz, et al.

StockQuoteClientAsyncWithExecutor provides this Executor to the web service client runtime through:

TimingThreadPool ttp = new TimingThreadPool();
......
service.setExecutor(ttp);

Running the client, it is obvious that when we invoke the callback operation, this Executor is responsible for handling invocation callbacks.

Generally, web service clients may use executors to tune thread usage of callback handler execution, and to serve other purposes such as logging, etc.

Binding Customization for Asynchrony

Let us drill down to the details about WSDL binding customization for asynchrony. Suppose our StockQuote service need to return the price for buying certain shares of a stock. To support this requirement, we add one more operation, getPrice(String ticker, int share), to the class StockQuoteImpl. Annotating this method with @WebMethod() exposes it as a web service operation.

Create the web service as before. If we keep all the artifacts unchanged, and run the client-async task to generate the client artifacts, we will find in the service endpoint interface StockQuote that both getQuote() and getPrice() are enabled for asynchronous invocation.

Now, suppose that we want to expose the getQuote() operation for asynchronous invocation, but leave the getPrice() operation for synchronous invocation only. How can we achieve this?

As pointed out before, asynchrony is a feature controlled by WSDL binding customizations in JAX-WS 2.0. Particularly, the binding process is instrumented by jaxws:enableAsyncMapping for asynchrony when generating web service client artifacts. Here, jaxws is bound to the namespace http://java.sun.com/xml/ns/jaxws, as defined by the specification.

In custom-client-async.xml, we declare the jaxws:enableAsyncMapping to be true for the node wsdl:definitions. JAX-WS 2.0 actually allows for customizing the default WSDL binding for asynchrony through jaxws:enableAsyncMapping at all the following target nodes:

  • wsdl:definitions: Global scope, applies to all the wsdl:operations of all wsdl:portType attributes.
  • wsdl:portType: Applies to all the wsdl:operations in the portType.
  • wsdl:operation: Applies to only this wsdl:operation.

In addition, jaxws:enableAsyncMapping can also appear on the top-level bindings element with the @wsdlLocation attribute when we are working with external files like custom-client-async.xml.

Therefore, to enable asynchronous invocation only for getQuote(), we can modify the custom-client-async.xml as follows (custom-client-async-1.xml):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bindings
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    wsdlLocation="http://localhost:1970/stockquote/stockquote?wsdl"
    xmlns="http://java.sun.com/xml/ns/jaxws">
        ......
    <bindings node="wsdl:definitions/wsdl:portType[@name='StockQuote']
                       /wsdl:operation[@name='getQuote']">
        <enableAsyncMapping>true</enableAsyncMapping>
    </bindings>
</bindings>

Here, we enable asynchronous mapping for the getQuote() operation by pointing the node for binding customization to its xPath.

wsdl:definitions/wsdl:portType[@name='StockQuote']/wsdl:operation[@name='getQuote']

The following binding customization file (client-custom-async-2.xml) applies jaxws:enableAsyncMappping to the GetQuote port, and thus enables asynchronous invocation for all the operations of this port, which has the same effect with custom-client-async.xml for our simple example:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bindings
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    wsdlLocation="http://localhost:1970/stockquote/stockquote?wsdl"
    xmlns="http://java.sun.com/xml/ns/jaxws">
    ......
    <bindings node="wsdl:definitions/wsdl:portType[@name='StockQuote']">
        <enableAsyncMapping>true</enableAsyncMapping>
    </bindings>
</bindings>

client-custom-async-3.xml applies jaxws:enableAsyncMapping to the top-level binding element for the WSDL, and has the same effect as applying it to wsdl:definitions element.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bindings
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    wsdlLocation="http://localhost:1970/stockquote/stockquote?wsdl"
    xmlns="http://java.sun.com/xml/ns/jaxws">
    <enableAsyncMapping>true</enableAsyncMapping>
    ......
</bindings>

Let us point out that JAX-WS allows jaxws:enableAsyncMApping to appear at several different levels (global or wsdl:definitions, wsdl:port and wsdl:operation) of the WSDL semantic structure at the same time. In this case, to decide whether an operation is enabled for asynchronous invocation, the steps listed below are performed in order until a decision can be reached:

  1. If jaxws:enableAsynsMapping appears for the operation, then this value applies.
  2. If jaxws:enableAsynsMapping appears for the port to which the operation belongs, then this value for the port applies.
  3. If jaxws:enableAsynsMapping appears at the top level, or for the wsdl:definitions element, then the value here applies.
  4. However, if jaxws:enableAsynsMapping appears both at the top level, and for the wsdl:definitions element, then the value for the top level applies.

Based on this rule, we can also use custom-client-async-4.xml to enable getQuote() only for asynchronous invocation.

Asynchrony with Embedded Binding Declarations

Up to this point, we have been using standalone XML files (client-schema.xml, client-custom-async-*.xml, etc.) to customize WSDL binding for asynchrony and for other purposes (in the case of custom-schema.xml). JAX-WS 2.0, following JAXB 2.0, actually permits two different approaches for inputting binding declarations. In addition to the external binding file approach as exemplified in previous sections, we can also embed binding declarations directly into a WSDL document, and use this approach to customize the client for asynchrony.

Let us save the on-the-fly generated WSDL for our sample web service into a local file (under /etc), stockquote.wsdl, and change the wsimport Ant task in the build.xml so that its wsdl attribute points to this locally saved WSDL file (or its variations we will come up with).

Modifying this WSDL file as follows has the same effect, as we use client-custom-async.xml for client binding in wsimport:

<?xml version="1.0" encoding="UTF-8"?>
<definitions  xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
    xmlns:tns="http://server.stockquote.jaxws.company.com/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    targetNamespace="http://server.stockquote.jaxws.company.com/"
    name="StockQuoteService">
    ......
    <service name="StockQuoteService">
       <port name="StockQuotePort" binding="tns:StockQuotePortBinding">
          <soap:address location="http://localhost:1970/stockquote/stockquote"/>
       </port>
    </service>
    <jaxws:bindings>
       <jaxws:package name="com.company.jaxws.stockquote.client"/>
       <jaxws:enableAsyncMapping>true</jaxws:enableAsyncMapping>
    </jaxws:bindings>

</definitions>

Here, we declare the namespace jaxws in the definitions element, and add the jaxws:enableAsyncMapping (and other) binding declaration(s) as an extension element for definitions.

jaxws: enableAsyncMapping may also appear as an extension element for both definitions/PortType/operation[@name=...] and definitions/portType in WSDL. See stock-quote-1.wsdl and stockquote-2.xml; their effects correspond, respectively, to those of the external binding files custom-client-async-1.xml and custom-client-async-2.xml.

With embedded binding declarations, if jaxws:enableAsyncMapping appears in several places in the WSDL file, the same algorithm mentioned before for customization with an external binding file applies to decide whether or not an operation is enabled for asynchronous invocation. For an example, see stockquote-3.wsdl.

To conclude this section, let us point out that JAX-WS 2.0 in fact further allows external binding files and embedded binding declarations to work together, and it has this to say about the end result in such a situation:

External binding files are semantically equivalent to embedded binding declarations. When a JAX-WS implementation processes a WSDL document for which there is an external binding file, it must operate as if all binding declarations specified in the external binding file were instead specified as embedded declarations on the nodes in the WSDL document they target. It is an error if, upon embedding the binding declarations defined in one or more external binding files, the resulting WSDL document contains conflicting binding declarations.

(See Section 8.4 of the JAX-WS 2.0 specification.)

Conclusion

In this article, we introduced asynchronous web service invocation with a proxy stub in JAX-WS 2.0. This new specification also defines a second client programming model, the Dispatch API, which allows a client to interact with a web service at the XML-message level. Asynchronous service invocation can also be performed with the Dispatch API, and most of what we discussed in this article applies to this case as well.

Response time has long been a concern for web services. With the asynchronous invocation mechanism in JAX-WS 2.0, web service client applications on the Java platform will be able to execute other client processes and invoke web services concurrently. This should relieve the problem to a great degree for a large number of use cases.

Resources

width="1" height="1" border="0" alt=" " />
Young Yang works in software, consulting and corporate IT as a tech lead, architect, development and project manager.
Related Topics >> Web Services and XML   |