h2. Camel CDI
|
As of 2.10 we now have support for [Contexts and Dependency Injection - JSR299|http://jcp.org/en/jsr/detail?id=299] and [Dependency Injection for Java - JSR330|http://jcp.org/en/jsr/detail?id=330] as a dependency injection framework. This offers new opportunities to develop and deploy Apache Camel projects in [Java EE 6 containers|http://en.wikipedia.org/wiki/Java_Platform,_Enterprise_Edition] but also in standalone Java SE or [CDI container|http://openwebbeans.apache.org/owb/index.html]
|
|
The current project is under active development and does not provide all the features that we have with injection frameworks like Spring or Blueprint
|
h3. Dependency Injecting Camel with CDI
|
The [GuiceCamelContext|http://camel.apache.org/maven/current/camel-guice/apidocs/org/apache/camel/guice/GuiceCamelContext.html] is designed to work nicely inside Guice. You then need to bind it using some Guice Module.
|
Basically, two things should be done to use Apache Camel in a CDI environment. First, we just need to create a [BootStrap|https://github.com/cmoulliard/cdi-camel/blob/master/src/main/java/com/fusesource/cdi/camel/simple/BootStrap.java] class which will be use by the Java EE 6 container or Java SE to start the Camel Context. The [CdiCamelContext|https://svn.apache.org/repos/asf/camel/trunk/components/camel-cdi/src/main/java/org/apache/camel/component/cdi/CdiCamelContext.java] when instantiated will add a CDI [Bean Registry|http://docs.oracle.com/javaee/6/api/javax/enterprise/inject/spi/BeanManager.html]. That will allow Camel to perform lookup of beans injected and registered in CDI container. Next, we must add CDI annotated beans (@inject, @named, ...) to use them from the Apache Camel routes.
|
|
The camel-guice library comes with a number of reusable Guice Modules you can use if you wish - or you can bind the GuiceCamelContext yourself in your own module.
|
h3. Bootstrapping Camel with CDI container
|
|
* [CamelModule|http://camel.apache.org/maven/current/camel-guice/apidocs/org/apache/camel/guice/CamelModule.html] is the base module which binds the GuiceCamelContext but leaves it up you to bind the RouteBuilder instances * [CamelModuleWithRouteTypes|hhttp://camel.apache.org/maven/current/camel-guice/apidocs/org/apache/camel/guice/CamelModuleWithRouteTypes.html] extends CamelModule so that in the constructor of the module you specify the RouteBuilder classes or instances to use * [CamelModuleWithMatchingRoutes|http://camel.apache.org/maven/current/camel-guice/apidocs/org/apache/camel/guice/CamelModuleWithMatchingRoutes.html] extends CamelModule so that all bound RouteBuilder instances will be injected into the CamelContext or you can supply an optional Matcher to find RouteBuilder instances matching some kind of predicate.
|
The following example shows how we can bootstrap an Apache Camel Context in a Boot Strap class. This class contains important annotations like the [javax.ejb.Singleton|http://docs.oracle.com/javaee/6/api/javax/ejb/Singleton.html]. This annotation will tell the container to create a Singleton instance of the BootStrapClass class. This mechanism is similar to Bean instance creation that we have with Spring framework. By combining this annotation with [javax.ejb.Startup|http://docs.oracle.com/javaee/6/api/javax/ejb/Startup.html], the container will start the camel context at the startup of the CDI container.
|
|
So you can specify the exact [RouteBuilder] instances you want
|
{code}
|
Injector injector = Guice.createInjector(new CamelModuleWithRouteTypes(MyRouteBuilder.class, AnotherRouteBuilder.class)); // if required you can lookup the CamelContext CamelContext camelContext = injector.getInstance(CamelContext.class);
|
@Singleton @Startup public class BootStrap { ...
|
{code}
|
Or inject them all
|
When the @PreConstruct annotation is called, then we inject a CdiCamelContext objet, register a SimpleCamelRoute using @Inject annotation and starts the Camel Route.
|
{code}
|
Injector injector = Guice.createInjector(new CamelModuleWithRouteTypes()); // if required you can lookup the CamelContext CamelContext camelContext = injector.getInstance(CamelContext.class);
|
{code} @PostConstruct
|
public void init() throws Exception { logger.info(">> Create CamelContext and register Camel Route.");
|
|
You can then use Guice in the usual way to inject the route instances or any other dependent objects.
|
// Define Timer URI simpleRoute.setTimerUri("timer://simple?fixedRate=true&period=10s");
|
|
h3. Bootstrapping with JNDI
|
// Add Camel Route camelCtx.addRoutes(simpleRoute);
|
|
A common pattern used in J2EE is to bootstrap your application or root objects by looking them up in JNDI. This has long been the approach when working with JMS for example - looking up the JMS ConnectionFactory in JNDI for example.
|
// Start Camel Context camelCtx.start(); {code}
|
|
You can follow a similar pattern with Guice using the [GuiceyFruit JNDI Provider|http://code.google.com/p/guiceyfruit/wiki/GuiceyJndi] which lets you bootstrap Guice from a *jndi.properties* file which can include the Guice Modules to create along with environment specific properties you can inject into your modules and objects.
|
When you look to the following Camel Route code, you can see that we do a lookup to find a bean "helloWorld" which has been injected. This is possible because the CdiCamelContext registers a Camel Registry containing a reference to a CDI BeanManager.
|
|
If the *jndi.properties* is conflict with other component, you can specify the jndi properties file name in the Guice Main with option -j or -jndiProperties with the properties file location to let Guice Main to load right jndi properties file.
|
{code} @Override public void configure() throws Exception {
|
|
h3. Configuring Component, Endpoint or RouteBuilder instances
|
from(timerUri) .setBody() .simple("Bean Injected")
|
|
You can use [Guice] to dependency inject whatever objects you need to create, be it an [Endpoint], [Component], [RouteBuilder] or arbitrary [bean used within a route|Bean Integration].
|
// Lookup for bean injected by CDI container // The HellowWorld class is annotated using @Named .beanRef("helloWorld", "sayHello")
|
|
The easiest way to do this is to create your own Guice Module class which extends one of the above module classes and add a provider method for each object you wish to create. A provider method is annotated with *@Provides* as follows
|
.log(">> Response : ${body}");
|
|
{code} public class MyModule extends CamelModuleWithMatchingRoutes { @Provides @JndiBind("jms") JmsComponent jms(@Named("activemq.brokerURL") String brokerUrl) { return JmsComponent.jmsComponent(new ActiveMQConnectionFactory(brokerUrl));
|
}
|
}
|
{code}
|
You can optionally annotate the method with *@JndiBind* to bind the object to JNDI at some name if the object is a component, endpoint or bean you wish to refer to by name in your routes.
|
Here is the code of the HelloWorld Bean
|
|
You can inject any environment specific properties (such as URLs, machine names, usernames/passwords and so forth) from the jndi.properties file easily using the *@Named* annotation as shown above. This allows most of your configuration to be in Java code which is typesafe and easily refactorable - then leaving some properties to be environment specific (the jndi.properties file) which you can then change based on development, testing, production etc. h3. Creating multiple RouteBuilder instances per type It is sometimes useful to create multiple instances of a particular [RouteBuilder] with different configurations. To do this just create multiple provider methods for each configuration; or create a single provider method that returns a collection of RouteBuilder instances. For example
|
{code}
|
import org.apache.camel.guice.CamelModuleWithMatchingRoutes; import com.google.common.collect.Lists;
|
@Named public class HelloWorld {
|
|
public class MyModule extends CamelModuleWithMatchingRoutes { @Provides @JndiBind("foo") Collection<RouteBuilder> foo(@Named("fooUrl") String fooUrl) { return Lists.newArrayList(new MyRouteBuilder(fooUrl), new MyRouteBuilder("activemq:CheeseQueue"));
|
public String sayHello(@Body String message) { return ">> Hello " + message + " user.";
|
} } {code}
|
This project is started using the [GlassFish maven plugin|http://embedded-glassfish.java.net/nonav/plugindocs/3.1/plugin-info.html] but alternatively, you can deploy the war file produced in any Java EE 6 servers : Glassfish, JBoss AS 7, OpenEJB, Apache TomEE or Apache KarafEE or using a [Java SE|http://agoncal.wordpress.com/2011/01/12/bootstrapping-cdi-in-several-environments/].
|
h3. See Also
|
* there are a number of [Examples] you can look at to see Guice and Camel being used such as [Guice JMS Example] * [Guice Maven Plugin] for running your Guice based routes via Maven
|
* Simple [Camel CDI BootStrap project|https://github.com/cmoulliard/cdi-camel] * [JSR299|http://docs.jboss.org/weld/reference/1.1.5.Final/en-US/html_single/] and [JSR330|http://openwebbeans.apache.org/owb/jsr330.html] reference documentations * CDI revealed by Antonio - [part 1|https://agoncal.wordpress.com/2011/04/07/injection-with-cdi-part-i/], [part 2|https://agoncal.wordpress.com/2011/05/03/injection-with-cdi-part-ii/], [part 3|https://agoncal.wordpress.com/2011/09/25/injection-with-cdi-part-iii/] * Apache CDI implementation [OpenWebbeans|http://openwebbeans.apache.org/owb/index.html] and Apache [OpenEJB|http://openejb.apache.org/] which provide the containers to deploy camel CDI projects * Apache Karaf featured with OpenEJB and OpenCDI - [Apache KarafEE|https://svn.apache.org/repos/asf/openejb/trunk/openejb/osgi/]
|