>${basedir}/src/main/java</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/wsdl/customer-service.wsdl</wsdl>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

About the components used in pom.xml

** Project structure **
project-folder-structure

WSDL & Schema

Designing Schema

You can refer this post for detail discussion on designing schema

Below is the schema which we will use in this example
src/main/resources/wsdl/customer-service.wsdl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://service.customer.com/types/v1"
xmlns:tns="http://service.customer.com/types/v1" elementFormDefault="qualified">
<xs:element name="createCustomerRequest" type="tns:CreateCustomerRequest" />
<xs:element name="createCustomerResponse" type="tns:CreateCustomerResponse" />
<xs:element name="createCustomerFault" type="tns:CreateCustomerFault" />
<xs:complexType name="CreateCustomerRequest">
<xs:sequence>
<xs:element name="customerName">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="50" />
<xs:whiteSpace value="collapse" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="customerAge">
<xs:simpleType>
<xs:restriction base="xs:integer" />
</xs:simpleType>
</xs:element>
<xs:element name="customerCity">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="50" />
<xs:whiteSpace value="collapse" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="customerPhoneNumber">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="10" />
<xs:whiteSpace value="collapse" />
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CreateCustomerResponse">
<xs:sequence>
<xs:element name="customerID" type="xs:integer" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="CreateCustomerFault">
<xs:sequence>
<xs:element name="errorMessage" type="xs:normalizedString" />
<xs:element name="errorCode" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:schema>

Designing WSDL

You can refer this post for detail discussion on designing wsdl.

Below is the WSDL which we will use in this example
src/main/resources/wsdl/customer-service.xsd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:cs="http://service.customer.com/types/v1"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://service.customer.com/v1"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="customer-service" targetNamespace="http://service.customer.com/v1">
<wsdl:types>
<xsd:schema>
<xsd:import namespace="http://service.customer.com/types/v1" schemaLocation="schemas/customer-service.xsd" />
</xsd:schema>
</wsdl:types>
<wsdl:message name="createCustomerRequest">
<wsdl:part name="createCustomerRequest" element="cs:createCustomerRequest" />
</wsdl:message>
<wsdl:message name="createCustomerResponse">
<wsdl:part name="createCustomerResponse" element="cs:createCustomerResponse" />
</wsdl:message>
<wsdl:message name="createCustomerFault">
<wsdl:part name="createCustomerFault" element="cs:createCustomerFault" />
</wsdl:message>
<wsdl:portType name="CustomerServicePort">
<wsdl:operation name="createCustomer">
<wsdl:input message="tns:createCustomerRequest" name="createCustomerRequest" />
<wsdl:output message="tns:createCustomerResponse" name="createCustomerResponse" />
<wsdl:fault message="tns:createCustomerFault" name="createCustomerFault" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="CustomerServiceBinding" type="tns:CustomerServicePort">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="createCustomer">
<soap:operation soapAction="createCustomer" style="document" />
<wsdl:input name="createCustomerRequest">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="createCustomerResponse">
<soap:body use="literal" />
</wsdl:output>
<wsdl:fault name="createCustomerFault">
<soap:fault name="createCustomerFault" use="literal" />
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="CustomerService">
<wsdl:port name="CustomerServicePort" binding="tns:CustomerServiceBinding">
<soap:address location="http://0.0.0.0:8181/cxf/customerservice/v1" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

XML Based Spring configuration

We’ll use the xml based spring configuration for this example to expose the service.

Define the CXF Endpoint

Now, we’ll define the CXF Endpoint to expose the service

1
2
3
<cxf:cxfEndpoint name="customerServiceCXF" address="/customerservice" 
endpointName="cs:CustomerServicePort" serviceName="cs:CustomerService"
xmlns:cs="http://service.customer.com/v1" wsdlURL="wsdl/customer-service.wsdl"/>
Define the CamelContext

All the camel routes will be written in the camel context, so we will create a camelContext in the same file.

1
2
3
<camel:camelContext>
<-- Camel routes here -->
</camel:camelContext>
Define the dataFormats

We will define the data format to convert the soap xml payload into java object and vice versa. Context path is the root folder where you have all the auto-generated classes (WSDL & schema objects created using codegen-plugin)

1
2
3
<camel:dataFormats>
<camel:jaxb id="customerJaxb" contextPath="com.customer.service.types.v1"/>
</camel:dataFormats>
Define the camel route

We’ll define the camel route based on the logic, in this example route is pretty small & simple, but trust me at enterprise level it can be bit complex and longer.

1
2
3
4
5
6
<camel:route id="customer-service-main-route">
<camel:from uri="cxf:bean:customerServiceCXF?dataFormat=PAYLOAD"/>
<camel:unmarshal ref="customerJaxb"/>
<camel:process ref="customerResponseProcessor"></camel:process>
<camel:marshal ref="customerJaxb"/>
</camel:route>

