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>https://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="https://memorynotfound.com/beer"
targetNamespace="https://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 https://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 = "https://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/beers.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="https://memorynotfound.com/beer"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="https://memorynotfound.com/beer"
targetNamespace="https://memorynotfound.com/beer">
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
targetNamespace="https://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.
References
- jaxb2-maven-plugin XJC Doc
- DefaultWsdl11Definition JavaDoc
- Spring WS API
- Spring WS Reference Documentation
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 ?
You should use http://localhost:8080/ws/beers.wsdl, instead of …/beer.wsdl
typo in wsdl url – http://localhost:8080/ws/beers.wsdl
Thanks, I corrected it