Apache Camel Support

Spring Integration provides an API and configuration to communicate with Apache Camel endpoints declared in the same application context.

You need to include this dependency into your project:

  • Maven

  • Gradle

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-camel</artifactId>
    <version>6.4.0</version>
</dependency>
compile "org.springframework.integration:spring-integration-camel:6.4.0"

Spring Integration and Apache Camel implement Enterprise Integration Patterns and provide a convenient way to compose them, but the projects use a different approach for their API and abstractions implementation. Spring Integration fully relies on a dependency injection container from Spring Core. It uses many other Spring projects (Spring Data, Spring AMQP, Spring for Apache Kafka etc.) for its channel adapter implementations. It also uses the MessageChannel abstraction as a first class citizen of which developers need to be aware of, when composing their integration flows. Apache Camel, on the other hand, does not provide a first class citizen abstraction of a message channel and proposes to compose its routes via internal exchanges, hidden from the API. In addition, it requires some extra dependencies and configurations for it to be used in a Spring application.

Even if it doesn’t matter for the final enterprise integration solution how its parts are implemented, a developer experience and high productivity are taken into account. Therefore, developers may choose one framework over another for many reasons, or both if there is a gap in some target systems support. Spring Integration and Apache Camel applications can interact with each other through many external protocols for which they implement channel adapters. For example, a Spring Integration flow may publish a record to an Apache Kafka topic which is consumed by an Apache Camel endpoint on the consumer side. Or, an Apache Camel route may write data into an SFTP file the directory, which is polled by a SFTP Inbound Channel Adapter from Spring Integration. Or, within the same Spring application context they can communicate via an ApplicationEvent abstraction.

To make a development process easier, and to avoid unnecessary network hops, Apache Camel provides a module to communicate with Spring Integration via message channels. All that is needed is a reference to a MessageChannel from the application context, to send or consume messages. This works well when Apache Camel routes are initiators of the message flow and Spring Integration plays only a supporting role as a part of the solution.

For a similar developer experience, Spring Integration now provides a channel adapter to call an Apache Camel endpoint and, optionally, wait for a reply. There is no inbound channel adapter because subscribing to a MessageChannel for consuming Apache Camel messages is enough from the Spring Integration API and abstractions perspective.

Outbound Channel Adapter for Apache Camel

The CamelMessageHandler is an AbstractReplyProducingMessageHandler implementation and can work in both one-way (default) and request-reply modes. It uses an org.apache.camel.ProducerTemplate to send (or send and receive) into an org.apache.camel.Endpoint. An interaction mode can be controlled by the ExchangePattern option (which can be evaluated at runtime against the request message via a SpEL expression). The target Apache Camel endpoint can be configured explicitly or as a SpEL expression to be evaluated at runtime. Otherwise, it falls back to the defaultEndpoint provided on the ProducerTemplate. Instead of specifying the endpoint, an in-line, explicit LambdaRouteBuilder can be provided, for example to make a call into an Apache Camel component for which there is no channel adapter support in Spring Integration.

In addition, a HeaderMapper<org.apache.camel.Message> (the CamelHeaderMapper is a default implementation) can be provided, to determine which headers to map between the Spring Integration and Apache Camel messages. By default, all headers are mapped.

The CamelMessageHandler supports an async mode calling ProducerTemplate.asyncSend() and producing a CompletableFuture for reply processing (if any).

The exchangeProperties can be customized via a SpEL expression, which must evaluate to a Map.

If a ProducerTemplate is not provided, it is created via a CamelContext bean resolved from the application context.

@Bean
@ServiceActivator(inputChannel = "sendToCamel")
CamelMessageHandler camelService(ProducerTemplate producerTemplate) {
    CamelHeaderMapper headerMapper = new CamelHeaderMapper();
    headerMapper.setOutboundHeaderNames("");
    headerMapper.setInboundHeaderNames("testHeader");

    CamelMessageHandler camelMessageHandler = new CamelMessageHandler(producerTemplate);
    camelMessageHandler.setEndpointUri("direct:simple");
    camelMessageHandler.setExchangePatternExpression(spelExpressionParser.parseExpression("headers.exchangePattern"));
    camelMessageHandler.setHeaderMapper(headerMapper);
    return camelMessageHandler;
}

For Java DSL flow definitions this channel adapter can be configured with a few variants provided by the Camel factory:

@Bean
IntegrationFlow camelFlow() {
    return f -> f
            .handle(Camel.gateway().endpointUri("direct:simple"))
            .handle(Camel.route(this::camelRoute))
            .handle(Camel.handler().endpointUri("log:com.mycompany.order?level=WARN"));
}

private void camelRoute(RouteBuilder routeBuilder) {
    routeBuilder.from("direct:inbound").transform(routeBuilder.simple("${body.toUpperCase()}"));
}