Produce Spring Ws Contract First Soap Service

This tutorial show you how to produce Spring Ws Contract First Soap services. Contract-First means starting with an XML Schema (XSD) or WSDL (Web Service Description Language) followed by the Java Code. In this example we start by an XSD schema and generating the Java Classes using the jaxb2-maven-plugin. Next we create our endpoint and configuring the application using spring Java Configuration. Lets see how it works with a concrete example.

Maven dependencies

We use Spring Boot to bootstrap our application and jaxb2-maven-plugin to generate the Java Classes from XSD.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.memorynotfound.spring.ws</groupId>
    <artifactId>produce-soap-web-service</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>SPRING-WS - ${project.artifactId}</name>
    <url>http://memorynotfound.com</url>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-ws</artifactId>
            <version>1.3.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
            <version>1.6.3</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxb2-maven-plugin</artifactId>
                <version>2.2</version>
                <executions>
                    <execution>
                        <id>xjc</id>
                        <goals>
                            <goal>xjc</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <sources>
                        <source>src/main/resources/xsd</source>
                    </sources>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

XSD Schema – Contract

The XSD we use describes a simple Request and Response for our Soap Service. We also define a ComplexType beer to be generated as a Java Class. With an XSD schema you make a contract where your request – response have to adhere. We’ll not go into validating the request and response in this tutorial you can find that in the following: Spring WS validate soap request and/or response with XSD.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:tns="http://memorynotfound.com/beer"
           targetNamespace="http://memorynotfound.com/beer"
           elementFormDefault="qualified">

    <xs:element name="getBeerRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="id" type="xs:int"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="getBeerResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="beer" type="tns:beer"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:complexType name="beer">
        <xs:sequence>
            <xs:element name="id" type="xs:int"/>
            <xs:element name="name" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>

</xs:schema>

Generating Java Classes From XSD

After running the following command, the Java Classes will be generated in the target/generated-sources/xjc/<package-name> folder.

mvn package

Produce Spring Ws Contract First Soap Service Endpoint

We can produce Spring Ws Contract First Soap Endpoint by annotating the class with the @Endpoint annotation. This makes the class as a special sort of @Component, suitable for handling XML messages in Spring WS and eligible for component scanning.

The @PayloadRoot annotation informs Spring Ws that the getBeer method is suitable for handling XML messages. This method can handle messages with localPart equal to getBeerRequest with a namespace of http://memorynotfound.com/beer.

Finally the @RequestPayload tells Spring Ws that a XML Payload of type GetBeerRequest is needed as a parameter of the request.

package com.memorynotfound.server;

import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;

@Endpoint
public class BeerEndpoint {

    public static final String NAMESPACE_URI = "http://memorynotfound.com/beer";

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getBeerRequest")
    @ResponsePayload
    public 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;
    }

}

Spring WS Java Configuration

We use Spring Java Configuration to configure our soap services. The @EnableWs annotation enables support for the @Endpoint and related Spring Ws annotations. The @Configuration annotations tells spring that this Java Class is used as configuration file. Next, we map the service on the /ws/* URI. We do this by creating a ServletRegistrationBean with a MessageDispatcherServlet which automatically transforms the WSDL location when deployed, together with the URI endpoint. Finally we create a DefaultWsdl11Definition bean which we provide information about the portType, locationUri, targetNamespace and schema.

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;

@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(BeerEndpoint.NAMESPACE_URI);
        wsdl11Definition.setSchema(schema);
        return wsdl11Definition;
    }

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

}

Run with Spring Boot

We use spring boot to bootstrap the application.

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

}

Dynamically Generated WSDL

We can access the dynamically generated WSDL with URI: http://localhost:8080/ws/beer.wsdl. We can see that our XSD schema is included in the WSDL.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                  xmlns:sch="http://memorynotfound.com/beer"
                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:tns="http://memorynotfound.com/beer"
                  targetNamespace="http://memorynotfound.com/beer">

    <wsdl:types>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
                   elementFormDefault="qualified"
                   targetNamespace="http://memorynotfound.com/beer">
            <xs:element name="getBeerRequest">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="id" type="xs:int"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="getBeerResponse">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="beer" type="tns:beer"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:complexType name="beer">
                <xs:sequence>
                    <xs:element name="id" type="xs:int"/>
                    <xs:element name="name" type="xs:string"/>
                </xs:sequence>
            </xs:complexType>
        </xs:schema>
    </wsdl:types>
    <wsdl:message name="getBeerRequest">
        <wsdl:part element="tns:getBeerRequest" name="getBeerRequest">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="getBeerResponse">
        <wsdl:part element="tns:getBeerResponse" name="getBeerResponse">
        </wsdl:part>
    </wsdl:message>
    <wsdl:portType name="BeersPort">
        <wsdl:operation name="getBeer">
            <wsdl:input message="tns:getBeerRequest" name="getBeerRequest">
            </wsdl:input>
            <wsdl:output message="tns:getBeerResponse" name="getBeerResponse">
            </wsdl:output>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="BeersPortSoap11" type="tns:BeersPort">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="getBeer">
            <soap:operation soapAction=""/>
            <wsdl:input name="getBeerRequest">
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output name="getBeerResponse">
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="BeersPortService">
        <wsdl:port binding="tns:BeersPortSoap11" name="BeersPortSoap11">
            <soap:address location="http://localhost:8080/ws"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

Testing Spring WS Soap Service with Soap UI

We can use Soap UI to test or Spring Ws Soap Service.

produce spring ws contract first soap service test soap ui

References

Download

You may also like...

  • Robert Cuong

    Hi Memory, thanks for tutorial.
    I download example, start spring boots, and run on browser http://localhost:8080/ws/beer.wsdl … error: 405 method not allowed.
    Could you plz check it ?