From 0b9bab313b6aacefb42d7b869f4eca015cd18e61 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Thu, 3 Nov 2022 12:18:35 -0400 Subject: [PATCH] Add Java DSL for Camel module and docs (#3937) * Add Java DSL for Camel module and docs * Introduce a `LambdaRouteBuilder` option into the `CamelMessageHandler` to easily provide the Camel route just in-place * * Fix language in docs Co-authored-by: Gary Russell Co-authored-by: Gary Russell --- build.gradle | 5 +- .../integration/camel/dsl/Camel.java | 93 ++++++++++++ .../camel/dsl/CamelMessageHandlerSpec.java | 134 ++++++++++++++++++ .../integration/camel/dsl/package-info.java | 7 + .../camel/outbound/CamelMessageHandler.java | 70 ++++++++- .../integration/camel/dsl/CamelDslTests.java | 90 ++++++++++++ .../outbound/CamelMessageHandlerTests.java | 1 + src/reference/asciidoc/camel.adoc | 101 +++++++++++++ src/reference/asciidoc/endpoint-summary.adoc | 6 + src/reference/asciidoc/index-single.adoc | 2 + src/reference/asciidoc/index.adoc | 1 + src/reference/asciidoc/whats-new.adoc | 6 + 12 files changed, 513 insertions(+), 3 deletions(-) create mode 100644 spring-integration-camel/src/main/java/org/springframework/integration/camel/dsl/Camel.java create mode 100644 spring-integration-camel/src/main/java/org/springframework/integration/camel/dsl/CamelMessageHandlerSpec.java create mode 100644 spring-integration-camel/src/main/java/org/springframework/integration/camel/dsl/package-info.java create mode 100644 spring-integration-camel/src/test/java/org/springframework/integration/camel/dsl/CamelDslTests.java create mode 100644 src/reference/asciidoc/camel.adoc diff --git a/build.gradle b/build.gradle index 2c3367752a..eb4c94429e 100644 --- a/build.gradle +++ b/build.gradle @@ -496,9 +496,12 @@ project('spring-integration-camel') { dependencies { api project(':spring-integration-core') - api 'org.apache.camel:camel-api' + api 'org.apache.camel:camel-core-model' testImplementation 'org.apache.camel:camel-test-junit5' + testImplementation ('org.apache.camel:camel-spring') { + exclude group: 'org.springframework' + } } } diff --git a/spring-integration-camel/src/main/java/org/springframework/integration/camel/dsl/Camel.java b/spring-integration-camel/src/main/java/org/springframework/integration/camel/dsl/Camel.java new file mode 100644 index 0000000000..b48d13b623 --- /dev/null +++ b/spring-integration-camel/src/main/java/org/springframework/integration/camel/dsl/Camel.java @@ -0,0 +1,93 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.camel.dsl; + +import org.apache.camel.ExchangePattern; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.LambdaRouteBuilder; + +import org.springframework.lang.Nullable; + +/** + * Factory class for Apache Camel components DSL. + * + * @author Artem Bilan + * + * @since 6.0 + */ +public final class Camel { + + /** + * Create an instance of {@link CamelMessageHandlerSpec} in a {@link ExchangePattern#InOnly} mode. + * @return the spec. + */ + public static CamelMessageHandlerSpec handler() { + return camelHandler(null, ExchangePattern.InOnly); + } + + /** + * Create an instance of {@link CamelMessageHandlerSpec} for the provided {@link ProducerTemplate} + * in a {@link ExchangePattern#InOnly} mode. + * @param producerTemplate the {@link ProducerTemplate} to use. + * @return the spec. + */ + public static CamelMessageHandlerSpec handler(ProducerTemplate producerTemplate) { + return camelHandler(producerTemplate, ExchangePattern.InOnly); + } + + /** + * Create an instance of {@link CamelMessageHandlerSpec} in a {@link ExchangePattern#InOut} mode. + * @return the spec. + */ + public static CamelMessageHandlerSpec gateway() { + return camelHandler(null, ExchangePattern.InOut); + } + + /** + * Create an instance of {@link CamelMessageHandlerSpec} for the provided {@link ProducerTemplate} + * in a {@link ExchangePattern#InOut} mode. + * @param producerTemplate the {@link ProducerTemplate} to use. + * @return the spec. + */ + public static CamelMessageHandlerSpec gateway(ProducerTemplate producerTemplate) { + return camelHandler(producerTemplate, ExchangePattern.InOut); + } + + /** + * Create an instance of {@link CamelMessageHandlerSpec} for the provided {@link LambdaRouteBuilder} + * in a {@link ExchangePattern#InOut} mode. + * The {@code CamelContext} is fetched as a bean from the application context. + * @param route the {@link LambdaRouteBuilder} to use. + * @return the spec. + */ + public static CamelMessageHandlerSpec route(LambdaRouteBuilder route) { + return camelHandler(null, ExchangePattern.InOut) + .route(route); + } + + private static CamelMessageHandlerSpec camelHandler(@Nullable ProducerTemplate producerTemplate, + ExchangePattern exchangePattern) { + + return new CamelMessageHandlerSpec(producerTemplate) + .exchangePattern(exchangePattern); + } + + + private Camel() { + } + +} diff --git a/spring-integration-camel/src/main/java/org/springframework/integration/camel/dsl/CamelMessageHandlerSpec.java b/spring-integration-camel/src/main/java/org/springframework/integration/camel/dsl/CamelMessageHandlerSpec.java new file mode 100644 index 0000000000..e6add10f56 --- /dev/null +++ b/spring-integration-camel/src/main/java/org/springframework/integration/camel/dsl/CamelMessageHandlerSpec.java @@ -0,0 +1,134 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.camel.dsl; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; + +import org.apache.camel.ExchangePattern; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.LambdaRouteBuilder; + +import org.springframework.expression.Expression; +import org.springframework.integration.camel.outbound.CamelMessageHandler; +import org.springframework.integration.camel.support.CamelHeaderMapper; +import org.springframework.integration.dsl.MessageHandlerSpec; +import org.springframework.integration.expression.FunctionExpression; +import org.springframework.integration.mapping.HeaderMapper; +import org.springframework.lang.Nullable; +import org.springframework.messaging.Message; +import org.springframework.util.Assert; + +/** + * The {@link MessageHandlerSpec} for {@link CamelMessageHandler}. + * + * @author Artem Bilan + * + * @since 6.0 + */ +public class CamelMessageHandlerSpec extends + MessageHandlerSpec { + + private String[] inboundHeaderNames = { "*" }; + + private String[] outboundHeaderNames = { "*" }; + + protected CamelMessageHandlerSpec(@Nullable ProducerTemplate producerTemplate) { + this.target = producerTemplate == null ? new CamelMessageHandler() : new CamelMessageHandler(producerTemplate); + } + + public CamelMessageHandlerSpec endpointUri(String endpointUri) { + this.target.setEndpointUri(endpointUri); + return this; + } + + public CamelMessageHandlerSpec endpointUri(Function, String> endpointUriFunction) { + return endpointUriExpression(new FunctionExpression<>(endpointUriFunction)); + } + + public CamelMessageHandlerSpec endpointUriExpression(String endpointUriExpression) { + return endpointUriExpression(PARSER.parseExpression(endpointUriExpression)); + } + + public CamelMessageHandlerSpec endpointUriExpression(Expression endpointUriExpression) { + this.target.setEndpointUriExpression(endpointUriExpression); + return this; + } + + protected CamelMessageHandlerSpec route(LambdaRouteBuilder route) { + this.target.setRoute(route); + return this; + } + + public CamelMessageHandlerSpec exchangePattern(ExchangePattern exchangePattern) { + this.target.setExchangePattern(exchangePattern); + return this; + } + + public CamelMessageHandlerSpec exchangePattern(Function, ExchangePattern> exchangePatternFunction) { + return exchangePatternExpression(new FunctionExpression<>(exchangePatternFunction)); + } + + public CamelMessageHandlerSpec exchangePatternExpression(String exchangePatternExpression) { + return exchangePatternExpression(PARSER.parseExpression(exchangePatternExpression)); + } + + public CamelMessageHandlerSpec exchangePatternExpression(Expression exchangePatternExpression) { + this.target.setExchangePatternExpression(exchangePatternExpression); + return this; + } + + public CamelMessageHandlerSpec inboundHeaderNames(String... inboundHeaderNames) { + Assert.notEmpty(inboundHeaderNames, "'inboundHeaderNames' must not be empty"); + this.inboundHeaderNames = Arrays.copyOf(inboundHeaderNames, inboundHeaderNames.length); + return addCamelHeaderMapper(); + } + + public CamelMessageHandlerSpec outboundHeaderNames(String... outboundHeaderNames) { + Assert.notEmpty(outboundHeaderNames, "'outboundHeaderNames' must not be empty"); + this.outboundHeaderNames = Arrays.copyOf(outboundHeaderNames, outboundHeaderNames.length); + return addCamelHeaderMapper(); + } + + private CamelMessageHandlerSpec addCamelHeaderMapper() { + CamelHeaderMapper headerMapper = new CamelHeaderMapper(); + headerMapper.setInboundHeaderNames(this.inboundHeaderNames); + headerMapper.setOutboundHeaderNames(this.outboundHeaderNames); + return headerMapper(headerMapper); + } + + public CamelMessageHandlerSpec headerMapper(HeaderMapper headerMapper) { + this.target.setHeaderMapper(headerMapper); + return this; + } + + public CamelMessageHandlerSpec exchangeProperties(Map exchangeProperties) { + this.target.setExchangeProperties(exchangeProperties); + return this; + } + + public CamelMessageHandlerSpec exchangePropertiesExpression(String exchangePropertiesExpression) { + return exchangePropertiesExpression(PARSER.parseExpression(exchangePropertiesExpression)); + } + + public CamelMessageHandlerSpec exchangePropertiesExpression(Expression exchangePropertiesExpression) { + this.target.setExchangePropertiesExpression(exchangePropertiesExpression); + return this; + } + +} diff --git a/spring-integration-camel/src/main/java/org/springframework/integration/camel/dsl/package-info.java b/spring-integration-camel/src/main/java/org/springframework/integration/camel/dsl/package-info.java new file mode 100644 index 0000000000..d89c111545 --- /dev/null +++ b/spring-integration-camel/src/main/java/org/springframework/integration/camel/dsl/package-info.java @@ -0,0 +1,7 @@ +/** + * Provides supporting classes for JavaDSL with Apache Camel components. + */ + +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.integration.camel.dsl; diff --git a/spring-integration-camel/src/main/java/org/springframework/integration/camel/outbound/CamelMessageHandler.java b/spring-integration-camel/src/main/java/org/springframework/integration/camel/outbound/CamelMessageHandler.java index 29db655d2e..c56b38678f 100644 --- a/spring-integration-camel/src/main/java/org/springframework/integration/camel/outbound/CamelMessageHandler.java +++ b/spring-integration-camel/src/main/java/org/springframework/integration/camel/outbound/CamelMessageHandler.java @@ -19,12 +19,18 @@ package org.springframework.integration.camel.outbound; import java.util.Map; import java.util.concurrent.CompletableFuture; +import org.apache.camel.CamelContext; import org.apache.camel.CamelExecutionException; import org.apache.camel.Endpoint; import org.apache.camel.Exchange; import org.apache.camel.ExchangePattern; import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.LambdaRouteBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.model.RouteDefinition; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanInitializationException; import org.springframework.expression.Expression; import org.springframework.expression.common.LiteralExpression; import org.springframework.expression.spel.support.StandardEvaluationContext; @@ -62,13 +68,16 @@ import org.springframework.util.StringUtils; */ public class CamelMessageHandler extends AbstractReplyProducingMessageHandler { - private final ProducerTemplate producerTemplate; + private ProducerTemplate producerTemplate; private Expression exchangePatternExpression = new ValueExpression<>(ExchangePattern.InOnly); @Nullable private Expression endpointUriExpression; + @Nullable + private LambdaRouteBuilder route; + private HeaderMapper headerMapper = new CamelHeaderMapper(); @Nullable @@ -76,21 +85,47 @@ public class CamelMessageHandler extends AbstractReplyProducingMessageHandler { private StandardEvaluationContext evaluationContext; + public CamelMessageHandler() { + } + public CamelMessageHandler(ProducerTemplate producerTemplate) { Assert.notNull(producerTemplate, "'producerTemplate' must not be null"); this.producerTemplate = producerTemplate; } + /** + * Set Camel route endpoint uri to send a message. + * Mutually exclusive with {@link #setEndpointUriExpression(Expression)} and {@link #setRoute(LambdaRouteBuilder)}. + * @param endpointUri the Camel route endpoint to send a message. + */ public void setEndpointUri(String endpointUri) { Assert.hasText(endpointUri, "'endpointUri' must not be empty"); setEndpointUriExpression(new LiteralExpression(endpointUri)); } + /** + * Set Camel route endpoint uri to send a message. + * Mutually exclusive with {@link #setEndpointUri(String)} and {@link #setRoute(LambdaRouteBuilder)}. + * @param endpointUriExpression the SpEL expression to determine a Camel route endpoint to send a message. + */ public void setEndpointUriExpression(Expression endpointUriExpression) { Assert.notNull(endpointUriExpression, "'endpointUriExpression' must not be null"); this.endpointUriExpression = endpointUriExpression; } + /** + * Set a {@link LambdaRouteBuilder} to add an inline Camel route definition. + * Can be used as a lambda {@code rb -> rb.from("direct:inbound").bean(MyBean.class)} + * or reference to external instance. + * Mutually exclusive with {@link #setEndpointUri(String)} and {@link #setEndpointUriExpression(Expression)}. + * The endpoint to send a message is extracted from the target {@link RouteBuilder}. + * @param route the {@link LambdaRouteBuilder} to use. + */ + public void setRoute(LambdaRouteBuilder route) { + Assert.notNull(route, "'route' must not be null"); + this.route = route; + } + public void setExchangePattern(ExchangePattern exchangePattern) { Assert.notNull(exchangePattern, "'exchangePattern' must not be null"); setExchangePatternExpression(new ValueExpression<>(exchangePattern)); @@ -126,7 +161,38 @@ public class CamelMessageHandler extends AbstractReplyProducingMessageHandler { @Override protected final void doInit() { - this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(getBeanFactory()); + Assert.state(this.endpointUriExpression == null || this.route == null, + "The 'endpointUri' option is mutually exclusive with 'route'"); + + BeanFactory beanFactory = getBeanFactory(); + if (this.producerTemplate == null) { + this.producerTemplate = beanFactory.getBean(CamelContext.class).createProducerTemplate(); + } + + if (this.route != null) { + CamelContext camelContext = this.producerTemplate.getCamelContext(); + RouteBuilder routeBuilder = + new RouteBuilder(camelContext) { + + @Override + public void configure() throws Exception { + CamelMessageHandler.this.route.accept(this); + } + + }; + + try { + camelContext.addRoutes(routeBuilder); + } + catch (Exception ex) { + throw new BeanInitializationException("Cannot load Camel route", ex); + } + + RouteDefinition routeDefinition = routeBuilder.getRouteCollection().getRoutes().get(0); + this.endpointUriExpression = new LiteralExpression(routeDefinition.getInput().getEndpointUri()); + } + + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(beanFactory); } @Override diff --git a/spring-integration-camel/src/test/java/org/springframework/integration/camel/dsl/CamelDslTests.java b/spring-integration-camel/src/test/java/org/springframework/integration/camel/dsl/CamelDslTests.java new file mode 100644 index 0000000000..c9ea099214 --- /dev/null +++ b/spring-integration-camel/src/test/java/org/springframework/integration/camel/dsl/CamelDslTests.java @@ -0,0 +1,90 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.camel.dsl; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.camel.CamelContext; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.spring.SpringCamelContext; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.integration.config.EnableIntegration; +import org.springframework.integration.core.MessagingTemplate; +import org.springframework.integration.dsl.IntegrationFlow; +import org.springframework.messaging.MessageChannel; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +/** + * @author Artem Bilan + * + * @since 6.0 + */ +@SpringJUnitConfig +@DirtiesContext +public class CamelDslTests { + + @Autowired + @Qualifier("camelFlow.input") + MessageChannel input; + + @Test + void sendAndReceiveCamelRoute() { + String result = new MessagingTemplate().convertSendAndReceive(this.input, "apache camel", String.class); + assertThat(result).isEqualTo("___APACHE CAMEL___"); + } + + @Configuration + @EnableIntegration + public static class Config { + + @Bean + CamelContext springCamelContext() { + return new SpringCamelContext(); + } + + @EventListener(ContextRefreshedEvent.class) + public void simpleRoute() throws Exception { + RouteBuilder.addRoutes(springCamelContext(), + rb -> rb.from("direct:simple").bean("camelDslTests.Config", "transformPayload")); + } + + @Bean + IntegrationFlow camelFlow() { + return f -> f + .handle(Camel.gateway().endpointUri("direct:simple")) + .handle(Camel.route(this::camelRoute)); + } + + private void camelRoute(RouteBuilder routeBuilder) { + routeBuilder.from("direct:inbound").transform(routeBuilder.simple("${body.toUpperCase()}")); + } + + public String transformPayload(String payload) { + return "___" + payload + "___"; + } + + } + +} diff --git a/spring-integration-camel/src/test/java/org/springframework/integration/camel/outbound/CamelMessageHandlerTests.java b/spring-integration-camel/src/test/java/org/springframework/integration/camel/outbound/CamelMessageHandlerTests.java index 94ecb4cffa..136fe8d6a0 100644 --- a/spring-integration-camel/src/test/java/org/springframework/integration/camel/outbound/CamelMessageHandlerTests.java +++ b/spring-integration-camel/src/test/java/org/springframework/integration/camel/outbound/CamelMessageHandlerTests.java @@ -183,6 +183,7 @@ public class CamelMessageHandlerTests extends CamelTestSupport { public void configure() { from("direct:simple").to("mock:result"); } + }; } diff --git a/src/reference/asciidoc/camel.adoc b/src/reference/asciidoc/camel.adoc new file mode 100644 index 0000000000..e32e0c55b3 --- /dev/null +++ b/src/reference/asciidoc/camel.adoc @@ -0,0 +1,101 @@ +[[camel]] +== Apache Camel Support + +Spring Integration provides an API and configuration to communicate with https://camel.apache.org[Apache Camel] endpoints declared in the same application context. + +You need to include this dependency into your project: + +==== +[source, xml, subs="normal", role="primary"] +.Maven +---- + + org.springframework.integration + spring-integration-camel + {project-version} + +---- +[source, groovy, subs="normal", role="secondary"] +.Gradle +---- +compile "org.springframework.integration:spring-integration-camel:{project-version}" +---- +==== + +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 https://camel.apache.org/components/3.18.x/spring-summary.html[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` https://camel.apache.org/components/3.18.x/spring-event-component.html[abstraction]. + +To make a development process easier, and to avoid unnecessary network hops, Apache Camel provides a https://camel.apache.org/components/3.18.x/spring-integration-component.html[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. + +[[camel-channel-adapter]] +=== 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` (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. + +==== +[source, java] +---- +@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: + +==== +[source, java] +---- +@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()}")); +} +---- +==== diff --git a/src/reference/asciidoc/endpoint-summary.adoc b/src/reference/asciidoc/endpoint-summary.adoc index ddd078dd03..8679839cc3 100644 --- a/src/reference/asciidoc/endpoint-summary.adoc +++ b/src/reference/asciidoc/endpoint-summary.adoc @@ -54,6 +54,12 @@ The following table summarizes the various endpoints with quick links to the app | <<./amqp.adoc#amqp-inbound-gateway,Inbound Gateway>> | <<./amqp.adoc#amqp-outbound-gateway,Outbound Gateway>> +| *Apache Camel* +| N +| <<./camel.adoc#camel-channel-adapter,Outbound Channel Adapter>> +| N +| <<./camel.adoc#camel-channel-adapter,Outbound Gateway>> + | *Apache Cassandra* | N | <<./cassandra.adoc#cassandra-outbound,Outbound Channel Adapter>> diff --git a/src/reference/asciidoc/index-single.adoc b/src/reference/asciidoc/index-single.adoc index ef85ddd9bb..17c95f7751 100644 --- a/src/reference/asciidoc/index-single.adoc +++ b/src/reference/asciidoc/index-single.adoc @@ -45,6 +45,8 @@ include::./endpoint-summary.adoc[] include::./amqp.adoc[] +include::./camel.adoc[] + include::./cassandra.adoc[] include::./event.adoc[] diff --git a/src/reference/asciidoc/index.adoc b/src/reference/asciidoc/index.adoc index 3949acbd4f..4d230c67b7 100644 --- a/src/reference/asciidoc/index.adoc +++ b/src/reference/asciidoc/index.adoc @@ -35,6 +35,7 @@ Welcome to the Spring Integration reference documentation! [horizontal] <<./endpoint-summary.adoc#spring-integration-endpoints,Integration Endpoint Summary>> :: Protocol-specific channel adapters and gateways summary <<./amqp.adoc#amqp,AMQP Support>> :: AMQP channels, adapters and gateways +<<./camel.adoc#camel,Apache Camel Support>> :: Apache Camel channel adapters and gateways <<./cassandra.adoc#cassandra,Apache Cassandra Support>> :: Apache Cassandra channel adapters <<./event.adoc#applicationevent,Spring `ApplicationEvent` Support>> :: Handling and consuming Spring application events with channel adapters <<./feed.adoc#feed,Feed Adapter>> :: RSS and Atom channel adapters diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index 27feca6a8b..618abcac61 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -32,6 +32,12 @@ See <<./mqtt.adoc#mqtt-shared-client,Shared MQTT Client Support>> for more infor The GraphQL support has been added. See <<./graphql.adoc#graphql,GraphQL Support>> for more information. +[[x6.0-camel]] +==== Apache Camel Support + +Support for Apache Camel routes has been introduced. +See <<./camel.adoc#camel,Apache Camel Support>> for more information. + [[x6.0-smb]] ==== SMB Support