Skip to main content

Spring SOAP Client

Introduction

SOAP (Simple Object Access Protocol) is a messaging protocol that allows programs running on disparate operating systems to communicate using HTTP and XML. While RESTful services have gained popularity, many enterprise systems still rely on SOAP-based services. Spring Web Services provides excellent support for creating SOAP clients that can consume these services.

In this guide, you'll learn how to build a Spring SOAP client that can communicate with SOAP web services. We'll cover the entire process from configuration to implementation, with practical examples to help you understand how to apply these concepts in real-world scenarios.

Prerequisites

Before diving into Spring SOAP clients, you should have:

  • Basic knowledge of Spring Framework
  • Familiarity with XML and XSD (XML Schema Definition)
  • Java Development Kit (JDK) 8 or higher
  • Maven or Gradle for dependency management
  • An IDE like IntelliJ IDEA or Eclipse

Setting Up Your Project

Let's start by setting up a Spring Boot project with the necessary dependencies for Spring Web Services.

Maven Dependencies

Add the following dependencies to your pom.xml file:

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

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

<!-- JAXB API -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>

<!-- JAXB Runtime -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>

Generating Java Classes from WSDL

To communicate with a SOAP service, we first need to generate Java classes from the Web Service Definition Language (WSDL) file. Maven provides a plugin for this:

xml
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.14.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaLanguage>WSDL</schemaLanguage>
<schemas>
<schema>
<url>http://www.example.com/weather/WeatherService.wsdl</url>
</schema>
</schemas>
<generatePackage>com.example.weather.wsdl</generatePackage>
</configuration>
</plugin>

Replace the URL with the actual WSDL endpoint of the service you want to consume.

Creating a SOAP Client

Now, let's create a simple SOAP client to consume a weather service. We'll use a fictional weather service for this example.

Step 1: Configure WebServiceTemplate

The WebServiceTemplate is the core class for client-side SOAP access in Spring WS. First, let's configure it:

java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.ws.client.core.WebServiceTemplate;

@Configuration
public class WeatherClientConfig {

@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// Set the package name where our generated JAXB classes are
marshaller.setContextPath("com.example.weather.wsdl");
return marshaller;
}

@Bean
public WebServiceTemplate webServiceTemplate(Jaxb2Marshaller marshaller) {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.setUnmarshaller(marshaller);
webServiceTemplate.setDefaultUri("http://www.example.com/weather");
return webServiceTemplate;
}
}

The Jaxb2Marshaller handles the conversion between Java objects and XML. The contextPath should point to the package where your generated JAXB classes are located.

Step 2: Create the Client Class

Now, let's create our weather client class that will use WebServiceTemplate to make SOAP calls:

java
import com.example.weather.wsdl.GetWeatherRequest;
import com.example.weather.wsdl.GetWeatherResponse;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.stereotype.Component;

@Component
public class WeatherClient {

private final WebServiceTemplate webServiceTemplate;

public WeatherClient(WebServiceTemplate webServiceTemplate) {
this.webServiceTemplate = webServiceTemplate;
}

public GetWeatherResponse getWeather(String city, String country) {
GetWeatherRequest request = new GetWeatherRequest();
request.setCity(city);
request.setCountry(country);

// Call the SOAP service
GetWeatherResponse response = (GetWeatherResponse) webServiceTemplate.marshalSendAndReceive(request);

return response;
}
}

Step 3: Using the Client

Let's create a simple service class that uses our WeatherClient:

java
import com.example.weather.wsdl.GetWeatherResponse;
import com.example.weather.wsdl.Weather;
import org.springframework.stereotype.Service;

@Service
public class WeatherService {

private final WeatherClient weatherClient;

public WeatherService(WeatherClient weatherClient) {
this.weatherClient = weatherClient;
}

public String getCurrentTemperature(String city, String country) {
GetWeatherResponse response = weatherClient.getWeather(city, country);
Weather weather = response.getWeather();

return String.format("The current temperature in %s, %s is %s°C with %s",
city, country, weather.getTemperature(), weather.getCondition());
}
}

Adding Error Handling

SOAP clients should handle errors gracefully. Let's enhance our client with error handling:

