Complete guide for using the SOAP client to communicate with SOAP/WSDL services.
- Quick Start
- Creating a SOAP Client
- Configuration Options
- Invoking Operations
- Authentication
- Advanced Features
- Best Practices
- Troubleshooting
import org.fireflyframework.client.SoapClient;
import org.fireflyframework.client.ServiceClient;
import reactor.core.publisher.Mono;
@Service
public class WeatherService {
private final SoapClient weatherClient;
public WeatherService() {
this.weatherClient = ServiceClient.soap("weather-service")
.wsdlUrl("http://www.webservicex.net/globalweather.asmx?WSDL")
.build();
}
public Mono<WeatherResponse> getWeather(String city, String country) {
WeatherRequest request = new WeatherRequest();
request.setCityName(city);
request.setCountryName(country);
return weatherClient.invokeAsync("GetWeather", request, WeatherResponse.class);
}
}SoapClient client = ServiceClient.soap("my-service")
.wsdlUrl("http://example.com/service?WSDL") // Required
.build();SoapClient client = ServiceClient.soap("secure-service")
.wsdlUrl("https://secure.example.com/service?WSDL")
.credentials("username", "password") // WS-Security
.build();Many SOAP services (like Equifax, PayNet) include credentials in the WSDL URL:
SoapClient client = ServiceClient.soap("equifax-spain")
.wsdlUrl("https://uat2.equifax.es/icflex/api?WSDL&user=myuser&password=mypass")
.build();
// Credentials are automatically extracted and used for WS-SecuritySoapClient client = ServiceClient.soap("payment-service")
.wsdlUrl("https://payment.example.com/service?WSDL")
.credentials("api-user", "secret-password")
.timeout(Duration.ofSeconds(60))
.enableMtom() // For large binary transfers
.enableSchemaValidation() // Validate XML against schema
.header("X-API-Key", "your-api-key") // Custom HTTP header
.header("X-Client-Version", "1.0.0")
.build();| Method | Description | Example |
|---|---|---|
wsdlUrl(String) |
WSDL URL (can include credentials) | .wsdlUrl("http://example.com/service?WSDL") |
| Method | Description | Default | Example |
|---|---|---|---|
credentials(String, String) |
WS-Security username/password | None | .credentials("user", "pass") |
username(String) |
WS-Security username | None | .username("api-user") |
password(String) |
WS-Security password | None | .password("secret") |
timeout(Duration) |
Request timeout | 30s | .timeout(Duration.ofSeconds(60)) |
enableMtom() |
Enable MTOM for attachments | false | .enableMtom() |
enableSchemaValidation() |
Validate XML against schema | true | .enableSchemaValidation() |
disableSchemaValidation() |
Disable schema validation | - | .disableSchemaValidation() |
header(String, String) |
Add custom HTTP header | None | .header("X-API-Key", "key") |
property(String, Object) |
Set JAX-WS property | None | .property("key", "value") |
serviceName(QName) |
Override service QName | Auto-detected | .serviceName(qname) |
portName(QName) |
Override port QName | Auto-detected | .portName(qname) |
endpointAddress(String) |
Override endpoint URL | From WSDL | .endpointAddress("https://prod.example.com") |
| Method | Description | Example |
|---|---|---|
trustStore(String, String) |
SSL trust store path and password | .trustStore("/path/to/truststore.jks", "password") |
keyStore(String, String) |
SSL key store path and password | .keyStore("/path/to/keystore.jks", "password") |
disableSslVerification() |
Disable SSL verification (dev only!) | .disableSslVerification() |
public Mono<PaymentResponse> processPayment(PaymentRequest request) {
return soapClient.invokeAsync("ProcessPayment", request, PaymentResponse.class)
.doOnSuccess(response -> log.info("Payment processed: {}", response.getTransactionId()))
.doOnError(error -> log.error("Payment failed", error));
}public Mono<WeatherResponse> getWeather(String city, String country) {
return soapClient.invoke("GetWeather")
.withParameter("cityName", city)
.withParameter("countryName", country)
.withTimeout(Duration.ofSeconds(30))
.execute(WeatherResponse.class);
}// Get the JAX-WS port for advanced operations
WeatherServicePort port = soapClient.getPort(WeatherServicePort.class);
WeatherResponse response = port.getWeather(city, country);// Get list of all operations from WSDL
List<String> operations = soapClient.getOperations();
operations.forEach(op -> log.info("Available operation: {}", op));
// Output: GetWeather, GetCitiesByCountry, etc.SoapClient client = ServiceClient.soap("secure-service")
.wsdlUrl("https://secure.example.com/service?WSDL")
.credentials("api-user", "secret-password")
.build();// Common pattern for services like Equifax, PayNet
SoapClient client = ServiceClient.soap("equifax")
.wsdlUrl("https://api.equifax.com/service?WSDL&user=myuser&password=mypass")
.build();
// Credentials are automatically extracted and usedSoapClient client = ServiceClient.soap("api-service")
.wsdlUrl("https://api.example.com/service?WSDL")
.header("X-API-Key", "your-api-key-here")
.header("Authorization", "Bearer your-token")
.build();String credentials = "username:password";
String encodedCredentials = Base64.getEncoder().encodeToString(credentials.getBytes());
SoapClient client = ServiceClient.soap("basic-auth-service")
.wsdlUrl("https://api.example.com/service?WSDL")
.header("Authorization", "Basic " + encodedCredentials)
.build();// Enable MTOM for efficient binary transfer
SoapClient client = ServiceClient.soap("document-service")
.wsdlUrl("https://docs.example.com/service?WSDL")
.enableMtom()
.build();
// Send document with attachment
DocumentRequest request = new DocumentRequest();
request.setFileName("report.pdf");
request.setContent(pdfBytes); // Large binary data
Mono<DocumentResponse> response = client.invokeAsync("UploadDocument", request, DocumentResponse.class);import javax.xml.namespace.QName;
QName serviceName = new QName("http://example.com/services", "PaymentService");
QName portName = new QName("http://example.com/services", "PaymentServicePort");
SoapClient client = ServiceClient.soap("payment-service")
.wsdlUrl("https://payment.example.com/service?WSDL")
.serviceName(serviceName)
.portName(portName)
.build();// Use different endpoint than what's in WSDL (e.g., for testing)
SoapClient client = ServiceClient.soap("payment-service")
.wsdlUrl("https://payment.example.com/service?WSDL") // WSDL from prod
.endpointAddress("https://uat.payment.example.com/service") // But call UAT
.build();SoapClient client = ServiceClient.soap("banking-service")
.wsdlUrl("https://secure-bank.example.com/service?WSDL")
.credentials("api-user", "password")
.trustStore("/path/to/truststore.jks", "truststore-password")
.keyStore("/path/to/keystore.jks", "keystore-password")
.build();// ⚠️ ONLY FOR DEVELOPMENT/TESTING - NEVER IN PRODUCTION!
SoapClient client = ServiceClient.soap("dev-service")
.wsdlUrl("https://dev.example.com/service?WSDL")
.disableSslVerification()
.build();SoapClient client = ServiceClient.soap("custom-service")
.wsdlUrl("https://api.example.com/service?WSDL")
.property("javax.xml.ws.client.connectionTimeout", "30000")
.property("javax.xml.ws.client.receiveTimeout", "60000")
.property("com.sun.xml.ws.request.timeout", "30000")
.build();// Check if WSDL is accessible and service is ready
boolean ready = soapClient.isReady();
// Perform comprehensive health check
Mono<Void> healthCheck = soapClient.healthCheck()
.doOnSuccess(v -> log.info("SOAP service is healthy"))
.doOnError(e -> log.error("SOAP service health check failed", e));
// Schedule periodic health checks
@Scheduled(fixedRate = 60000) // Every minute
public void checkHealth() {
soapClient.healthCheck()
.subscribe(
v -> log.debug("Health check passed"),
e -> log.error("Health check failed", e)
);
}import org.fireflyframework.client.exception.*;
public Mono<PaymentResponse> processPayment(PaymentRequest request) {
return soapClient.invokeAsync("ProcessPayment", request, PaymentResponse.class)
.onErrorMap(ServiceNotFoundException.class,
ex -> new PaymentServiceException("Payment service not found"))
.onErrorMap(ServiceUnavailableException.class,
ex -> new PaymentServiceException("Payment service unavailable"))
.onErrorMap(ServiceAuthenticationException.class,
ex -> new PaymentAuthException("Authentication failed"))
.retry(3)
.timeout(Duration.ofSeconds(60));
}// Get service information
String serviceName = soapClient.getServiceName();
String wsdlUrl = soapClient.getWsdlUrl();
QName serviceQName = soapClient.getServiceQName();
QName portQName = soapClient.getPortQName();
ClientType type = soapClient.getClientType(); // Returns ClientType.SOAP
// Shutdown client (releases resources)
soapClient.shutdown();// ✅ GOOD - Type-safe
private final SoapClient equifaxClient;
// ❌ BAD - Requires casting
private final ServiceClient equifaxClient;@Configuration
public class SoapClientConfig {
@Bean
public SoapClient equifaxClient(
@Value("${equifax.wsdl.url}") String wsdlUrl,
@Value("${equifax.username}") String username,
@Value("${equifax.password}") String password) {
return ServiceClient.soap("equifax-spain")
.wsdlUrl(wsdlUrl)
.credentials(username, password)
.timeout(Duration.ofSeconds(60))
.defaultHeader("dptOrchestrationCode", "equifax-comm360")
.build();
}
}# application.yml
equifax:
wsdl:
url: https://uat2.equifax.es/icflex/api?WSDL
username: ${EQUIFAX_USERNAME}
password: ${EQUIFAX_PASSWORD}
timeout: 60spublic Mono<Response> callService(Request request) {
return soapClient.invokeAsync("Operation", request, Response.class)
.onErrorMap(SOAPFaultException.class, ex -> {
String faultCode = ex.getFault().getFaultCode();
String faultString = ex.getFault().getFaultString();
log.error("SOAP Fault: {} - {}", faultCode, faultString);
return new ServiceException("SOAP Fault: " + faultString);
});
}// ✅ GOOD - MTOM for large binary data
SoapClient client = ServiceClient.soap("document-service")
.wsdlUrl("https://docs.example.com/service?WSDL")
.enableMtom() // Efficient binary transfer
.build();
// ❌ BAD - Base64 encoding without MTOM (inefficient)
SoapClient client = ServiceClient.soap("document-service")
.wsdlUrl("https://docs.example.com/service?WSDL")
.build();Problem: Cannot access WSDL URL
Solution:
- Verify WSDL URL is correct and accessible
- Check network connectivity
- Verify authentication if required
- Try accessing WSDL in browser
Problem: WS-Security authentication fails
Solution:
// Ensure credentials are correct
SoapClient client = ServiceClient.soap("service")
.wsdlUrl("https://api.example.com/service?WSDL")
.credentials("correct-username", "correct-password")
.build();
// Or use credentials in URL
SoapClient client = ServiceClient.soap("service")
.wsdlUrl("https://api.example.com/service?WSDL&user=username&password=pass")
.build();Problem: SSL handshake failures, certificate errors
Solution:
// For production: Use proper trust store
SoapClient client = ServiceClient.soap("service")
.wsdlUrl("https://secure.example.com/service?WSDL")
.trustStore("/path/to/truststore.jks", "password")
.build();
// For development only: Disable verification
SoapClient client = ServiceClient.soap("service")
.wsdlUrl("https://dev.example.com/service?WSDL")
.disableSslVerification() // ⚠️ DEV ONLY!
.build();Problem: Requests timing out
Solution:
// Increase timeout
SoapClient client = ServiceClient.soap("slow-service")
.wsdlUrl("https://slow.example.com/service?WSDL")
.timeout(Duration.ofMinutes(2)) // Increase from default 30s
.build();Problem: Operation name not found in WSDL
Solution:
// List available operations
List<String> operations = soapClient.getOperations();
operations.forEach(System.out::println);
// Use exact operation name from WSDL
soapClient.invokeAsync("GetWeather", request, Response.class); // Case-sensitive!Problem: Cannot parse SOAP response
Solution:
// Disable schema validation if schema is problematic
SoapClient client = ServiceClient.soap("service")
.wsdlUrl("https://api.example.com/service?WSDL")
.disableSchemaValidation()
.build();
// Enable message logging to see raw XML# In application.yml
firefly:
service-client:
soap:
message-logging-enabled: true # See raw SOAP messages✅ WSDL Parsing: Automatic service discovery
✅ WS-Security: Username/password authentication
✅ MTOM/XOP: Efficient binary attachments
✅ SSL/TLS: Custom trust stores and key stores
✅ Custom Headers: HTTP and SOAP headers
✅ Schema Validation: XML validation against WSDL
✅ Circuit Breaker: Automatic failure detection
✅ Health Checks: WSDL availability monitoring
✅ Timeouts: Configurable request timeouts
✅ Reactive: Non-blocking Mono responses
✅ Fluent API: Easy operation invocation
❌ SOAP Server: This is client-only
❌ WSDL Generation: Use JAX-WS tools
❌ WS-Policy: Not currently supported
❌ WS-ReliableMessaging: Not currently supported
Next Steps:
Core Clients:
Helper Utilities:
Configuration: