13 Roadmap for Developing Reliable Web Services and Clients

This chapter presents best practices for developing WebLogic web services and clients for Java API for XML Web Services (JAX-WS) for WebLogic Server 12.1.3.

This chapter includes the following sections:

Roadmap for Developing Reliable Web Service Clients

Table 13-1 provides best practices for developing reliable web service clients, including an example that illustrates the best practices presented. These guidelines should be used in conjunction with the guidelines provided in Chapter 7, "Roadmap for Developing JAX-WS Web Service Clients.".

Table 13-1 Roadmap for Developing Reliable Web Service Clients

Best Practice Description

Always implement a reliability error listener.

For more information, see Implementing the Reliability Error Listener.

Group messages into units of work.

Rather than incur the RM sequence creation and termination protocol overhead for every message sent, you can group messages into business units of work—also referred to as batching. For more information, see Grouping Messages into Business Units of Work (Batching).

Note: This best practice is not demonstrated in Example 13-1.

Set the acknowledgement interval to a realistic value for your particular scenario.

The recommended setting is two times the nominal interval between requests. For more information, see Configuring the Acknowledgement Interval.

Note: This best practice is not demonstrated in Example 13-1.

Set the base retransmission interval to a realistic value for your particular scenario.

The recommended setting is two times the acknowledgement interval or nominal response time, whichever is greater. For more information, see Configuring the Base Retransmission Interval.

Note: This best practice is not demonstrated in Example 13-1.

Set timeouts (inactivity and sequence expiration) to realistic values for your particular scenario.

For more information, see Configuring Inactivity Timeout and Configuring the Sequence Expiration.

Note: This best practice is not demonstrated in Example 13-1.


The following example illustrates best practices for developing reliable web service clients.

Example 13-1 Reliable Web Service Client Best Practices Example

import java.io.*;
import java.util.*;
 
import javax.servlet.*;
import javax.xml.bind.JAXBContext;
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 weblogic.wsee.reliability2.api.ReliabilityErrorContext;
import weblogic.wsee.reliability2.api.ReliabilityErrorListener;
import weblogic.wsee.reliability2.api.WsrmClientInitFeature;
 
import com.sun.xml.ws.developer.JAXWSProperties;
 
/**
 * Example client for invoking a reliable web service asynchronously.
 */
