Skip to main content

Bringing state back to web services: HttpSession-scope

Posted by kohsuke on October 17, 2006 at 9:03 AM PDT

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'll experience concurrency problem as soon as multiple threads hit your service.

So all too often the service code starts to look more like C code, not Java code, and I didn't like this at all. Since I started helping the JAX-WS RI, I've been trying to fix this. Yesterday, I finally managed to write one.

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, but as you can see in Rama's example, this is not pretty at all. Especially the server side, which I quote below for your reference:

@WebService
public class Hello {
    @Resource
    private WebServiceContext wsContext;
    public int getCounter(){
        MessageContext mc = wsContext.getMessageContext();
        HttpSession session = ((javax.servlet.http.HttpServletRequest)mc.get(MessageContext.SERVLET_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;

    }
}

Instead of writing this much code, with JAX-WS RI 2.1, you can do this:

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

The @com.sun.xml.ws.developer.servlet.HttpSessionScope annotation tells the JAX-WS RI to create one instance of Hello per each HTTP session. No need to mess with WebServiceContext, nor with HttpSession manually. It's all nicely typed and concise.

Not only that, but what I really like about this is that you can do something like this completely outside the JAX-WS RI. This is based on publicly available extension point in the JAX-WS RI 2.1 called InstanceResolver. This provides a pluggability point where 3rd party could control how the JAX-WS RI dispatches incoming requests to service instances, and I only needed to write a little more code to implement this logic.

Then I define HttpSessionScope annotation with JAX-WS RI's meta annotation InstanceResolverAnnotation, to connect all those things together.

I've barely scratched the surface with this mechanism. There's already a variant of this that takes advantage of WS-Addressing (which is the "proper" way of doing stateful web services), and we can write another that loads instances from Spring, which allows you to take advantage of all its DI features and AOJ mechanisms. So stay tuned for future entries.

To play with this feature today, go download the JAX-WS RI 2.1 nightly. Also, I think we'll be posting a new EA relatively soon.

Related Topics >>

Comments

<p>&nbsp;Hi,&nbsp;</p> <p>I tried to follow the example but ...

Hi,
I tried to follow the example but the counter always returns 1. I am using JAX-WS 2.2 and Glassfish 3.1. The client is another servlet and invokes the service by calling getCounter().
Do you have a working example of this feature?

I got the following exception

I got the following exception when i'm using HttpSessionScope : PingWebService has @HttpSessionScope but it's deployed on non-servlet endpoint I deploy the webservice on a grizzly server /n The Code looks like: GrizzlyWebServer server = new GrizzlyWebServer(8080); HttpContext context = GrizzlyHttpContextFactory.createHttpContext(server, "", "/PingWebService"); Endpoint endpoint = Endpoint.create(new AbaNetPingWebService1002()); endpoint.publish(context); // Use grizzly HTTP context for publishing server.start(); Any ideas why i got this exceptions?

stateful webservices

Well your article is really informative so thanks for posting .... is there any way in which i don't have to write a code to enable a session at client side. i have a use case in which i have to implement login system for a client. now when the user logs in successfully the server returns a session id to the client now in every subsequent requests a client has to pass this session id to server to keep track of known client requests. so if a client generate stubs based on a wsdl then how a client knows to enable a session at users end ... ? so do know any mechanism to implement stateful webservices at the server side only ?

hi .. do you know how to

hi .. do you know how to implement above example with @HttpSessionScope ?