Spring WS-Addressing Delegate Response

Previously, we saw how to create spring ws-addressing soap client and server. Next we dive deeper and use another cool feature of WS-Addressing. WS-Addressing specifies a transport-neutral routing mechanism. It is based on a To and Action SOAP header, which indicates the destination and intent of the SOAP message. Additionally, WS-Addressing allows you to define a return address (either successful responses or fault messages). All messages have a unique id which can be used for correlation.

Spring WS-Addressing Server

The @Action annotation maps the incoming request to the method if the soapAction property corresponds. For simplicity this returns a simple POJO and throws a IllegalArgumentException when the id property is 0.

package com.memorynotfound.server;

import com.memorynotfound.beer.*;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import org.springframework.ws.soap.addressing.server.annotation.Action;

@Endpoint
public class BeerEndpoint {

    @Action("http://memorynotfound.com/getBeerRequest")
    public @ResponsePayload GetBeerResponse getBeer(@RequestPayload GetBeerRequest request) {

        if (request.getId() == 0){
            throw new IllegalArgumentException("id cannot be 0");
        }

        GetBeerResponse response = new GetBeerResponse();
        Beer beer = new Beer();
        beer.setId(request.getId());
        beer.setName("La Chouffe");
        response.setBeer(beer);
        return response;
    }
}

The ResponseServlet will handle all successful responses delegated from the replyTo URI.

package com.memorynotfound.server;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ResponseServlet extends HttpServlet {

    private static final Log logger = LogFactory.getLog(ResponseServlet.class);

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
        logger.info("handling web service response");
    }
}

The FaultServlet will handle all faults delegated from the faultTo URI.

package com.memorynotfound.server;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class FaultServlet extends HttpServlet {

    private static final Log logger = LogFactory.getLog(FaultServlet.class);

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
        logger.info("handling web service fault");
    }
}

To register the previous servlets we register them using the ServletRegistrationBean and map the servlet with their corresponding URI Segment. Next, we create a AnnotationActionEndpointMapping and set the message sender for handling the out of bound requests. This message sender will handle the delegation of the outgoing responses.

package com.memorynotfound.server;

import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.soap.addressing.server.AnnotationActionEndpointMapping;
import org.springframework.ws.transport.http.HttpComponentsMessageSender;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;
import java.util.Properties;

@EnableWs
@Configuration
public class SoapServerConfig extends WsConfigurerAdapter {

    @Bean
    public ServletRegistrationBean responseServlet(){
        ResponseServlet servlet = new ResponseServlet();
        return new ServletRegistrationBean(servlet, "/response");
    }

    @Bean
    public ServletRegistrationBean faultServlet(){
        FaultServlet servlet = new FaultServlet();
        return new ServletRegistrationBean(servlet, "/fault");
    }

    @Bean
    public AnnotationActionEndpointMapping annotationActionEndpointMapping(){
        AnnotationActionEndpointMapping mapping = new AnnotationActionEndpointMapping();
        mapping.setMessageSender(new HttpComponentsMessageSender());
        return mapping;
    }

    @Bean
    public ServletRegistrationBean messageDispatcherServlet(ApplicationContext appContext){
        MessageDispatcherServlet servlet = new MessageDispatcherServlet();
        servlet.setApplicationContext(appContext);
        servlet.setTransformWsdlLocations(true);
        return new ServletRegistrationBean(servlet, "/ws/*");
    }

    @Bean(name = "beers")
    public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema schema){
        DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
        wsdl11Definition.setPortTypeName("BeersPort");
        wsdl11Definition.setLocationUri("/ws");
        wsdl11Definition.setTargetNamespace("http://meorynotfound.com/beer");
        wsdl11Definition.setSchema(schema);

        // fix for adding soap action to the dynamic generated wsdl
        Properties soapActions = new Properties();
        soapActions.setProperty("getBeer", "http://memorynotfound.com/getBeerRequest");
        wsdl11Definition.setSoapActions(soapActions);
        return wsdl11Definition;
    }

    @Bean
    public XsdSchema beersSchema(){
        return new SimpleXsdSchema(new ClassPathResource("xsd/beers.xsd"));
    }

}

Finally we bootstrap the application using spring boot. So now the server is configured, lets take a look at the client configuration up next.

package com.memorynotfound.server;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RunServer {

    public static void main(String[] args) {
        SpringApplication.run(RunServer.class);
    }

}

Spring WS-Addressing Client ReplyTo and FaultTo

Previously, we created and configured the server to handle WS-Addressing requests. Next, we configure the client. We start with creating a client by extending from the WebServiceGateWaySupport class. This gives us easy access to the WebServiceTemplate instance. We can delegate the successful response or fault response using the ActionCallback. First we create an instance by passing the soapAction in the constructor. Next we can set the replyTo and faultTo properties by providing a EndpointReference with the URI location of the delegate.

package com.memorynotfound.client;

import com.memorynotfound.beer.*;
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
import org.springframework.ws.soap.addressing.client.ActionCallback;
import org.springframework.ws.soap.addressing.core.EndpointReference;

import java.net.URI;
import java.net.URISyntaxException;

public class BeerClient extends WebServiceGatewaySupport {

    public void sendGetBeerRequest(Integer id) throws URISyntaxException {
        GetBeerRequest request = new GetBeerRequest();
        request.setId(id);

        ActionCallback callback = new ActionCallback(
                new URI("http://memorynotfound.com/getBeerRequest"));
        callback.setReplyTo(new EndpointReference(
                new URI("http://localhost:8080/response")));
        callback.setFaultTo(new EndpointReference(
                new URI("http://localhost:8080/fault")));

        getWebServiceTemplate().marshalSendAndReceive(request, callback);
    }
}

To configure the client we must create a marshaller to marshall the incoming requests and outgoing response. Finally, create and initialize the client.

package com.memorynotfound.client;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;

@Configuration
public class SoapClientConfig {

    @Bean
    public Jaxb2Marshaller marshaller() {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setContextPath("com.memorynotfound.beer");
        return marshaller;
    }

    @Bean
    public BeerClient beerClient(Jaxb2Marshaller marshaller) {
        BeerClient client = new BeerClient();
        client.setDefaultUri("http://localhost:8080/ws/beers");
        client.setMarshaller(marshaller);
        client.setUnmarshaller(marshaller);
        return client;
    }

}

In this example we create a simple request. The first one will be successful and delegated to the replyTo handler. The next one produces a fault and will be delegated to the faultTo handler.

package com.memorynotfound.client;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.net.URISyntaxException;

public class RunClient {

    public static void main(String[] args) throws URISyntaxException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SoapClientConfig.class);
        BeerClient client = context.getBean(BeerClient.class);
        client.sendGetBeerRequest(1);
        System.out.println("getBeerRequest handled by replyTo");

        client.sendGetBeerRequest(0);
        System.out.println("getBeerRequest handled by faultTo");
    }

}

References

Download

You may also like...