Friday, December 13, 2013

Programming Stateful JAX-WS Web Services Using HTTP Session

This chapter describes how you can develop JAX-WS Web services that interact with an Oracle database.

Overview of Stateful Web Services

Normally, a JAX-WS Web service is stateless: that is, none of the local variables and object values that you set in the Web service object are saved from one invocation to the next. Even sequential requests from a single client are treated each as independent, stateless method invocations.
There are Web service use cases where a client may want to save data on the service during one invocation and then use that data during a subsequent invocation. For example, a shopping cart object may be added to by repeated calls to the addToCart web method and then fetched by the getCart web method. In a stateless Web service, the shopping cart object would always be empty, no matter how many addToCart methods were called. But by using HTTP Sessions to maintain state across Web service invocations, the cart may be built up incrementally, and then returned to the client.
Enabling stateful support in a JAX-WS Web service requires a minimal amount of coding on both the client and server.

Accessing HTTP Session on the Server

On the server, every Web service invocation is tied to an HttpSession object. This object may be accessed from the Web service Context that, in turn, may be bound to the Web service object using resource injection. Once you have access to your HttpSession object, you can "hang" off of it any stateful objects you want. The next time your client calls the Web service, it will find that same HttpSession object and be able to lookup the objects previously stored there. Your Web service is stateful!
The steps required on the server:
  1. Add the @Resource (defined by Common Annotations for the Java Platform, JSR 250) to the top of your Web service.
  2. Add a variable of type WebServiceContext that will have the context injected into it.
  3. Using the Web service context, get the HttpSession object.
  4. Save objects in the HttpSession using the setAttribute method and retrieve saved object using getAttribute. Objects are identified by a string value you assign.
The following snippet shows its usage:
Example 18-1 Accessing HTTP Session on the Server
@WebService
public class ShoppingCart {
   @Resource    // Step 1
   private WebServiceContext wsContext;    // Step 2
   public int addToCart(Item item) {
      // Find the HttpSession
      MessageContext mc = wsContext.getMessageContext();    // Step 3
      HttpSession session = ((javax.servlet.http.HttpServletRequest)mc.get(MessageContext.SERVLET_REQUEST)).getSession();
      if (session == null)
         throw new WebServiceException("No HTTP Session found");
      // Get the cart object from the HttpSession (or create a new one)
      List cart = (List)session.getAttribute("myCart");  // Step 4
      if (cart == null)
         cart = new ArrayList();
      // Add the item to the cart (note that Item is a class defined 
      // in the WSDL)
      cart.add(item);
      // Save the updated cart in the HTTPSession (since we use the same 
      // "myCart" name, the old cart object will be replaced)
      session.setAttribute("myCart", cart);
      // return the number of items in the stateful cart
      return cart.size();
   }
}

Enabling HTTP Session on the Client

The client-side code is quite simple. All you need to do is set the SESSION_MAINTAIN_PROPERTY on the request context. This tells the client to pass back the HTTP Cookies that it receives from the Web service. The cookie contains a session ID that allows the server to match the Web service invocation with the correct HttpSession, providing access to any saved stateful objects.
Example 18-2 Enabling HTTP Session on the Client
ShoppingCart proxy = new CartService().getCartPort();
((BindingProvider)proxy).getRequestContext().put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true);
// Create a new Item object with a part number of '123456' and an item 
// count of 4.
Item item = new Item('123456', 4);
// After first call, we'll print '1' (the return value is the number of objects 
// in the Cart object)
System.out.println(proxy.addToCart(item));
// After the second call, we'll print '2', since we've added another 
// Item to the stateful, saved Cart object.
System.out.println(proxy.addToCart(item));

Developing Stateful Services in a Cluster Using Session State Replication

In a high-availability environment, a JAX-WS Web service may be replicated across multiple server instances in a cluster. A stateful JAX-WS Web service is supported in this environment through the use of the WebLogic Server HTTP Session State Replication feature. For more information, see "HTTP Session State Replication" in Using Clusters for Oracle WebLogic Server.
There are a variety of techniques and configuration requirements for setting up a clustered environment using session state replication (for example, supported servers and load balancers, and so on). From the JAX-WS programming perspective, the only new consideration is that the objects you store in the HttpSession using the HttpSession.setAttribute method (as in Example 18-1) must be Serializable. If they are Serializable, then these stateful objects become available to the Web service on all replicated Web service instances in the cluster, providing both load balancing and failover capabilities for JAX-WS stateful Web services.

A Note About the JAX-WS RI @Stateful Extension

The JAX-WS 2.1 Reference Implementation (RI) contains a vendor extension that supports a different model for stateful JAX-WS Web services using the @Stateful annotation. It's implementation "pins" the state to a particular instance and is not designed to be scalable or fault-tolerant. This feature is not supported for WebLogic Server JAX-WS Web services.

No comments: