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 sections:
Configuring the Host WebLogic Server Instance for the Asynchronous Web Service
Updating the build.xml File When Using Asynchronous Request-Response
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 11, "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.
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:
Table 7-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 |
Use your favorite IDE or text editor. See Writing the Asynchronous JWS File. |
3 |
Update your |
You will add a See Updating the build.xml File When Using Asynchronous Request-Response. |
4 |
Run the Ant target to build the |
For example: prompt> ant build-clientService |
5 |
Deploy the |
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 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".
Notes:
Alternatively, you can use WLST to configure the resources. For information about using WLST to extend the domain, see "Configuring Existing Domains" in Understanding the 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 7-2 Steps to Configure Host WebLogic Server Instance Manually for the Asynchronous Web Service
# |
Step | Description |
---|---|---|
1 |
Invoke the WebLogic Server Administration Console for the domain that contains the host WebLogic Server instance. |
To invoke the WebLogic Server Administration Console in your browser, enter the following URL: http://host:port/console where
See "Invoking the Administration Console" in Understanding WebLogic 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 Online 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 Online Help. If you want the asynchronous web service to use the default web services queue, set the JNDI name of the JMS queue to Clustering Considerations: If you are using the web service asynchronous feature in a cluster, you must:
|
4 |
Create a Work Manager. |
Define a Work Manager named See "Create global Work Managers" in Oracle WebLogic Server Administration Console Online Help. |
5 |
Tune your domain environment, as required. (Optional) |
Review "Tuning Heavily Loaded Systems to Improve Web Service Performance" in Tuning Performance of Oracle WebLogic Server. |
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("-------------------"); } }
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.
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 on
Operationname
AsyncResponse
, 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 on
Operationname
AsyncFailure
, 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.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:
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;
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();
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);
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");
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("-------------------");
}
}
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.
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 Administering Server Startup and Shutdown for Oracle WebLogic 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 Administering Server Environments for Oracle WebLogic Server.
For each server instance:
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 Online Help for general information about creating a network channel.
Configure the network channel, updating the External Listen Address and External Listen Port fields with the address and port of the proxy server, respectively.