This is how your routes file will look like
src/main/resources/META-INF/spring/bundle-context-routes.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:camel="http://camel.apache.org/schema/spring"
xmlns:cxf="http://camel.apache.org/schema/cxf" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd">
<cxf:cxfEndpoint name="customerServiceCXF" address="/customerservice" endpointName="cs:CustomerServicePort"
xmlns:cs="http://service.customer.com/v1" serviceName="cs:CustomerService" wsdlURL="wsdl/customer-service.wsdl" />
<camel:camelContext>
<camel:dataFormats>
<camel:jaxb id="customerJaxb" contextPath="com.customer.service.types.v1"/>
</camel:dataFormats>
<camel:route id="customer-service-main-route">
<camel:from uri="cxf:bean:customerServiceCXF?dataFormat=PAYLOAD"/>
<camel:unmarshal ref="customerJaxb"/>
<camel:process ref="customerResponseProcessor"></camel:process>
</camel:route>
</camel:camelContext>
<bean id="customerResponseProcessor" class="com.customer.service.processor.CustomerResponseProcessor"/>
</beans>

Response Processor

For this example, we will write a processor which stores the employee data in map and return customer ID.

Create CustomerResponseProcessor.java in src/main/resources

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.customer.service.processor;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.customer.service.types.v1.CreateCustomerRequest;
import com.customer.service.types.v1.CreateCustomerResponse;

public class CustomerRequestProcessor implements Processor {

private static final Logger LOGGER = LoggerFactory.getLogger(CustomerRequestProcessor.class);
private static long counter = 1;
private static Map < Long, CreateCustomerRequest > customers = new HashMap();

@Override
public void process(Exchange exchange) throws Exception {
CreateCustomerRequest request = exchange.getIn().getBody(CreateCustomerRequest.class);
customers.put(counter, request);
synchronized(this) {
counter++;
}

CreateCustomerResponse response = new CreateCustomerResponse();
response.setCustomerID(BigInteger.valueOf(counter));

LOGGER.debug("Employee stored in map ID: " + counter);

exchange.getOut().setBody(response);
}

}

Code Build

Now we’ll compile the code & package it as .jar using maven.

1
$ mvn clean install

Deploy on JBoss Fuse

Once we have the jar file ready with us, we’ll deploy it JBoss Fuse 6.3. You can refer this post to learn more about the deployments on JBoss Fuse.

1
2
$ cd fuse/bin
$ ./fuse

jboss-fuse-welcome
Run following command for the deployment

JBossFuse:admin@root> install -s mvn:com.customer.service/customer-service-impl/1.0.0

Now you can check the service url by visiting below url
http://localhost:8181/cxf

How to create a WSDL file

Introduction

WSDL stands for Web Services Description Language. WSDL is used to describe web services and also known as Contract. It is the base building block to create a SOAP webservice in Contract-First Approach.

Creation of WSDL consist of following:

  • Schema Design
  • WSDL Design

In the below example, we are creating a WSDL which contain a single operation createCustomer. You can follow the similar approach to add more operation in the WSDL.

Schema Design

First of all, we will create a schema file to define the request, response and fault structure.

XML Definition

Create a file of .xsd extension, in this case we are using customer-service.xsd

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://service.customer.com/types/v1"
xmlns:tns="http://service.customer.com/types/v1" elementFormDefault="qualified">

<!-- Schema Content here -->

</xs:schema>

Request structure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<xs:element name="createCustomerRequest" type="tns:CreateCustomerRequest" />
<xs:complexType name="CreateCustomerRequest">
<xs:sequence>
<xs:element name="customerName" type="xs:normalizedString"/>
<xs:element name="customerAge" type="xs:int" />
<xs:element name="customerPhoneNumber">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="10" />
<xs:whiteSpace value="collapse" />
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:complexType>

Response structure

1
2
3
4
5
6
<xs:element name="createCustomerResponse" type="tns:CreateCustomerResponse" />
<xs:complexType name="CreateCustomerResponse">
<xs:sequence>
<xs:element name="customerID" type="xs:integer" />
</xs:sequence>
</xs:complexType>

Fault structure

1
2
3
4
5
6
7
<xs:element name="createCustomerFault" type="tns:CreateCustomerFault" />
<xs:complexType name="CreateCustomerFault">
<xs:sequence>
<xs:element name="errorMessage" type="xs:normalizedString" />
<xs:element name="errorCode" type="xs:int" />
</xs:sequence>
</xs:complexType>

WSDL Design

Definitions

Create a file of .wsdl extension, in this case we are using customer-service.wsdl

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="customer-service"
xmlns:tns="http://service.customer.com/v1"
xmlns:cs="http://service.customer.com/types/v1" targetNamespace="http://service.customer.com/v1">

