This section is about regular Camel. The examples presented here in this section is much more in common of all the examples we have in the Camel documentation.
If you have been reading the previous 3 parts then, this quote applies:
So we start all over again!
Camel is particular strong as a light-weight and agile routing and mediation framework. In this part we will introduce the routing concept and how we can introduce this into our solution.
Before we jump into it, we want to state that this tutorial is about Developers not loosing control. In my humble experience one of the key fears of developers is that they are forced into a tool/framework where they loose control and/or power, and the possible is now impossible. So in this part we stay clear with this vision and our starting point is as follows:
So the starting point is:
Yes we have a simple plain Java class where we have the implementation of the webservice. The cursor is blinking at the WE ARE HERE block and this is where we feel home. More or less any Java Developers have implemented webservices using a stack such as: Apache AXIS, Apache CXF or some other quite popular framework. They all allow the developer to be in control and implement the code logic as plain Java code. Camel of course doesn't enforce this to be any different. Okay the boss told us to implement the solution from the figure in the Introduction page and we are now ready to code.
RouteBuilder is the hearth in Camel of the Java DSL routing. This class does all the heavy lifting of supporting EIP verbs for end-users to express the routing. It does take a little while to get settled and used to, but when you have worked with it for a while you will enjoy its power and realize it is in fact a little language inside Java itself. Camel is the only integration framework we are aware of that has Java DSL, all the others are usually only XML based.
As an end-user you usually use the RouteBuilder as of follows:
So we create a new class ReportIncidentRoutes and implement the first part of the routing:
What to notice here is the configure method. Here is where all the action is. Here we have the Java DSL langauge, that is expressed using the fluent builder syntax that is also known from Hibernate when you build the dynamic queries etc. What you do is that you can stack methods separating with the dot.
In the example above we have a very common routing, that can be distilled from pseudo verbs to actual code with:
from("direct:start") is the consumer that is kick-starting our routing flow. It will wait for messages to arrive on the direct queue and then dispatch the message.
So what we have implemented so far with our ReportIncidentRoutes RouteBuilder is this part of the picture:
Adding the RouteBuilder
Now we have our RouteBuilder we need to add/connect it to our CamelContext that is the hearth of Camel. So turning back to our webservice implementation class ReportIncidentEndpointImpl we add this constructor to the code, to create the CamelContext and add the routes from our route builder and finally to start it.
Okay how do you use the routes then? Well its just as before we use a ProducerTemplate to send messages to Endpoints, so we just send to the direct:start endpoint and it will take it from there.
Notice that we get the producer template using the createProducerTemplate method on the CamelContext. Then we send the input parameters to the direct:start endpoint and it will route it to the velocity endpoint that will generate the mail body. Since we use direct as the consumer endpoint (=from) and its a synchronous exchange we will get the response back from the route. And the response is of course the output from the velocity endpoint.
About creating ProducerTemplate
In the example above we create a new
We have now completed this part of the picture:
Now is the time we would like to unit test what we got now. So we call for camel and its great test kit. For this to work we need to add it to the pom.xml
After adding it to the pom.xml you should refresh your Java Editor so it pickups the new jar. Then we are ready to create out unit test class.
That is easy just return an instance of our route builder and this unit test will use our routes.
It is quite common in Camel itself to unit test using routes defined as an anonymous inner class, such as illustrated below:
The same technique is of course also possible for end-users of Camel to create parts of your routes and test them separately in many test classes.
We then code our unit test method that sends a message to the route and assert that its transformed to the mail body using the Velocity template.
Adding the File Backup
The next piece of puzzle that is missing is to store the mail body as a backup file. So we turn back to our route and the EIP patterns. We use the Pipes and Filters pattern here to chain the routing as:
Notice that we just add a 2nd .to on the newline. Camel will default use the Pipes and Filters pattern here when there are multi endpoints chained liked this. We could have used the pipeline verb to let out stand out that its the Pipes and Filters pattern such as:
But most people are using the multi .to style instead.
We re-run out unit test and verifies that it still passes:
But hey we have added the file producer endpoint and thus a file should also be created as the backup file. If we look in the
Setting the filename
For starters we show the simple solution and build from there. We start by setting a constant filename, just to verify that we are on the right path, to instruct the file producer what filename to use. The file producer uses a special header
What we do is to send the header when we "kick-start" the routing as the header will be propagated from the direct queue to the file producer. What we need to do is to use the
However we could also have used the route builder itself to configure the constant filename as shown below:
But Camel can be smarter and we want to dynamic set the filename based on some of the input parameters, how can we do this?
Using Bean Language to compute the filename
First we create our plain java class that computes the filename, and it has 100% no dependencies to Camel what so ever.
The class is very simple and we could easily create unit tests for it to verify that it works as expected. So what we want now is to let Camel invoke this class and its generateFilename with the input parameters and use the output as the filename. Pheeeww is this really possible out-of-the-box in Camel? Yes it is. So lets get on with the show. We have the code that computes the filename, we just need to call it from our route using the Bean Language:
Notice that we use the bean language where we supply the class with our bean to invoke. Camel will instantiate an instance of the class and invoke the suited method. For completeness and ease of code readability we add the method name as the 2nd parameter
Then other developers can understand what the parameter is, instead of
Now we have a nice solution, but as a sidetrack I want to demonstrate the Camel has other languages out-of-the-box, and that scripting language is a first class citizen in Camel where it etc. can be used in content based routing. However we want it to be used for the filename generation.
Using a script language to set the filename
We could do as in the previous parts where we send the computed filename as a message header when we "kick-start" the route. But we want to learn new stuff so we look for a different solution using some of Camels many Languages. As OGNL is a favorite language of mine (used by WebWork) so we pick this baby for a Camel ride. For starters we must add it to our pom.xml:
And remember to refresh your editor so you got the new .jars.
In OGNL glory this is done as:
Now we got the expression to dynamic compute the filename on the fly we need to set it on our route so we turn back to our route, where we can add the OGNL expression:
And since we are on Java 1.5 we can use the static import of ognl so we have:
Notice the import static also applies for all the other languages, such as the Bean Language we used previously.
Whatever worked for you we have now implemented the backup of the data files:
Sending the email
What we need to do before the solution is completed is to actually send the email with the mail body we generated and stored as a file. In the previous part we did this with a File consumer, that we manually added to the CamelContext. We can do this quite easily with the routing.
The last 3 lines of code does all this. It adds a file consumer from("file://target/subfolder"), sets the mail subject, and finally send it as an email.
The DSL is really powerful where you can express your routing integration logic.
We have now completed the integration:
We have just briefly touched the routing in Camel and shown how to implement them using the fluent builder syntax in Java. There is much more to the routing in Camel than shown here, but we are learning step by step. We continue in part 5. See you there.