Camel is a powerful open-source integration framework based on known Enterprise Integration Patterns. Camel define routing and mediation rules in a variety of domain-specific languages or even in XML. It uses URIs to work directly with any kind of Transport or messaging model such as HTTP and JMS and also allows you to define your own.
What I particularly like is the small library with minimal dependencies which makes embedding the framework within a Java application seamless. Furthermore, you work with the same API regardless of the Transport used. This makes a plug-and-play application where one endpoint can be swapped for another quite possible.
The demonstration application that I will discuss here is inspired by my new employer, Mediswitch. One of their products connects various medical aids and other third parties with all kinds of service providers (each using a different communication medium). We will define two endpoints (a rather ambiguous term referring to either a URL/URI or an entire service) for our application:
- The first endpoint is an HTTP-GET client. I like these in tests as they can be simply called from a web browser or using good old curl on the command line.
- A server using Thrift that services the request from the first endpoint and returns the result via the central switch.
For this application we will require the following:
- A Component (already implemented for us) to handle the incoming HTTP-GET requests.
- A Processor for each of the possible request that is received by the consumer component (i.e. the HTTP-GET side). For this application we will service four different requests:
- Getting a member's details.
- Modifying a member's detail.
- Deleting a member (I called it 'zap' for some reason).
- Process a claim submitted.
- A Processor in between that does something additional with the request.
- A mechanism to wire all these things up and a main class that kicks off this process.
We do not need to write the Component, so next is the Processor to handle the Thrift call:
The Thrift call is handled by the ThriftMember member = getThriftClient().getMemberDetail(id); call. The Thrift IDL and the detail on how Thrift works is beyond the scope of this article. All I can say is that it is one of the best communication framework that I have worked with and it is quite easy to get going. Our GetMemberDetailProcessor class overrides the public void process(Exchange exchange) throws Exception method from the Processor interface. I handle the Thrift-connection aspects in the inherited abstract class, which looks like this:
The other Processor classes follow exactly the same pattern (and is therefore left as an exercise to you, the avid reader). To test chaining (which I'll show later when we wire everything together), I've written a DummyProcessor class, which looks like:
...and finally, all this needs to be wired up using the a subclass of the RouteBuilder class. Camel uses (amongs others) Java code to implement a DSL (domain specific language). This is where the true power of the framework is evident. There are two ways it can be wired up. I've commented out one approach:
In the first approach a single route is used where we route the request to the DummyProcessor, followed by a choice(), which, based on the CamelHttpPath, routes the request to the correct Processor. The second approach sets up four different routes rather than one. As far as I understand, these two approaches are equivalent. I prefer the first approach (in the same way that brown is my favourite colour).
To wire everything up, requires a CamelContext object, adding the routes and starting the context, which starts threads and internal processes to get Camel going:
Notice that the new-keyword above implies that Camel will work well with a dependency injection framework like Guice (if you don't know about it, you have to have a look!).
Now we can test the application. After starting the Thrift server (a separate little application to allow testing of our central switch application), we can issue a command from either the browser or the command line. Let's use a browser this time:
The log messages (after trimming down on the org.apache entries in the log4j.xml file), looks like this (notice the entries written by the DummyProcessor):
There are a number of errors that I got while writing this application. It was usually related to either an incorrect dependency/version or an incorrect route being used.
In summary, Camel makes routing between unknown endpoints as easy as possible. Notice how the internal Jetty boilerplate code is completely absent from the code. It is all handled internally and setting it up essentially required a single line of code. The chaining between Processors also makes it easy to intercept a request for some pre-processing before passing it on.