2009-04-28

On the road to Camel 2.0 - Interceptors

Camel 2.0 have the intercept mechanism but it comes in different flavors.

They have the same common goal to intercept an message while its being routed and allows to do some custom work or processing. 

Before digging into showing code we distill the two distinct flavors as:
1) the classic AOP interceptor that is applied at each step in the route path
2a) route interceptors that intercept incoming messages
  and its related
2b) endpoint interceptors that intercept message about to be sent to an endpoint

Ad 1)
The classic AOP interceptors is defined in the interface org.apache.camel.spi.InterceptStrategy that allows you to attach a custom org.apache.camel.Processor with the work you want to do. This interceptor is mostly used internally in Camel to apply needed work that each message must undergo before they are continued routed to the next node in the route path.

We are looking into adding some syntax sugar so you can attach your own code logic without using the Camel API, for instance to use POJO. Expect this are to improve in the future.

Ad 2a)
As the Camel wiki documentation for interceptor was a bit sparse in this area I did some work yesterday to improve it and add some samples.

This interceptor is used for intercepting any new messages coming into Camel (i.e. think it as all the from DSL being intercepted).

For instance to log all incoming messages you can do:
intercept().to("log:received");

from("jms:queue:neworder").to("bean:ordeService?method=newOrder").to("jms:queue:handleorder");

People that already is familiar with Camel could know that Camel have extensive Predicate support. And with intercept that is no exceptions. So what we can do is to attach a predicate to the intercept to only trigger for certain conditions.

For instance to do a bit of custom processing on test messages received, we can add an interceptor with a predicate to determine if whether its a test message. If so we route it to our custom processor where we can "fixup" the test message before we let the message continue being routed from the origin of interception

intercept()
    .when(header("usertype").isEqualTo("test"))
    .process(new MyTestServiceProcessor());

Well what if you wanted to filter our test message instead? Well Camel supports a stop DSL keyword allowing Camel to stop continue routing. So in the sample above we could just append stop() in the end of the route. 

intercept()
    .when(header("usertype").isEqualTo("test"))
    .stop();

As the intercept() is also just a route you have all the DSLs at your fingertips.

Ad 2b)
This is a new interceptor introduced in Camel 2.0m2. The idea is to allow interception before a message is sent to a given Endpoint. So for instance if we want to do some custom processing before we send a message to the neworder JMS queue we can do:

interceptSendToEndpoint("jms:queue:neworder")
    .process(new MyJmsNewOrderProcessor());

So when anyone using the Camel API to send a JMS message to that given endpoint jms:queue:neworder, the producer is intercepted and the message is routed to our MyJmsNewOrderProcessor where we can alter the message or whatever we want to do. And afterwards the message is send to the original intended endpoint. The default behavior is to afterwards send it to the intended endpoint. But if you want to skip this you can add the stop() DSL to the route. We use the stop() keyword as this interceptor shares the same code base with the other interceptor, and thus also DSL keywords. We might consider separating the code to introduce a skip() DSL keyword as its better expression what it does, skipping sending the message to the intended endpoint.

So we could use this to intercept sending to the handleorder JMS queue and e.g. store it in a file instead.

// notice: use stop() to skip sending to the intended endpoint.
interceptSendToEndpoint("jms:queue:handleorder")
    .to("file://output/orders").stop();

We have a few other wacky ideas that we are considering. For instance why not allow wildcards or reg exp expressions in the intercepted endpoint uri so you can intercept any JMS queue or a group of queues.

Well that's it for now. Just wanted to give a bit of update on the interceptor side in Camel.

2009-04-20

On the road to Camel 2.0 - Try Catch Finally

Today I have just completed an overhaul of the try catch finally DSLs we have in Apache Camel.

And since I improved (hmm added) the documentation to the Apache Camel wiki I might as well do a blog about it as well :)

Using try catch finally in Apache Camel
Camel supports the Java equivalent of try .. catch and finally directly in the DSL.
It aims to work like its Java sisters but with more power. Especially in Camel 2.0 where we gave this feature an overhaul.

In Camel we prefix the keywords with do to avoid having same keyword as Java. So we have:
  • doTry
  • doCatch
  • doFinally
  • end to end the block in Java DSL (Spring DSL have the </doTry> tag to end)
About doCatch and its power over Java
The doCatch in Camel is empowered over its Java sister. First of all you can define multiple exceptions to catch in a single block. And secondly you can attach a onWhen predicate to signal if the catch should trigger or not at runtime.

