Contexts and Dependency Injection (CDI) in Camel Quarkus

CDI plays a central role in Quarkus and Camel Quarkus offers a first class support for it too.

You may use @Inject, @ConfigProperty and similar annotations e.g. to inject beans and configuration values to your Camel RouteBuilder. Here is the RouteBuilder from our timer-log-cdi example:

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.apache.camel.builder.RouteBuilder;
import org.eclipse.microprofile.config.inject.ConfigProperty;

@ApplicationScoped (1)
public class TimerRoute extends RouteBuilder {

    @ConfigProperty(name = "timer.period", defaultValue = "1000") (2)
    String period;

    @Inject
    Counter counter;

    @Override
    public void configure() throws Exception {
        fromF("timer:foo?period=%s", period)
                .setBody(exchange -> "Incremented the counter: " + counter.increment())
                .to("log:cdi-example?showExchangePattern=false&showBodyType=false");
    }
}
1 The @ApplicationScoped annotation is required for @Inject and @ConfigProperty to work in a RouteBuilder. Note that the @ApplicationScoped beans are managed by the CDI container and their life cycle is thus a bit more complex than the one of the plain RouteBuilder. In other words, using @ApplicationScoped in RouteBuilder comes with some boot time penalty and you should therefore only annotate your RouteBuilder with @ApplicationScoped when you really need it.
2 The value for the timer.period property is defined in src/main/resources/application.properties of the example project.
Please refer to the Quarkus Dependency Injection guide for more details.

Accessing CamelContext

To access CamelContext just inject it into your bean:

import jakarta.inject.Inject;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.stream.Collectors;
import java.util.List;
import org.apache.camel.CamelContext;

@ApplicationScoped
public class MyBean {

    @Inject
    CamelContext context;

    public List<String> listRouteIds() {
        return context.getRoutes().stream().map(Route::getId).sorted().collect(Collectors.toList());
    }
}

ProducerTemplate and ConsumerTemplate

If you want to use ProducerTemplate or ConsumerTemplate, then they can be injected into beans.

import jakarta.inject.Inject;
import jakarta.enterprise.context.ApplicationScoped;
import org.apache.camel.ConsumerTemplate;
import org.apache.camel.FluentProducerTemplate;
import org.apache.camel.ProducerTemplate;

@ApplicationScoped
public class MyBean {
    @Inject
    ProducerTemplate producerTemplate;

    @Inject
    FluentProducerTemplate fluentProducerTemplate;

    @Inject
    ConsumerTemplate consumerTemplate;

    public String produceToDirectEndpoint() {
        return producerTemplate.requestBody("direct:start", "Hello World", String.class);
    }

    public String produceToDirectEndpointFluently() {
        return fluentProducerTemplate.to("direct:start").withBody("Hello World").request(String.class);
    }

    public String consumeFromJMSEndpoint() {
        return consumerTemplate.receiveBody("jms:queue:camel", String.class);
    }
}

Accessing the Camel Registry

The Camel registry can be injected into beans.

import jakarta.inject.Inject;
import jakarta.enterprise.context.ApplicationScoped;
import org.apache.camel.spi.Registry;

@ApplicationScoped
public class MyBean {
    @Inject
    Registry registry;

    public CoolBean lookupBean() {
        return registry.lookupByNameAndType("coolBean", CoolBean.class);
    }
}

@EndpointInject and @Produce

If you are used to @org.apache.camel.EndpointInject and @org.apache.camel.Produce from plain Camel or from Camel on SpringBoot, you can continue using them on Quarkus too.

The following use cases are supported by org.apache.camel.quarkus:camel-quarkus-core:

import jakarta.enterprise.context.ApplicationScoped;
import org.apache.camel.EndpointInject;
import org.apache.camel.FluentProducerTemplate;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;

@ApplicationScoped
class MyBean {

    @EndpointInject("direct:myDirect1")
    ProducerTemplate producerTemplate;

    @EndpointInject("direct:myDirect2")
    FluentProducerTemplate fluentProducerTemplate;

    @EndpointInject("direct:myDirect3")
    DirectEndpoint directEndpoint;

    @Produce("direct:myDirect4")
    ProducerTemplate produceProducer;

    @Produce("direct:myDirect5")
    FluentProducerTemplate produceProducerFluent;

}

You can use any other Camel producer endpoint URI instead of direct:myDirect*.

@EndpointInject and @Produce are not supported on setter methods - see #2579

The following use case is supported by org.apache.camel.quarkus:camel-quarkus-bean:

import jakarta.enterprise.context.ApplicationScoped;
import org.apache.camel.Produce;

@ApplicationScoped
class MyProduceBean {

    public interface ProduceInterface {
        String sayHello(String name);
    }

    @Produce("direct:myDirect6")
    ProduceInterface produceInterface;

    void doSomething() {
        produceInterface.sayHello("Kermit")
    }

}

CDI and the Camel Bean component

org.apache.camel.quarkus:camel-quarkus-bean artifact brings support for the following features:

Refer to a bean by name

To refer to a bean in a route definition by name, just annotate the bean with @Named("myNamedBean") and @ApplicationScoped (or some other supported scope). The @RegisterForReflection annotation is important for the native mode.

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Named;
import io.quarkus.runtime.annotations.RegisterForReflection;

@ApplicationScoped
@Named("myNamedBean")
@RegisterForReflection
public class NamedBean {
    public String hello(String name) {
        return "Hello " + name + " from the NamedBean";
    }
}

Then you can use the myNamedBean name in a route definition:

import org.apache.camel.builder.RouteBuilder;
public class CamelRoute extends RouteBuilder {
    @Override
    public void configure() {
        from("direct:named")
                .bean("myNamedBean", "hello");
        /* ... which is an equivalent of the following: */
        from("direct:named")
                .to("bean:myNamedBean?method=hello");
    }
}

As an alternative to @Named, you may also use io.smallrye.common.annotation.Identifier to name and identify a bean.

import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.runtime.annotations.RegisterForReflection;
import io.smallrye.common.annotation.Identifier;

@ApplicationScoped
@Identifier("myBeanIdentifier")
@RegisterForReflection
public class MyBean {
    public String hello(String name) {
        return "Hello " + name + " from MyBean";
    }
}

Then refer to the identifier value within the Camel route:

import org.apache.camel.builder.RouteBuilder;
public class CamelRoute extends RouteBuilder {
    @Override
    public void configure() {
        from("direct:start")
                .bean("myBeanIdentifier", "Camel");
    }
}
We aim at supporting all use cases listed in Bean binding section of Camel documentation. Do not hesitate to file an issue if some bean binding scenario does not work for you.

@Consume

The camel-quarkus-bean artifact brings support for @org.apache.camel.Consume - see the Pojo consuming section of Camel documentation.

Declaring a class like the following

import org.apache.camel.Consume;
public class Foo {

  @Consume("activemq:cheese")
  public void onCheese(String name) {
    ...
  }
}

will automatically create the following Camel route

from("activemq:cheese").bean("foo1234", "onCheese")

for you. Note that Camel Quarkus will implicitly add @jakarta.inject.Singleton and jakarta.inject.Named("foo1234") to the bean class, where 1234 is a hash code obtained from the fully qualified class name. If your bean has some CDI scope (such as @ApplicationScoped) or @Named("someName") set already, those will be honored in the auto-created route.