public class BestPracticeAsyncRmClient
  extends GenericServlet {
 
  private BackendReliableServiceService _service;
  private BackendReliableService _singletonPort;
  private WebServiceFeature[] _features;
 
  private static int _requestCount;
  private static String _lastResponse;
  private static final String MY_PROPERTY = "MyProperty";
 
  @Override
  public void init()
    throws ServletException {
 
    _requestCount = 0;
    _lastResponse = null;
 
    // Only create the web service object once as it is expensive to create repeatedly.
    if (_service == null) {
      _service = new BackendReliableServiceService();
    }
 
    // Best Practice: Use a stored list of features, per client ID, to create client instances.
    // Define all features for the web service port, per client ID, so that they are 
    // consistent each time the port is called. For example: 
    // _service.getBackendServicePort(_features);
 
    List<WebServiceFeature> features = new ArrayList<WebServiceFeature>();
 
    // Best Practice: Explicitly define the client ID.
    ClientIdentityFeature clientIdFeature =
      new ClientIdentityFeature("MyBackendServiceAsyncRmClient");
    features.add(clientIdFeature);
 
    // Best Practice: Always implement a reliability error listener. 
    // Include this feature in your reusable feature list. This enables you to determine
    // a reason for failure, for example, RM cannot deliver a request or the RM sequence fails in
    // some way (for example, client credentials refused at service).
    WsrmClientInitFeature rmFeature = new WsrmClientInitFeature();
    features.add(rmFeature);
    rmFeature.setErrorListener(new ReliabilityErrorListener() {
      public void onReliabilityError(ReliabilityErrorContext context) {
 
        // At a *minimum* do this
        System.out.println("RM sequence failure: " +
                           context.getFaultSummaryMessage());
        _lastResponse = context.getFaultSummaryMessage();
 
        // And optionally do this...
 
        // The context parameter conveys whether a request or the entire
        // sequence has failed. If a sequence fails, you will get a notification
        // for each undelivered request (if any) on the sequence.
        if (context.isRequestSpecific()) {
          // Single request failure (possibly as part of a larger sequence failure).
          // Retrieve the original request.
          String operationName = context.getOperationName();
          System.out.println("Failed to deliver request for operation '" +
                             operationName + "'. Fault summary: " +
                             context.getFaultSummaryMessage());
          if ("DoSomething".equals(operationName)) {
            try {
              String request = context.getRequest(JAXBContext.newInstance(),
                                                  String.class);
              System.out.println("Failed to deliver request for operation '" +
                                 operationName + "' with content: " +
                                 request);
              Map<String, Serializable> requestProps =
                context.getUserRequestContextProperties();
              if (requestProps != null) {
                // Retrieve the request property. Use MyProperty
                // to describe the request that failed and print this value
                // during the simple 'error recovery' below.
                String myProperty = (String)requestProps.get(MY_PROPERTY);
                System.out.println("Got MyProperty value propagated from request: "+
                                   myProperty);
                System.out.println(myProperty + " failed!");
              }
            } catch (Exception e) {
              e.printStackTrace();
            }
          }
        } else {
          // The entire sequence has encountered an error.
          System.out.println("Entire sequence failed: " +
                             context.getFaultSummaryMessage());
 
        }
      }
    });
 
    // 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.
    BackendReliableServiceAsyncHandler handler =
      new BackendReliableServiceAsyncHandler() {
        public void onDoSomethingResponse(Response<DoSomethingResponse> res) {
          // ... Handle Response ...
          try {
            // Show getting the MyProperty value back.
            DoSomethingResponse response = res.get();
            _lastResponse = response.getReturn();
            System.out.println("Got (reliable) 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 "MyBackendServiceAsyncRmClient."
 
    _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, we will get a call to destroy() before this.
    _singletonPort = _service.getBackendReliableServicePort(_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 in order to make a new invocation against
    // BackendService to generate a new response
 
    // Best Practice: Synchronize use of client instances.
    // Create another port 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.
    // NOTE: This is *DEFINITELY* not best practice or ideal because our application is
    //       incurring the cost of an RM handshake and sequence termination
    //       for *every* reliable request sent. It would be better to send
    //       multiple requests on each sequence. If there is not a natural grouping 
    //       for messages (a business 'unit of work'), then you could batch
    //       requests onto a sequence for efficiency. For more information, see
    //        Grouping Messages into Business Units of Work (Batching).
    BackendReliableService anotherPort =
      _service.getBackendReliableServicePort(_features);
 
    // Set the endpoint address for BackendService.
    ((BindingProvider)anotherPort).getRequestContext().
      put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
          "http://localhost:7001/BestPracticeReliableService/BackendReliableService");
 
    // Make the invocation. Our asynchronous handler implementation (set 
    // into the AsyncClientHandlerFeature above) receives the response.
    String request = "Protect and serve";
    System.out.println("Invoking DoSomething reliably/async with request: " +
                       request);
    // Add a persistent context property that will be returned on the response.
    // This property can be used to 'remember' the context of this
    // request and subsequently process the response. This property will *not*
    // get passed over 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 (reliably) with MyProperty value: " +
                       myProperty);
    anotherPort.doSomethingAsync(request);
 
    // Return a canned string indicating the response was not received
    // synchronously. Client needs 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, the port will be closed automatically when it goes out of scope.
    // This will force the termination of the RM sequence we created when sending the first
    // doSomething request. For a better way to handle this, see 
    //  Grouping Messages into Business Units of Work (Batching).
    // NOTE: Even though the port is closed explicitly (or even if it goes out of scope)
    //       the reliable request sent above will still be delivered
    //       under the scope of the client ID used. So, even if the service endpoint
    //       is down, RM retries the request and delivers it when the service endpoint 
    //       available. The asynchronous resopnse will be delivered as if the port instance was 
    //       still available.
    ((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 our 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();
    }
  }
}

Roadmap for Developing Reliable Web Services

Table 13-2 provides best practices for developing reliable web services. For best practices when accessing reliable web services from behind a firewall, see Roadmap for Accessing Reliable Web Services from Behind a Firewall (Make Connection).

Table 13-2 Roadmap for Developing Reliable Web Services

Best Practice Description

Set the base retransmission interval to a realistic value for your particular scenario.

For more information, see Configuring the Base Retransmission Interval.

Set the acknowledgement interval to a realistic value for your particular scenario.

The recommended setting is two times the nominal interval between requests. For more information, see Configuring the Acknowledgement Interval.

Set timeouts (inactivity and sequence expiration) to realistic values for your particular scenario.

Consider the following:

  • For very short-lived exchanges, the default timeouts may be too long and sequence state might be maintained longer than necessary.

  • Set timeouts to two times the expected lifetime of a given business unit of work. This allows the sequence to live long enough

For more information, see Configuring Inactivity Timeout and Configuring the Sequence Expiration.

Use an reliable messaging policy that reflects the minimum delivery assurance (or quality of service) required.

By default, the delivery assurance is set to Exactly Once, In Order. If you do not require ordering, it can increase performance to set the delivery assurance to simply Exactly Once. Similarly, if your service can tolerate duplicate requests, delivery assurance can be set to At Least Once.

For more information about delivery assurance for reliable messaging, see Table 14-1, "Delivery Assurances for Reliable Messaging" and Creating the Web Service Reliable Messaging WS-Policy File.


Roadmap for Accessing Reliable Web Services from Behind a Firewall (Make Connection)

Table 13-3 provides best practices for accessing reliable web services from behind a firewall using Make Connection. These guidelines should be used in conjunction with the general guidelines provided in Roadmap for Developing Reliable Web Services and Chapter 11, "Roadmap for Developing Asynchronous Web Service Clients."

Table 13-3 Roadmap for Accessing Reliable Web Services from Behind a Firewall (Make Connection)

Best Practice Description

Coordinate the Make Connection polling interval with the reliable messaging base retransmission interval.

The polling interval you set for Make Connection transport sets the lower limit for the amount of time it takes for reliable messaging protocol messages to make the round trip between the client and service. If you set the reliable messaging base retransmission interval to a value near to the Make Connection polling interval, it will be unlikely that a reliable messaging request will be received by the web service, and the accompanying RM acknowledgement sent for that request (at best one Make Connection polling interval later) before the reliable messaging runtime attempts to retransmit the request. Setting the reliable messaging base retransmission interval to a value that is too low results in unnecessary retransmissions for requests, and potentially a cascading load on the service side as it attempts to process redundant incoming requests and Make Connection poll messages to retrieve the responses from those requests.

Oracle recommends setting the base retransmission interval to a value that is at least two times the Make Connection polling interval.

Note: When web services reliable messaging and Make Connection are used together, the Make Connection polling interval value will be adjusted at runtime, if necessary, to ensure that the value is set at least 3 seconds less than the reliable messaging base transmission interval. If the base transmission interval is three seconds or less, the Make Connection polling interval is set to the value of the base retransmission interval.

For more information setting the Make Connection polling interval and reliable messaging base retransmission interval, see Configuring the Polling Interval and Configuring the Base Retransmission Interval, respectively.


Roadmap for Securing Reliable Web Services

Table 13-4 provides best practices for securing reliable web services using WS-SecureConversation. These guidelines should be used in conjunction with the guidelines provided in Roadmap for Developing Reliable Web Services.

Table 13-4 Roadmap for Securing Reliable Web Services

Best Practice Description

Coordinate the WS-SecureConversation lifetime with the reliable messaging base retransmission and acknowledgement intervals.

A WS-SecureConversation lifetime that is set to a value near to or less than the reliable messaging base retransmission and acknowledgement intervals may result in the WS-SecureConversation token expiring before the reliable messaging handshake message can be sent to the web service. For this reason, Oracle recommends setting the WS-SecureConversation lifetime to a value that is at least two times the base retransmission interval.

For more information setting the base retransmission interval, see Configuring the Base Retransmission Interval.