Spring WS WS-Addressing @Action Example

This example shows how to use Spring WS WS-Addressing to send and receive SOAP requests or responses. We are not covering the details what WS-Addressing is, there are numerous papers describing this in detail.

Spring WS WS-Addressing Server

The @Endpoint annotation tells spring that this class is eligible for handling soap requests. Next, we create a method and annotate it with @Action. The @Action annotation maps this method with a particular soap action described in ws-addressing. For simplicity the method returns a simple POJO.

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) {
        GetBeerResponse response = new GetBeerResponse();
        Beer beer = new Beer();
        beer.setId(request.getId());
        beer.setName("La Chouffe");
        response.setBeer(beer);
        return response;
    }

}

Resolve empty soapAction

We configure spring ws with java configuration. By default, the generated WSDL contains an empty soapAction. We can override this by mapping the soapActions with the appropriate properties using the DefaultWsdl11Definition.setSoapActions(soapActions) method.

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.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 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 soapAction 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. Running this will initialize and start the server. Next, we will implement the client to leverage the ws-addressing features.

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 WS-Addressing Client

Previously we created a server which is configured with Spring WS to handle WS-Addressing requests. Now we will configure a client to make the WS-Addressing enabled soap requests. We create a client by extending from the WebServiceGateWaySupport, this gives easy acces to the WebServiceTemplate which we’ll be using to send soap requests to the server. First we create an ActionCallback class and provide the action URI location. This location is registered in the server method and WSDL. Finally we send the request using the WebServiceTemplate and marshal and return the response.

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 java.net.URI;
import java.net.URISyntaxException;

public class BeerClient extends WebServiceGatewaySupport {

    public GetBeerResponse getBeer(int id) throws URISyntaxException {
        GetBeerRequest request = new GetBeerRequest();
        request.setId(id);

        ActionCallback callback = new ActionCallback(
                new URI("http://memorynotfound.com/getBeerRequest"));

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

To configure the client we also use spring java configuration. We create a marshaller for marshalling the request and responses to and from XML. We also initialize our client to make the actual requests to the server.

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

}

We obtain a reference to our client by initializing spring using the AnnotationConfigApplicationContext.

package com.memorynotfound.client;

import com.memorynotfound.beer.GetBeerResponse;
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);
        GetBeerResponse response = client.getBeer(1);
        System.out.println(response);
    }

}

Demo

When we run the client, it produces the following request and response.

Example Request

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
      <wsa:To SOAP-ENV:mustUnderstand="1">http://localhost:8080/ws/beers</wsa:To>
      <wsa:Action>http://memorynotfound.com/getBeerRequest</wsa:Action>
      <wsa:MessageID>urn:uuid:853dacfa-40ad-4248-8767-5ae694834cf9</wsa:MessageID>
   </SOAP-ENV:Header>
   <SOAP-ENV:Body>
      <ns2:getBeerRequest xmlns:ns2="http://memorynotfound.com/beer">
         <ns2:id>1</ns2:id>
      </ns2:getBeerRequest>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Example Response

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
      <wsa:To SOAP-ENV:mustUnderstand="1">http://www.w3.org/2005/08/addressing/anonymous</wsa:To>
      <wsa:Action>http://memorynotfound.com/getBeerRequestResponse</wsa:Action>
      <wsa:MessageID>urn:uuid:aa12d10c-82fa-41b8-b6f9-cced508b685f</wsa:MessageID>
      <wsa:RelatesTo>urn:uuid:853dacfa-40ad-4248-8767-5ae694834cf9</wsa:RelatesTo>
   </SOAP-ENV:Header>
   <SOAP-ENV:Body>
      <ns2:getBeerResponse xmlns:ns2="http://memorynotfound.com/beer">
         <ns2:beer>
            <ns2:id>1</ns2:id>
            <ns2:name>La Chouffe</ns2:name>
         </ns2:beer>
      </ns2:getBeerResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

References

Download

You may also like...