Camel CDI
The Camel CDI component provides auto-configuration for Apache Camel using CDI as dependency injection framework based on convention-over-configuration. It auto-detects Camel routes available in the application and provides beans for common Camel primitives like Endpoint
, ProducerTemplate
or TypeConverter
. It implements standard Camel bean integration so that Camel annotations like @Consume
, @Produce
and @PropertyInject
can be used seamlessly in CDI beans. Besides, it bridges Camel events (e.g. RouteAddedEvent
, CamelContextStartedEvent
, ExchangeCompletedEvent
, ...) as CDI events and provides a CDI events endpoint that can be used to consume / produce CDI events from / to Camel routes.
While the Camel CDI component is available as of Camel 2.10, it's been rewritten in Camel 2.17 to better fit into the CDI programming model. Hence some of the features like the Camel events to CDI events bridge and the CDI events endpoint only apply starting Camel 2.17.
Auto-configured Camel context
Camel CDI automatically deploys and configures a CamelContext
bean. That CamelContext
bean is automatically instantiated, configured and started (resp. stopped) when the CDI container initialises (resp. shuts down). It can be injected in the application, e.g.:
@Inject CamelContext context;
That default CamelContext
bean is qualified with the built-in @Default
qualifier, is scoped @ApplicationScoped
and is of type DefaultCamelContext
.
Note that this bean can be customised programmatically and other Camel context beans can be deployed in the application as well.
Auto-detecting Camel routes
Camel CDI automatically collects all the RoutesBuilder
beans in the application, instantiates and add them to the CamelContext
bean instance when the CDI container initialises. For example, adding a Camel route is as simple as declaring a class, e.g.:
class MyRouteBean extends RoutesBuilder { @Override public void configure() { from("jms:invoices").to("file:/invoices"); } }
Note that you can declare as many RoutesBuilder
beans as you want. Besides, RouteContainer
beans are also automatically collected, instantiated and added to the CamelContext
bean instance managed by Camel CDI when the container initialises.
Custom Camel context customisation
If you just want to change the name of the default CamelContext
bean, you can used the @ContextName
qualifier provided by Camel CDI, e.g.:
@ContextName("camel-context") class MyRouteBean extends RoutesBuilder { @Override public void configure() { from("jms:invoices").to("file:/invoices"); } }
Else, if more customisation is needed, any CamelContext
class can be used to declare a custom Camel context bean. Then, the @PostConstruct
and @PreDestroy
lifecycle callbacks can be done to do the customisation, e.g.:
@ApplicationScoped class CustomCamelContext extends DefaultCamelContext { @PostConstruct void customize() { // Set the Camel context name setName("custom"); // Disable JMX disableJMX(); } @PreDestroy void cleanUp() { // ... } }
Producer and disposer methods can also be used as well to customize the Camel context bean, e.g.:
class CamelContextFactory { @Produces @ApplicationScoped CamelContext customize() { DefaultCamelContext context = new DefaultCamelContext(); context.setName("custom"); return context; } void cleanUp(@Disposes CamelContext context) { // ... } }
Similarly, producer fields can be used, e.g.:
@Produces @ApplicationScoped CamelContext context = new CustomCamelContext(); class CustomCamelContext extends DefaultCamelContext { CustomCamelContext() { setName("custom"); } }
This pattern can be used for example to avoid having the Camel context routes started automatically when the container initialises by calling the setAutoStartup
method, e.g.:
@ApplicationScoped class ManualStartupCamelContext extends DefaultCamelContext { @PostConstruct void manual() { setAutoStartup(false); } }
Multiple Camel contexts support
Any number of CamelContext
beans can actually be declared in the application as documented above. In that case, the CDI qualifiers declared on these CamelContext
beans are used to bind the Camel routes and other Camel primitives to the corresponding Camel contexts. From example, if the following beans get declared:
@ApplicationScoped @ContextName("foo") class FooCamelContext extends DefaultCamelContext { } @ApplicationScoped @ContextName("bar") class BarCamelContext extends DefaultCamelContext { } @ContextName("foo") class RouteAdddedToFooContext extends RoutesBuilder { @Override public void configure() { // ... } } @ContextName("bar") class RouteAdddedToBarContext extends RoutesBuilder { @Override public void configure() { // ... } } @ContextName("baz") class RouteAdddedToBazContext extends RoutesBuilder { @Override public void configure() { // ... } } @MyQualifier class RouteNotAddedToAnyContext extends RoutesBuilder { @Override public void configure() { // ... } }
The RoutesBuilder
beans qualified with @ContextName
get added to the corresponding CamelContext
beans. If no such CamelContext
bean exists, it gets automatically added by Camel CDI, as for the RouteAdddedToBazContext
bean. This only happens for the @ContextName
qualifier provided by Camel CDI. Hence the RouteNotAddedToAnyContext
bean qualified with the user-defined @MyQualifier
qualifier does not get added to any Camel contexts. That may be useful for Camel routes that may be required to be added later during the application execution.
Configuration properties
To configure the sourcing of the configuration properties used by Camel to resolve properties placeholders, you can declare a PropertiesComponent
bean qualified with @Named("properties")
, e.g.:
@Produces @ApplicationScoped @Named("properties") PropertiesComponent propertiesComponent() { Properties properties = new Properties(); properties.put("property", "value"); PropertiesComponent component = new PropertiesComponent(); component.setInitialProperties(properties); component.setLocation("classpath:placeholder.properties"); return component; }
If you want to use DeltaSpike configuration mechanism you can declare the following PropertiesComponent
bean:
@Produces @ApplicationScoped @Named("properties") PropertiesComponent properties(PropertiesParser parser) { PropertiesComponent component = new PropertiesComponent(); component.setPropertiesParser(parser); return component; } // PropertiesParser bean that uses DeltaSpike to resolve properties static class DeltaSpikeParser extends DefaultPropertiesParser { @Override public String parseProperty(String key, String value, Properties properties) { return ConfigResolver.getPropertyValue(key); } }
camel-example-cdi-properties
example for a working example of a Camel CDI application using DeltaSpike configuration mechanism.Auto-configured type converters
CDI beans annotated with the @Converter
annotation are automatically registered into the deployed Camel contexts, e.g.:
@Converter public class MyTypeConverter { @Converter public Output convert(Input input) { //... } }
Note that CDI injection is supported within the type converters.
Camel bean integration
Camel annotations
As part of the Camel bean integration, Camel comes with a set of annotations that are seamlessly supported by Camel CDI. So you can use any of these annotations in your CDI beans, e.g.:
Camel annotation | CDI equivalent | |
---|---|---|
Configuration property | @PropertyInject("property") String property; | If using DeltaSpike configuration mechanism: @Inject @ConfigProperty(name = "message") String property; See configuration properties for more details. |
Producer template injection (default Camel context) | @Produce(uri = "mock:outbound") ProducerTemplate producer; | @Inject @Uri("direct:outbound") ProducerTemplate producer; |
Endpoint injection (default Camel context) | @EndpointInject(uri = "direct:inbound") Endpoint endpoint; | @Inject @Uri("direct:inbound") Endpoint endpoint; |
Endpoint injection (Camel context by name) | @EndpointInject(uri = "direct:inbound", context = "foo") Endpoint contextEndpoint; | @Inject @ContextName("foo") @Uri("direct:inbound") Endpoint contextEndpoint; |
Bean injection (by type) | @BeanInject MyBean bean; | @Inject MyBean bean; |
Bean injection (by name) | @BeanInject("foo") MyBean bean; | @Inject @Named("foo") MyBean bean; |
POJO consuming | @Consume(uri = "seda:inbound") void consume(@Body String body) { //... } |
Bean component
You can refer to CDI beans, either by type or name, From the Camel DSL, e.g. with the Java Camel DSL:
class MyBean { //... } from("direct:inbound").bean(MyBean.class);
Or to lookup a CDI bean by name from the Java DSL:
@Named("foo") class MyBean { //... } from("direct:inbound").bean("foo");
Referring beans from Endpoint URIs
When configuring endpoints using the URI syntax you can refer to beans in the Registry using the #
notation. If the URI parameter value starts with a #
sign then Camel CDI will lookup for a bean of the given type by name, e.g.:
from("jms:queue:{{destination}}?transacted=true&transactionManager=#jtaTransactionManager").to("...");
Having the following CDI bean qualified with @Named("jtaTransactionManager")
:
@Produces @Named("jtaTransactionManager") PlatformTransactionManager createTransactionManager(TransactionManager transactionManager, UserTransaction userTransaction) { JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(); jtaTransactionManager.setUserTransaction(userTransaction); jtaTransactionManager.setTransactionManager(transactionManager); jtaTransactionManager.afterPropertiesSet(); return jtaTransactionManager; }
Camel events to CDI events
Camel provides a set of management events that can be subscribed to for listening to Camel context, service, route and exchange events. Camel CDI seamlessly translates these Camel events into CDI events that can be observed using CDI observer methods, e.g.:
void onContextStarting(@Observes CamelContextStartingEvent event) { // Called before the default Camel context is about to start }
When multiple Camel contexts exist in the CDI container, the Camel context bean qualifiers, like @ContextName
, can be used to refine the observer method resolution to a particular Camel context as specified in observer resolution, e.g.:
void onRouteStarted(@Observes @ContextName("foo") RouteStartedEvent event) { // Called after the route 'event.getRoute()' for the Camel context 'foo' has started } void onContextStarted(@Observes @Manual CamelContextStartedEvent event) { // Called after the the Camel context qualified with '@Manual' has started }
Similarly, the @Default
qualifier can be used to observe Camel events for the default Camel context if multiples contexts exist, e.g.:
void onExchangeCompleted(@Observes @Default ExchangeCompletedEvent event) { // Called after the exchange 'event.getExchange()' processing has completed }
In that example, if no qualifier is specified, the @Any
qualifier is implicitly assumed, so that corresponding events for all the Camel contexts get received.
Note that the support for Camel events translation into CDI events is only activated if observer methods listening for Camel events are detected in the deployment, and that per Camel context.
CDI events endpoint
The CDI event endpoint bridges the CDI events with the Camel routes so that CDI events can be seamlessly observed / consumed (resp. produced / fired) from Camel consumers (resp. by Camel producers).
The CdiEventEndpoint<T>
bean provided by Camel CDI can be used to observe / consume CDI events whose event type is T
, for example:
@Inject CdiEventEndpoint<String> cdiEventEndpoint; from(cdiEventEndpoint).log("CDI event received: ${body}");
This is equivalent to writing:
@Inject @Uri("direct:event") ProducerTemplate producer; void observeCdiEvents(@Observes String event) { producer.sendBody(event); } from("direct:event").log("CDI event received: ${body}");
Conversely, the CdiEventEndpoint<T>
bean can be used to produce / fire CDI events whose event type is T
, for example:
@Inject CdiEventEndpoint<String> cdiEventEndpoint; from("direct:event").to(cdiEventEndpoint).log("CDI event sent: ${body}");
This is equivalent to writing:
@Inject Event<String> event; from("direct:event").process(new Processor() { @Override public void process(Exchange exchange) { event.fire(exchange.getBody(String.class)); } }).log("CDI event sent: ${body}");
Or using a Java 8 lambda expression:
@Inject Event<String> event; from("direct:event") .process(exchange -> event.fire(exchange.getIn().getBody(String.class))) .log("CDI event sent: ${body}");
The type variable T
(resp. the qualifiers) of a particular CdiEventEndpoint<T>
injection point are automatically translated into the parameterized event type (resp. into the event qualifiers) e.g.:
@Inject @FooQualifier CdiEventEndpoint<List<String>> cdiEventEndpoint; from("direct:event").to(cdiEventEndpoint); void observeCdiEvents(@Observes @FooQualifier List<String> event) { logger.info("CDI event: {}", event); }
When multiple Camel contexts exist in the CDI container, the Camel context bean qualifiers, like @ContextName
, can be used to qualify the CdiEventEndpoint<T>
injection points, e.g.:
@Inject @ContextName("foo") CdiEventEndpoint<List<String>> cdiEventEndpoint; // Only observes / consumes events having the @ContextName("foo") qualifier from(cdiEventEndpoint).log("Camel context 'foo' > CDI event received: ${body}"); // Produces / fires events with the @ContextName("foo") qualifier from("...").to(cdiEventEndpoint); void observeCdiEvents(@Observes @ContextName("foo") List<String> event) { logger.info("Camel context 'foo' > CDI event: {}", event); }
Note that the CDI event Camel endpoint dynamically adds an observer method for each unique combination of event type and event qualifiers and solely relies on the container typesafe observer resolution, which leads to an implementation as efficient as possible.
Besides, as the impedance between the typesafe nature of CDI and the dynamic nature of the Camel component model is quite high, it is not possible to create an instance of the CDI event Camel endpoint via URIs. Indeed, the URI format for the CDI event component is:
cdi-event://PayloadType<T1,...,Tn>[?qualifiers=QualifierType1[,...[,QualifierTypeN]...]]
With the authority PayloadType
(resp. the QualifierType
) being the URI escaped fully qualified name of the payload (resp. qualifier) raw type followed by the type parameters section delimited by angle brackets for payload parameterized type. Which leads to unfriendly URIs, e.g.:
cdi-event://org.apache.camel.cdi.example.EventPayload%3Cjava.lang.Integer%3E?qualifiers=org.apache.camel.cdi.example.FooQualifier%2Corg.apache.camel.cdi.example.BarQualifier
But more fundamentally, that would prevent efficient binding between the endpoint instances and the observer methods as the CDI container doesn't have any ways of discovering the Camel context model during the deployment phase.
Auto-configured OSGi integration
The Camel context beans are automatically adapted by Camel CDI so that they are registered as OSGi services and the various resolvers (like ComponentResolver
and DataFormatResolver
) integrate with the OSGi registry. That means that the Karaf Camel commands can be used to operate the Camel contexts auto-configured by Camel CDI, e.g.:
karaf@root()> camel:context-list Context Status Total # Failed # Inflight # Uptime ------- ------ ------- -------- ---------- ------ camel-cdi Started 1 0 0 1 minute
See the camel-example-cdi-osgi
example for a working example of the Camel CDI OSGi integration.
Maven Archetype
Among the available Camel Maven archetypes, you can use the provided camel-archetype-cdi
to generate a Camel CDI Maven project, e.g.:
mvn archetype:generate -DarchetypeGroupId=org.apache.camel.archetypes -DarchetypeArtifactId=camel-archetype-cdi
Supported containers
The Camel CDI component is compatible with any CDI 1.0, CDI 1.1 and CDI 1.2 compliant runtime. It's been successfully tested against the following runtimes:
Container | Version | Runtime |
---|---|---|
Weld SE | 1.1.28.Final | CDI 1.0 / Java SE 7 |
OpenWebBeans |
| CDI 1.0 / Java SE 7 |
Weld SE |
| CDI 1.2 / Java SE 7 |
OpenWebBeans |
| CDI 1.2 / Java SE 7 |
WildFly | 8.2.1.Final | CDI 1.2 / Java EE 7 |
WildFly | 9.0.1.Final | CDI 1.2 / Java EE 7 |
Karaf | 2.4.4 | CDI 1.2 / OSGi 4 / PAX CDI |
Karaf | 3.0.5 | CDI 1.2 / OSGi 5 / PAX CDI |
Karaf | 4.0.4 | CDI 1.2 / OSGi 6 / PAX CDI |
Examples
The following examples are available in the examples
directory of the Camel project:
camel-example-cdi-metrics
- illustrates the integration between Camel, Dropwizard Metrics and CDI,camel-example-cdi-properties
- illustrates the integration between Camel, DeltaSpike and CDI for configuration properties,camel-example-cdi-osgi
- a CDI application using the SJMS component that can be executed inside an OSGi container using PAX CDI,camel-example-cdi-rest-servlet
- illustrates the Camel REST DSL being used in a Web application that uses CDI as dependency injection framework,camel-example-widget-gadget-cdi
- The Widget and Gadget use-case from the EIP book implemented in Java with CDI dependency Injection.