Skip to content
Olaf Bergner edited this page Sep 13, 2011 · 13 revisions

CamelPE - Exposing Apache Camel in a CDI-friendly way

What is CamelPE?

Apache Camel is a routing engine written in Java, offering an intuitive and comparatively simple programming model based on custom annotations and a nice fluent API. Implementing most of the Enterprise Integration Patterns made popular in the seminal homonymous book it positions itself as a lightweight alternative to full-grown ESBs. In complex integration scenarios, Apache Camel aids in routing, splitting and aggregating incoming and outgoing messages.

CDI - Contexts and Dependency Injection - is an official standard specified in JSR 299 defining a modern DI container that builds on experience gained in e.g. Spring and especially Guice. It offers users type-safe dependency injection where type-safety is achieved by using annotations instead of string identifiers - which often tend to render refactorings unsafe and error-prone - to control which dependencies get injected into which injection points. Being part of the JEE 6 specification - though certainly usable outside a JEE context - it is fully interoperable with most JEE defined resources: session beans, servlets, servlet filters, JAX-WS endpoints, JAX-WS handlers and so forth may be injected into CDI beans and vice versa. A long-term goal of CDI is to offer a basis for unifying the JEE component model, envisaging a future where services like declarative transaction demarcation currently available exclusively to EJB components will be extended to CDI beans.

In a recent personal project I found use for both technologies, and having grown accustomed to CDI's simplicity I set out to lift Apache Camel into the world of CDI. My goal was and still is to make the services offered by Camel available in a CDI-friendly way. This overarching goal may be stated more precisely as:

  • Make an application's CamelContext - Apache Camel's central runtime component - configurable via CDI.
  • Delegate discovery, instantiation and configuration of selected Camel resources - RouteBuilders, TypeConverters and so forth - to the CDI container.

Luckily, CDI's Portable Extension mechanism aids in achieving these goals. Implementing a portable extension third parties may integrate deeply with a CDI container, observing and potentially modifying its lifecycle events as for instance CDI bean discovered. And that's what CamelPE is at its heart: a Camel Portable Extension.

Where does CamelPE stand today?

Today, CamelPE is still in a pre-release state, lacking mainly documentation and test coverage to be considered ready for primetime. Still, I have used it successfully in personal projects where admittedly I have utilized only a fraction of what Camel has to offer. Yet bear in mind that CamelPE plays no role in Camel's runtime behaviour, i.e. it does not alter its semantics in any way. CamelPE offers merely an alternative configuration mechanism, much in the same way Camel itself uses primarily Spring for this purpose. Consequently, once a CamelContext is up and running, CamelPE's job is done.

Currently, CamelPE supports three major use cases:

How do I adapt Apache Camel's runtime to my chosen platform?

Apache Camel was designed to be usable in a vast array of diverse environments, each of which may have special requirements that need to be fulfilled by Camel's runtime. For instance, auto-discovery of TypeConverters and RouteBuilders rests on Camel's ability to scan the classpath. By default, this capability is provided by DefaultPackageScanClassResolver, the default implementation of the interface - you guessed it - PackageScanClassResolver. Some application server environments, however, have special classloading needs that cause this default implementation to fail. CamelPE, therefore, defines an SPI - Service Provider Interface - that allows users to swap out default implementations of many core classes for custom ones tailored to the target environment.

CamelPE partially exposes this SPI to the CDI container. It achieves this by introducing a custom Qualifier net.camelpe.api.CamelContextInjectable. A developer may annotate a custom implementation of one of Camel's SPI's as e.g. PackageScanClassResolver and thus have it injected into the CamelContext.

Currently, the list of supported SPIs comprises:

  1. org.apache.camel.spi.ClassResolver
  2. org.apache.camel.spi.DataFormatResolver
  3. org.apache.camel.spi.ExecutorServiceStrategy (No public link available)
  4. org.apache.camel.spi.FactoryFinderResolver
  5. org.apache.camel.spi.InflightRepository
  6. org.apache.camel.spi.ManagementStrategy
  7. org.apache.camel.spi.PackageScanClassResolver
  8. org.apache.camel.spi.ProcessorFactory (No public link available)
  9. org.apache.camel.spi.ShutdownStrategy

