Chapter 15. Camel CDI
15.1. Basic Features
Overview
The Camel CDI component provides auto-configuration for Apache Camel using CDI as the 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 (for example 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.
How to enable Camel CDI in Apache Karaf
To enable Camel CDI in Apache Karaf, perform the following steps:
- Add the required
pax-cdi
,pax-cdi-weld
, andcamel-cdi
features to the Karaf container, as follows:JBossFuse:karaf@root> features:install pax-cdi pax-cdi-weld camel-cdi
- To enable Camel CDI in a bundle, open the
pom.xml
file in your bundle's Maven project and add the followingRequire-Capability
element to the configuration of the Maven bundle plug-in:<project ...> ... <build> <plugins> ... <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <extensions>true</extensions> <configuration> <instructions> <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName> <Import-Package>*</Import-Package> <Require-Capability> osgi.extender; filter:="(osgi.extender=pax.cdi)", org.ops4j.pax.cdi.extension; filter:="(extension=camel-cdi-extension)" </Require-Capability> </instructions> </configuration> </plugin> ... </plugins> </build> ... </project>
- To access the CDI annotations in Java, you must add a dependency on the CDI API package and on the Camel CDI package. Edit your bundle's POM file,
pom.xml
, to add the CDI API package as a Maven dependency:<project ...> ... <dependencies> ... <!-- CDI API --> <dependency> <groupId>javax.enterprise</groupId> <artifactId>cdi-api</artifactId> <version>${cdi-api-1.2-version}</version> <scope>provided</scope> </dependency> <!-- Camel CDI API --> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-cdi</artifactId> <version>2.17.0.redhat-630xxx</version> </dependency> ... </dependencies> ... </project>
- Rebuild your bundle in the usual way for your Maven project. For example, using the command:
mvn clean install
- Deploy the bundle to the Karaf container in the usual way (for example, using the
osgi:install
console command).
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, for example:
@Inject CamelContext context;
The 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, for example:
class MyRouteBean extends RouteBuilder { @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.
Auto-configured Camel primitives
Camel CDI provides beans for common Camel primitives that can be injected in any CDI beans, for example:
@Inject @Uri("direct:inbound") ProducerTemplate producerTemplate; @Inject MockEndpoint outbound; // URI defaults to the member name, i.e. mock:outbound @Inject @Uri("direct:inbound") Endpoint endpoint; @Inject TypeConverter converter;
Camel context configuration
If you just want to change the name of the default
CamelContext
bean, you can used the @ContextName
qualifier provided by Camel CDI, for example:
@ContextName("camel-context") class MyRouteBean extends RouteBuilder { @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, for example:
@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, for example:
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, for example:
@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, for example:
@ApplicationScoped class ManualStartupCamelContext extends DefaultCamelContext { @PostConstruct void manual() { setAutoStartup(false); } }
Multiple Camel contexts
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 @BarContextQualifier class BarCamelContext extends DefaultCamelContext { } @ContextName("foo") class RouteAdddedToFooCamelContext extends RouteBuilder { @Override public void configure() { // ... } } @BarContextQualifier class RouteAdddedToBarCamelContext extends RouteBuilder { @Override public void configure() { // ... } } @ContextName("baz") class RouteAdddedToBazCamelContext extends RouteBuilder { @Override public void configure() { // ... } } @MyOtherQualifier class RouteNotAddedToAnyCamelContext extends RouteBuilder { @Override public void configure() { // ... } }
The
RoutesBuilder
beans qualified with @ContextName
are automatically added to the corresponding CamelContext
beans by Camel CDI. If no such CamelContext
bean exists, it gets automatically created, as for the RouteAddedToBazCamelContext
bean. Note this only happens for the @ContextName
qualifier provided by Camel CDI. Hence the RouteNotAddedToAnyCamelContext
bean qualified with the user-defined @MyOtherQualifier
qualifier does not get added to any Camel contexts. That may be useful, for example, for Camel routes that may be required to be added later during the application execution.
Since Camel version 2.17.0, Camel CDI is capable of managing any kind of
CamelContext
beans. In previous versions, it is only capable of managing beans of type CdiCamelContext
so it is required to extend it.
The CDI qualifiers declared on the
CamelContext
beans are also used to bind the corresponding Camel primitives, for example:
@Inject @ContextName("foo") @Uri("direct:inbound") ProducerTemplate producerTemplate; @Inject @BarContextQualifier MockEndpoint outbound; // URI defaults to the member name, i.e. mock:outbound @Inject @ContextName("baz") @Uri("direct:inbound") Endpoint endpoint;
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")
, for example:
@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); } }
You can see the
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, for example:
@Converter public class MyTypeConverter { @Converter public Output convert(Input input) { //... } }
Note that CDI injection is supported within the type converters.
Lazy Injection / Programmatic Lookup
Available as of Camel 2.17
While the CDI programmatic model favors a type-safe resolution mechanism that occurs at application initialization time, it is possible to perform dynamic / lazy injection later during the application execution using the programmatic lookup mechanism.
Camel CDI provides for convenience the annotation literals corresponding to the CDI qualifiers that you can use for standard injection of Camel primitives. These annotation literals can be used in conjunction with the
javax.enterprise.inject.Instance
interface which is the CDI entry point to perform lazy injection / programmatic lookup.
For example, you can use the provided annotation literal for the
@Uri
qualifier to lazily lookup for Camel primitives, for example for ProducerTemplate
beans:
@Any @Inject Instance<ProducerTemplate> producers; ProducerTemplate inbound = producers .select(Uri.Literal.of("direct:inbound")) .get();
Or for
Endpoint
beans, for example:
@Any @Inject Instance<Endpoint> endpoints; MockEndpoint outbound = endpoints .select(MockEndpoint.class, Uri.Literal.of("mock:outbound")) .get();
Similarly, you can use the provided annotation literal for the
@ContextName
qualifier to lazily lookup for CamelContext
beans, for example:
@Any @Inject Instance<CamelContext> contexts; CamelContext context = contexts .select(ContextName.Literal.of("foo")) .get();
You can also refined the selection based on the Camel context type, for example:
@Any @Inject Instance<CamelContext> contexts; // Refine the selection by type Instance<DefaultCamelContext> context = contexts.select(DefaultCamelContext.class); // Check if such a bean exists then retrieve a reference if (!context.isUnsatisfied()) context.get();
Or even iterate over a selection of Camel contexts, for example:
@Any @Inject Instance<CamelContext> contexts; for (CamelContext context : contexts) context.setUseBreadcrumb(true);
Injecting a Camel context from Spring XML
While CDI favors a type safe dependency injection mechanism, it may be useful to reuse existing Camel XML configurations by injecting them into a Camel CDI application. In other use cases, it might be handy to rely on the Camel XML DSL to configure its Camel context(s).
To inject by CDI a camelContext defined in Spring XML, you need to use the Java
@Resource
annotation, instead of the @Inject @ContextName
annotations in the Camel CDI extension. For example,
... public class RouteCaller { ... @Resource(name = "java:jboss/camel/context/simple-context") private CamelContext context; ...The string
java:jboss/camel/context/simple-context
is the name of the deployed context registered in the JNDI registry. simple-context
is the xml:id
of the camelContext
element in the Spring XML file.
Important
Using the
@Inject @ContextName
annotations can result in the creation of a new camelContext instead of injecting the named context, which later causes endpoint lookups to fail.