1. Overview
In this tutorial, weβll learn how to create a SOAP-based web service with Spring Boot Starter Web Services.
2. SOAP Web Services
In short, a web service is a machine-to-machine, platform-independent service that allows communication over a network.
SOAP is a messaging protocol. Messages (requests and responses) are XML documents over HTTP. The XML contract is defined by the WSDL (Web Services Description Language). It provides a set of rules to define the messages, bindings, operations, and location of the service.
The XML used in SOAP can become extremely complex. For this reason, itβs best to use SOAP with a framework like JAX-WS or Spring, as weβll see in this tutorial.
3. Contract-First Development Style
There are two possible approaches when creating a web service: Contract-Last and Contract-First. When we use a contract-last approach, we start with the Java code and generate the web service contract () from the classes. When using contract-first, we start with the WSDL contract, from which we generate the Java classes.
Spring-WS only supports the contract-first development style.
4. Setting Up the Spring Boot Project
Weβll create a Spring Boot project where weβll define our SOAP WS server.
4.1. Maven Dependencies
Letβs start by adding the spring-boot-starter-parent to our project:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
</parent>
Next, letβs add the spring-boot-starter-web-services and wsdl4j dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
</dependency>
4.2. The XSD File
The contract-first approach requires us to create the domain (methods and parameters) for our service first. Weβll use an XML schema file (XSD) that Spring-WS will export automatically as a WSDL:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.baeldung.com/springsoap/gen"
targetNamespace="http://www.baeldung.com/springsoap/gen" elementFormDefault="qualified">
<xs:element name="getCountryRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="getCountryResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="country" type="tns:country"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="country">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="population" type="xs:int"/>
<xs:element name="capital" type="xs:string"/>
<xs:element name="currency" type="tns:currency"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="currency">
<xs:restriction base="xs:string">
<xs:enumeration value="GBP"/>
<xs:enumeration value="EUR"/>
<xs:enumeration value="PLN"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
In this file, we can see the format of the getCountryRequest web service request. Weβll define it to accept one parameter of the type string.
Next, weβll define the format of the response, which contains an object of the type country.
Finally, we can see the currency object used within the country object.
4.3. Generate the Domain Java Classes
Now weβll generate the Java classes from the XSD file defined in the previous section. The jaxb2-maven-plugin will do this automatically during build time. The plugin uses the XJC tool as a code-generation engine. XJC compiles the XSD schema file into fully annotated Java classes.
Letβs add and configure the plugin in our pom.xml:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<sources>
<source>src/main/resources/countries.xsd</source>
</sources>
<outputDirectory>src/main/java</outputDirectory>
<clearOutputDir>false</clearOutputDir>
</configuration>
</plugin>
Here we notice two important configurations:
- <source>src/main/resources/countries.xsd</source> β The location of the XSD file
- <outputDirectory>src/main/java</outputDirectory> β Where we want our Java code to be generated to
To generate the Java classes, we could use the XJC tool from our Java installation. Itβs even more simple in our Maven project, as the classes will be automatically generated during the usual Maven build:
mvn compile
4.4. Add the SOAP Web Service Endpoint
The SOAP web service endpoint class will handle all the incoming requests for the service. Itβll initiate the processing and send the response back.
Before defining this, weβll create a Country repository in order to provide data to the web service:
@Component
public class CountryRepository {
private static final Map<String, Country> countries = new HashMap<>();
@PostConstruct
public void initData() {
// initialize countries map
}
public Country findCountry(String name) {
return countries.get(name);
}
}
Next, weβll configure the endpoint:
@Endpoint
public class CountryEndpoint {
private static final String NAMESPACE_URI = "http://www.baeldung.com/springsoap/gen";
private CountryRepository countryRepository;
@Autowired
public CountryEndpoint(CountryRepository countryRepository) {
this.countryRepository = countryRepository;
}
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest")
@ResponsePayload
public GetCountryResponse getCountry(@RequestPayload GetCountryRequest request) {
GetCountryResponse response = new GetCountryResponse();
response.setCountry(countryRepository.findCountry(request.getName()));
return response;
}
}
Here are a few details to notice:
- @Endpoint β registers the class with Spring WS as a Web Service Endpoint
- @PayloadRoot β defines the handler method according to the namespace and localPart attributes
- @ResponsePayload β indicates that this method returns a value to be mapped to the response payload
- @RequestPayload β indicates that this method accepts a parameter to be mapped from the incoming request
4.5. The SOAP Web Service Configuration Beans
Now letβs create a class for configuring the Spring message dispatcher servlet to receive the request:
@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
// bean definitions
}
@EnableWs enables SOAP Web Service features in this Spring Boot application. The WebServiceConfig class extends the WsConfigurerAdapter base class, which configures the annotation-driven Spring-WS programming model.
Letβs create a MessageDispatcherServlet, which is used for handling SOAP requests:
@Bean
public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<>(servlet, "/ws/*");
}
Weβll set the injected ApplicationContext object of the servlet so that Spring-WS can find other Spring beans.
Weβll also enable the WSDL location servlet transformation. This transforms the location attribute of soap:address in the WSDL so that it reflects the URL of the incoming request.
Finally, weβll create a DefaultWsdl11Definition object. This exposes a standard WSDL 1.1 using an XsdSchema. The WSDL name will be the same as the bean name:
@Bean(name = "countries")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("CountriesPort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace("http://www.baeldung.com/springsoap/gen");
wsdl11Definition.setSchema(countriesSchema);
return wsdl11Definition;
}
@Bean
public XsdSchema countriesSchema() {
return new SimpleXsdSchema(new ClassPathResource("countries.xsd"));
}
5. Testing the SOAP Project
Once the project configuration has been completed, weβre ready to test it.
5.1. Build and Run the Project
Itβs possible to create a WAR file and deploy it to an external application server. Instead, weβll use Spring Boot, which is a faster and easier way to get the application up and running.
First, weβll add the following class to make the application executable:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Notice that weβre not using any XML files (like web.xml) to create this application. Itβs all pure Java.
Now weβre ready to build and run the application:
mvn spring-boot:run
To check if the application is running properly, we can open the WSDL through the URL: http://localhost:8080/ws/countries.wsdl
5.2. Test a SOAP Request
To test a request, weβll create the following file and name it request.xml:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:gs="http://www.baeldung.com/springsoap/gen">
<soapenv:Header/>
<soapenv:Body>
<gs:getCountryRequest>
<gs:name>Spain</gs:name>
</gs:getCountryRequest>
</soapenv:Body>
</soapenv:Envelope>
To send the request to our test server, we can use external tools, like SoapUI or the Google Chrome extension Wizdler. Another way is to run the following command in our shell:
curl --header "content-type: text/xml" -d @request.xml http://localhost:8080/ws
The resulting response might not be easy to read without indentation or line breaks.
To see it formatted, we can copy and paste it to our IDE or another tool. If weβve installed xmllib2, we can pipe the output of our curl command to xmllint:
curl [command-line-options] | xmllint --format -
The response should contain information about Spain:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:getCountryResponse xmlns:ns2="http://www.baeldung.com/springsoap/gen">
<ns2:country>
<ns2:name>Spain</ns2:name>
<ns2:population>46704314</ns2:population>
<ns2:capital>Madrid</ns2:capital>
<ns2:currency>EUR</ns2:currency>
</ns2:country>
</ns2:getCountryResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
6. Conclusion
In this article, we learned how to create a SOAP web service using Spring Boot. We also demonstrated how to generate Java code from an XSD file. Finally, we configured the Spring beans needed to process the SOAP requests.
