Overview In this post, we will walk through the steps to develop a SOAP service using Java and we will also leverage Spring , Apache Camel & Apache CXF.
Pre-requisties
JDK 1.8 for code development
Maven for dependency management
JBoss Fuse 6.3 for deployment
Code Development Maven Project with required dependencies Create a maven project using IDE and add following dependencies in the pom.xml
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > com.customer.service</groupId > <artifactId > customer-service-impl</artifactId > <version > 1.0.0</version > <packaging > bundle</packaging > <name > Customer Service</name > <properties > <camel.version > 2.17.0</camel.version > </properties > <dependencies > <dependency > <groupId > org.apache.camel</groupId > <artifactId > camel-core</artifactId > <version > ${camel.version}</version > </dependency > <dependency > <groupId > org.apache.camel</groupId > <artifactId > camel-cxf</artifactId > <version > ${camel.version}</version > </dependency > <dependency > <groupId > org.apache.camel</groupId > <artifactId > camel-sql</artifactId > <version > ${camel.version}</version > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-log4j12</artifactId > <version > 1.6.6</version > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-compiler-plugin</artifactId > <version > 3.1</version > <configuration > <source > 1.7</source > <target > 1.7</target > </configuration > </plugin > <plugin > <groupId > org.apache.felix</groupId > <artifactId > maven-bundle-plugin</artifactId > <extensions > true</extensions > <version > 2.4.0</version > </plugin > <plugin > <groupId > org.apache.cxf</groupId > <artifactId > cxf-codegen-plugin</artifactId > <version > 2.7.8</version > <executions > <execution > <id > generate-sources</id > <phase > generate-sources</phase > <configuration > <sourceRoot > ${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 **
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 examplesrc/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
address specify the path where you want to expose your service
serviceName & endpointName should be same as specified in wsdl,
wsdlURL relative to resources folder or it can a http url also.
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 >
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 >
cxf component & bean id is used to lookup the cxfEndpoint.
camel:from will allow to consume the request(s) incoming the cxf endpoint.
camel:unmarshal is used for converting soap payload to java objects
camel:process is used for the logic to be performed in the service
camel:marshal is used for converting java object to soap xml.
This is how your routes file will look likesrc/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.
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.
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 urlhttp://localhost:8181/cxf