</wsdl:definitions>

Schema Import

Import the schema which we have created in above example.

1
2
3
4
5
6
<wsdl:types>
<xsd:schema>
<xsd:import namespace="http://service.customer.com/types/v1" schemaLocation="schemas/customer-service.xsd"/>
</xsd:schema>
</wsdl:types>

Messages

Defines the data elements for each operation

1
2
3
4
5
6
7
8
9
<wsdl:message name="createCustomerRequest">
<wsdl:part name="createCustomerRequest" element="cs:createCustomerRequest"></wsdl:part>
</wsdl:message>
<wsdl:message name="createCustomerResponse">
<wsdl:part name="createCustomerResponse" element="cs:createCustomerResponse"></wsdl:part>
</wsdl:message>
<wsdl:message name="createCustomerFault">
<wsdl:part name="createCustomerFault" element="cs:createCustomerFault"></wsdl:part>
</wsdl:message>

PortType

Describes the operations that can be performed and the messages involved.

1
2
3
4
5
6
7
<wsdl:portType name="CustomerServicePort">
<wsdl:operation name="createCustomer">
<wsdl:input message="tns:createCustomerRequest" name="createCustomerRequest" />
<wsdl:output message="tns:createCustomerResponse" name="createCustomerResponse" />
<wsdl:fault message="tns:createCustomerFault" name="createCustomerFault"></wsdl:fault>
</wsdl:operation>
</wsdl:portType>

Binding

Defines the protocol and data format for each port type

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<wsdl:binding name="CustomerServiceBinding" type="tns:CustomerServicePort">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="createCustomer">
<soap:operation soapAction="createCustomer" style="document" />
<wsdl:input name="createCustomerRequest">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="createCustomerResponse">
<soap:body use="literal" />
</wsdl:output>
<wsdl:fault name="createCustomerFault">
<soap:fault name="createCustomerFault" use="literal" />
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
  • binding element has two attributes - name and type. The name attribute (you can use any name you want) defines the name of the binding, and the type attribute points to the port for the binding, in this case the “CustomerServicePort” port.

  • soap:binding element has two attributes - style and transport. The style attribute can be “rpc” or “document”. In this case we use document. The transport attribute defines the SOAP protocol to use. In this case we use HTTP.

  • operation element defines each operation that the portType exposes. For each operation the corresponding SOAP action has to be defined. You must also specify how the input and output are encoded. In this case we use “literal”.

Service

Defines the port and binding with the address where you want to expose your service.

<wsdl:service name="CustomerService">
    <wsdl:port name="CustomerServicePort" binding="tns:CustomerServiceBinding">
        <soap:address location="http://0.0.0.0:8181/cxf/customerservice/v1"/>
    </wsdl:port>
</wsdl:service>

You can download the WSDL and XSD file for your reference

SOAP - Simple Object Access Protocol

What is SOAP ?

Simple Object Access Protocol is a messaging protocol specification for exchanging structured information. Its purpose is to provide extensibility, neutrality and independence.

  • SOAP uses XML for its message format.
  • SOAP relies on application layer protocols such as HTTP, SMTP, TCP, UDP, or JMS for message negotiation and transmission.

SOAP consists of three parts

  • an envelope, which defines the message structure and how to process it
  • a set of encoding rules for expressing instances of application-defined datatypes
  • a convention for representing procedure calls and responses

SOAP building blocks

A SOAP message is an ordinary XML document containing the following elements:

Element Description Required
Envelope Identifies the XML document as a SOAP message Yes
Header Contains header information No
Body Contains call, and response information Yes
Fault Provides information about errors that occurred while processing the message No

Request Format

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://service.customer.com/types/v1">
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-F434D320D890F7CF1715251639631263">
<wsse:Username>admin</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">admin</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<v1:deleteCustomerRequest>
<v1:customerID>123321</v1:customerID>
</v1:deleteCustomerRequest>
</soapenv:Body>
</soapenv:Envelope>

Response Format

1
2
3
4
5
6
7
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://service.customer.com/types/v1">
<soapenv:Body>
<v1:deleteCustomerResponse>
<v1:message>Customer Deleted Successfully</v1:message>
</v1:deleteCustomerResponse>
</soapenv:Body>
</soapenv:Envelope>

Fault Format

1
2
3
4
5
6
7
8
9
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd=="http://www.w3.org/1999/XMLSchema">
<soapenv:Body>
<soapenv:Fault>
<faultcode xsi:type="xsd:string">SOAP_FAULT_DELETECUSTOMER</faultcode>
<faultstring xsi:type="xsd:string">Customer not present</faultstring>
<faultactor>CustNotFound</faultactor>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>