2 Invoking a Web Service Using Asynchronous Request-Response

This chapter describes how to invoke a WebLogic Java API for XML-based RPC (JAX-RPC) Web service using asynchronous request-response.

This chapter includes the following topics:

Overview of the Asynchronous Request-Response Feature

When you invoke a Web service synchronously, the invoking client application waits for the response to return before it can continue with its work. In cases where the response returns immediately, this method of invoking the Web service might be adequate. However, because request processing can be delayed, it is often useful for the client application to continue its work and handle the response later on, or in other words, use the asynchronous request-response feature of WebLogic Web services.

You invoke a Web service asynchronously only from a client running in a WebLogic Web service, never from a stand-alone client application. The invoked Web service does not change in any way, thus you can invoke any deployed Web service (both WebLogic and non-WebLogic) asynchronously as long as the application server that hosts the Web service supports the WS-Addressing specification at http://www.w3.org/Submission/2004/SUBM-ws-addressing-20040810/.

When implementing asynchronous request-response in your client, rather than invoking the operation directly, you invoke an asynchronous flavor of the same operation. (This asynchronous flavor of the operation is automatically generated by the jwsc Ant task.) For example, rather than invoking an operation called getQuote directly, you would invoke getQuoteAsync instead. The asynchronous flavor of the operation always returns void, even if the original operation returns a value. You then include methods in your client that handle the asynchronous response or failures when it returns later on. You put any business logic that processes the return value of the Web service operation invoke or a potential failure in these methods. You use both naming conventions and JWS annotations to specify these methods to the JWS compiler. For example, if the asynchronous operation is called getQuoteAsync, then these methods might be called onGetQuoteAsyncResponse and onGetQuoteAsyncFailure.

Note:

For information about using asynchronous request-response with other asynchronous features, such as Web service reliable messaging or buffering, see Chapter 6, "Using the Asynchronous Features Together." This section describes how to use the asynchronous request-response feature on its own.

The asynchronous request-response feature works only with HTTP; you cannot use it with the HTTPS or JMS transport.

Using Asynchronous Request-Response: Main Steps

The following procedure describes how to create a client Web service that asynchronously invokes an operation in a different Web service. The procedure shows how to create the JWS file that implements the client Web service from scratch; if you want to update an existing JWS file, use this procedure as a guide.

For clarity, it is assumed in the procedure that:

  • The client Web service is called StockQuoteClientService.

  • The StockQuoteClientService service is going to invoke the getQuote(String) operation of the already-deployed StockQuoteService service whose WSDL is found at the following URL:

    http://localhost:7001/async/StockQuote?WSDL 
    

It is further assumed that you have set up an Ant-based development environment and that you have a working build.xml file to which you can add targets for running the jwsc Ant task and deploying the generated service. For more information, see the following sections in Getting Started With JAX-RPC Web Services for Oracle WebLogic Server:

Table 2-1 Steps to Use Asynchronous Request-Response

#
Step Description

1

Configure the WebLogic Server instances.

Configure the asynchronous response service, as described in Configuring the Host WebLogic Server Instance for the Asynchronous Web Service.

2

Create a new JWS file, or update an existing one, that implements the StockQuoteClientService Web service.

Use your favorite IDE or text editor. See Writing the Asynchronous JWS File.

3

Update your build.xml file to compile the JWS file that implements the StockQuoteClientService.

You will add a <clientgen> child element to the jwsc Ant task so as to automatically generate the asynchronous flavor of the Web service operations you are invoking.

See Updating the build.xml File When Using Asynchronous Request-Response.

4

Run the Ant target to build the StockQuoteClientService.

For example:

prompt> ant build-clientService

5

Deploy the StockQuoteClientService Web service as usual.

See "Deploying and Undeploying WebLogic Web Services" in Getting Started With JAX-RPC Web Services for Oracle WebLogic Server.


When you invoke the StockQuoteClientService Web service, which in turn invokes the StockQuoteService Web service, the second invoke will be asynchronous rather than synchronous.

Configuring the Host WebLogic Server Instance for the Asynchronous Web Service

Configuring the WebLogic Server instance on which the asynchronous Web service is deployed involves configuring JMS resources, such as JMS servers and modules, that are used internally by the Web services runtime.

You can configure these resources manually or you can use the Configuration Wizard to extend the WebLogic Server domain using a Web services-specific extension template. Using the Configuration Wizard greatly simplifies the required configuration steps; for details, see "Configuring Your Domain For Web Services Features" in Getting Started With JAX-RPC Web Services for Oracle WebLogic Server.

Notes:

Alternatively, you can use WLST to configure the resources. For information about using WLST to extend the domain, see "Configuring Existing Domains" in Oracle WebLogic Scripting Tool.

A domain that does not contain Web Services resources will still boot and operate correctly for non-Web services scenarios, and any Web Services scenario that does not involve asynchronous request and response. You will, however, see INFO messages in the server log indicating that asynchronous resources have not been configured and that the asynchronous response service for Web services has not been completely deployed.

If you prefer to configure the resources manually, perform the following steps.

Table 2-2 Steps to Configure Host WebLogic Server Instance Manually for the Asynchronous Web Service

#
Step Description

1

Invoke the Administration Console for the domain that contains the host WebLogic Server instance.

To invoke the Administration Console in your browser, enter the following URL:

http://host:port/console

where

  • host refers to the computer on which the Administration Server is running.

  • port refers to the port number where the Administration Server is listening for connection requests. The default port number for the Administration server is 7001.

See "Invoking the Administration Console" in Getting Started With JAX-RPC Web Services for Oracle WebLogic Server.

2

Create a JMS Server.

Create a JMS Server. If a JMS server already exists, you can use it if you do not want to create a new one.

See "Create JMS servers" in Oracle WebLogic Server Administration Console Help.

3

Create JMS module and define queue.

Create a JMS module, and then define a JMS queue in the module. If a JMS module already exists, you can use it if you do not want to create a new one. Target the JMS queue to the JMS server you created in the preceding step. Be sure you specify that this JMS queue is local, typically by setting the local JNDI name. See "Create JMS system modules" and "Create queues in a system module" in Oracle WebLogic Server Administration Console Help.

If you want the asynchronous Web service to use the default Web services queue, set the JNDI name of the JMS queue to weblogic.wsee.DefaultQueue.

Clustering Considerations:

If you are using the Web service asynchronous feature in a cluster, you must:

  • Create a local JMS queue, rather than a distributed queue, when creating the JMS queue.

  • Explicitly target this JMS queue to each server in the cluster.

4

Create a Work Manager.

Define a Work Manager named weblogic.wsee.mdb.DispatchPolicy, which is used by the asynchronous request-response feature, by default.

See "Create global Work Managers" in Oracle WebLogic Server Administration Console Help.

5

Tune your domain environment, as required. (Optional)

Review "Tuning Heavily Loaded Systems to Improve Web Service Performance" in Performance and Tuning for Oracle WebLogic Server.


Writing the Asynchronous JWS File

The following example shows a simple JWS file that implements a Web service called StockQuoteClient that has a single method, asyncOperation, that in turn asynchronously invokes the getQuote method of the StockQuote service. The Java code in bold is described Coding Guidelines for Invoking a Web Service Asynchronously. See Example of a Synchronous Invoke to see how the asynchronous invoke differs from a synchronous invoke of the same operation.

package examples.webservices.async_req_res;

import weblogic.jws.WLHttpTransport;
import weblogic.jws.ServiceClient; 
import weblogic.jws.AsyncResponse; 
import weblogic.jws.AsyncFailure; 

import weblogic.wsee.async.AsyncPreCallContext; 
import weblogic.wsee.async.AsyncCallContextFactory; 
import weblogic.wsee.async.AsyncPostCallContext; 

import javax.jws.WebService;
import javax.jws.WebMethod;

import examples.webservices.async_req_res.StockQuotePortType; 

import java.rmi.RemoteException;

@WebService(name="StockQuoteClientPortType",
            serviceName="StockQuoteClientService",
            targetNamespace="http://examples.org/")

@WLHttpTransport(contextPath="asyncClient",
                 serviceUri="StockQuoteClient",
                 portName="StockQuoteClientServicePort")

/**
 *  Client Web Service that invokes the StockQuote Service asynchronously.
 */

public class StockQuoteClientImpl {

  @ServiceClient(wsdlLocation="http://localhost:7001/async/StockQuote?WSDL", 
                 serviceName="StockQuoteService", portName="StockQuote") 

  private StockQuotePortType port; 

  @WebMethod
  public void asyncOperation (String symbol, String userName) 
    throws RemoteException {

    AsyncPreCallContext apc = AsyncCallContextFactory.getAsyncPreCallContext(); 
    apc.setProperty("userName", userName); 

    try {
      port.getQuoteAsync(apc, symbol ); 
      System.out.println("in getQuote method of StockQuoteClient WS");

     } catch (RemoteException re) {

        System.out.println("RemoteException thrown");
        throw new RuntimeException(re);
    }

  }

  @AsyncResponse(target="port", operation="getQuote") 
  public void onGetQuoteAsyncResponse(AsyncPostCallContext apc, int quote) { 
    // Get the userName property we set on AsyncPreCallContext 
    String userName = (String)apc.getProperty("userName"); 
    System.out.println("-------------------"); 
    System.out.println(username + " Got quote " + quote ); 
    System.out.println("-------------------"); 
  }

  @AsyncFailure(target="port", operation="getQuote") 
  public void onGetQuoteAsyncFailure(AsyncPostCallContext apc, Throwable e) { 
    System.out.println("-------------------"); 
    e.printStackTrace(); 
    System.out.println("-------------------"); 
  } 

}

Coding Guidelines for Invoking a Web Service Asynchronously

The following guidelines for invoking an operation asynchronously correspond to the Java code shown in bold in the example described in Writing the Asynchronous JWS File. These guidelines are in addition to the standard ones for creating JWS files. See Example of a Synchronous Invoke to see how the asynchronous invoke differs from a synchronous invoke of the same operation.

To invoke an operation asynchronously in your JWS file:

  • Import the following WebLogic-specific JWS annotations related to the asynchronous request-response feature:

    import weblogic.jws.ServiceClient;
    import weblogic.jws.AsyncResponse;
    import weblogic.jws.AsyncFailure;
    
  • Import the JAX-RPC stub, created later by the jwsc Ant task, of the port type of the Web service you want to invoke. The stub package is specified by the packageName attribute of the <clientgen> child element of jwsc, and the name of the stub is determined by the WSDL of the invoked Web service.

    import examples.webservices.async_req_res.StockQuotePortType;
    
  • Import the asynchronous pre- and post-call context WebLogic APIs:

    import weblogic.wsee.async.AsyncCallContextFactory;
    import weblogic.wsee.async.AsyncPreCallContext;
    import weblogic.wsee.async.AsyncPostCallContext;
    

    For more information about asynchronous pre- and post-call context, see Using Asynchronous Pre- and Post-call Contexts. See the "weblogic.wsee.async" package in Oracle WebLogic Server API Reference for additional reference information about these APIs.

  • In the body of the JWS file, use the required @ServiceClient JWS annotation to specify the WSDL, name, and port of the Web service you will be invoking asynchronously. You specify this annotation at the field-level on a variable, whose data type is the JAX-RPC port type of the Web service you are invoking.

      @ServiceClient(
           wsdlLocation="http://localhost:7001/async/StockQuote?WSDL",
           serviceName="StockQuoteService", 
           portName="StockQuote")
    
      private StockQuotePortType port;
    

    When you annotate a variable (in this case, port) with the @ServiceClient annotation, the Web services runtime automatically initializes and instantiates the variable, preparing it so that it can be used to invoke another Web service asynchronously.

  • In the method of the JWS file which is going to invoke the getQuote operation asynchronously, get a pre-call asynchronous context using the context factory:

    AsyncPreCallContext apc =
        AsyncCallContextFactory.getAsyncPreCallContext();
    

    For more information about asynchronous pre- and post-call context, see Using Asynchronous Pre- and Post-call Contexts.

  • Use the setProperty method of the pre-call context to create a property to store the username:

    apc.setProperty("userName", userName);
    
  • Using the stub you annotated with the @ServiceClient annotation, invoke the operation (in this case, getQuote). Instead of invoking it directly, however, invoke the asynchronous flavor of the operation, which has Async added on to the end of its name. The asynchronous flavor always returns void. Pass the asynchronous context as the first parameter:

    port.getQuoteAsync(apc, symbol);
    
  • For each operation you will be invoking asynchronously, create a method called onOperationnameAsyncResponse, where Operationname refers to the name of the operation, with initial letter always capitalized. The method must return void, and have two parameters: the post-call asynchronous context and the return value of the operation you are invoking. Annotate the method with the @AsyncResponse JWS annotation; use the target attribute to specify the variable whose datatype is the JAX-RPC stub and the operation attribute to specify the name of the operation you are invoking asynchronously. Inside the body of the method, put the business logic that processes the value returned by the operation. Use the getProperty method of the post-call context to get the property that was set by pre-call context before invoking the asynchronous method:

      @AsyncResponse(target="port", operation="getQuote")
      public void onGetQuoteAsyncResponse(AsyncPostCallContext apc, 
           int quote) {
        // Get the userName property we set on AsyncPreCallContext
        String userName = (String)apc.getProperty("userName");
        System.out.println("-------------------");
        System.out.println("Got quote " + quote );
        System.out.println("-------------------");
      }
    

    For more information about asynchronous pre- and post-call context, see Using Asynchronous Pre- and Post-call Contexts.

  • For each operation you will be invoking asynchronously, create a method called onOperationnameAsyncFailure, where Operationname refers to the name of the operation, with initial letter capitalized. The method must return void, and have two parameters: the post-call asynchronous context and a Throwable object, the superclass of all exceptions to handle any type of exception thrown by the invoked operation. Annotate the method with the @AsyncFailure JWS annotation; use the target attribute to specify the variable whose datatype is the JAX-RPC stub and the operation attribute to specify the name of the operation you are invoking asynchronously. Inside the method, you can determine the exact nature of the exception and write appropriate Java code.

      @AsyncFailure(target="port", operation="getQuote")
      public void onGetQuoteAsyncFailure(AsyncPostCallContext apc, 
          Throwable e) {
        System.out.println("-------------------");
        e.printStackTrace();
        System.out.println("-------------------");
      }
    

    Note:

    You are not required to use the @AsyncResponse and @AsyncFailure annotations, although it is a good practice because it clears up any ambiguity and makes your JWS file clean and understandable. However, in the rare use case where you want one of the onXXX methods to handle the asynchronous response or failure from two (or more) stubs that are invoking operations from two different Web services that have the same name, then you should explicitly NOT use these annotations. Be sure that the name of the onXXX methods follow the correct naming conventions exactly, as described above.

