Apache Camel CXF Example
In this article, I am going to show you an example of Apache Camel CXF. We will explore Camel’s capabilities for interacting with SOAP web services, which are commonly used in integration technology. The CXF component provides integration with Apache CXF for connecting to Java XML Web Services (JAX-WS) hosted in CXF and what is Apache CXF? Apache CXF is an open-source, fully featured Web services framework. And where does the name CXF comes from? It originated as the combination of two open-source projects: Celtix and XFire so CXF was derived by combining “Celtix” and “XFire”.
In this example, we will use CXF to create Camel routes that request external web services. We will also use CXF to act as a web service listener.
Before we start with our example, Let’s look into the setup details.
This example uses the following frameworks:
- Maven 3.2.3
- Apache Camel 2.15.1
- Apache CXF 3.0.4
- Spring 4.1.5.RELEASE
- Eclipse as the IDE, version Luna 4.4.1.
1. Dependencies
You need following dependencies:
camel-core
– basic module of apache camel.camel-cxf
– We want to use Apache CXF for the webservice stuff.cxf-rt-transports-http-jetty
– We want Apache CXF to also act as webservice listener.spring-context
andcamel-spring
– Since we be configuring our camel context in spring.
pom.xml:
<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.javacodegeeks.camel</groupId> <artifactId>camelHelloWorld</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-core</artifactId> <version>2.15.1</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.1.5.RELEASE</version> </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-spring</artifactId> <version>2.15.1</version> </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-cxf</artifactId> <version>2.15.1</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http-jetty</artifactId> <version>3.0.4</version> </dependency> </dependencies> </project>
2. Developing Webservice
Our webservice consists of a product service. Given a product ID, it will provide us with product details. Product details consists of product ID, product name and its price.
We will define a contract for the service through a WSDL file.
productService.wsdl:
<?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions name="wsdl-first" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://ws.javacodegeeks.com/product-service" xmlns:typens="http://ws.javacodegeeks.com/product-service/types" targetNamespace="http://ws.javacodegeeks.com/product-service"> <wsdl:types> <xsd:schema targetNamespace="http://ws.javacodegeeks.com/product-service/types" elementFormDeerror="qualified"> <xsd:element name="productRequest"> <xsd:complexType> <xsd:sequence> <xsd:element name="id" type="xsd:string" /> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="productResponse"> <xsd:complexType> <xsd:sequence> <xsd:element name="id" type="xsd:string" /> <xsd:element name="description" type="xsd:string" /> <xsd:element name="price" type="xsd:int" /> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="error"> <xsd:complexType> <xsd:sequence> <xsd:element minOccurs="0" name="reason" type="xsd:string" /> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema> </wsdl:types> <wsdl:message name="ProductRequest"> <wsdl:part name="request" element="typens:productRequest" /> </wsdl:message> <wsdl:message name="ProductResponse"> <wsdl:part name="response" element="typens:productResponse" /> </wsdl:message> <wsdl:message name="ErrorMessage"> <wsdl:part name="error" element="typens:error" /> </wsdl:message> <wsdl:portType name="Product"> <wsdl:operation name="getProductDetails"> <wsdl:input message="tns:ProductRequest" /> <wsdl:output message="tns:ProductResponse" /> <wsdl:fault name="error" message="tns:ErrorMessage" /> </wsdl:operation> </wsdl:portType> <wsdl:binding name="ProductSOAPBinding" type="tns:Product"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="getProductDetails"> <wsdl:input> <soap:body use="literal" /> </wsdl:input> <wsdl:output> <soap:body use="literal" /> </wsdl:output> <wsdl:fault name="error"> <soap:fault name="error" use="literal" /> </wsdl:fault> </wsdl:operation> </wsdl:binding> <wsdl:service name="ProductService"> <wsdl:port binding="tns:ProductSOAPBinding" name="ProductPort"> <soap:address location="http://localhost:9090/productService" /> </wsdl:port> </wsdl:service> </wsdl:definitions>
3. CXF wsdl2java
We need to generate JAX-WS and JAXB (Java Architecture for XML Binding) annotated Java classes and interfaces from the above WSDL.
Let’s add Maven plugin cxf-codegen-plugin
to automate the build-time generation of JAX-WS artifacts from the WSDL document. Plugin cxf-codegen-plugin
is provided by the Apache CXF project.
We will integrate CXF wsdl2java generator in the pom.xml
so we have CXF generate the needed POJO classes for our webservice contract.
pom.xml:
<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.javacodegeeks.camel</groupId> <artifactId>camelHelloWorld</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-core</artifactId> <version>2.15.1</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.1.5.RELEASE</version> </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-spring</artifactId> <version>2.15.1</version> </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-cxf</artifactId> <version>2.15.1</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http-jetty</artifactId> <version>3.0.4</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>2.7.0</version> <executions> <execution> <id>generate-sources</id> <phase>generate-sources</phase> <configuration> <sourceRoot>${project.build.directory}/generated/cxf</sourceRoot> <wsdlOptions> <wsdlOption> <wsdl>${basedir}/src/main/resources/productService.wsdl</wsdl> </wsdlOption> </wsdlOptions> </configuration> <goals> <goal>wsdl2java</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
4. Implementing a web service using Camel CXF
In this example, we will use Camel CXF Component to act as a SOAP web service listener.
First, configure the Camel CXF endpoint.
applicationContext.xml:
<?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: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 "> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" /> <cxf:cxfEndpoint id="productServiceEndpoint" address="http://localhost:${port1}/productService" serviceClass="com.javacodegeeks.ws.product_service.Product" /> </beans>
Next, create a POJO that will accept the the web service request, process it and return a response.
ProductServiceImpl:
package com.javacodegeeks.camel; import java.util.HashMap; import java.util.Map; import com.javacodegeeks.ws.product_service.types.ProductResponse; public class ProductServiceImpl { public ProductResponse getProductDetails(com.javacodegeeks.ws.product_service.types.ProductRequest request) { Product product = PRODUCT_DETAILS.get(request.getId()); if (product == null) { throw new ProductNotFoundException(request.getId()); } ProductResponse response = new ProductResponse(); response.setId(product.id); response.setDescription(product.description); response.setPrice(product.price); return response; } private static Map<String, Product> PRODUCT_DETAILS = new HashMap<String, Product>(); private static class Product { private String id; private String description; private int price; Product(String id, String desc, int price) { this.id = id; this.description = desc; this.price = price; } } static { PRODUCT_DETAILS.put("P01", new Product("P01", "Laptop", 40000)); PRODUCT_DETAILS.put("P02", new Product("P02", "Mobile", 14000)); PRODUCT_DETAILS.put("P03", new Product("P03", "Tablet", 30000)); } }
If product is not found, we will throw ProductNotFoundException
.
ProductNotFoundException:
package com.javacodegeeks.camel; public class ProductNotFoundException extends RuntimeException { private static final long serialVersionUID = 1L; private String productId; public ProductNotFoundException(String id) { this.productId = id; } public String toString() { return "Product " + productId + " not found exception"; } }
Finally, we need to build the route to consume the request, delegate it to route built, our above POJO will handle the request and return the response. The CXF endpoint configured will become the Camel consumer. It will create an HTTP listener to receive the SOAP messages and feed the messages into the Camel route.
Our route would look like:
applicationContext.xml:
<?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: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 "> <camelContext xmlns="http://camel.apache.org/schema/spring"> <route id="wsRoute"> <from uri="cxf:bean:productServiceEndpoint" /> <bean ref="productServiceImpl" /> </route> </camelContext> <bean id="productServiceImpl" class="com.javacodegeeks.camel.ProductServiceImpl"/> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" /> <cxf:cxfEndpoint id="productServiceEndpoint" address="http://localhost:${port1}/productService" serviceClass="com.javacodegeeks.ws.product_service.Product" /> </beans>
5. Invoking a web service using Camel CXF
Our route consists of from(“direct:start”) which is the consumer that will kick-start our routing flow. It will wait for messages to arrive on the direct queue and then dispatch the message to invoke the CXF endpoint using the cxf:bean:
prefix with the operation name that you want to trigger.
applicationContext.xml:
<?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: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 "> <camelContext xmlns="http://camel.apache.org/schema/spring"> <route id="wsRoute"> <from uri="cxf:bean:productServiceEndpoint" /> <bean ref="productServiceImpl" /> </route> <route id="wsClient"> <from uri="direct:start" /> <to uri="cxf:bean:productServiceEndpoint?defaultOperationName=getProductDetails" /> </route> </camelContext> <bean id="productServiceImpl" class="com.javacodegeeks.camel.ProductServiceImpl"/> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" /> <cxf:cxfEndpoint id="productServiceEndpoint" address="http://localhost:${port1}/productService" serviceClass="com.javacodegeeks.ws.product_service.Product" /> </beans>
6. Camel CXF Example Main Class
Let’s analyse our main class CamelCxfExample
.
- Since
CamelContext
is defined inapplicationContext.xml
, we will first create theApplicationContext
object. - Next, we will call
SpringCamelContext.springCamelContext
to return us theCamelContext
. - Start the camel context.
- We will use a
ProducerTemplate
to send messages to thedirect:start
endpoint to initiate the webservice request. - Finally, we print the product details response.
CamelCxfExample:
package com.javacodegeeks.camel; import org.apache.camel.CamelContext; import org.apache.camel.CamelExecutionException; import org.apache.camel.ProducerTemplate; import org.apache.camel.spring.SpringCamelContext; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.javacodegeeks.ws.product_service.types.ProductRequest; import com.javacodegeeks.ws.product_service.types.ProductResponse; public class CamelCxfExample { private static ProducerTemplate template; public static void main(String[] args) throws Exception { System.setProperty("port1", "9000"); ApplicationContext appContext = new ClassPathXmlApplicationContext( "applicationContext.xml"); CamelContext camelContext = SpringCamelContext.springCamelContext( appContext, false); try { template = camelContext.createProducerTemplate(); System.out.println("Start camel context"); camelContext.start(); printProductDetails("P01"); printProductDetails("P02"); printProductDetails("Uknown"); } finally { System.out.println("Stop camel context"); camelContext.stop(); } } private static void printProductDetails(String id) { try { System.out.println("Request: Get " + id + " details "); ProductRequest request = new ProductRequest(); request.setId(id); ProductResponse response = template.requestBody("direct:start", request, ProductResponse.class); System.out.println("Response: Id: " + response.getId() + ", Product: " + response.getDescription() + ", Price: " + response.getPrice()); } catch (CamelExecutionException p) { System.out.println(p.getCause()); } } }
After the first two successful requests, we deliberately request for an unknown product to make sure an exception is thrown.
Output:
INFO: Creating Service {http://ws.javacodegeeks.com/product-service}ProductService from class com.javacodegeeks.ws.product_service.Product Apr 20, 2015 10:17:03 PM org.apache.cxf.endpoint.ServerImpl initDestination INFO: Setting the server's publish address to be http://localhost:9000/productService Apr 20, 2015 10:17:03 PM org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean buildServiceFromClass INFO: Creating Service {http://ws.javacodegeeks.com/product-service}ProductService from class com.javacodegeeks.ws.product_service.Product Start camel context Request: Get P01 details Response: Id: P01, Product: Laptop, Price: 40000 Request: Get P02 details Response: Id: P02, Product: Mobile, Price: 14000 Request: Get Uknown details Apr 20, 2015 10:17:04 PM org.apache.cxf.phase.PhaseInterceptorChain doDefaultLogging WARNING: Application {http://ws.javacodegeeks.com/product-service}ProductService#{http://ws.javacodegeeks.com/product-service}getProductDetails has thrown exception, unwinding now org.apache.cxf.binding.soap.SoapFault: ProductNotFoundException Stop camel context
7. Download the Eclipse Project
This was an example about Camel CXF.
Hello,
Could you show how to write camel cxfProducer, which send request to external soap service.
Thank you.
Sanjeev
Doesn’t work, please provide update.
Not working