SOAP Web Services
Master SOAP web services with visual diagrams covering WSDL, JAX-WS, contract-first vs contract-last, SOAP message structure, and complete implementation examples
SOAP (Simple Object Access Protocol) is an XML-based protocol for exchanging structured information in web services. Understanding SOAP architecture, WSDL, and JAX-WS is essential for enterprise integration.
SOAP Architecture Stack
graph TB
A[Service Discovery - UDDI] --> B[Service Description - WSDL]
B --> C[XML Messaging - SOAP]
C --> D[Service Transport - HTTP/SMTP/FTP]
A1[Find Services] --> A
B1[Define Interface] --> B
C1[Message Format] --> C
D1[Network Protocol] --> D
style A fill:#FF9800
style B fill:#4CAF50
style C fill:#2196F3
style D fill:#9C27B0
Key Points:
- Transport Layer: HTTP, SMTP, FTP for message delivery
- Messaging Layer: SOAP for XML message format
- Description Layer: WSDL defines service interface
- Discovery Layer: UDDI for service registry
- Protocol Stack: Each layer builds on the previous
SOAP Message Structure
<!-- SOAP Request -->
POST /PriceService HTTP/1.1
Host: www.example.com
Content-Type: application/soap+xml; charset=utf-8
Content-Length: 350
<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:m="http://www.example.com/prices">
<!-- Optional Header -->
<soap:Header>
<m:Authentication>
<m:Username>user123</m:Username>
<m:Password>pass456</m:Password>
</m:Authentication>
</soap:Header>
<!-- Required Body -->
<soap:Body>
<m:GetPrice>
<m:Item>PlasmaTV</m:Item>
<m:Category>Electronics</m:Category>
</m:GetPrice>
</soap:Body>
</soap:Envelope>
<!-- SOAP Response -->
HTTP/1.1 200 OK
Content-Type: application/soap+xml; charset=utf-8
Content-Length: 280
<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:m="http://www.example.com/prices">
<soap:Body>
<m:GetPriceResponse>
<m:Price currency="USD">3500.00</m:Price>
<m:Availability>In Stock</m:Availability>
</m:GetPriceResponse>
</soap:Body>
</soap:Envelope>
Contract-First vs Contract-Last
flowchart TB
subgraph "Contract-First Approach"
A1[Define XSD Schema] --> A2[Define WSDL]
A2 --> A3[Generate Java Classes]
A3 --> A4[Implement Service]
A4 --> A5[Deploy]
end
subgraph "Contract-Last Approach"
B1[Write Java Classes] --> B2[Add Annotations]
B2 --> B3[Generate WSDL]
B3 --> B4[Deploy]
end
style A2 fill:#4CAF50
style B3 fill:#FF9800
Key Points:
- Contract-First: Define WSDL/XSD first, generate Java code
- Contract-Last: Write Java code first, generate WSDL
- Best Practice: Contract-first for loose coupling and control
- Flexibility: Contract-first allows independent client/server development
- Maintenance: Contract-first easier to version and maintain
Contract-First Implementation
// 1. Define XSD Schema (customer.xsd)
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/customer"
xmlns:tns="http://example.com/customer">
<xs:element name="GetCustomerRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="customerId" type="xs:long"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="GetCustomerResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="customer" type="tns:Customer"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="Customer">
<xs:sequence>
<xs:element name="id" type="xs:long"/>
<xs:element name="name" type="xs:string"/>
<xs:element name="email" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
// 2. Generate Java classes using JAXB
// mvn jaxb2:xjc
// 3. Implement Service Endpoint
@WebService(serviceName = "CustomerService",
portName = "CustomerPort",
targetNamespace = "http://example.com/customer")
public class CustomerServiceImpl {
@Autowired
private CustomerRepository customerRepository;
@WebMethod(operationName = "getCustomer")
@WebResult(name = "GetCustomerResponse")
public GetCustomerResponse getCustomer(
@WebParam(name = "GetCustomerRequest")
GetCustomerRequest request) {
Long customerId = request.getCustomerId();
Customer customer = customerRepository.findById(customerId);
GetCustomerResponse response = new GetCustomerResponse();
response.setCustomer(customer);
return response;
}
}
// 4. Spring Configuration
@Configuration
@EnableWs
public class WebServiceConfig extends WsConfigurerAdapter {
@Bean
public ServletRegistrationBean<MessageDispatcherServlet>
messageDispatcherServlet(ApplicationContext context) {
MessageDispatcherServlet servlet =
new MessageDispatcherServlet();
servlet.setApplicationContext(context);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<>(servlet, "/ws/*");
}
@Bean(name = "customers")
public DefaultWsdl11Definition defaultWsdl11Definition(
XsdSchema customersSchema) {
DefaultWsdl11Definition wsdl11Definition =
new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("CustomerPort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace("http://example.com/customer");
wsdl11Definition.setSchema(customersSchema);
return wsdl11Definition;
}
@Bean
public XsdSchema customersSchema() {
return new SimpleXsdSchema(
new ClassPathResource("customer.xsd"));
}
}
Contract-Last Implementation
// 1. Write Java Classes with Annotations
@WebService(name = "CustomerService",
serviceName = "CustomerWebService",
targetNamespace = "http://example.com/customer")
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT,
use = SOAPBinding.Use.LITERAL)
public class CustomerServiceImpl {
@WebMethod(operationName = "getCustomer")
@WebResult(name = "customer")
public Customer getCustomer(
@WebParam(name = "customerId") Long customerId) {
// Business logic
Customer customer = new Customer();
customer.setId(customerId);
customer.setName("John Doe");
customer.setEmail("[email protected]");
return customer;
}
@WebMethod(operationName = "createCustomer")
@WebResult(name = "customerId")
public Long createCustomer(
@WebParam(name = "customer") Customer customer) {
// Save customer
return 123L;
}
}
// 2. Customer POJO
@XmlRootElement(name = "customer")
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlElement(required = true)
private Long id;
@XmlElement(required = true)
private String name;
@XmlElement(required = true)
private String email;
// Getters and setters
}
// 3. Publish Service
public class ServicePublisher {
public static void main(String[] args) {
String url = "http://localhost:8080/ws/customer";
Endpoint.publish(url, new CustomerServiceImpl());
System.out.println("Service published at: " + url);
}
}
// 4. WSDL automatically generated at:
// http://localhost:8080/ws/customer?wsdl
WSDL Structure
graph TB
A[WSDL Document] --> B[Types]
A --> C[Message]
A --> D[PortType]
A --> E[Binding]
A --> F[Service]
B --> B1[XSD Schema<br/>Data Types]
C --> C1[Input/Output<br/>Messages]
D --> D1[Operations<br/>Abstract Interface]
E --> E1[Protocol Binding<br/>SOAP/HTTP]
F --> F1[Endpoint<br/>URL Location]
style D fill:#4CAF50
style E fill:#FF9800
Key Points:
- Types: Data type definitions using XSD
- Message: Input and output message definitions
- PortType: Abstract operations (like Java interface)
- Binding: Protocol and data format specifications
- Service: Endpoint location (URL)
WSDL Example
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="CustomerService"
targetNamespace="http://example.com/customer"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://example.com/customer"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!-- Types: Data type definitions -->
<types>
<xsd:schema targetNamespace="http://example.com/customer">
<xsd:element name="GetCustomerRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="customerId" type="xsd:long"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="GetCustomerResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="customer" type="tns:Customer"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="Customer">
<xsd:sequence>
<xsd:element name="id" type="xsd:long"/>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="email" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
</types>
<!-- Messages: Input and output -->
<message name="GetCustomerRequest">
<part name="parameters" element="tns:GetCustomerRequest"/>
</message>
<message name="GetCustomerResponse">
<part name="parameters" element="tns:GetCustomerResponse"/>
</message>
<!-- PortType: Operations (Abstract interface) -->
<portType name="CustomerPortType">
<operation name="getCustomer">
<input message="tns:GetCustomerRequest"/>
<output message="tns:GetCustomerResponse"/>
</operation>
</portType>
<!-- Binding: Protocol specification -->
<binding name="CustomerBinding" type="tns:CustomerPortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="getCustomer">
<soap:operation soapAction="getCustomer"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<!-- Service: Endpoint location -->
<service name="CustomerService">
<port name="CustomerPort" binding="tns:CustomerBinding">
<soap:address location="http://localhost:8080/ws/customer"/>
</port>
</service>
</definitions>
WSDL Operation Types
// 1. One-Way: Receive message, no response
@WebMethod
@Oneway
public void logEvent(@WebParam(name = "event") Event event) {
eventLogger.log(event);
// No return value
}
// 2. Request-Response: Most common, receive and respond
@WebMethod
public Customer getCustomer(@WebParam(name = "id") Long id) {
return customerService.findById(id);
}
// 3. Solicit-Response: Send request, wait for response (server-initiated)
// Less common, typically used in async scenarios
// 4. Notification: Send message, no response expected
@WebMethod
@Oneway
public void sendNotification(@WebParam(name = "message") String message) {
notificationService.send(message);
}
JAX-WS Client Implementation
// 1. Generate client stubs from WSDL
// wsimport -keep -s src http://localhost:8080/ws/customer?wsdl
// 2. Use generated client
public class CustomerClient {
public static void main(String[] args) {
// Create service
CustomerWebService service = new CustomerWebService();
// Get port
CustomerService port = service.getCustomerPort();
// Call web service method
Customer customer = port.getCustomer(123L);
System.out.println("Customer: " + customer.getName());
System.out.println("Email: " + customer.getEmail());
}
}
// 3. Spring Integration
@Configuration
public class WebServiceClientConfig {
@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.example.customer");
return marshaller;
}
@Bean
public CustomerClient customerClient(Jaxb2Marshaller marshaller) {
CustomerClient client = new CustomerClient();
client.setDefaultUri("http://localhost:8080/ws");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
@Component
public class CustomerClient extends WebServiceGatewaySupport {
public Customer getCustomer(Long customerId) {
GetCustomerRequest request = new GetCustomerRequest();
request.setCustomerId(customerId);
GetCustomerResponse response = (GetCustomerResponse)
getWebServiceTemplate().marshalSendAndReceive(
"http://localhost:8080/ws/customer",
request
);
return response.getCustomer();
}
}
Best Practices
- Use Contract-First: Better for loose coupling and versioning
- Version Your Services: Include version in namespace
- Document Operations: Use clear operation names and descriptions
- Error Handling: Use SOAP faults for errors
- Security: Implement WS-Security for authentication
- Testing: Use SoapUI or Postman for testing
- Performance: Consider message size and compression
- Monitoring: Log requests and responses for debugging