Skip to main content

Spring SOAP Configuration

Introduction

Spring Web Services (Spring-WS) is a product of the Spring community focused on creating document-driven web services. Unlike other frameworks that start with Java code and generate web service contracts from it, Spring-WS follows a "contract-first" approach, where you start with the XML Schema that defines the contract and then map it to your Java code.

In this tutorial, we'll explore how to configure SOAP (Simple Object Access Protocol) web services using Spring-WS. SOAP is an XML-based messaging protocol that enables applications to communicate over HTTP or other transports. Spring-WS simplifies the creation and exposure of SOAP web services in your Spring application.

Setting Up Your Project

Maven Dependencies

To get started with Spring SOAP web services, you need to add the appropriate dependencies to your project.

xml
<dependencies>
<!-- Spring Boot Starter Web Services -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>

<!-- WSDL4J for WSDL support -->
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
</dependency>

<!-- JAXB API (for Java 11 and above) -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>

<!-- JAXB Runtime (for Java 11 and above) -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
</dependencies>

JAXB Plugin Configuration

To auto-generate Java classes from your XML schema, you can use the JAXB Maven plugin:

xml
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.5.0</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<sources>
<source>${project.basedir}/src/main/resources/schemas</source>
</sources>
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<clearOutputDir>false</clearOutputDir>
</configuration>
</plugin>

Contract-First Approach

Creating the XML Schema (XSD)

The first step in building a SOAP web service with Spring-WS is to define the XML Schema. Let's create a simple schema for a country service:

xml
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:tns="http://spring.io/guides/gs-producing-web-service"
targetNamespace="http://spring.io/guides/gs-producing-web-service"
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="USD"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>

Save this file as countries.xsd in src/main/resources/schemas/.

Configuring Spring SOAP Web Service

WebServiceConfig Class

Now, let's create a configuration class for our SOAP web service:

java
@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {

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

@Bean(name = "countries")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("CountriesPort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace("http://spring.io/guides/gs-producing-web-service");
wsdl11Definition.setSchema(countriesSchema);
return wsdl11Definition;
}

@Bean
public XsdSchema countriesSchema() {
return new SimpleXsdSchema(new ClassPathResource("schemas/countries.xsd"));
}
}

This configuration:

  1. Enables Spring Web Services with @EnableWs
  2. Registers the MessageDispatcherServlet to handle SOAP requests
  3. Sets up a WSDL definition for the countries service
  4. Points to our XML schema

Creating the Endpoint

Now that we have the configuration in place, let's create the endpoint that will handle the SOAP requests:

java
@Endpoint
public class CountryEndpoint {

private static final String NAMESPACE_URI = "http://spring.io/guides/gs-producing-web-service";

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's what this endpoint does:

  • The @Endpoint annotation registers the class with Spring WS as a Web Service Endpoint
  • @PayloadRoot maps the incoming request to this method
  • @RequestPayload indicates that the parameter should be mapped from the request payload
  • @ResponsePayload indicates that the return value should be mapped to the response payload

Implementing the Country Repository

Let's create a simple repository to store our country data:

java
@Component
public class CountryRepository {
private static final Map<String, Country> countries = new HashMap<>();

@PostConstruct
public void initData() {
Country spain = new Country();
spain.setName("Spain");
spain.setCapital("Madrid");
spain.setCurrency(Currency.EUR);
spain.setPopulation(46704314);
countries.put(spain.getName(), spain);

Country uk = new Country();
uk.setName("United Kingdom");
uk.setCapital("London");
uk.setCurrency(Currency.GBP);
uk.setPopulation(65110000);
countries.put(uk.getName(), uk);

Country usa = new Country();
usa.setName("United States");
usa.setCapital("Washington D.C.");
usa.setCurrency(Currency.USD);
usa.setPopulation(328200000);
countries.put(usa.getName(), usa);
}

public Country findCountry(String name) {
return countries.get(name);
}
}

Testing the SOAP Service

Sample SOAP Request

Once your application is running, you can test your SOAP service using a tool like SoapUI or by sending a raw SOAP request. Here's an example request:

xml
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:gs="http://spring.io/guides/gs-producing-web-service">
<soapenv:Header/>
<soapenv:Body>
<gs:getCountryRequest>
<gs:name>Spain</gs:name>
</gs:getCountryRequest>
</soapenv:Body>
</soapenv:Envelope>

Expected Response

When you send the above request to your service (e.g., http://localhost:8080/ws), you should receive a response like:

xml
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<gs:getCountryResponse xmlns:gs="http://spring.io/guides/gs-producing-web-service">
<gs:country>
<gs:name>Spain</gs:name>
<gs:population>46704314</gs:population>
<gs:capital>Madrid</gs:capital>
<gs:currency>EUR</gs:currency>
</gs:country>
</gs:getCountryResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Advanced Configuration

Adding WS-Security

To add basic security to your SOAP service, you can configure Spring WS Security:

java
@Bean
public XwsSecurityInterceptor securityInterceptor() {
XwsSecurityInterceptor securityInterceptor = new XwsSecurityInterceptor();
securityInterceptor.setCallbackHandler(callbackHandler());
securityInterceptor.setPolicyConfiguration(new ClassPathResource("securityPolicy.xml"));
return securityInterceptor;
}

@Bean
public SimplePasswordValidationCallbackHandler callbackHandler() {
SimplePasswordValidationCallbackHandler callbackHandler = new SimplePasswordValidationCallbackHandler();
callbackHandler.setUsersMap(Collections.singletonMap("admin", "secret"));
return callbackHandler;
}

@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(securityInterceptor());
}

Custom SOAP Faults

You can also configure custom SOAP faults to handle errors in a more user-friendly way:

java
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest")
@ResponsePayload
public GetCountryResponse getCountry(@RequestPayload GetCountryRequest request) {
try {
GetCountryResponse response = new GetCountryResponse();
Country country = countryRepository.findCountry(request.getName());

if (country == null) {
throw new CountryNotFoundException(request.getName());
}

response.setCountry(country);
return response;
} catch (CountryNotFoundException ex) {
throw new SoapFaultClientException("Country not found",
new Locale("en"), ex, new QName("http://spring.io/guides/gs-producing-web-service", "CountryFault"));
}
}

Common Best Practices

  1. Use Contract-First Approach: Always start with defining your XSD schema before writing any Java code.
  2. Versioning: Include version information in your namespace to manage service evolution.
  3. Error Handling: Implement proper error handling with custom SOAP faults.
  4. Documentation: Document your WSDL and schema for API consumers.
  5. Security: Apply appropriate security measures, especially for production environments.
  6. Testing: Create comprehensive tests for your SOAP endpoints.

Summary

In this tutorial, we've covered how to configure a SOAP web service using Spring WS:

  1. We set up the Maven dependencies required for Spring SOAP services
  2. We followed the contract-first approach by defining an XML schema
  3. We configured Spring WS to expose our SOAP service
  4. We implemented an endpoint to handle incoming SOAP requests
  5. We created a repository to provide data for our service
  6. We explored advanced configurations like security and error handling

SOAP web services continue to be important in enterprise environments, especially when interfacing with legacy systems or when strict contracts are required. Spring Web Services provides a powerful and flexible framework for creating and consuming these services.

Additional Resources

Exercises

  1. Extend the country service to add a new operation for retrieving all available countries.
  2. Implement validation for the country name in the request.
  3. Add WS-Security to secure your endpoint using username/password authentication.
  4. Create a client application that consumes your SOAP service.
  5. Implement logging for incoming and outgoing SOAP messages.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)