java
import com.example.weather.wsdl.GetWeatherRequest;
import com.example.weather.wsdl.GetWeatherResponse;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.client.SoapFaultClientException;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component
public class WeatherClient {

private static final Logger logger = LoggerFactory.getLogger(WeatherClient.class);
private final WebServiceTemplate webServiceTemplate;

public WeatherClient(WebServiceTemplate webServiceTemplate) {
this.webServiceTemplate = webServiceTemplate;
}

public GetWeatherResponse getWeather(String city, String country) {
GetWeatherRequest request = new GetWeatherRequest();
request.setCity(city);
request.setCountry(country);

try {
// Call the SOAP service
GetWeatherResponse response = (GetWeatherResponse) webServiceTemplate.marshalSendAndReceive(request);
return response;
} catch (SoapFaultClientException e) {
logger.error("SOAP fault: {}", e.getFaultStringOrReason());
throw new WeatherServiceException("Error fetching weather data: " + e.getFaultStringOrReason(), e);
} catch (Exception e) {
logger.error("Error during SOAP call", e);
throw new WeatherServiceException("Error fetching weather data", e);
}
}
}

Don't forget to create a custom exception class:

java
public class WeatherServiceException extends RuntimeException {

public WeatherServiceException(String message) {
super(message);
}

public WeatherServiceException(String message, Throwable cause) {
super(message, cause);
}
}

Customizing the SOAP Client

Let's look at some advanced customizations for your SOAP client.

Setting HTTP Authentication

Many SOAP services require authentication:

java
@Bean
public WebServiceTemplate webServiceTemplate(Jaxb2Marshaller marshaller) {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.setUnmarshaller(marshaller);
webServiceTemplate.setDefaultUri("http://www.example.com/weather");

// Add HTTP Basic Authentication
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
HttpClient httpClient = HttpClientBuilder.create()
.setDefaultCredentialsProvider(createCredentialsProvider())
.build();
((HttpComponentsClientHttpRequestFactory) requestFactory).setHttpClient(httpClient);
webServiceTemplate.setMessageSender(new HttpComponentsMessageSender(httpClient));

return webServiceTemplate;
}

private CredentialsProvider createCredentialsProvider() {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials("username", "password")
);
return credentialsProvider;
}

Don't forget to add the HttpComponents dependency:

xml
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>

Setting Timeouts

For production systems, set appropriate timeouts:

java
@Bean
public WebServiceTemplate webServiceTemplate(Jaxb2Marshaller marshaller) {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.setUnmarshaller(marshaller);
webServiceTemplate.setDefaultUri("http://www.example.com/weather");

HttpComponentsMessageSender messageSender = new HttpComponentsMessageSender();
HttpClient httpClient = HttpClientBuilder.create()
.setConnectionTimeToLive(30, TimeUnit.SECONDS)
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(5000)
.setSocketTimeout(5000)
.build())
.build();
messageSender.setHttpClient(httpClient);
webServiceTemplate.setMessageSender(messageSender);

return webServiceTemplate;
}

Real-World Example: Currency Conversion Service

Let's create a practical example using a public SOAP service for currency conversion:

java
// Configuration
@Configuration
public class CurrencyClientConfig {

@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.example.currency.wsdl");
return marshaller;
}

@Bean
public WebServiceTemplate webServiceTemplate(Jaxb2Marshaller marshaller) {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.setUnmarshaller(marshaller);
webServiceTemplate.setDefaultUri("http://www.example.com/currency-service");
return webServiceTemplate;
}
}

// Client
@Component
public class CurrencyClient {

private final WebServiceTemplate webServiceTemplate;

public CurrencyClient(WebServiceTemplate webServiceTemplate) {
this.webServiceTemplate = webServiceTemplate;
}

public ConvertCurrencyResponse convertCurrency(String fromCurrency, String toCurrency, double amount) {
ConvertCurrencyRequest request = new ConvertCurrencyRequest();
request.setFromCurrency(fromCurrency);
request.setToCurrency(toCurrency);
request.setAmount(amount);

return (ConvertCurrencyResponse) webServiceTemplate.marshalSendAndReceive(request);
}
}

