Enterprise Integration Zone is brought to you in partnership with:

Christian is a Principal Middleware Specialist/Architect at Red Hat specializing in developing enterprise software applications with an emphasis on software integration and messaging. His strengths include helping clients build software using industry best practices, Test Driven Design, ActiveMQ,Apache Camel, ServiceMix, Spring Framework, and most importantly, modeling complex domains so that they can be realized in software. He works primarily using Java and its many frameworks, but his favorite programming language is Python. He's in the midst of learning Scala and hopes to contribute to the Apache Apollo project. Christian is a DZone MVB and is not an employee of DZone and has posted 53 posts at DZone. You can read more from them at their website. View Full User Profile

From Inside the Code: Camel Routing Engine, Part I

08.20.2013
| 4517 views |
  • submit to reddit

So I’ve recently re-kindled my interest in how Apache Camel works. Camel is such a powerful integration tool and is very widely used, but anytime I put it to work, I can’t help but think “well, how the hell does it do all that!!” … guess I just have knack for not just accepting that it’s wonderful... I want to know why.

Camel pic

If you’ve followed some of my posts in the past, you’ll remember I do have a blog post that dives into how the Camel DSL API works. More specifically, how can you just magically type “from(..)” “choice(..)” “simple(..)” etc and the DSL is just smart enough to know what you mean and how to string everything together. If you’re interested in how that all works, please take a peak at that post. Be forewarned, there is a couple UML class diagrams in there that are quite verbose and long.

So if you recall from the previous post, the DSL (whether it’s the Java DSL, or XML, or Scala, or any others) has a very specific role. It helps the author of the integration route express very clearly his/her intent and then build that into an abstraction called a RouteDefinition. The RouteDefinition is kind of the “blueprint” of your route, and knows about all of your processors, EIPs, and components. You can consider any time you call from(..) to be the start of a new RouteDefinition. The rest of the route gives it its shape. So for every from(..) there is a one-to-one mapping to RouteDefinition.

So when the CamelContext starts up, it gathers up all of the RouteDefinition(s) and begins to build a route from them. Take a look at DefaultCamelContext#doStartCamel for the entry point to all of this. Among other important things, like putting together the registry, endpoints, starting basic services, management, etc, etc.. you’ll see a call to DefaultCamelContext#startRouteDefinitions. This tells the RouteDefinitions to build Route objects, which are the actual Consumers (input) + Pipeline of processors (output) that route exchanges.

The next block goes into a little more detail about how this all works. Just like in previous blog posts about the internal workings of Camel, this detail is mostly for me: i.e., in the future when I have forgotten half of this, I want a refresher. And some people who’d like to contribute to Camel may find this interesting. For all others.. Feel free to skip this block.

– Start detailed section –

The DefaultCamelContext will loop through each RouteDefinition and will try to build a list of RouteS and RouteContextS.

Quick Detour:
What is a RouteContext though? In simple terms, you can think of it as the brains of the Route, and a place where the route-specific configurations live (stream caching? tracing? handle faults? etc, etc). It knows about the “from” consumer, the rest of the pipeline, the intercept strategies, route policy, and is able to construct the Route that will operate on exchanges.

It can be kind of confusing because there are a chain of calls to methods named “addRoutes()” when really they are building RouteContexts and building Routes. But look that aside for a moment. So the call to RouteDefinition#addRoutes(..) will return a list of RouteContext objects. It will also populate a List of (initially empty) routes. The multiplicity here is basically n to n. Because you can have multiple inputs to a RouteDefinition (e.g., by stringing together multiple from(..).from(..)), a single 1-to-1 RouteContext to Route can be expected, with a single pair PER from(). So in the previous example, there would be two RouteContexts and two Routes in the List. In the call to addRoutes(..) it also tries to turn the output definitions into real Processors. The Processors are the meat of the Route object. Each processor is built based on its respective definition (e.g., ChoiceDefinition, LogDefinition), but ultimately gets wrapped in a Channel object and added to the RouteContext.

Sequence diagram

So to wrap this up, The RouteDefinition will create the Route, RouteContext, and will also turn the individual output definitions into Processors. After these are created, a RouteService is created from the newly minted Route + RouteContext pairs, and this is established with the CamelContext for later starting and controlling the lifecycle of the route.

– End detailed section –

Blocks

So …once we have the RouteService initialized, we need to start the routes, depending on whether they are auto-start up or not and their order. Take a look at DefaultCamelContext#doStartCamel once more, and toward the bottom is the call to DefaultCamelContext#doStartOrResumeRoutes. This will loop through our RouteServiceS and identify the correct startup order for the routes and then start them.

Routes are started in two phases:

  • WarmUp
    In warm up phase, we look through all of the routes and start all of the “outputs” or processors/eips. Camel takes care to make sure
    this happens first, because we do NOT want to have the “inputs” or consumers started before all of the “outputs” are available.
  • Start/Resume
    Start the consumer! This is endpoint/component specific. For example, the JMS Consumer will begin listening to a destination.

Note, at all parts of these stages in the lifecycle, there are callbacks that can be invoked so that you can plug in to this lifecycle and add your own custom startup code to coordinate with your application. Take a look at the Camel API for the type of callbacks available LifecylceStrategy

And there you have it. This is how route definitions are converted to actual routes and then started.

In Part II, I hope to go into how the processors are chained together, including a little more advanced chaining with the AsyncProcessor.

Stay tuned!

Published at DZone with permission of Christian Posta, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)