2009-05-05

On the road to Camel 2.0 - Interceptors round two

A week have passed since the last blog post on interceptors in Apache Camel.

So this is round two, where I present the latest updates and the new whistles and bells features added recently.

With this overhaul of the interceptors in Camel 2.0 we fell that they are first class citizen in the Camel family and that they will provide value in the integration space.

To recap Camel supports 3 kinds of interceptors:
  1. intercept
  2. interceptFrom
  3. interceptSendToEndpoint

Ad 1)
Intercept is the regular interceptor that is like a AOP before. Its triggered before a message is forwarded to the next step in the route path. Camel also leverages this internally to apply needed processing of messages during routing such as JMX instrumentation, Tracing, Error Handling and more.

What we have done lately is to improve the intercept with syntax sugar so you can define intercept as regular routing rules that you are already familiar with. 

So for instance to do the Hello World logging AOP we just do:
intercept().to("log:hello");

And Camel will thus log the message at each processing step while being routed.

As intercept() is also a regular route you can do routing as well, so you can do a little detour if you like:
intercept().to("log:hello")
    .to("bean:sanityCheck").delay(100);

Camel have build in support for predicates, so we have added this to the intercept as well. This allows you to attach a predicate to the intercept to let it only trigger in certain conditions. For instance to only trigger if the message is a test message we can add the when predicate as:
intercept().when(header("test").isEqualTo("true"))
    .to("log:hello");

So what is next? Well what if you want to stop continue routing, lets say the test message should not be routed normally but intercepted and logged. Well we have added support for stop as a general purpose stop continue routing. So we just prepend stop() to our intercept route:
intercept().when(header("test").isEqualTo("true"))
    .to("log:hello").stop();


Ad 2)
InterceptFrom is opposed to intercept only triggered once = when a new message arrives to our route. So this allows you to intercept any incoming messages and e.g. log them.
interceptFrom().to("log:incoming");

Well recently we added a new whistle and bells feature to this interceptor. You can now filter by endpoint URI. So if you only want to intercept certain endpoints you can filter using:
  • exact match
  • match by wildcard
  • match by regular expression

Exact match is to match a single endpoint, and its just to provide the endpoint URI:
interceptFrom("activemq:queue:order").to("log:neworder");

Wildcards is supported by using a * in the end of the uri. So to match all jms queues you can do:
interceptFrom("activemq:queue:*").to("log:neworder");

And you can use regular expression for fine grained matching, to match for instance a gold and silver queues:
interceptFrom("activemq:queue:(gold|silver)")
    .to("log:neworder");

Camel provides the real intercepted endpoint URI as a message header with the key: Exchange.INTERCEPTED_ENDPOINT.

So in the sample above it will contain either: "activemq:queue:gold" or "activemq:queue:silver". This allows you to known which endpoint was intercepted.


Ad 3)
InterceptSendToEndpoint is triggered when you are about to send a message to the given endpoint. It basically intercepts the Producer. So this allows you for instance to log all outgoing messages as:
interceptSendToEndpoint().to("log:outgoing");

And just like interceptFrom it also have the same URI matching, so you can for instance intercept all cxf based endpoints such as:
interceptSendToEndpoint("cxf:*).to("log:callingwebservice");

Oh and just as intercept they all support the when predicate. So you can attach a predicate to only trigger in certain conditions. Can for instance be used to patch a message before sending:
interceptSendToEndpoint("cxf:*)
    .when().xpath("//order@id = '0')
    .to("bean:fixMissingOrderId");

The interceptSendToEndpoint brings in a new interesting feature as you can use it to mock endpoints in unit test. Lets say you write a unit test to invoke a http service to retrieve some data. With the interceptSendToEndpoint() we can intercept this and construct our own canned response. But how do you avoid invoking the real http endpoint? Well we added the option skipSendToOriginalEndpoint() to force Camel to not send it to the real intended endpoint. That leaves us with:
interceptSendToEndpoint("http:*")
    .skipSendToOriginalEndpoint().to("bean:simulateResponse");

Well I guess the 3 minutes is up and its time to end round two. Next blog entry will be on another subject as I kinda got full with interceptors now :)

The Camel wiki page for intercept has been updated with the latest details.

2 comments:

anydoby said...

Hi Claus.

I did not find a way to intercept AFTER in Camel. Is this possible at all? The idea is that every piece in the chain sets some progress message/status which an interceptor will log. I cannot seem to find anything to to this except for manually inserting the logger into a chain. But this does not work when an exception or route stop occurs.

anydoby said...
This comment has been removed by the author.