At least for some time the whole world seemed to only talk about ESB and webservices. These technologies have their place in integration but they are quite complex and starting with them means you have to invest a lot of time and or money. Recently around the release of Java EE 6 the idea of simplicity came back to the Enterprise Java world. In this mindset I will look into some ways to do really light weight messaging with Apache Camel.
So basically we have the problem of moving some data from Application A to Application B. So let´s assume we have java objects representing this data and want to transport it in a simple yet open format. So one obvious choice is to use xml and do the transformation using JAXB. In the following examples I will use code first but it will work the same if the JAXB annotated classes were created from an XSD using a code generator.
As an example for our payload I will use the following java bean class:
So how can we send this class as XML to a JMS queue?
This almost looks a bit too easy. So what is camel doing behind the scenes. The ProducerTemplate allows us to send any java object to any camel endpoint. The endpoint uri will setup the endpoint on the fly. In our case the endpoint is a jms endpoint and will send the data to the queue "test". As the object needs to be serialized to be transfered using jms camel will use a TypeConverter to do this. In our case the camel-jaxb component is on the classpath and the Customer class has JAXB annotations. So a TypeConverter will kick in that serializes the object using JAXB.
So the message on the queue will look like this:
Now that we sent the message how can we receive it on the other side? For this we can use a little route and a simple java class:
The route listens on the jms queue and will trigger for each message received. The to(log:testlog) is not strictly necessary but shows that we indeed transport xml. In the end the message is sent to the CustomerServiceReceiver using the camel bean component. The bean component uses a lot of convention over configuration to determine how to process the message. What happens in our case is the following:
- We only have one public method so camel knows it has to use the receive method
- The recieve method has only one parameter so camel will give the body of the message to this parameter
- As we get a String or byte from jms and again the class Customer has jaxb annotations the data is automatically deserialized into a Customer object
Using another protocol
By simply changing from "jms://test" to e.g. "file://target/test" we can switch the transfer protocol to files. So when doing sendBody a file with the xml is placed in the directory target/test. The route for the receiver will detect that file and send the content into the route. The result is the same as for jms: We transport our data as nice xml and can work with it as a java object on both sides.
You can try the same for http using "http://localhost:8080/test" for the sender and "jetty://localhost:8080/test" for the receiver.
What about request / reply?
What we saw was nice asynchonous one way messaging. Camel also allows a simlar aproach for request / reply.
So for sending a Customer object to the other side and receiving a changed Customer object synchronously you can use:
This would send customer to the queue "test" as xml and create a temp queue where it wait for a reply. When the reply is received the data is deserialized and returned as the object changedCustomer.
On the receiver side the route can be kept as is and the CustomerReceiver.recive method simply should look like:
It is really just as easy as this.
So what is good and what is bad about this aproach
- very easy to use
- very few lines of code
- CustomerReceiver does not contain any camel specifics
- The sending side with the ProducerTemplate is very camel technical. You typically will not want this in your business code
How can I keep my business code clean from camel stuff
A simple way to keep camel classes out of your business code on the sender side is to use an interface that just has business logic in it:
And the implementation for sending using Camel:
So you can inject the CustomerSenderImpl into your business code which only needs to know about the CustomerSender interface. This way your business code is nicely separated from camel.
Can I have this even simpler?
Camel has some annotations which allow to do this with even less code. You still need the CustomerSender interface but you can get rid of the route and the CustomerServiceImpl.
On the sender side you use:
Camel will inject a dynamic proxy for customerSender which sends the customer to the queue. The drawback is that it currently will always send a BeanInvocation. So while this works for remoting it is not the nice xml document we want to have on the route. Besides that the annotations only work in spring beans and in the camel test framework.
On the receiver side you only need the CustomerReceiver enhanced with the @Consume annotation.
Like before the reciever works even a little nicer than the sender. It can receive a BeanInvocation but will also work with the xml from our first example.
In general I am not so sure about the annotation aproach in general as this way your business code still contains camel specific stuff like the annotation and the endpoint uri.