此内容没有您所选择的语言版本。

Chapter 40. Developing Asynchronous Applications


Abstract

JAX-WS provides an easy mechanism for accessing services asynchronously. The SEI can specify additional methods that can be used to access a service asynchronously. The Apache CXF code generators generate the extra methods for you. You simply add the business logic.

40.1. Types of Asynchronous Invocation

In addition to the usual synchronous mode of invocation, Apache CXF supports two forms of asynchronous invocation:

  • Polling approach — To invoke the remote operation using the polling approach, you call a method that has no output parameters, but returns a javax.xml.ws.Response object. The Response object (which inherits from the javax.util.concurrency.Future interface) can be polled to check whether or not a response message has arrived.
  • Callback approach — To invoke the remote operation using the callback approach, you call a method that takes a reference to a callback object (of javax.xml.ws.AsyncHandler type) as one of its parameters. When the response message arrives at the client, the runtime calls back on the AsyncHandler object, and gives it the contents of the response message.

40.2. WSDL for Asynchronous Examples

Example 40.1, “WSDL Contract for Asynchronous Example” shows the WSDL contract that is used for the asynchronous examples. The contract defines a single interface, GreeterAsync, which contains a single operation, greetMeSometime.

Example 40.1. WSDL Contract for Asynchronous Example

<?xml version="1.0" encoding="UTF-8"?><wsdl:definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:tns="http://apache.org/hello_world_async_soap_http"
                  xmlns:x1="http://apache.org/hello_world_async_soap_http/types"
                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                  targetNamespace="http://apache.org/hello_world_async_soap_http"
                  name="HelloWorld">
  <wsdl:types>
    <schema targetNamespace="http://apache.org/hello_world_async_soap_http/types"
            xmlns="http://www.w3.org/2001/XMLSchema"
            xmlns:x1="http://apache.org/hello_world_async_soap_http/types"
            elementFormDefault="qualified">
      <element name="greetMeSometime">
        <complexType>
          <sequence>
            <element name="requestType" type="xsd:string"/>
          </sequence>
        </complexType>
      </element>
      <element name="greetMeSometimeResponse">
        <complexType>
          <sequence>
            <element name="responseType"
                     type="xsd:string"/>
          </sequence>
        </complexType>
      </element>
    </schema>
  </wsdl:types>

  <wsdl:message name="greetMeSometimeRequest">
    <wsdl:part name="in" element="x1:greetMeSometime"/>
  </wsdl:message>
  <wsdl:message name="greetMeSometimeResponse">
    <wsdl:part name="out"
               element="x1:greetMeSometimeResponse"/>
  </wsdl:message>

  <wsdl:portType name="GreeterAsync">
    <wsdl:operation name="greetMeSometime">
      <wsdl:input name="greetMeSometimeRequest"
                  message="tns:greetMeSometimeRequest"/>
      <wsdl:output name="greetMeSometimeResponse"
                   message="tns:greetMeSometimeResponse"/>
    </wsdl:operation>
  </wsdl:portType>

  <wsdl:binding name="GreeterAsync_SOAPBinding"
                type="tns:GreeterAsync">
    ...
  </wsdl:binding>

  <wsdl:service name="SOAPService">
    <wsdl:port name="SoapPort"
               binding="tns:GreeterAsync_SOAPBinding">
      <soap:address location="http://localhost:9000/SoapContext/SoapPort"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

40.3. Generating the Stub Code

Overview

The asynchronous style of invocation requires extra stub code for the dedicated asynchronous methods defined on the SEI. This special stub code is not generated by default. To switch on the asynchronous feature and generate the requisite stub code, you must use the mapping customization feature from the WSDL 2.0 specification.

