The Source for Java Technology Collaboration
User: Password:



Kohsuke Kawaguchi

Kohsuke Kawaguchi's Blog

Stateful web service with JAX-WS RI 2.1 EA2

Posted by kohsuke on October 24, 2006 at 12:19 PM | Comments (7)

One of the additions in the JAX-WS RI EA2 is the support of stateful web services by using WS-Addressing underneath.

Normally, the JAX-WS RI only creates one instance of your service class, and have it handle all incoming requests concurrently. This effectively takes you back to pre-OO programming, as you will not be able to make use of instance fields at all — it's as if all your methods are static. The problem with that is that if the rest of your application is modeled in OO, this mismatch becomes painful. It would be much nicer if you can make it OO all the way, like you do with RMI, where you can export "objects". The stateful web service support brings you that. On the wire, it uses WS-Addressing standard, so it's fully interoperable.

Now, how do you use it? First, your class has to have @Stateful annotation, like this. In this version you'd also need to explicitly set @Addressing to enable WS-Addressing, but I'm going to change that so you won't need it.

@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) {
       ...
    }
    

}

Your class would also have StatefulWebServiceManager static field or method, where the JAX-WS RI injects an instance. You'll talk with the manager to get objects exported.

The other thing to note is that the BankAccount instance is now completely stateful. Each instance represents a specific bank account denoted by ID, and incoming requests are dispatched to the right instance (although you still need to synchronize methods if you expect concurrent invocations to the same object.)

The following normal stateless service shows how you can send a "remote reference" to a BankAccount object to the client:

@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);
    }
}

The key here is the export operation. It takes a reference of a BankAccount object, then creates an "endpoint reference" object, which is really a remote reference to this specific bank account. In this example, it then sends it back to the client.

The client code can look like this:

BankAccount account1 = accountService.getPort(bankServicePort.getAccount(1), Account.class);
BankAccount account2 = accountService.getPort(bankServicePort.getAccount(2), Account.class);

account1.deposit(100);
account2.deposit(-100);

Admittedly it's bit ugly to get to the BankAccount proxy, but two account proxies created this way are connected to two different server-side BankAccount objects, so things are much object-oriented now.

The obvious improvement to this is to hook this up with Java Persistence API, so that you can create a remote reference to a persisted object. In that way, you can create a durable remote reference that other systems can use to talk to your objects. So please expect more improvements in this area in the future.

(This is a follow up to my earlier post about the state support in the JAX-WS RI. )


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • See a relevant thread in the forum.

    Posted by: kohsuke on October 27, 2006 at 10:48 PM

  • GREAT !! .. i am waiting for this "feature" long time ;). No more web-modules needed for web-service sessions. Thanks for that ;)

    Posted by: stenlee on November 14, 2006 at 02:56 AM

  • I'm glad you liked it.

    Posted by: kohsuke on November 14, 2006 at 08:40 AM

  • Looks nice but to tell you the truth, exposing persistent objects seems to me like exposing remote EJB Entity Beans and it just does not make sense.

    Posted by: miluch on June 18, 2007 at 05:25 AM

  • Hi, this is all very nice and works fine. But I need to go futher. To support recovery after a crash I need to make the second port use the same session of the first one. The full story is here...

    http://forums.java.net/jive/thread.jspa?threadID=35390

    As you see I've only found an unportable way of doing it... Any suggestion?

    Posted by: evetere on January 15, 2008 at 07:14 AM

  • I solved my problem and posted to the same thread the solution.

    Posted by: evetere on January 15, 2008 at 08:32 AM

  • Hi,

    I was trying the stateful WS sample in the latest metro distribution.
    I had conflicting behavior when I use different JDK.

    In JDK 1.5, it runs just fine.

    In JDK 1.6, it fails with the below error
    C:\metro\jax-ws-latest-wsit\samples\stateful\src\stateful\server\Book.java:53: Classes annotated with @javax.jws.WebService must have a public defau
    t constructor. Class: stateful.server.Book
    [apt] public class Book {

    Is this expected behavior? If I fix it by providing a public constructor and make the instance variable non-final, would it still retain the stateful nature of the web service?

    Please clarify.

    Posted by: aruld on March 21, 2008 at 10:38 AM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds