Skip Headers
Oracle® Fusion Middleware Programming Advanced Features of JAX-WS Web Services for Oracle WebLogic Server
11g Release 1 (10.3.6)

Part Number E13734-06
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Master Index
Master Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
PDF · Mobi · ePub

3 Roadmaps for Developing Web Service Clients

This chapter presents best practices for developing WebLogic Web service clients for Java API for XML Web Services (JAX-WS).

This chapter includes the following sections:

Note:

It is assumed that you are familiar with the general concepts for developing Web service clients, as described "Invoking Web Services" in Getting Started With JAX-WS Web Services for Oracle WebLogic Server.

For best practices for developing reliable Web service clients, see Chapter 5, "Roadmap for Developing Reliable Web Services and Clients."

In the following sections, client instance can be a port or a Dispatch instance.

Roadmap for Developing Web Service Clients

Table 3-0 provides best practices for developing Web service clients, including an example that illustrates the best practices presented. For additional best practices when developing asynchronous Web service clients, see Roadmap for Developing Asynchronous Web Service Clients.

Table 3-1 Roadmap for Developing Web Service Clients

Best Practice Description

Synchronize use of client instances.

Create client instances as you need them; do not store them long term.

Use a stored list of features, including client ID, to create client instances.

Define all features for the Web service client instance, including client ID, so that they are consistent each time the client instance is created. For example:

_service.getBackendServicePort(_features);

Explicitly define the client ID.

Use the ClientIdentityFeature to define the client ID explicitly. This client ID is used to group statistics and other monitoring information, and for reporting runtime validations, and so on. For more information, see "Managing Client Identity" in Getting Started With JAX-WS Web Services for Oracle WebLogic Server.

Note: Oracle strongly recommends that you define the client ID explicitly. If not explicitly defined, the server generates the client ID automatically, which may not be user-friendly.

Explicitly close client instances when processing is complete.

For example:

((java.io.Closeable)port).close();

If not closed explicitly, the client instance will be closed automatically when it goes out of scope.

Note: The client ID remains registered and visible until the container (Web application or EJB) is deactivated. For more information, see "Client Identity Lifecycle" in Getting Started With JAX-WS Web Services for Oracle WebLogic Server.


The following example illustrates best practices for developing Web service clients.

Example 3-1 Web Service Client Best Practices Example

import java.io.IOException;
import java.util.*;
 
import javax.servlet.*;
import javax.xml.ws.*;
 
import weblogic.jws.jaxws.client.ClientIdentityFeature;
 
/**
 * Example client for invoking a Web service.
 */
