Omgo's Blog

January 5, 2010

WS-Addresssing ReplyTo with CXF

Filed under: WebServices, WS-*, XML — aswin @ 6:18 pm

Normally in a webservice interaction scenario a client would initiate a call to a service and receive the response back synchronously. There are situations where you would want a different application to process the response than the one originating the request. One example could be a loan search application where the request for quote is sent to multiple loan providers and they all can respond back to a quote aggregator service at some later point in time when they have the individual quotes ready. The quote aggregator app can then aggregate the quote and let the user know about it by say an email or something. WS-Addresssing specification for web services provides the necessary means by which this can be achieved and java’s webservice stack JAX-WS has very good support for enabling WSA features rather seamlessly. Using WSA a webservice client could invoke an operation on the service provider and instruct the provider to send the response to a different service that could deal with the response. WS-Addressing specifies a EndpointReference structure that could be used to point to a webservice endpoint and defines the soap headers to transfer this information between the applications. In this post, we will see an example of the client sending the EndpointReference of a callback service to a webservice provider application. The provider application would initiate a call to the service referred by the EPR when it is done processing the request, all magically handled by CXF (JAX-WS impl).

Gerad Davison has a post on how to use WS-Addressing to receive the callback from a webservice on the service specified by the WSA “replyto” endpoint reference (EPR) and which is exactly what I wanted to, but needed it to work with CXF (2.2.5)  and this post talks about that.  The setup is pretty simple with only difference in the client part and is as follows.   The setReplyDestination method actually sets the WS-Addressing reply to headers to the message and is the only difference from Gerad’s post.  I have all the server and client side code shown in this post  if anyone wants to try it out.

import static org.apache.cxf.ws.addressing.JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES;

import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Endpoint;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;

import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.ws.addressing.AddressingPropertiesImpl;
import org.apache.cxf.ws.addressing.EndpointReferenceType;
import org.apache.cxf.ws.addressing.soap.VersionTransformer;
import com.aswin.service.wsatest.hello.server.HelloService;

public class Client {

    private static final String HELLOSERVICE_EP_URL = "http://localhost:9091/hello";
    private static final String CALLBACK_EP_URL = "http://localhost:9092/callback";

    public static void main(String[] args) {

        EndpointReferenceType callbackEP = startCallBackEndpoint();
        HelloService hello = getHelloService();
        setReplyDestination(hello, callbackEP);
        hello.sayHello("John Doe");
        System.out.println("Done.....");
    }

    private static EndpointReferenceType startCallBackEndpoint() {
        EndpointImpl endpoint = (EndpointImpl) Endpoint.publish(CALLBACK_EP_URL, new CallbackImpl());
        return VersionTransformer.convertToInternal(endpoint.getEndpointReference());
    }

    private static void setReplyDestination(HelloService hello, EndpointReferenceType replyTo) {

        AddressingPropertiesImpl maps = new AddressingPropertiesImpl();
        maps.setReplyTo(replyTo);

        Map requestContext = ((BindingProvider) hello).getRequestContext();
        requestContext.put(CLIENT_ADDRESSING_PROPERTIES, maps);

    }

    private static HelloService getHelloService() {
        QName serviceName = new QName(HelloService.NS, "HelloService");
        QName portName = new QName(HelloService.NS, "HelloServicePorts");

        Service service = Service.create(serviceName);
        service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING, HELLOSERVICE_EP_URL);

        HelloService hello = service.getPort(portName, HelloService.class);
        return hello;
    }
}

The actual hello service and the callback interface are pretty simple and are as follows

Hello Service SEI

import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.ws.soap.Addressing;

@WebService(targetNamespace=HelloService.NS)
@Addressing(enabled=true, required=true)

public interface HelloService {
String NS = "hello.wsatest.service.aswin.com";

@WebResult(name="response")
public abstract String sayHello(String user);
}

The Service Implementation that can be run as a java application

import java.util.Date;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
import javax.xml.ws.soap.Addressing;

@WebService
@Addressing
public class HelloServiceImpl implements HelloService {

public String sayHello(String user) {
System.out.println("Invoking sayHello in " + getClass());
return "Hello " + user + " @ " + new Date();
}

public static void main(String[] args) {
Endpoint.publish("http://localhost:9091/hello", new HelloServiceImpl());
}
}

The callback sei should be able to recieve the resopnse from the hello service and should define compatible input messages  and must be same as the output message format of the hello service.  The Requestparameter and the Webparam annotations in the example below makes sure that  and the message structure matches that of the Hello service.

import javax.jws.Oneway;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.xml.ws.RequestWrapper;

@WebService()
public interface Callback {
String NS = "hello.wsatest.service.aswin.com";

@Oneway
@RequestWrapper(localName="sayHelloResponse", targetNamespace=NS)
public void callBack(@WebParam(name="response") String callbackMessage);

}
import javax.jws.Oneway;
import javax.jws.WebParam;
import javax.jws.WebService;

@WebService
@org.apache.cxf.interceptor.InInterceptors (interceptors = {"org.apache.cxf.interceptor.LoggingInInterceptor" })
public class CallbackImpl implements Callback {

@Oneway
public void callBack(@WebParam(name="response")String callbackMessage) {
System.out.println("Recieved callback message " + callbackMessage);
}
}

To try this out, you should can run the main method in HelloServiceImpl followed by that of Client.java. You should have cxf in your classpath and if you are using maven , have the following in your pom.xml

<properties>
 <cxf.version>2.2.5</cxf.version>
</properties>
<dependencies>
 <dependency>
 <groupId>org.apache.cxf</groupId>
 <artifactId>cxf-rt-frontend-jaxws</artifactId>
 <version>${cxf.version}</version>
 </dependency>
 <dependency>
 <groupId>org.apache.cxf</groupId>
 <artifactId>cxf-rt-transports-http</artifactId>
 <version>${cxf.version}</version>
 </dependency>
 <dependency>
 <groupId>org.apache.cxf</groupId>
 <artifactId>cxf-rt-transports-http-jetty</artifactId>
 <version>${cxf.version}</version>
 </dependency>
</dependencies>

CXF Docs:  http://cwiki.apache.org/CXF20DOC/ws-addressing.html

CXF samples  also includes an example application that show some ws addressing feature support.

The w3c spec is also a good read in understanding the details and message structure.

Advertisements

3 Comments »

  1. […] Omgo's Blog a simple log of useful technical information Skip to content About « WS-Addresssing ReplyTo with CXF […]

    Pingback by WS-Addresssing with CXF for Async calls « Omgo's Blog — January 6, 2010 @ 9:55 pm

  2. I’m attempting this using tomcat as the server. Everything works great except that, in the client, the call to the server doesn’t return until a timeout has occurred. Shouldn’t it return immediately? The callback does happen but it just seems to stop there. The client never reached the “Done…….” step. Any thoughts?

    Comment by Larry — May 20, 2010 @ 5:02 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: