This is an automated email from the ASF dual-hosted git repository. ggrzybek pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push: new 2d912c91799 [CAMEL-18189] Add documentation about <bean> support 2d912c91799 is described below commit 2d912c91799a18c2c17e8617de592171ee62eee0 Author: Grzegorz Grzybek <gr.grzy...@gmail.com> AuthorDate: Wed Jun 7 12:51:03 2023 +0200 [CAMEL-18189] Add documentation about <bean> support --- .../modules/ROOT/pages/camel-jbang.adoc | 149 +++++++++++++++++++++ .../java/org/apache/camel/main/KameletMain.java | 1 + .../org/apache/camel/main/KameletMainTest.java | 16 +++ .../java/org/apache/camel/main/app/Greeter.java | 15 ++- .../org/apache/camel/main/xml/spring-camel2.xml | 44 ++++++ .../src/main/docs/java-xml-io-dsl.adoc | 58 ++++++++ 6 files changed, 282 insertions(+), 1 deletion(-) diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc index 94c7acdb2cf..8ef85480537 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc @@ -438,6 +438,13 @@ If you do not want Camel JBang to download over the internet, you can turn this camel run foo.java --download=false ---- +If you do not want Camel JBang to use your existing Maven settings file, you can use: + +[source,bash] +---- +camel run foo.java --maven-settings=false +---- + [#_adding_custom_jars] === Adding custom JARs @@ -1589,6 +1596,8 @@ And these annotations should work on all runtimes - `@org.apache.camel.BindToRegistry` on a method to create a bean by invoking the method. - `@org.apache.camel.Converter` on class level to auto-register the xref:type-converter.adoc[type converters] from the class. +IMPORTANT: You can use `@BeanInject` annotation to refer to existing bean annotated with `@BindToRegistry`, however this is possibe if the dependency is registered before the dependant. + ==== Using Spring Boot dependency injection You can use the following Spring Boot annotations: @@ -1607,6 +1616,146 @@ You can use the following Quarkus annotations: - `@org.eclipse.microprofile.config.inject.ConfigProperty` to inject a xref:using-propertyplaceholder.adoc[property placeholder]. Such as a property defined in `application.properties`. - `@javax.enterprise.inject.Produces` on a method to create a bean by invoking the method. `@javax.inject.Named` can be used to specify the bean id. +==== Using beans in Camel XML DSL + +Since Camel *4.0.0*, when using xref:components:others:java-xml-io-dsl.adoc[XML DSL] we can declare additional beans in similar way as in xref:components:others:yaml-dsl.adoc[YAML DSL]. Such beans will be added to xref:registry.adoc[Registry] and can be referred to for example from routes. + +[source,xml] +---- +<camel> + + <bean name="beanFromMap" type="com.acme.MyBean"> + <properties> + <property key="foo" value="bar" /> + </properties> + </bean> + +</camel> +---- + +The properties of the bean can be defined using either nested `<property>` and `<properties>` elements or using dotted properties style, as shown in the example below: + +[source,xml] +---- +<camel> + + <!-- nested properties style --> + <bean name="beanFromMap" type="com.acme.MyBean"> + <properties> + <property key="field1" value="f1_p" /> + <property key="field2" value="f2_p" /> + <property key="nested"> + <properties> + <property key="field1" value="nf1_p" /> + <property key="field2" value="nf2_p" /> + </properties> + </property> + </properties> + </bean> + + <!-- dotted properties style --> + <bean name="beanFromProps" type="com.acme.MyBean"> + <properties> + <property key="field1" value="f1_p" /> + <property key="field2" value="f2_p" /> + <property key="nested.field1" value="nf1_p" /> + <property key="nested.field2" value="nf2_p" /> + </properties> + </bean> + +</camel> +---- + +==== Using Spring beans in Camel XML DSL + +Since Camel *4.0.0*, when using xref:components:others:java-xml-io-dsl.adoc[XML DSL], we can also declare _beans_ using Spring Beans XML namespace. All these beans will be added to xref:registry.adoc[Registry]. + +This will not make the application managed by Spring Framework / Spring Boot. Simply Camel will leverage existing support for generic bean definition including: + +* dependency injection +* constructor injection +* dependency cycles +* wiring existing Camel objects (like `org.apache.camel.CamelContext`) + +xref:components:others:java-xml-io-dsl.adoc[XML DSL] allows to use XML documents that define routes, rests and route templates. Since Camel *4.0.0* these documents may use new root XML element (either `<camel>` or `<beans>` to resemble Spring XML DSL), where other Camel elements (like `<routes>`) are contained. + +Here's an example `camel.xml` file, which defines both the routes and beans used (referred to) by the route definition: + +.camel.xml +[source,xml] +---- +<camel> + + <beans xmlns="http://www.springframework.org/schema/beans"> + <bean id="messageString" class="java.lang.String"> + <constructor-arg index="0" value="Hello"/> + </bean> + + <bean id="greeter" class="org.apache.camel.main.app.Greeter"> + <description>Spring Bean</description> + <property name="message"> + <bean class="org.apache.camel.main.app.GreeterMessage"> + <property name="msg" ref="messageString"/> + </bean> + </property> + </bean> + </beans> + + <route id="my-route"> + <from uri="direct:start"/> + <bean ref="greeter"/> + <to uri="mock:finish"/> + </route> + +</camel> +---- + +This document contains embedded `<beans>` element using Spring Beans XML namespace (`http://www.springframework.org/schema/beans`) - Camel passes this element directly to Spring `org.springframework.beans.factory.xml.XmlBeanDefinitionReader` and all read beans are used to populate xref:registry.adoc[Camel Registry]. + +The beans declared this way may use references to some predefined Camel beans. Currently these are handled: + +* "CamelContext" - an instance of current `org.apache.camel.CamelContext` +* "MainConfiguration" - an instance of `org.apache.camel.main.MainConfigurationProperties` used for `org.apache.camel.main.KameletMain` + +So we can use this XML fragment without actually defining what `CamelContext` is. + +[source,xml] +---- +<camel> + + <beans xmlns="http://www.springframework.org/schema/beans"> + <bean id="greeter" class="org.apache.camel.main.app.Greeter"> + <property name="camelContext" ref="CamelContext"/> + <!-- ... --> + </bean> + </beans> + + <route id="my-route"> + <from uri="direct:start"/> + <bean ref="greeter"/> + <to uri="mock:finish"/> + </route> + +</camel> +---- + +What's more, we can declare some additional beans that can affect internal mechanics of `CamelContext` being run. `org.apache.camel.main.DefaultConfigurationConfigurer` is used by xref:components:others:main.adoc[Camel Main] to configure `CamelContext` using beans found in xref:registry.adoc[Camel Registry]. + +For example we can customize used `org.apache.camel.spi.UuidGenerator` with this XML fragment to replace UUID generator used by Camel (which defaults to `org.apache.camel.support.DefaultUuidGenerator`): + +[source,xml] +---- +<camel> + + <beans xmlns="http://www.springframework.org/schema/beans"> + <bean id="customUUIDGenerator" class="org.apache.camel.support.ShortUuidGenerator" /> + </beans> + +</camel> +---- + +That's it - Camel context will then look up for the instances of `org.apache.camel.spi.UuidGenerator` and if one is found, it'll be used by Camel. + === Debugging There are two kinds of debugging: diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java index e72f650bb03..955d9fe38a6 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java @@ -616,6 +616,7 @@ public class KameletMain extends MainCommandLineSupport { beanFactory.setAllowCircularReferences(true); // for now // register some existing beans (the list may change) + // would be nice to keep the documentation up to date: docs/user-manual/modules/ROOT/pages/camel-jbang.adoc Set<String> infraBeanNames = Set.of("CamelContext", "MainConfiguration"); beanFactory.registerSingleton("CamelContext", camelContext); beanFactory.registerSingleton("MainConfiguration", config); diff --git a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/KameletMainTest.java b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/KameletMainTest.java index 33257f6c3f2..854bf77b9c0 100644 --- a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/KameletMainTest.java +++ b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/KameletMainTest.java @@ -54,6 +54,22 @@ public class KameletMainTest { }); } + @Test + public void testRouteWithSpringBeansAndCamelDependencies() throws Exception { + doTestMain("classpath:org/apache/camel/main/xml/spring-camel2.xml", (main, camelContext) -> { + try { + MockEndpoint endpoint = camelContext.getEndpoint("mock:finish", MockEndpoint.class); + endpoint.expectedBodiesReceived("Hello World (" + System.identityHashCode(camelContext) + ")"); + + main.getCamelTemplate().sendBody("direct:start", "I'm World"); + + endpoint.assertIsSatisfied(); + } catch (Exception e) { + fail(e.getMessage()); + } + }); + } + protected void doTestMain(String includes, BiConsumer<KameletMain, CamelContext> consumer) throws Exception { KameletMain main = new KameletMain(); diff --git a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/app/Greeter.java b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/app/Greeter.java index 1ec8ef91036..52d29755c1c 100644 --- a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/app/Greeter.java +++ b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/app/Greeter.java @@ -16,6 +16,7 @@ */ package org.apache.camel.main.app; +import org.apache.camel.CamelContext; import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.apache.camel.util.StringHelper; @@ -28,6 +29,8 @@ public class Greeter implements Processor { private Integer number; + private CamelContext camelContext; + public void setMessage(GreeterMessage message) { this.message = message; } @@ -40,10 +43,20 @@ public class Greeter implements Processor { this.bean = bean; } + public void setCamelContext(CamelContext camelContext) { + this.camelContext = camelContext; + } + @Override public void process(Exchange exchange) throws Exception { String msg = exchange.getIn().getBody(String.class); - exchange.getIn().setBody(message.getMsg() + " " + StringHelper.after(msg, "I'm ") + " (" + number + ")"); + if (camelContext != null) { + exchange.getIn().setBody(message.getMsg() + " " + StringHelper.after(msg, "I'm ") + + " (" + System.identityHashCode(camelContext) + ")"); + } else { + exchange.getIn().setBody(message.getMsg() + " " + StringHelper.after(msg, "I'm ") + + " (" + number + ")"); + } } } diff --git a/dsl/camel-kamelet-main/src/test/resources/org/apache/camel/main/xml/spring-camel2.xml b/dsl/camel-kamelet-main/src/test/resources/org/apache/camel/main/xml/spring-camel2.xml new file mode 100644 index 00000000000..e39caba291c --- /dev/null +++ b/dsl/camel-kamelet-main/src/test/resources/org/apache/camel/main/xml/spring-camel2.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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 + + http://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. + +--> +<camel> + + <beans xmlns="http://www.springframework.org/schema/beans"> + + <bean id="messageString" class="java.lang.String"> + <constructor-arg index="0" value="Hello"/> + </bean> + + <bean id="greeter" class="org.apache.camel.main.app.Greeter"> + <property name="camelContext" ref="CamelContext"/> + <property name="message"> + <bean class="org.apache.camel.main.app.GreeterMessage"> + <property name="msg" ref="messageString"/> + </bean> + </property> + </bean> + </beans> + + <route id="r2"> + <from uri="direct:start"/> + <bean ref="greeter"/> + <to uri="mock:finish"/> + </route> + +</camel> diff --git a/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc b/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc index 823923e973a..d2f5d7b66fc 100644 --- a/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc +++ b/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc @@ -57,6 +57,64 @@ kamel run my-route.xml camel run my-route.xml ---- +*Since Camel 4.0.0* + +It is now possible with `xml-io-dsl` to declare some beans to be bound to xref:manual::registry.adoc[Camel Registry] in similar way as with xref:yaml-dsl.adoc[YAML DSL]. Beans may be declared in XML and have their properties (also nested) defined. For example: + +[source,xml] +---- +<camel> + + <bean name="beanFromProps" type="com.acme.MyBean"> + <properties> + <property key="field1" value="f1_p" /> + <property key="field2" value="f2_p" /> + <property key="nested.field1" value="nf1_p" /> + <property key="nested.field2" value="nf2_p" /> + </properties> + </bean> + +</camel> +---- + +While keeping all the benefits of fast XML parser used by `xml-io-dsl`, Camel can also process XML elements declared in other XML namespaces and process them separately. With this mechanism it is possible to include XML elements using Spring's `http://www.springframework.org/schema/beans` namespace. + +This brings the flexibility of Spring Beans into xref:components:others:main.adoc[Camel Main] without actually running any Spring Application Context (or Spring Boot). When elements from Spring namespace are found, they are used to populate and configure an instance of `org.springframework.beans.factory.support.DefaultListableBeanFactory` and leverage Spring dependency injection to wire the beans together. These beans are then exposed through normal xref:manual::registry.adoc[Camel Regis [...] + +Here's an example `camel.xml` file, which defines both the routes and beans used (referred to) by the route definition: + +.camel.xml +[source,xml] +---- +<camel> + + <beans xmlns="http://www.springframework.org/schema/beans"> + <bean id="messageString" class="java.lang.String"> + <constructor-arg index="0" value="Hello"/> + </bean> + + <bean id="greeter" class="org.apache.camel.main.app.Greeter"> + <description>Spring Bean</description> + <property name="message"> + <bean class="org.apache.camel.main.app.GreeterMessage"> + <property name="msg" ref="messageString"/> + </bean> + </property> + </bean> + </beans> + + <route id="my-route"> + <from uri="direct:start"/> + <bean ref="greeter"/> + <to uri="mock:finish"/> + </route> + +</camel> +---- + +A `my-route` route is referring to `greeter` bean which is defined using Spring `<bean>` element. + +More examples can be found in xref:manual:ROOT:camel-jbang.adoc#_using_spring_beans_in_camel_xml_dsl[Camel JBang] page. == See Also