Saturday, December 20, 2008

Let Spring load service class for JAX-WS

During deployment JAX-WS RI by default creates an instance (singleton) of web service implementation class and uses that to serve requests. At times we may want to customize the instantiation of the web service implementation class e.g. let Spring configure and load the object.

JAX-WS Commons has an extension for Spring integration, which delegates total configuration to Spring and makes RI specific files (sun-jaxws.xml) redundant. Currently I am happy with the way JAX-WS RI exposes web services and do not want everything to move to Spring or something else. I was just looking for a way to load the service implementation class by my code instead of RI doing so. JAX-WS RI has a poorly documented (or may be I am not able find it easily) feature called InstanceResolver, which lets developers to customize the way service implementation class is loaded. The code snippets below demostrate how to so-

public class MyResolver extends InstanceResolver<Object> {

    private final Object serviceImpl;

    /**
     * @param clazz
     */
    public MyResolver(Class<?> clazz) {
        serviceImpl= . . . load the class here or get it from Spring or somewhere else . . .;
    }

    /**
     * @see com.sun.xml.ws.api.server.InstanceResolver#resolve(com.sun.xml.ws.api.message.Packet)
     */
    @Override
    public Object resolve(Packet request) {
        return serviceImpl;
    }

}

// Now create an Annotation, which has @InstanceResolverAnnotation with MyResolver.class
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@InstanceResolverAnnotation(MyResolver.class)
public @interface MyWs {
}

// Now annotate the service class with above-mentioned annotation
@WebService(endpointInterface = "com.vinodsingh.Address")
@MyWs
public class AddressImpl implements Address {
    . . .
}

During deployment (WSServletContextListener) JAX-WS RI iterates through all annotations on the service class and sees if there is any annotation annotated with @InstanceResolverAnnotation. On finding one it will delegate the task of creating instance of the service class to the user defined InstanceResolver. With this I am able to load service classes using Spring.

1 Comment:

Dhiraj said...

We are facing some issue with caching of instance but I am not sure if it same as you have mentioned here in your blog. What we are doing is trying to create a failover implementation where our webservice is deployed onto two hosts.

We are caching the WS Client(custom made) object after we hit the webservice with a test message. If it works we cache it else failover takes place and the second url is tested with the message. It is cached if it works fine else says- webservice not available

We have 2 urls: url1(working WS) and url2(non working WS). If we try to hit the url1 then it works fine but if we reverse the order of the url, it fails both the time. I found that after some debug that it is caching the stub.

Here was my long story.
Any thoughts on this?