Given a set of requirements, create and configure a Web service client that accesses a stateful Web service.

[Note]

Traditionally JAX-WS has never taken advantage of object state, just like servlet. That is, the container creates only one instance of your service class, and then have it serve all the requests concurrently. This makes it impossible to set values to instance fields, as you will experience concurrency problem as soon as multiple threads hit your service.

On HTTP, session is often used to store state. This technique is still useful for web services over HTTP. JAX-WS lets you do this today:

@WebService
public class Hello {

    @Resource
    private WebServiceContext wsContext;
    
    public int getCounter() {
        MessageContext msgContext = wsContext.getMessageContext();
        HttpServletRequest request = (HttpServletRequest) msgContext.get(MessageContext.SERVLET_REQUEST);
        HttpSession session = request.getSession();
        
        // Get a session property "counter" from context
        if (session == null) {
            throw new WebServiceException("No session in WebServiceContext");
        }
        
        Integer counter = (Integer)session.getAttribute("counter");
        
        if (counter == null) {
            counter = new Integer(0);
            System.out.println("Starting the Session");
        }
        
        counter = new Integer(counter.intValue() + 1);
        session.setAttribute("counter", counter);
        return counter;
    }
}					
					

When the JAX-WS stack implementation you are running your web services on is deploying your web service endpoint, it will inject an instance of WebServiceContext into the implementation instance. You can then ask it for a MessageContext, and from this get the request, session, response etc.

NOTE: By doing this, you are binding your implementation to knowledge about which transport mechanism, it is deployed on and accessed through. It could be something else than HTTP.

Using @HttpSessionScope

One way to bring state to Web Services is to use HttpSession cookies. Storing the state and accessing it from the HttpSession (from WebServiceContext) for each request takes multiple steps and does not fit well with the OO way of accessing/storing it with instance fields. One can use @com.sun.xml.ws.developer.servlet.HttpSessionScope annotation tells the JAX-WS RI to create one instance of Hello per each HTTP session. As you see this approach still relies on Http cookies but makes it easier to access state from the context just like normal instance variables:


@HttpSessionScope 
@WebService
public class Hello {
    int counter = 0;
    public int getCounter() {
        return counter++;
    }
}
					
					

Using @Stateful

JAX-WS also provides a transport neutral mechanism to do this. It introduces @com.sun.xml.ws.developer.Stateful to take care of maintaining all the state and the user can access it using instance variables. All the user needs to do is to add @Stateful to Web Service implementation.

The JAX-WS RI has a vendor extension that allows developers to bring back object state to the web service world. Normally, the JAX-WS RI only creates one instance of a service class, and have it serve all incoming requests concurrently. This makes it essentially impossible to use instance fields of the service class for any meaningful purpose.

The stateful web service support in the JAX-WS RI resolves this problem by having the JAX-WS RI maintain multiple instances of a service. By using WS-Addressing behind the scene, it provides a standard-based on-the-wire protocol and easy-to-use programming model.

Application service implementation classes (or providers) who'd like to use the stateful web service support must declare @Stateful annotation on a class. It should also have a public static method/field that takes StatefulWebServiceManager:


@Stateful 
@WebService 
@Addressing
class BankAccount {
    protected final int id;
    private int balance;
    
    Account(int id) { this.id = id; }
    
    @WebMethod
    public synchronized void deposit(int amount) { balance+=amount; }

    // either via a public static field    
    public static StatefulWebServiceManager<BankAccount> manager;
    
    // or  via a public static method (the method name could be anything)    
    public static void setManager(StatefulWebServiceManager<BankAccount> manager) {
       ...
    }
}

					

After your service is deployed but before you receive a first request, the resource injection occurs on the field or the method.

A stateful web service class does not need to have a default constructor. In fact, most of the time you want to define a constructor that takes some arguments, so that each instance carries certain state (as illustrated in the above example).

Each instance of a stateful web service class is identified by an unique EndpointReference. Your application creates an instance of a class, then you'll have the JAX-WS RI assign this unique EPR for the instance as follows:

@WebService
class Bank { // this is ordinary stateless service
    @WebMethod
    public synchronized W3CEndpointReference login(int accountId, int pin) {
        if(!checkPin(pin)) {
            throw new AuthenticationFailedException("invalid pin");
        }
        BankAccount acc = new BankAccount(accountId);
        return BankAccount.manager.export(acc);
    }
}
					

Typically you then pass this EPR to remote systems. When they send messages to this EPR, the JAX-WS RI makes sure that the particular exported instance associated with that EPR will receive a service invocation.

Professional hosting     Belorussian informational portal         Free SCWCD 1.4 Study Guide     Free SCDJWS 1.4 Study Guide     SCDJWS 1.4 Quiz     Free IBM Certified Associate Developer Study Guide     IBM Test 000-287. Enterprise Application Development with IBM WebSphere Studio, V5.0 Study Guide     SCDJWS 5.0 Quiz