To simulate rehrowing an exception from a doCatch you should use the handled predicate. If its evaluated to false Camel will reattach the exception on the Exchange.

And just like Java the order in which you have multiple doCatch blocks matter. Camel will iterate from the top going down and use the first doCatch that matches the exception and if the onWhen predicate matches as well (if any provided). This is the same behavior as the Exception Clause.

Using try .. catch .. finally in Java DSL
In the route below we have all keywords in action. As the code is based on a unit test we route using Mock.

from("direct:start")
    .doTry()
        .process(new ProcessorFail())
        .to("mock:result")
    .doCatch(IOException.class, IllegalStateException.class)
        .to("mock:catch")
    .doFinally()
        .to("mock:finally")
    .end();

And here we have the same route in Spring DSL:
<route>
    <from uri="direct:start"/>
    <!-- here the try starts. its a try .. catch .. finally just as regular java code -->
    <doTry>
        <process ref="processorFail"/>
        <to uri="mock:result"/>
        <doCatch>
            <!-- catch multiple exceptions -->
            <exception>java.io.IOException</exception>
            <exception>java.lang.IllegalStateException</exception>
            <to uri="mock:catch"/>
        </doCatch>
        <doFinally>
            <to uri="mock:finally"/>
        </doFinally>
    </doTry>
</route>

There are more samples in the Camel Try Catch Finally documentation that also demonstrates the onWhen and handled predicates and what they are used for.

The overhaul was needed as it really brings the power of using try .. catch .. finally up to the same level as the exception clauses and error handlers in Camel. But having them right at your fingertips as regular Java like try .. catch .. finally brings the gaps from thinking Java to EIP patterns a bit closer.

PS: Mind that the static wiki documentation takes a few hours to be updated based on the dynamic changes I recently added, so some of the links might not work until then. 

2009-04-05

On the road to Camel 2.0 - API restructure

We are closing in on the Apache Camel 2.0 and I promised to write some blogs what to expect in the 2.0 release.

We begin where it all started back in summer 2008. The internal API in Camel had some spots that caused a bit of pain to work with from within the framework itself. For instance especially generics backfired. So we had to get rid of them in Camel 2.0 and only only keep it where it brings value.

The API changes was also needed to allow us to do some optimizations in the 2.x timeframe, such as minimizing some of the overhead when messages is passed from node to node during routing.

Then the framework itself had some package tangles that we needed to look at also. To help with this we are using the excellent tool Structure 101 by Headway Software. Its incredible which mistakes you can spot with this tool. I was able to even spot wrong classes for Log categories based on copy/paste mistakes. Using this tool we where able to restructure and remove the bad tangles.

And from a tooling perspective the API is more open to allow tooling to inspect and being able to alter routes at runtime. James Strachan has even added a REST API so it should be much easier for tooling to integrate than relying on for instance JMX.

Now the API is clean and crisp.

Below is an architecture overview diagram created with Structure 101 that shows the structure of Camel 2.0. 
Structure 101 can create all kind of diagrams and its easier to create great looking diagrams as its drag and drop editor is very intuitive. And you can compact/expand the packages to hide details. For instance we could compact the processor package to be a single box instead of showing the inner packages as the diagram above.

So what does the API restructure have as impact for Camel 1.x end users? That is a good question. You definitely need to migrate your code from 1.x to 2.0 as several key issues have changed. The list below is what I can remember from the top of my header at the time of writing this blog entry:
  • Camel 2.0 uses the Apache Top Level Schema for namespaces, whereas Camel 1.x uses the old schema namespace
  • Several DSL keywords have been renamed in Camel 2.0
  • @deprecated code has been removed
  • Some components have been renamed (albeit components not used often)
  • ProducerTemplate has been tighten a bit regarding send vs. request
  • Default error handler have changed to not use a dead letter queue if processing failed
We are sure that this API should suit us well over the next many releases.

2009-04-02

I was interviewed at DZone: Navigating the Integration Landscape - Claus Ibsen on Apache Camel

I was interviewed at DZone. Here is the teaser:
DZone had a chance to sit down with Claus Ibsen, author of the recently published Refcard Enterprise Integration Patterns with Apache Camel. In this interview, Claus, a Principal Engineer at Progress Software in the FUSE team, discusses best practices and pitfalls associated with application integration, and how Apache Camel addresses some of these challenges.  He surveys the open source ESB landscape, describes where Camel fits in and gives some valuable advice on how to properly evaluate and select the right ESB solution.

I had no influence on the questions asked, hence I was a bit puzzled with all the questions related to Synapse - maybe WSO2 had an influence on the questions :)