The following example illustrates how you can implement a composite service using Apache Camel. The service is exposed as a REST resource, and it uses two other resources to collect the data required.
The composite service will enable a client to get info on a customer and it’s orders. To get this data the composite service first needs to query a customer service. Using a relation id retrieved from the customer service, the composite then needs to query an orders services. The steps are as follows:
The composite gets the customer resource, but this resource expects the customer id to be named differently: GET http://localhost/customer?customer=123. Here’s an xml representation of the resource returned:
<customer
custNo='123'
relationId='443'
firstName='Peter'
lastName='Semper'
/>
Next the composite needs to use the relationId in the customer resource to get it’s orders, e.g., GET http://localhost/orders?relId=443. This will result in the following xml document:
<orders relationId='443'>
<order id='1' product='Car'/>
<order id='2' product='Bicycle'/>
</orders>
The composite service will use the data returned by the customer resource and the order resource to create a new xml document containing both the customer info and the order info:
<customer>
<id>123</id>
<orders>
<order><id>1</id><product name="Car"/></order>
<order><id>2</id><product name="Bicycle"/></order>
</orders>
</customer>
Here’s the sequence diagram illustrating the steps outlined above.
You can achieve this with the following route:
from("jetty:http://localhost:9012/customerOrders")
.removeHeaders("CamelHttp*")
.setHeader("customer",header("custNo"))
.setHeader(Exchange.HTTP_METHOD,constant("GET"))
.to("http://localhost:9012/customer")
.removeHeaders("CamelHttp*")
.setHeader("relId", xpath("/customer/@relationId",String.class))
.setHeader(Exchange.HTTP_METHOD,constant("GET"))
.enrich("http://localhost:9012/orders",aggregate)
.to("xquery:merge-customer-orders.xquery")
.setHeader(Exchange.CONTENT_TYPE,constant("text/xml"));
Line by line explanation:
The aggregate object takes the message (exchange) received from the customer resource and the message received from the orders resource and concatenes them into one xml document:
AggregationStrategy aggregate = new AggregationStrategy(){
@Override
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
newExchange.getOut().setBody("<all>"
+ oldExchange.getIn().getBody(String.class)
+ newExchange.getIn().getBody(String.class)
+ "</all>");
return newExchange;
}
};
The combined xml document can easily be used in an xquery to build a new xml document:
<customer>
<id>{data(/all/customer/@custNo)}</id>
<orders>
{ for $order
in /all/orders/order
return <order><id>{data($order/@id)}</id><product name='{data($order/@product)}'/></order>
}
</orders>
</customer>
That’s most of the code you need. You’ll have to put the route configuration into a route builder and start a camel context to run the route:
public static void main(String args[]) throws Exception {
CamelContext ctx = new DefaultCamelContext();
ctx.addRoutes(new CustomerOrdersResourceRouteBuilder());
ctx.start();
}
The nice thing about camel is that’s easy to test and debug. You can simply start the main class in your IDE and give the route a try. You can use the same approach to unit test the route. No need to do a complete deployment to some ESB server.
Another advantage of using Camel is that all your endpoints are normal strings in some java code that can easily be made configurable, for example using a property file