Using Asynchronous Pre- and Post-call Contexts

The AsyncPreCallContext and AsyncPostCallContext APIs describe asynchronous contexts that you can use in your Web service for a variety of reasons. For example:

  • Set a property in the pre-context so that the method that handles the asynchronous response can distinguish between different asynchronous calls.

  • Get and set contextual variables, such as the name of the user invoking the operation, their password, and so on.

  • Get the name of the JAX-RPC stub that invoked a method asynchronously; and to set a time-out interval on the context.

To use asynchronous pre- and post-call contexts:

  1. Import the asynchronous pre- and post-call context WebLogic APIs:

    import weblogic.wsee.async.AsyncCallContextFactory;
    import weblogic.wsee.async.AsyncPreCallContext;
    import weblogic.wsee.async.AsyncPostCallContext;
    
  2. In the method of the JWS file that is going to invoke the asynchronous operation, get a pre-call asynchronous context using the context factory. For example:

    AsyncPreCallContext apc =
        AsyncCallContextFactory.getAsyncPreCallContext();
    
  3. Use the pre-call context methods to operate on the asynchronous context before the asynchronous method is called. The following example uses the setProperty method of the pre-call context to create a property that stores the username:

    apc.setProperty("userName", userName);
    
  4. Use the post-call context methods to operate on the asynchronous context after the asynchronous method is called. The following example uses the getProperty method of the post-call context to get the property that was set by pre-call context before invoking the asynchronous method:

    String userName = (String)apc.getProperty("userName");
    

Example of a Synchronous Invoke

The following example shows a JWS file that invokes the getQuote operation of the StockQuote Web service synchronously. The example is shown only so you can compare it with the corresponding asynchronous invoke shown in Writing the Asynchronous JWS File.

package examples.webservices.async_req_res;

import weblogic.jws.WLHttpTransport;
import weblogic.jws.ServiceClient;

import javax.jws.WebService;
import javax.jws.WebMethod;

import java.rmi.RemoteException;

@WebService(name="SyncClientPortType",
            serviceName="SyncClientService",
            targetNamespace="http://examples.org/")

@WLHttpTransport(contextPath="syncClient",
                 serviceUri="SyncClient",
                 portName="SyncClientPort")

/**
 *  Normal service-to-service client that invokes StockQuote service
 *  synchronously.
 */

public class SyncClientImpl {

  @ServiceClient(wsdlLocation="http://localhost:7001/async/StockQuote?WSDL",
                 serviceName="StockQuoteService", portName="StockQuote")
  private StockQuotePortType port;

  @WebMethod
  public void nonAsyncOperation(String symbol) throws RemoteException {

    int quote = port.getQuote(symbol); 

    System.out.println("-------------------");
    System.out.println("Got quote " + quote );
    System.out.println("-------------------");

  }

}

Updating the build.xml File When Using Asynchronous Request-Response

To update a build.xml file to generate the JWS file that invokes a Web service operation asynchronously, add taskdefs and a build-clientService target that looks something like the following; see the description after the example for details:

<taskdef name="jwsc"
    classname="weblogic.wsee.tools.anttasks.JwscTask" />

<target name="build-clientService">

    <jwsc
        enableAsyncService="true"
        srcdir="src"
        destdir="${clientService-ear-dir}" >

        <jws file="examples/webservices/async_req_res/StockQuoteClientImpl.java" >

          <clientgen 
            wsdl="http://${wls.hostname}:${wls.port}/async/StockQuote?WSDL" 
            packageName="examples.webservices.async_req_res"/> 

        </jws>

    </jwsc>

</target>

Use the taskdef Ant task to define the full classname of the jwsc Ant tasks.

Update the jwsc Ant task that compiles the client Web service to include a <clientgen> child element of the <jws> element so as to generate and compile the JAX-RPC stubs for the deployed StockQuote Web service. The jwsc Ant task automatically packages them in the generated WAR file so that the client Web service can immediately access the stubs. By default, the jwsc Ant task in this case generates both synchronous and asynchronous flavors of the Web service operations in the JAX-RPC stubs. You do this because the StockQuoteClientImpl JWS file imports and uses one of the generated classes.

Disabling The Internal Asynchronous Service

By default, every WebLogic Server instance deploys an internal asynchronous Web service that handles the asynchronous request-response feature. To specify that you do not want to deploy this internal service, start the WebLogic Server instance using the -Dweblogic.wsee.skip.async.response=true Java system property.

One reason for disabling the asynchronous service is if you use a WebLogic Server instance as a Web proxy to a WebLogic cluster. In this case, asynchronous messages will never get to the cluster, as required, because the asynchronous service on the proxy server consumes them instead. For this reason, you must disable the asynchronous service on the proxy server using the system property.

For details on specifying Java system properties to configure WebLogic Server, see "Specifying Java Options for a WebLogic Server Instance" in Managing Server Startup and Shutdown for Oracle WebLogic Server.

Using Asynchronous Request Response With a Proxy Server

Client applications that use the asynchronous request-response feature might not invoke the operation directly, but rather, use a proxy server. Reasons for using a proxy include the presence of a firewall or the deployment of the invoked Web service to a cluster.

In this case, the WebLogic Server instance that hosts the invoked Web service must be configured with the address and port of the proxy server. If your Web service is deployed to a cluster, you must configure every server in the cluster.

This procedure describes how to create a network channel, the primary configurable WebLogic Server resource for managing network connection. Network channels enable you to provide a consistent way to access the front-end address of a cluster. For more information about network channels, see "Understanding Network Channels" in Configuring Server Environments for Oracle WebLogic Server.

For each server instance:

  1. Create a network channel for the protocol you use to invoke the Web service. You must name the network channel weblogic-wsee-proxy-channel-XXX, where XXX refers to the protocol. For example, to create a network channel for HTTPS, call it weblogic-wsee-proxy-channel-https.

    See "Configure custom network channels" in Oracle WebLogic Server Administration Console Help for general information about creating a network channel.

  2. Configure the network channel, updating the External Listen Address and External Listen Port fields with the address and port of the proxy server, respectively.