// Service
@Service
public class CurrencyService {

private final CurrencyClient currencyClient;

public CurrencyService(CurrencyClient currencyClient) {
this.currencyClient = currencyClient;
}

public String convertAmount(String from, String to, double amount) {
ConvertCurrencyResponse response = currencyClient.convertCurrency(from, to, amount);

return String.format("%.2f %s = %.2f %s (Rate: %.4f)",
amount, from, response.getConvertedAmount(), to, response.getExchangeRate());
}
}

// Controller
@RestController
@RequestMapping("/api/currency")
public class CurrencyController {

private final CurrencyService currencyService;

public CurrencyController(CurrencyService currencyService) {
this.currencyService = currencyService;
}

@GetMapping("/convert")
public String convertCurrency(
@RequestParam String from,
@RequestParam String to,
@RequestParam double amount) {
return currencyService.convertAmount(from, to, amount);
}
}

For an example response:

Request:

GET /api/currency/convert?from=USD&to=EUR&amount=100

Response:

100.00 USD = 85.20 EUR (Rate: 0.8520)

Testing Your SOAP Client

It's important to test your SOAP client. Spring provides a mock web service server for testing:

java
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.ws.test.client.MockWebServiceServer;
import org.springframework.xml.transform.StringSource;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ws.test.client.RequestMatchers.payload;
import static org.springframework.ws.test.client.ResponseCreators.withPayload;

@SpringBootTest
public class WeatherClientTest {

@Autowired
private WeatherClient weatherClient;

@Autowired
private WebServiceTemplate webServiceTemplate;

private MockWebServiceServer mockServer;

@BeforeEach
public void setUp() {
mockServer = MockWebServiceServer.createServer(webServiceTemplate);
}

@Test
public void testGetWeather() {
// Expected request
String request =
"<getWeatherRequest>" +
"<city>New York</city>" +
"<country>USA</country>" +
"</getWeatherRequest>";

// Mock response
String response =
"<getWeatherResponse>" +
"<weather>" +
"<temperature>22</temperature>" +
"<condition>Sunny</condition>" +
"</weather>" +
"</getWeatherResponse>";

mockServer.expect(payload(new StringSource(request)))
.andRespond(withPayload(new StringSource(response)));

GetWeatherResponse weatherResponse = weatherClient.getWeather("New York", "USA");

assertThat(weatherResponse).isNotNull();
assertThat(weatherResponse.getWeather().getTemperature()).isEqualTo("22");
assertThat(weatherResponse.getWeather().getCondition()).isEqualTo("Sunny");

mockServer.verify();
}
}

Best Practices for SOAP Clients

  1. Set appropriate timeouts - Never use the default timeouts in production.
  2. Implement proper error handling - Catch and handle SOAP faults properly.
  3. Use logging - Log requests and responses for debugging, but be careful with sensitive information.
  4. Add circuit breakers - Use libraries like Resilience4j to handle service unavailability gracefully.
  5. Pool connections - For high-volume applications, use connection pooling.
  6. Secure credentials - Never hardcode credentials; use a secure configuration source.

Summary

In this guide, we've learned how to create a SOAP client using Spring Web Services. We've covered:

  • Setting up the necessary dependencies
  • Generating Java classes from WSDL
  • Configuring WebServiceTemplate
  • Creating a client class for SOAP service consumption
  • Implementing error handling
  • Customizing the SOAP client with authentication and timeouts
  • Creating a real-world currency conversion example
  • Testing SOAP clients using MockWebServiceServer
  • Best practices for working with SOAP clients

By following these steps, you can build robust SOAP clients that effectively communicate with SOAP web services in various enterprise environments.

Additional Resources

Exercises

  1. Create a SOAP client for a public weather service that displays the forecast for the next 5 days.
  2. Implement a retry mechanism using Spring Retry for your SOAP client.
  3. Build a simple web application that uses a SOAP client to convert currencies with a proper UI.
  4. Add a caching layer to your SOAP client to reduce API calls for frequently accessed data.
  5. Enhance your error handling to provide more specific error messages based on different SOAP faults.


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