For a brief usage example take a look here.

How can I have my Routes discovered, instantiated, configured and registered via our CDI container?

This is almost a no-brainer since CamelPE will scan each CDI-enabled module - those sporting a META-INF/beans.xml file - for subclasses of Apache Camel's RouteBuilder, instantiate and configure those like any other CDI bean, and will register the set of discovered RouteBuilders in the CamelContext immediately prior to starting it.

Maybe the only caveat you should be aware of is that you cannot make a RouteBuilder @ApplicationScoped since it does not declare a public no-args constructor and thus cannot be proxied. While this may seem conceptually dissatisfying - it certainly seemed so to me - in practice this should rarely if ever be a problem. Routes discovery and registration is a one-time process executed at system startup. So while not @ApplicationScoped in the strict CDI sense, RouteBuilder instances will nonetheless exist for the lifetime of the application.

For a brief usage example take a look here.

How can I have my Type Converters discovered, instantiated, configured and registered via our CDI container?

Easy. Just annotate your TypeConverters with org.apache.camel.Converter or org.apache.camel.FallbackConverter and you are good to go. CamelPE will discover, instantiate and configure those just like any other CDI bean, and will take care of registering them in the CamelContext.

You should, however, take care not to use Apache Camel's standard mechanism of listing packages containing TypeConverters in META-INF/services/org/apache/camel/TypeConverter. Otherwise, they will be registered twice in the CamelContext, once by CamelPE and then again by Apache Camel's AnnotationTypeConverterLoader. CamelPE does not disable the latter since it is required to load the set of built-in TypeConverters which are essential to Apache Camel's working properly.

What may seem counterintuitive is that you have to apply org.apache.camel.Converter twice, once on the class level and then again on the method level. While this is strictly speaking not necessary, it is in keeping with the restrictions imposed by Apache Camel itself, and I consciously chose not to relax this requirement.

For a brief usage example take a look here.

What can I expect from CamelPE's first release?

CamelPE's first release, CamelPE 0.1, will probably not offer any features beyond those already available. Instead, I will focus on

  1. adding more documentation, i.e. a user guide will probably prove helpful, and more and better javadocs are always an issue
  2. creating a distribution for those users that don't buy into Maven
  3. enabling the community to give feedback on bugs, feature requests and design considerations, as well as to send patches
  4. improving the test coverage
  5. cleaning up the code base and
  6. sorting out licensing issues.

Where is CamelPE headed in the future?

Well, it largely depends on what the community deems desirable, to wit it depends on your needs. I hope for feedback from users to influence CamelPE's moving forward, especially since it already fully supports what little I need from Apache Camel.

That being said, I do have some ideas I would like to explore, namely:

  1. Support more and eventually all of Apache Camel's custom annotations as e.g. @Consume, @RecipientList. Again, I would appreciate feedback on what you think would be the most useful additions in this area.
  2. Support discovery, instantiation and configuration of org.apache.camel.spi.DataFormats.
  3. Eventually I would like to support the whole range of Apache Camel's SPI, at least where it makes sense to provide integration with CDI.
  4. One of CDI's strong points is, in my view, its extensive support for event-based designs. Apache Camel defines a comprehensive set of lifecycle events observers may be informed about. It would be nice to have these automatically converted into CDI events.

How do I build CamelPE?

Check out the source with

$ git clone git://github.com/obergner/camelpe.git

This will create a new subdirectory camelpe in the current directory, containing the CamelPE multi-module project. In the toplevel module camelpe you will find settings-sample.xml. Copy the profile net.camelpe.profile into Maven's user-level settings.xml, usually found in ~/.m2/settings.xml. You need to do this because CamelPE does consciously not declare any repositories in its master pom. For an explanation for why this is usually (though not always) a bad idea look here.

Now, after

$ cd camelpe

you may build CamelPE with e.g.

$ mvn -Pnet.camelpe.profile clean install

and you're done. Note that building has been tested using Maven 3.0+. Older versions should work, yet YMMV.