Customization enables you to modify the way the Maven code generation plug-in generates stub code. In particular, it enables you to modify the WSDL-to-Java mapping and to switch on certain features. Here, customization is used to switch on the asynchronous invocation feature. Customizations are specified using a binding declaration, which you define using a jaxws:bindings tag (where the jaxws prefix is tied to the http://java.sun.com/xml/ns/jaxws namespace). There are two ways of specifying a binding declaration:

External Binding Declaration
When using an external binding declaration the jaxws:bindings element is defined in a file separate from the WSDL contract. You specify the location of the binding declaration file to code generator when you generate the stub code.
Embedded Binding Declaration
When using an embedded binding declaration you embed the jaxws:bindings element directly in a WSDL contract, treating it as a WSDL extension. In this case, the settings in jaxws:bindings apply only to the immediate parent element.

Using an external binding declaration

The template for a binding declaration file that switches on asynchronous invocations is shown in Example 40.2, “Template for an Asynchronous Binding Declaration”.

Example 40.2. Template for an Asynchronous Binding Declaration

<bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
          xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
          wsdlLocation="AffectedWSDL"
          xmlns="http://java.sun.com/xml/ns/jaxws">
  <bindings node="AffectedNode">
    <enableAsyncMapping>true</enableAsyncMapping>
  </bindings>
</bindings>

Where AffectedWSDL specifies the URL of the WSDL contract that is affected by this binding declaration. The AffectedNode is an XPath value that specifies which node (or nodes) from the WSDL contract are affected by this binding declaration. You can set AffectedNode to wsdl:definitions, if you want the entire WSDL contract to be affected. The jaxws:enableAsyncMapping element is set to true to enable the asynchronous invocation feature.

For example, if you want to generate asynchronous methods only for the GreeterAsync interface, you can specify <bindings node="wsdl:definitions/wsdl:portType[@name='GreeterAsync']"> in the preceding binding declaration.

Assuming that the binding declaration is stored in a file, async_binding.xml, you would set up your POM as shown in Example 40.3, “Consumer Code Generation”.

Example 40.3. Consumer Code Generation

<plugin>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-codegen-plugin</artifactId>
  <version>${cxf.version}</version>
  <executions>
    <execution>
      <id>generate-sources</id>
      <phase>generate-sources</phase>
      <configuration>
        <sourceRoot>outputDir</sourceRoot>
        <wsdlOptions>
          <wsdlOption>
            <wsdl>hello_world.wsdl</wsdl>
            <extraargs>
              <extraarg>-client</extraarg>
              <extraarg>-b async_binding.xml</extraarg>
            </extraargs>
          </wsdlOption>
        </wsdlOptions>
      </configuration>
      <goals>
        <goal>wsdl2java</goal>
      </goals>
    </execution>
  </executions>
</plugin>

The -b option tells the code generator where to locate the external binding file.

For more information on the code generator see Section 44.2, “cxf-codegen-plugin”.

Using an embedded binding declaration

You can also embed the binding customization directly into the WSDL document defining the service by placing the jaxws:bindings element and its associated jaxws:enableAsynchMapping child directly into the WSDL. You also must add a namespace declaration for the jaxws prefix.

Example 40.4, “WSDL with Embedded Binding Declaration for Asynchronous Mapping” shows a WSDL file with an embedded binding declaration that activates the asynchronous mapping for an operation.

Example 40.4. WSDL with Embedded Binding Declaration for Asynchronous Mapping

<wsdl:definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
                  ...
                  xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
                  ...>
  ...
  <wsdl:portType name="GreeterAsync">
    <wsdl:operation name="greetMeSometime">
      <jaxws:bindings> <jaxws:enableAsyncMapping>true</jaxws:enableAsyncMapping> </jaxws:bindings>
      <wsdl:input name="greetMeSometimeRequest"
                  message="tns:greetMeSometimeRequest"/>
      <wsdl:output name="greetMeSometimeResponse"
                   message="tns:greetMeSometimeResponse"/>
    </wsdl:operation>
  </wsdl:portType>
  ...
</wsdl:definitions>

When embedding the binding declaration into the WSDL document you can control the scope affected by the declaration by changing where you place the declaration. When the declaration is placed as a child of the wsdl:definitions element the code generator creates asynchronous methods for all of the operations defined in the WSDL document. If it is placed as a child of a wsdl:portType element the code generator creates asynchronous methods for all of the operations defined in the interface. If it is placed as a child of a wsdl:operation element the code generator creates asynchronous methods for only that operation.

It is not necessary to pass any special options to the code generator when using embedded declarations. The code generator will recognize them and act accordingly.

Generated interface

After generating the stub code in this way, the GreeterAsync SEI (in the file GreeterAsync.java) is defined as shown in Example 40.5, “Service Endpoint Interface with Methods for Asynchronous Invocations”.

Example 40.5. Service Endpoint Interface with Methods for Asynchronous Invocations

package org.apache.hello_world_async_soap_http;

import org.apache.hello_world_async_soap_http.types.GreetMeSometimeResponse;
...

public interface GreeterAsync
{
  public Future<?> greetMeSometimeAsync(
        java.lang.String requestType,
        AsyncHandler<GreetMeSometimeResponse> asyncHandler
    );

    public Response<GreetMeSometimeResponse> greetMeSometimeAsync(
        java.lang.String requestType
    );

    public java.lang.String greetMeSometime(
        java.lang.String requestType
    );
}

In addition to the usual synchronous method, greetMeSometime(), two asynchronous methods are also generated for the greetMeSometime operation:

  • Callback approach publicFuture<?>greetMeSomtimeAsyncjava.lang.StringrequestTypeAsyncHandler<GreetMeSomtimeResponse>asyncHandler
  • Polling approach publicResponse<GreetMeSomeTimeResponse>greetMeSometimeAsyncjava.lang.StringrequestType

40.4. Implementing an Asynchronous Client with the Polling Approach

Overview

The polling approach is the more straightforward of the two approaches to developing an asynchronous application. The client invokes the asynchronous method called OperationNameAsync() and is returned a Response<T> object that it polls for a response. What the client does while it is waiting for a response is depends on the requirements of the application. There are two basic patterns for handling the polling:

  • Non-blocking polling— You periodically check to see if the result is ready by calling the non-blocking Response<T>.isDone() method. If the result is ready, the client processes it. If it not, the client continues doing other things.
  • Blocking polling— You call Response<T>.get() right away, and block until the response arrives (optionally specifying a timeout).

Using the non-blocking pattern

Example 40.6, “Non-Blocking Polling Approach for an Asynchronous Operation Call” illustrates using non-blocking polling to make an asynchronous invocation on the greetMeSometime operation defined in Example 40.1, “WSDL Contract for Asynchronous Example”. The client invokes the asynchronous operation and periodically checks to see if the result is returned.

Example 40.6. Non-Blocking Polling Approach for an Asynchronous Operation Call

package demo.hw.client;

import java.io.File;
import java.util.concurrent.Future;

import javax.xml.namespace.QName;
import javax.xml.ws.Response;

import org.apache.hello_world_async_soap_http.*;

public final class Client {
  private static final QName SERVICE_NAME
    = new QName("http://apache.org/hello_world_async_soap_http",
                "SOAPService");

  private Client() {}

  public static void main(String args[]) throws Exception {

    // set up the proxy for the client

    Response<GreetMeSometimeResponse> greetMeSomeTimeResp =
      port.greetMeSometimeAsync(System.getProperty("user.name"));

      while (!greetMeSomeTimeResp.isDone()) {
      // client does some work
      }
      GreetMeSometimeResponse reply = greetMeSomeTimeResp.get();
      // process the response

      System.exit(0);
  }
}

The code in Example 40.6, “Non-Blocking Polling Approach for an Asynchronous Operation Call” does the following:

Invokes the greetMeSometimeAsync() on the proxy.

The method call returns the Response<GreetMeSometimeResponse> object to the client immediately. The Apache CXF runtime handles the details of receiving the reply from the remote endpoint and populating the Response<GreetMeSometimeResponse> object.

Note

The runtime transmits the request to the remote endpoint’s greetMeSometime() method and handles the details of the asynchronous nature of the call transparently. The endpoint, and therefore the service implementation, never worries about the details of how the client intends to wait for a response.

Checks to see if a response has arrived by checking the isDone() of the returned Response object.

If the response has not arrived, the client continues working before checking again.

When the response arrives, the client retrieves it from the Response object using the get() method.

Using the blocking pattern

When using the block polling pattern, the Response object’s isDone() is never called. Instead, the Response object’s get() method is called immediately after invoking the remote operation. The get() blocks until the response is available.

You can also pass a timeout limit to the get() method.

Example 40.7, “Blocking Polling Approach for an Asynchronous Operation Call” shows a client that uses blocking polling.

Example 40.7. Blocking Polling Approach for an Asynchronous Operation Call

package demo.hw.client;

import java.io.File;
import java.util.concurrent.Future;

import javax.xml.namespace.QName;
import javax.xml.ws.Response;

import org.apache.hello_world_async_soap_http.*;

public final class Client {
  private static final QName SERVICE_NAME
    = new QName("http://apache.org/hello_world_async_soap_http",
                "SOAPService");

  private Client() {}

  public static void main(String args[]) throws Exception {

    // set up the proxy for the client

    Response<GreetMeSometimeResponse> greetMeSomeTimeResp =
      port.greetMeSometimeAsync(System.getProperty("user.name"));
    GreetMeSometimeResponse reply = greetMeSomeTimeResp.get();
      // process the response
      System.exit(0);
  }
}

40.5. Implementing an Asynchronous Client with the Callback Approach

Overview

An alternative approach to making an asynchronous operation invocation is to implement a callback class. You then call the asynchronous remote method that takes the callback object as a parameter. The runtime returns the response to the callback object.

To implement an application that uses callbacks, do the following:

  1. Create a callback class that implements the AsyncHandler interface.

    Note

    Your callback object can perform any amount of response processing required by your application.

  2. Make remote invocations using the operationNameAsync() that takes the callback object as a parameter and returns a Future<?> object.
  3. If your client requires access to the response data, you can poll the returned Future<?> object’s isDone() method to see if the remote endpoint has sent the response.

    If the callback object does all of the response processing, it is not necessary to check if the response has arrived.

Implementing the callback

The callback class must implement the javax.xml.ws.AsyncHandler interface. The interface defines a single method: handleResponseResponse<T>res The Apache CXF runtime calls the handleResponse() method to notify the client that the response has arrived. Example 40.8, “The javax.xml.ws.AsyncHandler Interface” shows an outline of the AsyncHandler interface that you must implement.

Example 40.8. The javax.xml.ws.AsyncHandler Interface

public interface javax.xml.ws.AsyncHandler
{
  void handleResponse(Response<T> res)
}

Example 40.9, “Callback Implementation Class” shows a callback class for the greetMeSometime operation defined in Example 40.1, “WSDL Contract for Asynchronous Example”.

Example 40.9. Callback Implementation Class

package demo.hw.client;

import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Response;

import org.apache.hello_world_async_soap_http.types.*;

public class GreeterAsyncHandler implements AsyncHandler<GreetMeSometimeResponse>
{
  private GreetMeSometimeResponse reply;

  public void handleResponse(Response<GreetMeSometimeResponse>
                             response)
  {
    try
    {
      reply = response.get();
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }

  public String getResponse()
  {
    return reply.getResponseType();
  }
}

The callback implementation shown in Example 40.9, “Callback Implementation Class” does the following:

Defines a member variable, response, that holds the response returned from the remote endpoint.

Implements handleResponse().

This implementation simply extracts the response and assigns it to the member variable reply.

Implements an added method called getResponse().

This method is a convenience method that extracts the data from reply and returns it.

Implementing the consumer

Example 40.10, “Callback Approach for an Asynchronous Operation Call” illustrates a client that uses the callback approach to make an asynchronous call to the GreetMeSometime operation defined in Example 40.1, “WSDL Contract for Asynchronous Example”.

Example 40.10. Callback Approach for an Asynchronous Operation Call

package demo.hw.client;

import java.io.File;
import java.util.concurrent.Future;

import javax.xml.namespace.QName;
import javax.xml.ws.Response;

import org.apache.hello_world_async_soap_http.*;

public final class Client {
  ...

  public static void main(String args[]) throws Exception
  {
    ...
    // Callback approach
    GreeterAsyncHandler callback = new GreeterAsyncHandler();

    Future<?> response =
      port.greetMeSometimeAsync(System.getProperty("user.name"),
                                callback);
    while (!response.isDone())
    {
      // Do some work
    }
    resp = callback.getResponse();
    ...
    System.exit(0);
  }
}

The code in Example 40.10, “Callback Approach for an Asynchronous Operation Call” does the following:

Instantiates a callback object.

Invokes the greetMeSometimeAsync() that takes the callback object on the proxy.

The method call returns the Future<?> object to the client immediately. The Apache CXF runtime handles the details of receiving the reply from the remote endpoint, invoking the callback object’s handleResponse() method, and populating the Response<GreetMeSometimeResponse> object.

Note

The runtime transmits the request to the remote endpoint’s greetMeSometime() method and handles the details of the asynchronous nature of the call without the remote endpoint’s knowledge. The endpoint, and therefore the service implementation, does not need to worry about the details of how the client intends to wait for a response.

Uses the returned Future<?> object’s isDone() method to check if the response has arrived from the remote endpoint.

Invokes the callback object’s getResponse() method to get the response data.

40.6. Catching Exceptions Returned from a Remote Service

Overview

Consumers making asynchronous requests will not receive the same exceptions returned when they make synchronous requests. Any exceptions returned to the consumer asynchronously are wrapped in an ExecutionException exception. The actual exception thrown by the service is stored in the ExecutionException exception’s cause field.

Catching the exception

Exceptions generated by a remote service are thrown locally by the method that passes the response to the consumer’s business logic. When the consumer makes a synchronous request, the method making the remote invocation throws the exception. When the consumer makes an asynchronous request, the Response<T> object’s get() method throws the exception. The consumer will not discover that an error was encountered in processing the request until it attempts to retrieve the response message.

Unlike the methods generated by the JAX-WS framework, the Response<T> object’s get() method throws neither user modeled exceptions nor generic JAX-WS exceptions. Instead, it throws a java.util.concurrent.ExecutionException exception.

Getting the exception details

The framework stores the exception returned from the remote service in the ExecutionException exception’s cause field. The details about the remote exception are extracted by getting the value of the cause field and examining the stored exception. The stored exception can be any user defined exception or one of the generic JAX-WS exceptions.

Example

Example 40.11, “Catching an Exception using the Polling Approach” shows an example of catching an exception using the polling approach.

Example 40.11. Catching an Exception using the Polling Approach

package demo.hw.client;

import java.io.File;
import java.util.concurrent.Future;

import javax.xml.namespace.QName;
import javax.xml.ws.Response;

import org.apache.hello_world_async_soap_http.*;

public final class Client
{
  private static final QName SERVICE_NAME
    = new QName("http://apache.org/hello_world_async_soap_http",
                "SOAPService");

  private Client() {}

  public static void main(String args[]) throws Exception
  {
    ...
    // port is a previously established proxy object.
    Response<GreetMeSometimeResponse> resp =
       port.greetMeSometimeAsync(System.getProperty("user.name"));

    while (!resp.isDone())
    {
      // client does some work
    }

    try
    {
      GreetMeSometimeResponse reply = greetMeSomeTimeResp.get();
      // process the response
    }
    catch (ExecutionException ee)
    {
       Throwable cause = ee.getCause();
       System.out.println("Exception "+cause.getClass().getName()+" thrown by the remote service.");
    }
  }
}

The code in Example 40.11, “Catching an Exception using the Polling Approach” does the following:

Wraps the call to the Response<T> object’s get() method in a try/catch block.

Catches a ExecutionException exception.

Extracts the cause field from the exception.

If the consumer was using the callback approach the code used to catch the exception would be placed in the callback object where the service’s response is extracted.

Red Hat logoGithubRedditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

© 2024 Red Hat, Inc.