Spring WS client.

In my previous post I described how to create sample Spring Web Service.
In this article I am going to show how to write generic Web Services client for it (or for any other Web Services).

The idea here is to define java interface that will mirror WS request/response messages and have a Spring proxy to take care of WS calls and XML marshalling.
In previous article we defined SampleEndpoint that was Java back bone of our WS.
What we whould like to do is to have Java interface that will mimic SampleEndpoint public methods and have Spring create WS proxy for it to be injected into our WS clients.

1. Generic WS client spring proxy:

package com.localmatters.util.spring.proxy;

import java.lang.reflect.InvocationTargetException;

import org.springframework.util.ClassUtils;
import org.aopalliance.intercept.MethodInvocation;
import org.aopalliance.intercept.MethodInterceptor;

import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.InitializingBean;

import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;

public class WebServiceProxy implements FactoryBean, InitializingBean{

    private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();

    private String uri = null;
    private Marshaller marshaller = null;
    private Unmarshaller unmarshaller = null;
    private WebServiceTemplate webServiceTemplate = null;

    private Class serviceInterface = null;

    private Object proxy = null;

    public String getUri(){
        return uri;
    }

    public void setUri(String uri){
        this.uri = uri;
    }

    public Marshaller getMarshaller(){
        return marshaller;
    }

    public void setMarshaller(Marshaller marshaller){
        this.marshaller = marshaller;
    }

    public Unmarshaller getUnmarshaller(){
        return unmarshaller;
    }

    public void setUnmarshaller(Unmarshaller unmarshaller){
        this.unmarshaller = unmarshaller;
    }

    public WebServiceTemplate getWebServiceTemplate(){
        return webServiceTemplate;
    }

    public void setWebServiceTemplate(WebServiceTemplate webServiceTemplate){
        this.webServiceTemplate = webServiceTemplate;
    }

    public Class getServiceInterface(){
        return serviceInterface;
    }

    public void setServiceInterface(Class serviceInterface){
        this.serviceInterface = serviceInterface;
    }

    public void afterPropertiesSet() throws Exception{
        this.proxy = new ProxyFactory( serviceInterface, createMethodInterceptor() ).getProxy(beanClassLoader);
        if (webServiceTemplate != null) return;
        webServiceTemplate = new WebServiceTemplate();
        SaajSoapMessageFactory factory = new SaajSoapMessageFactory();
        factory.afterPropertiesSet();
        webServiceTemplate.setMessageFactory(factory);
        webServiceTemplate.setDefaultUri(uri);
        webServiceTemplate.setMarshaller(marshaller);
        webServiceTemplate.setUnmarshaller(unmarshaller);
    }

    public Object getObject() throws Exception{
        return proxy;
    }

    public Class getObjectType(){
        return serviceInterface;
    }

    public boolean isSingleton(){
        return true;
    }

    private MethodInterceptor createMethodInterceptor(){
        return (new MethodInterceptor(){
        public Object invoke(MethodInvocation methodInvocation) throws Throwable{
        return invokeMethod(methodInvocation);} });
    }

    private Object invokeMethod(MethodInvocation methodInvocation) throws
    IllegalArgumentException, IllegalAccessException, InvocationTargetException{
        //we assume that WS should contain only one argument (something like SampleRequest)
        if ( methodInvocation.getArguments() == null || methodInvocation.getArguments().length != 1 ) throw new
        IllegalArgumentException("Should contain exactly one argument");
        Object arg = methodInvocation.getArguments()[0];
        Object result = webServiceTemplate.marshalSendAndReceive(arg);
        return result;
    }
}

Note that we are assuming that WS is based on single argument Request object (which should be true for most of cases).

2. WS service interface:

As said before we have to define Java interface for WS to be used by out client:

public interface SampleService{

    public SampleResponse processSampleRequest(SampleRequest request);
}

3. Defining proxy with SampleService:

<bean id=“sampleJaxbMarshaller” class=“org.springframework.oxm.jaxb.Jaxb2Marshaller”>
        <property name=“classesToBeBound”>
              <list>
                     <value>com.equilibriums.samplespringws.SampleRequest</value>
                    <value>com.equilibriums.samplespringws.SampleResponse</value>
              </list>
         </property>
        <property name=“marshallerProperties”>
             <map>
                  <entry key=“jaxb.formatted.output”>
                       <value type=“java.lang.Boolean”>true</value>
                  </entry>
             </map>
        </property>
   </bean>
   <alias name=“sampleJaxbMarshaller” alias=“sampleJaxbUnmarshaller” />

   <bean id="wsSampleService" class="WebServiceProxy">
        <property name="uri" value="http://localhost:8080/sample"/>
        <property name="serviceInterface" value="SampleService"/>
        <property name="marshaller" ref="sampleJaxbMarshaller"/>
        <property name="unmarshaller" ref="sampleJaxbUnmarshaller"/>
    </bean>

   <bean name="someSampleClient" class="…">
         <property name="sampleService" ref="wsSampleService"/>
   </bean>

Note that we need to redefine Marshallers in our context file. To simplify this you can define them in separate Spring XML file and share then between client and WS server web app.

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>