public class BestPracticeClient
  extends GenericServlet {
 
  private BackendServiceService _service;
  private WebServiceFeature[] _features;
  private ClientIdentityFeature _clientIdFeature;
 
  @Override
  public void init()
    throws ServletException {
 
    // Create a single instance of a Web service as it is expensive to create repeatedly.
    if (_service == null) {
      _service = new BackendServiceService();
    }
 
    // Best Practice: Use a stored list of features, per client ID, to create client instances.
    // Define all features for the Web service client instance, per client ID, so that they are 
    // consistent each time the client instance is created. For example: 
    // _service.getBackendServicePort(_features);
 
    List<WebServiceFeature> features = new ArrayList<WebServiceFeature>();
 
    // Best Practice: Explicitly define the client ID.
    // TODO: Maybe allow ClientIdentityFeature to store other features, and
    //       then create new client instances simply by passing the
    //       ClientIdentityFeature (and the registered features are used).
    _clientIdFeature = new ClientIdentityFeature("MyBackendServiceClient");
    features.add(_clientIdFeature);
 
    // Set the features used when creating clients with
    // the client ID "MyBackendServiceClient". The features are stored in an array to 
    // reinforce that the list should be treated as immutable.
    _features = features.toArray(new WebServiceFeature[features.size()]);
  }
 
  @Override
  public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException {
 
    // ... Read the servlet request ...
 
    // Best Practice: Synchronize use of client instances.
    // Create a Web service client instance to talk to the backend service.
    // Note, at this point the client ID is 'registered' and becomes
    // visible to monitoring tools such as the Administration Console and WLST.
    // The client ID *remains* registered and visible until the container
    // (the Web application hosting our servlet) is deactivated (undeployed).
    //
    // A client ID can be used when creating multiple client instances (port or Dispatch client).
    // The client instance should be created with the same set of features each time, and should
    // use the same service class and refer to the same port type. 
    // A given a client ID should be used for a given port type, but not across port types.
    // It can be used for both port and Dispatch clients.
    BackendService port =
      _service.getBackendServicePort(_features);
 
    // Set the endpoint address for BackendService.
    ((BindingProvider)port).getRequestContext().
      put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
          "http://localhost:7001/BestPracticeService/BackendService");
 
    // Print out the explicit client ID, and compare it to the client ID 
    // that would have been generated automatically for the client instance.
    showClientIdentity();
 
    // Make the invocation on our real port
    String request = "Make a cake";
    System.out.println("Invoking DoSomething with request: " + request);
    String response = port.doSomething(request);
    System.out.println("Got response: " + response);
    res.getWriter().write(response);
 
    // Best Practice: Explicitly close client instances when processing is complete.
    // If not closed, the client instance will be closed automatically when it goes out of 
    // scope. Note, this client ID will remain registered and visible until our
    // container (Web application) is undeployed.
    ((java.io.Closeable)port).close();
  }
 
  /**
    // Print out the client's full ID, which is a combination of
    // the client ID provided above and qualifiers from the application and
    // Web application that contain the client. Then compare this with the client ID that
    // would have been generated for the client instance if not explicitly set.
    //
  private void showClientIdentity()
    throws IOException {
 
    System.out.println("Client Identity is: " + _clientIdFeature.getClientId());
 
    // Create a client instance without explicitly defining the client ID to view the 
    // client ID that is generated automatically.
    ClientIdentityFeature dummyClientIdFeature =
      new ClientIdentityFeature(null);
    BackendService dummyPort =
      _service.getBackendServicePort(dummyClientIdFeature);
    System.out.println("Generated Client Identity is: " +
                       dummyClientIdFeature.getClientId());
    // Best Practice: Explicitly close client instances when processing is complete.
    // If not closed, the client instance will be closed automatically when it goes out of 
    // scope. Note, this client ID will remain registered and visible until our
    // container (Web application) is undeployed.
    ((java.io.Closeable)dummyPort).close();
  }
 
  @Override
  public void destroy() {
  }
}

Roadmap for Developing Asynchronous Web Service Clients

Table 3-1 provides best practices for developing asynchronous Web service clients, including an example that illustrates the best practices presented. These guidelines should be used in conjunction with the general guidelines provided in Roadmap for Developing Web Service Clients.

Table 3-2 Roadmap for Developing Asynchronous Web Service Clients

Best Practice Description

Define a port-based asynchronous callback handler, AsyncClientHandlerFeature, for asynchronous and dispatch callback handling.

Use of AsyncClientHandlerFeature is recommended as a best practice when using asynchronous invocation due to its scalability and ability to survive a JVM restart. It can be used by any client (survivable or not.) For information, see Developing the Asynchronous Handler Interface.

Define a singleton port instance and initialize it when the client container initializes (upon deployment).

Creation of the singleton port:

  • Triggers the asynchronous response endpoint to be published upon deployment.

  • Supports failure recovery by re-initializing the singleton port instance after VM restart.

Within a cluster, initialization of a singleton port will ensure that all member servers in the cluster publish an asynchronous response endpoint.This ensures that the asynchronous response messages can be delivered to any member server and optionally forwarded to the correct server via in-place cluster routing. For complete details, see Clustering Considerations for Asynchronous Web Service Messaging..

If using Make Connection for clients behind a firewall, set the Make Connection polling interval to a value that is realistic for your scenario.

The Make Connection polling interval should be set as high as possible to avoid unnecessary polling overhead, but also low enough to allow responses to be retrieved in a timely fashion. A recommended value for the Make Connection polling interval is one-half of the expected average response time of the Web service being invoked. For more information setting the Make Connection polling interval, see Configuring the Polling Interval.

Note: This best practice is not demonstrated in Example 3-2.

If using the JAX-WS Reference Implementation (RI), implement the AsyncHandler<T> interface.

Use of the AsyncHandler<T> interface is more efficient than the Response<T> interface. For more information and an example, see Using the JAX-WS Reference Implementation.

Note: This best practice is not demonstrated in Example 3-2.

Define a Work Manager and set the thread pool minimum size constraint (min-threads-constraint) to a value that is at least as large as the expected number of concurrent requests or responses into the service.

For example, if a Web service client issues 20 requests in rapid succession, the recommended thread pool minimum size constraint value would be 20 for the application hosting the client. If the configured constraint value is too small, performance can be severely degraded as incoming work waits for a free processing thread.

For more information about the thread pool minimum size constraint, see "Constraints" in Configuring Server Environments for Oracle WebLogic Server.


The following example illustrates best practices for developing asynchronous Web service clients.

Example 3-2 Asynchronous Web Service Client Best Practices Example

import java.io.*;
import java.util.*;
 
import javax.servlet.*
import javax.xml.ws.*
 
import weblogic.jws.jaxws.client.ClientIdentityFeature;
import weblogic.jws.jaxws.client.async.AsyncClientHandlerFeature;
import weblogic.jws.jaxws.client.async.AsyncClientTransportFeature;
 
import com.sun.xml.ws.developer.JAXWSProperties;
 
/**
 * Example client for invoking a Web service asynchronously.
 */
public class BestPracticeAsyncClient
  extends GenericServlet {
 
  private static final String MY_PROPERTY = "MyProperty";
 
  private BackendServiceService _service;
  private WebServiceFeature[] _features;
  private BackendService _singletonPort;
 
  private static String _lastResponse;
  private static int _requestCount;
 
  @Override
  public void init()
    throws ServletException {
 
    // Only create the Web service object once as it is expensive to create repeatedly.
    if (_service == null) {
      _service = new BackendServiceService();
    }
 
    // Best Practice: Use a stored list of features, including client ID, to create client 
    // instances.
    // Define all features for the Web service client instance, including client ID, so that they
    // are consistent each time the client instance is created. For example: 
    // _service.getBackendServicePort(_features);
 
    List<WebServiceFeature> features = new ArrayList<WebServiceFeature>();
 
    // Best Practice: Explicitly define the client ID.
    ClientIdentityFeature clientIdFeature =
      new ClientIdentityFeature("MyBackendServiceAsyncClient");
    features.add(clientIdFeature);
 
    // Asynchronous endpoint
    AsyncClientTransportFeature asyncFeature =
      new AsyncClientTransportFeature(getServletContext());
    features.add(asyncFeature);
 
    // Best Practice: Define a port-based asynchronous callback handler,
    // AsyncClientHandlerFeature, for asynchronous and dispatch callback handling.
    BackendServiceAsyncHandler handler =
      new BackendServiceAsyncHandler() {
        // This class is stateless and should not depend on
        // having member variables to work with across restarts.
        public void onDoSomethingResponse(Response<DoSomethingResponse> res) {
          // ... Handle Response ...
          try {
            DoSomethingResponse response = res.get();
            res.getContext();
            _lastResponse = response.getReturn();
            System.out.println("Got async response: " + _lastResponse);
            // Retrieve the request property. This property can be used to 
            // 'remember' the context of the request and subsequently process
            // the response.
            Map<String, Serializable> requestProps =
              (Map<String, Serializable>)
                res.getContext().get(JAXWSProperties.PERSISTENT_CONTEXT);
            String myProperty = (String)requestProps.get(MY_PROPERTY);
            System.out.println("Got MyProperty value propagated from request: "+
                               myProperty);
          } catch (Exception e) {
            _lastResponse = e.toString();
            e.printStackTrace();
          }
        }
      };
    AsyncClientHandlerFeature handlerFeature =
      new AsyncClientHandlerFeature(handler);
    features.add(handlerFeature);
 
    // Set the features used when creating clients with
    // the client ID "MyBackendServiceAsyncClient".
 
    _features = features.toArray(new WebServiceFeature[features.size()]);

    // Best Practice: Define a singleton port instance and initialize it when 
    // the client container initializes (upon deployment).
    // The singleton port will be available for the life of the servlet.
    // Creation of the singleton port triggers the asynchronous response endpoint to be published
    // and it will remain published until our container (Web application) is undeployed.
    // Note, the destroy() method will be called before this.
    // The singleton port ensures proper/robust operation in both
    // recovery and clustered scenarios.
    _singletonPort = _service.getBackendServicePort(_features);
  }
 
  @Override
  public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException {
 
    // TODO: ... Read the servlet request ...
 
    // For this simple example, echo the _lastResponse captured from 
    // an asynchronous DoSomethingResponse response message.
 
    if (_lastResponse != null) {
      res.getWriter().write(_lastResponse);
      _lastResponse = null; // Clear the response so we can get another
      return;
    }
 
    // Set _lastResponse to NULL to to support the invocation against
    // BackendService to generate a new response.
 
    // Best Practice: Synchronize use of client instances.
    // Create another client instance using the *exact* same features used when creating _
    // singletonPort. Note, this port uses the same client ID as the singleton port 
    // and it is effectively the same as the singleton
    // from the perspective of the Web services runtime. 
    // This port will use the asynchronous response endpoint for the client ID, 
    // as it is defined in the _features list.
    BackendService anotherPort =
      _service.getBackendServicePort(_features);
 
    // Set the endpoint address for BackendService.
    ((BindingProvider)anotherPort).getRequestContext().
      put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
          "http://localhost:7001/BestPracticeService/BackendService");
 
    // Add a persistent context property that will be retrieved on the
    // response. This property can be used as a reminder of the context of this
    // request and subsequently process the response. This property will *not*
    // be passed over the wire, so the properties can change independent of the
    // application message.
    Map<String, Serializable> persistentContext =
      (Map<String, Serializable>)((BindingProvider)anotherPort).
        getRequestContext().get(JAXWSProperties.PERSISTENT_CONTEXT);
    String myProperty = "Request " + (++_requestCount);
    persistentContext.put(MY_PROPERTY, myProperty);
    System.out.println("Request being made with MyProperty value: " +
                       myProperty);
 
    // Make the asychronous invocation. The asynchronous handler implementation (set 
    // into the AsyncClientHandlerFeature above) receives the response.
    String request = "Dance and sing";
    System.out.println("Invoking DoSomething asynchronously with request: " +
                       request);
    anotherPort.doSomethingAsync(request);
 
    // Return a canned string indicating the response was not received
    // synchronously. Client will need to invoke the servlet again to get
    // the response.
    res.getWriter().write("Waiting for response...");
 
    // Best Practice: Explicitly close client instances when processing is complete.
    // If not closed explicitly, the port will be closed automatically when it goes out of scope.
    ((java.io.Closeable)anotherPort).close();
  }
 
  @Override
  public void destroy() {
 
    try {
      // Best Practice: Explicitly close client instances when processing is complete.
      // Close the singleton port created during initialization. Note, the asynchronous
      // response endpoint generated by creating _singletonPort *remains*
      // published until our container (Web application) is undeployed.
      ((java.io.Closeable)_singletonPort).close();

      // Upon return, the Web application is undeployed, and the asynchronous
      // response endpoint is stopped (unpublished). At this point,
      // the client ID used for _singletonPort will be unregistered and will no longer be
      // visible from the Administration Console and WLST.
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}