CAMEL-11756: camel-spring-boot2 - Work in progress
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/cb70f538 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/cb70f538 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/cb70f538 Branch: refs/heads/boot2 Commit: cb70f5387f220852b06064fe2c8ea90b924d4cb4 Parents: 3a478cb Author: Claus Ibsen <davscl...@apache.org> Authored: Sat Sep 9 13:43:35 2017 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Sat Sep 9 13:43:35 2017 +0200 ---------------------------------------------------------------------- components/camel-spring-boot2/pom.xml | 180 ++++ .../src/main/docs/spring-boot.adoc | 456 +++++++++ .../spring/boot/CamelAutoConfiguration.java | 524 ++++++++++ .../boot/CamelConfigurationProperties.java | 951 +++++++++++++++++++ .../spring/boot/CamelContextConfiguration.java | 27 + .../spring/boot/CamelMainRunController.java | 56 ++ .../CamelSpringBootApplicationController.java | 103 ++ .../CamelSpringBootInitializationException.java | 25 + .../boot/ComponentConfigurationProperties.java | 23 + .../ComponentConfigurationPropertiesCommon.java | 60 ++ .../boot/DataFormatConfigurationProperties.java | 23 + ...DataFormatConfigurationPropertiesCommon.java | 60 ++ .../boot/FatJarPackageScanClassResolver.java | 100 ++ .../camel/spring/boot/FilePropertySource.java | 81 ++ .../boot/LanguageConfigurationProperties.java | 23 + .../LanguageConfigurationPropertiesCommon.java | 60 ++ .../camel/spring/boot/RoutesCollector.java | 317 +++++++ .../SpringBootXmlCamelContextConfigurer.java | 47 + .../spring/boot/SpringPropertiesParser.java | 39 + .../camel/spring/boot/SpringTypeConverter.java | 85 ++ ...rvisingRouteControllerAutoConfiguration.java | 93 ++ ...SupervisingRouteControllerConfiguration.java | 179 ++++ .../boot/TypeConversionConfiguration.java | 64 ++ .../endpoint/CamelRouteControllerEndpoint.java | 60 ++ ...outeControllerEndpointAutoConfiguration.java | 49 + .../CamelRouteControllerMvcEndpoint.java | 79 ++ .../actuate/endpoint/CamelRoutesEndpoint.java | 377 ++++++++ .../CamelRoutesEndpointAutoConfiguration.java | 49 + .../endpoint/CamelRoutesMvcEndpoint.java | 247 +++++ .../health/CamelHealthAutoConfiguration.java | 46 + .../actuate/health/CamelHealthIndicator.java | 52 + .../boot/cloud/CamelCloudAutoConfiguration.java | 30 + .../CamelCloudConfigurationProperties.java | 274 ++++++ ...rviceCallConfigurationAutoConfiguration.java | 83 ++ ...melCloudServiceChooserAutoConfiguration.java | 43 + .../boot/cloud/CamelCloudServiceDiscovery.java | 47 + ...lCloudServiceDiscoveryAutoConfiguration.java | 128 +++ .../boot/cloud/CamelCloudServiceFilter.java | 36 + ...amelCloudServiceFilterAutoConfiguration.java | 124 +++ ...usteredRouteControllerAutoConfiguration.java | 101 ++ .../ClusteredRouteControllerConfiguration.java | 147 +++ .../security/CamelSSLAutoConfiguration.java | 63 ++ .../CamelSSLConfigurationProperties.java | 42 + .../spring/boot/util/CamelPropertiesHelper.java | 95 ++ ...OnCamelContextAndAutoConfigurationBeans.java | 36 + .../camel/spring/boot/util/GroupCondition.java | 44 + .../util/HierarchicalPropertiesEvaluator.java | 58 ++ .../src/main/resources/META-INF/LICENSE.txt | 203 ++++ .../src/main/resources/META-INF/NOTICE.txt | 11 + ...dditional-spring-configuration-metadata.json | 16 + .../main/resources/META-INF/spring.factories | 30 + .../camel/spring/boot/CamelAnnotationsTest.java | 71 ++ .../CamelAutoConfigurationPropertiesTest.java | 88 ++ .../spring/boot/CamelAutoConfigurationTest.java | 194 ++++ .../boot/CamelConfigurationLocationsTest.java | 67 ++ .../spring/boot/CamelEventNotifierTest.java | 105 ++ .../boot/CamelNonInvasiveCamelContextTest.java | 85 ++ .../boot/CamelSpringBootShutdownTest.java | 127 +++ .../CamelSpringBootTemplateShutdownTest.java | 84 ++ .../spring/boot/CustomShutdownStrategyTest.java | 93 ++ .../boot/ExistingConversionServiceTest.java | 58 ++ .../boot/MixedBootAndXmlConfigurationTest.java | 55 ++ .../spring/boot/MixedJavaDslAndXmlTest.java | 78 ++ .../camel/spring/boot/NoConvertersTest.java | 46 + .../org/apache/camel/spring/boot/PlainTest.java | 54 ++ .../RouteConfigWithCamelContextInjected.java | 44 + .../boot/SpringConverterDelegationTest.java | 65 ++ .../spring/boot/SpringTypeConverterTest.java | 126 +++ .../spring/boot/StartupShutdownOrderTest.java | 238 +++++ .../SupervisingRouteControllerRestartTest.java | 140 +++ .../boot/SupervisingRouteControllerTest.java | 126 +++ .../actuate/endpoint/ActuatorTestRoute.java | 30 + .../endpoint/CamelRoutesEndpointTest.java | 59 ++ .../endpoint/CamelRoutesMvcEndpointTest.java | 68 ++ .../boot/actuate/health/CamelHealthTest.java | 55 ++ .../boot/actuate/health/MyCamelRoute.java | 29 + .../CamelCloudServiceCallConfigurationTest.java | 76 ++ ...CloudServiceCallGlobalConfigurationTest.java | 127 +++ .../CamelCloudServiceCallRefExpressionTest.java | 133 +++ ...melCloudServiceCallSimpleExpressionTest.java | 125 +++ .../boot/cloud/CamelCloudServiceCallTest.java | 126 +++ .../boot/cloud/SpringBootPropertyUtil.java | 57 ++ .../boot/componentroute/ComponentRoute.java | 36 + .../boot/componentroute/ComponentRouteTest.java | 55 ++ .../camel/spring/boot/dummy/DummyComponent.java | 38 + .../camel/spring/boot/dummy/DummyConsumer.java | 54 ++ .../camel/spring/boot/dummy/DummyEndpoint.java | 63 ++ .../camel/spring/boot/issues/CountryPojo.java | 78 ++ .../spring/boot/issues/RestDslPostTest.java | 102 ++ .../spring/boot/issues/SimpleOgnlTest.java | 69 ++ .../spring/boot/issues/StreamCachingTest.java | 96 ++ .../camel/spring/boot/issues/UserPojo.java | 74 ++ .../boot/mockendpoints/AdviceWithTest.java | 68 ++ .../boot/mockendpoints/MockEndpointsTest.java | 57 ++ .../spring/boot/mockendpoints/MyRoute.java | 30 + .../parent/SpringBootRefreshContextTest.java | 57 ++ .../camel/spring/boot/routefilter/BarRoute.java | 30 + .../camel/spring/boot/routefilter/BarTest.java | 54 ++ .../spring/boot/routefilter/DrinkRoute.java | 30 + .../camel/spring/boot/routefilter/FooRoute.java | 30 + .../camel/spring/boot/routefilter/FooTest.java | 54 ++ .../security/CamelSSLAutoConfigurationTest.java | 68 ++ .../boot/security/CamelSSLNoConfigTest.java | 53 ++ .../boot/util/CamelPropertiesHelperTest.java | 170 ++++ .../spring/boot/util/GroupConditionTest1.java | 49 + .../spring/boot/util/GroupConditionTest2.java | 49 + .../spring/boot/util/GroupConditionTest3.java | 49 + .../spring/boot/util/GroupConditionTest4.java | 49 + .../boot/util/GroupConditionTestBase.java | 41 + .../HierarchicalPropertiesEvaluatorTest.java | 63 ++ .../src/test/resources/application.properties | 23 + .../src/test/resources/camel/camelContext.xml | 25 + .../src/test/resources/externalCamelContext.xml | 37 + .../src/test/resources/logback.xml | 42 + .../src/test/resources/mixed-camel-context.xml | 30 + .../src/test/resources/test-camel-context.xml | 35 + .../src/test/secret/do-not-tell.properties | 18 + parent/pom.xml | 1 + 118 files changed, 10852 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/cb70f538/components/camel-spring-boot2/pom.xml ---------------------------------------------------------------------- diff --git a/components/camel-spring-boot2/pom.xml b/components/camel-spring-boot2/pom.xml new file mode 100644 index 0000000..b8c0898 --- /dev/null +++ b/components/camel-spring-boot2/pom.xml @@ -0,0 +1,180 @@ +<?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. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.camel</groupId> + <artifactId>components</artifactId> + <version>2.20.0-SNAPSHOT</version> + </parent> + + <name>Camel :: Spring Boot 2</name> + <artifactId>camel-spring-boot2</artifactId> + <description>Using Camel with Spring Boot 2</description> + + <properties> + <!-- use by camel-catalog --> + <firstVersion>2.20.0</firstVersion> + <label>spring,microservice</label> + + <camel.osgi.export.pkg/> + + <spring-version>5.0.0.RC3</spring-version> + <jackson2-version>2.9.1</jackson2-version> + + </properties> + + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter</artifactId> + <optional>true</optional> + <version>${spring-boot2-version}</version> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-configuration-processor</artifactId> + <optional>true</optional> + <version>${spring-boot2-version}</version> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-actuator</artifactId> + <optional>true</optional> + <version>${spring-boot2-version}</version> + </dependency> + + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-spring</artifactId> + </dependency> + + <!-- Optional Spring web support --> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-web</artifactId> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + <version>${spring-boot2-version}</version> + <optional>true</optional> + </dependency> + + <!-- Testing dependencies --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-test-spring</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-core-xml</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.awaitility</groupId> + <artifactId>awaitility</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + <version>${commons-logging-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <version>${spring-boot2-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-jetty</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-jackson</artifactId> + <scope>test</scope> + </dependency> + + </dependencies> + + <!-- TODO: using milestone of spring-boot 2 --> + <repositories> + <repository> + <id>spring-milestones</id> + <name>Spring Milestones</name> + <url>https://repo.spring.io/libs-milestone</url> + <snapshots> + <enabled>false</enabled> + </snapshots> + </repository> + </repositories> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <forkCount>1</forkCount> + <reuseForks>false</reuseForks> + </configuration> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>jdk9-build</id> + <activation> + <jdk>9</jdk> + </activation> + <build> + <plugins> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <forkedProcessTimeoutInSeconds>3000</forkedProcessTimeoutInSeconds> + <argLine>--add-modules java.xml.bind --add-opens java.base/java.lang=ALL-UNNAMED</argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> http://git-wip-us.apache.org/repos/asf/camel/blob/cb70f538/components/camel-spring-boot2/src/main/docs/spring-boot.adoc ---------------------------------------------------------------------- diff --git a/components/camel-spring-boot2/src/main/docs/spring-boot.adoc b/components/camel-spring-boot2/src/main/docs/spring-boot.adoc new file mode 100644 index 0000000..9f148a6 --- /dev/null +++ b/components/camel-spring-boot2/src/main/docs/spring-boot.adoc @@ -0,0 +1,456 @@ +[[SpringBoot-SpringBoot]] +Spring Boot +~~~~~~~~~~~ + +*Available as of Camel 2.15* + +Spring Boot component provides auto-configuration for Apache Camel. Our +opinionated auto-configuration of the Camel context auto-detects Camel +routes available in the Spring context and registers the key Camel +utilities (like producer template, consumer template and the type +converter) as beans. + +Maven users will need to add the following dependency to their `pom.xml` +in order to use this component: + +[source,xml] +------------------------------------------------------------------------------------------------ +<dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-spring-boot</artifactId> + <version>${camel.version}</version> <!-- use the same version as your Camel core version --> +</dependency> +------------------------------------------------------------------------------------------------ + +`camel-spring-boot` jar comes with the `spring.factories` file, so as +soon as you add that dependency into your classpath, Spring Boot will +automatically auto-configure Camel for you. + +[[SpringBoot-CamelSpringBootStarter]] +Camel Spring Boot Starter +^^^^^^^^^^^^^^^^^^^^^^^^^ + +*Available as of Camel 2.17* + +Apache Camel ships +a https://github.com/spring-projects/spring-boot/tree/master/spring-boot-starters[Spring +Boot Starter] module that allows you to develop Spring Boot applications +using starters. There is a +https://github.com/apache/camel/tree/master/examples/camel-example-spring-boot-starter[sample +application] in the source code also. + +To use the starter, add the following to your spring boot pom.xml file: + +[source,xml] +------------------------------------------------------ +<dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-spring-boot-starter</artifactId> + <version>2.17.0</version> +</dependency> +------------------------------------------------------ + +Then you can just add classes with your Camel routes such as: + +[source,java] +------------------------------------------------ +package com.example; + +import org.apache.camel.builder.RouteBuilder; +import org.springframework.stereotype.Component; + +@Component +public class MyRoute extends RouteBuilder { + + @Override + public void configure() throws Exception { + from("timer:foo").to("log:bar"); + } +} +------------------------------------------------ + +Then these routes will be started automatically. + +You can customize the Camel application in the `application.properties` +or `application.yml` file. + +[[SpringBoot-Auto-configuredCamelcontext]] +Auto-configured Camel context +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The most important piece of functionality provided by the Camel +auto-configuration is `CamelContext` instance. +Camel auto-configuration creates a `SpringCamelContext` for you and +takes care of the proper initialization and shutdown of that context. +The created Camel context is also registered in the Spring application +context (under `camelContext` bean name), so you can access it just as + any other Spring bean. + +[source,java] +---------------------------------------------- +@Configuration +public class MyAppConfig { + + @Autowired + CamelContext camelContext; + + @Bean + MyService myService() { + return new DefaultMyService(camelContext); + } + +} +---------------------------------------------- + +[[SpringBoot-Auto-detectingCamelroutes]] +Auto-detecting Camel routes +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Camel auto-configuration collects all the `RouteBuilder` instances from +the Spring context and automatically injects them into the provided +`CamelContext`. That means that creating new Camel route with the Spring +Boot starter is as simple as adding the `@Component` annotated class to +your classpath: + +[source,java] +---------------------------------------------- +@Component +public class MyRouter extends RouteBuilder { + + @Override + public void configure() throws Exception { + from("jms:invoices").to("file:/invoices"); + } + +} +---------------------------------------------- + + + +...or creating a new route `RouteBuilder` bean in your `@Configuration` +class: + +[source,java] +-------------------------------------------------- +@Configuration +public class MyRouterConfiguration { + + @Bean + RoutesBuilder myRouter() { + return new RouteBuilder() { + + @Override + public void configure() throws Exception { + from("jms:invoices").to("file:/invoices"); + } + + }; + } + +} +-------------------------------------------------- + +[[SpringBoot-Camelproperties]] +Camel properties +^^^^^^^^^^^^^^^^ + +Spring Boot auto-configuration automatically connects +to http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config[Spring +Boot external configuration] (like properties placeholders, OS +environment variables or system properties) with +the link:properties.html[Camel properties support]. It basically means +that any property defined in `application.properties` file:  + +[source,xml] +------------------------- +route.from = jms:invoices +------------------------- + +...or set via system property... + +[source,xml] +----------------------------------------------------------- +java -Droute.to=jms:processed.invoices -jar mySpringApp.jar +----------------------------------------------------------- + +...can be used as placeholders in Camel route: + +[source,java] +---------------------------------------------- +@Component +public class MyRouter extends RouteBuilder { + + @Override + public void configure() throws Exception { + from("{{route.from}}").to("{{route.to}}"); + } + +} +---------------------------------------------- + +[[SpringBoot-CustomCamelcontextconfiguration]] +Custom Camel context configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you would like to perform some operations on `CamelContext` bean +created by Camel auto-configuration, +register `CamelContextConfiguration` instance in your Spring context: + +[source,java] +--------------------------------------------------------- +@Configuration +public class MyAppConfig { + + ... + + @Bean + CamelContextConfiguration contextConfiguration() { + return new CamelContextConfiguration() { + @Override + void beforeApplicationStart(CamelContext context) { + // your custom configuration goes here + } + }; + } + +} +--------------------------------------------------------- + +Method +C`amelContextConfiguration#``beforeApplicationStart(CamelContext)` will +be called just before the Spring context is started, so the +`CamelContext` instance passed to this callback is +fully auto-configured. You can add many instances of +C`amelContextConfiguration` into your Spring context - all of them will +be executed. + +[[SpringBoot-DisablingJMX]] +Disabling JMX +^^^^^^^^^^^^^ + +To disable JMX of the auto-configured `CamelContext` use +`camel.springboot.jmxEnabled` property (JMX is enabled by default). For +example you could add the following property to your +`application.properties` file: + +[source,xml] +----------------------------------- +camel.springboot.jmxEnabled = false +----------------------------------- + +[[SpringBoot-Auto-configuredconsumerandproducertemplates]] +Auto-configured consumer and producer templates +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Camel auto-configuration provides pre-configured `ConsumerTemplate` and +`ProducerTemplate` instances. You can simply inject them into your +Spring-managed beans: + +[source,java] +------------------------------------------------------------------------------------------ +@Component +public class InvoiceProcessor { + + @Autowired + private ProducerTemplate producerTemplate; + + @Autowired + private ConsumerTemplate consumerTemplate; + public void processNextInvoice() { + Invoice invoice = consumerTemplate.receiveBody("jms:invoices", Invoice.class); + ... + producerTemplate.sendBody("netty-http:http://invoicing.com/received/" + invoice.id()); + } + +} +------------------------------------------------------------------------------------------ + +By default consumer templates and producer templates come with the +endpoint cache sizes set to 1000. You can change those values via the +following Spring properties: + +[source,xml] +------------------------------------------------ +camel.springboot.consumerTemplateCacheSize = 100 +camel.springboot.producerTemplateCacheSize = 200 +------------------------------------------------ + +[[SpringBoot-Auto-configuredTypeConverter]] +Auto-configured TypeConverter +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Camel auto-configuration registers a `TypeConverter` instance named +`typeConverter` in the Spring context. + +[source,java] +------------------------------------------------------------- +@Component +public class InvoiceProcessor { + + @Autowired + private TypeConverter typeConverter; + + public long parseInvoiceValue(Invoice invoice) { + String invoiceValue = invoice.grossValue(); + return typeConverter.convertTo(Long.class, invoiceValue); + } + +} +------------------------------------------------------------- + +[[SpringBoot-SpringtypeconversionAPIbridge]] +Spring type conversion API bridge +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Spring comes with +the powerful http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert[type +conversion API]. Spring API happens to be very similar to the Camel +link:type-converter.html[type converter API]. As those APIs are so +similar, Camel Spring Boot automatically registers a bridge converter +(`SpringTypeConverter`) that delegates to the Spring conversion API.That +means that out-of-the-box Camel will treat Spring Converters like Camel +ones. With this approach you can enjoy both Camel and Spring converters +accessed via Camel `TypeConverter` API: + +[source,java] +--------------------------------------------------------------------------- +@Component +public class InvoiceProcessor { + + @Autowired + private TypeConverter typeConverter; + + public UUID parseInvoiceId(Invoice invoice) { + // Using Spring's StringToUUIDConverter + UUID id = invoice.typeConverter.convertTo(UUID.class, invoice.getId()); + } + +} +--------------------------------------------------------------------------- + + + +Under the hood Camel Spring Boot delegates conversion to the Spring's +`ConversionService` instances available in the application context. If +no `ConversionService` instance is available, Camel Spring Boot +auto-configuration will create one for you. + +[[SpringBoot-Disablingtypeconversionsfeatures]] +Disabling type conversions features +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you don't want Camel Spring Boot to register type-conversions related +features (like `TypeConverter` instance or Spring bridge) set the +`camel.springboot.typeConversion` property to `false`. + +[source,xml] +--------------------------------------- +camel.springboot.typeConversion = false +--------------------------------------- + + +[[SpringBoot-Blockingmainthread]] +Blocking main thread +^^^^^^^^^^^^^^^^^^^^ + +This feature is available starting from Camel *2.15.2*. Camel +applications extending FatJarRouter by default block the main thread of +the application. It means that after you start your fat jar, your +application waits for Ctrl+C signal and does not exit immediately. If +you would like to achieve similar behavior for non-`FatJarRouter` +applications, retrieve `CamelSpringBootApplicationController `bean from +your `ApplicationContext` and use the former to block the main thread of +your application using +`CamelSpringBootApplicationController#blockMainThread()` method. + +[source,java] +------------------------------------------------------------------------------------------------------ +public static void main(String... args) { + ApplicationContext applicationContext = new SpringApplication(MyCamelApplication.class).run(args); + CamelSpringBootApplicationController applicationController = + applicationContext.getBean(CamelSpringBootApplicationController.class); + applicationController.blockMainThread(); +} +------------------------------------------------------------------------------------------------------ + +[[SpringBoot-AddingXMLroutes]] +Adding XML routes +^^^^^^^^^^^^^^^^^ + +By default you can put Camel XML routes in the classpath under the +directory camel, which camel-spring-boot will auto detect and include. +From *Camel 2.17* onwards you can configure the directory name or turn +this off using the configuration option + +[source,java] +----------------------------------------------------------- +// turn off +camel.springboot.xmlRoutes = false +// scan in the com/foo/routes classpath +camel.springboot.xmlRoutes = classpath:com/foo/routes/*.xml +----------------------------------------------------------- + +The XML files should be Camel XML routes (not CamelContext) such as + +[source,xml] +--------------------------------------------------------- + <routes xmlns="http://camel.apache.org/schema/spring"> + <route id="test"> + <from uri="timer://trigger"/> + <transform> + <simple>ref:myBean</simple> + </transform> + <to uri="log:out"/> + </route> + </routes> +--------------------------------------------------------- + +[[SpringBoot-AddingREST]] +Adding XML Rest-DSL +^^^^^^^^^^^^^^^^^^^ + +By default you can put Camel Rest-DSL XML routes in the classpath under the +directory camel-rest, which camel-spring-boot will auto detect and include. +You can configure the directory name or turn this off using the configuration option + +[source,java] +----------------------------------------------------------- +// turn off +camel.springboot.xmlRests = false +// scan in the com/foo/routes classpath +camel.springboot.xmlRests = classpath:com/foo/rests/*.xml +----------------------------------------------------------- + +The Rest-DSL XML files should be Camel XML rests (not CamelContext) such as + +[source,xml] +--------------------------------------------------------- + <rests xmlns="http://camel.apache.org/schema/spring"> + <rest> + <post uri="/persons"> + <to uri="direct:postPersons"/> + </post> + <get uri="/persons"> + <to uri="direct:getPersons"/> + </get> + <get uri="/persons/{personId}"> + <to uri="direct:getPersionId"/> + </get> + <put uri="/persons/{personId}"> + <to uri="direct:putPersionId"/> + </put> + <delete uri="/persons/{personId}"> + <to uri="direct:deletePersionId"/> + </delete> + </rest> + </rests> +--------------------------------------------------------- + +[[SpringBoot-SeeAlso]] +See Also +^^^^^^^^ + +* link:configuring-camel.html[Configuring Camel] +* link:component.html[Component] +* link:endpoint.html[Endpoint] +* link:getting-started.html[Getting Started] + http://git-wip-us.apache.org/repos/asf/camel/blob/cb70f538/components/camel-spring-boot2/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-boot2/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java b/components/camel-spring-boot2/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java new file mode 100644 index 0000000..2f99fca --- /dev/null +++ b/components/camel-spring-boot2/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java @@ -0,0 +1,524 @@ +/** + * 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. + */ +package org.apache.camel.spring.boot; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.camel.CamelContext; +import org.apache.camel.ConsumerTemplate; +import org.apache.camel.Exchange; +import org.apache.camel.FluentProducerTemplate; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.TypeConverters; +import org.apache.camel.component.properties.PropertiesComponent; +import org.apache.camel.component.properties.PropertiesParser; +import org.apache.camel.ha.CamelClusterService; +import org.apache.camel.impl.FileWatcherReloadStrategy; +import org.apache.camel.processor.interceptor.BacklogTracer; +import org.apache.camel.processor.interceptor.DefaultTraceFormatter; +import org.apache.camel.processor.interceptor.HandleFault; +import org.apache.camel.processor.interceptor.TraceFormatter; +import org.apache.camel.processor.interceptor.Tracer; +import org.apache.camel.spi.AsyncProcessorAwaitManager; +import org.apache.camel.spi.EndpointStrategy; +import org.apache.camel.spi.EventFactory; +import org.apache.camel.spi.EventNotifier; +import org.apache.camel.spi.InflightRepository; +import org.apache.camel.spi.InterceptStrategy; +import org.apache.camel.spi.LifecycleStrategy; +import org.apache.camel.spi.ManagementNamingStrategy; +import org.apache.camel.spi.ManagementStrategy; +import org.apache.camel.spi.ReloadStrategy; +import org.apache.camel.spi.RouteController; +import org.apache.camel.spi.RoutePolicyFactory; +import org.apache.camel.spi.RuntimeEndpointRegistry; +import org.apache.camel.spi.ShutdownStrategy; +import org.apache.camel.spi.StreamCachingStrategy; +import org.apache.camel.spi.ThreadPoolProfile; +import org.apache.camel.spi.UnitOfWorkFactory; +import org.apache.camel.spring.CamelBeanPostProcessor; +import org.apache.camel.spring.SpringCamelContext; +import org.apache.camel.spring.spi.XmlCamelContextConfigurer; +import org.apache.camel.util.ObjectHelper; +import org.apache.camel.util.jsse.GlobalSSLContextParametersSupplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Lazy; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MutablePropertySources; + +@Configuration +@EnableConfigurationProperties(CamelConfigurationProperties.class) +@Import(TypeConversionConfiguration.class) +public class CamelAutoConfiguration { + + private static final Logger LOG = LoggerFactory.getLogger(CamelAutoConfiguration.class); + + /** + * Allows to do custom configuration when running XML based Camel in Spring Boot + */ + // must be named xmlCamelContextConfigurer + @Bean(name = "xmlCamelContextConfigurer") + XmlCamelContextConfigurer springBootCamelContextConfigurer() { + return new SpringBootXmlCamelContextConfigurer(); + } + + /** + * Spring-aware Camel context for the application. Auto-detects and loads all routes available in the Spring context. + */ + // We explicitly declare the destroyMethod to be "" as the Spring @Bean + // annotation defaults to AbstractBeanDefinition.INFER_METHOD otherwise + // and in that case CamelContext::shutdown or CamelContext::stop would + // be used for bean destruction. As SpringCamelContext is a lifecycle + // bean (implements Lifecycle) additional invocations of shutdown or + // close would be superfluous. + @Bean(destroyMethod = "") + @ConditionalOnMissingBean(CamelContext.class) + CamelContext camelContext(ApplicationContext applicationContext, + CamelConfigurationProperties config) throws Exception { + CamelContext camelContext = new SpringCamelContext(applicationContext); + return doConfigureCamelContext(applicationContext, camelContext, config); + } + + static CamelContext doConfigureCamelContext(ApplicationContext applicationContext, + CamelContext camelContext, + CamelConfigurationProperties config) throws Exception { + + if (ObjectHelper.isNotEmpty(config.getFileConfigurations())) { + Environment env = applicationContext.getEnvironment(); + if (env instanceof ConfigurableEnvironment) { + MutablePropertySources sources = ((ConfigurableEnvironment) env).getPropertySources(); + if (sources != null) { + if (!sources.contains("camel-file-configuration")) { + sources.addFirst(new FilePropertySource("camel-file-configuration", applicationContext, config.getFileConfigurations())); + } + } + } + } + + if (!config.isJmxEnabled()) { + camelContext.disableJMX(); + } + + if (config.getName() != null) { + ((SpringCamelContext) camelContext).setName(config.getName()); + } + + if (config.getShutdownTimeout() > 0) { + camelContext.getShutdownStrategy().setTimeout(config.getShutdownTimeout()); + } + camelContext.getShutdownStrategy().setSuppressLoggingOnTimeout(config.isShutdownSuppressLoggingOnTimeout()); + camelContext.getShutdownStrategy().setShutdownNowOnTimeout(config.isShutdownNowOnTimeout()); + camelContext.getShutdownStrategy().setShutdownRoutesInReverseOrder(config.isShutdownRoutesInReverseOrder()); + camelContext.getShutdownStrategy().setLogInflightExchangesOnTimeout(config.isShutdownLogInflightExchangesOnTimeout()); + + if (config.getLogDebugMaxChars() > 0) { + camelContext.getGlobalOptions().put(Exchange.LOG_DEBUG_BODY_MAX_CHARS, "" + config.getLogDebugMaxChars()); + } + + // stream caching + camelContext.setStreamCaching(config.isStreamCachingEnabled()); + camelContext.getStreamCachingStrategy().setAnySpoolRules(config.isStreamCachingAnySpoolRules()); + camelContext.getStreamCachingStrategy().setBufferSize(config.getStreamCachingBufferSize()); + camelContext.getStreamCachingStrategy().setRemoveSpoolDirectoryWhenStopping(config.isStreamCachingRemoveSpoolDirectoryWhenStopping()); + camelContext.getStreamCachingStrategy().setSpoolChiper(config.getStreamCachingSpoolChiper()); + if (config.getStreamCachingSpoolDirectory() != null) { + camelContext.getStreamCachingStrategy().setSpoolDirectory(config.getStreamCachingSpoolDirectory()); + } + if (config.getStreamCachingSpoolThreshold() != 0) { + camelContext.getStreamCachingStrategy().setSpoolThreshold(config.getStreamCachingSpoolThreshold()); + } + if (config.getStreamCachingSpoolUsedHeapMemoryLimit() != null) { + StreamCachingStrategy.SpoolUsedHeapMemoryLimit limit; + if ("Committed".equalsIgnoreCase(config.getStreamCachingSpoolUsedHeapMemoryLimit())) { + limit = StreamCachingStrategy.SpoolUsedHeapMemoryLimit.Committed; + } else if ("Max".equalsIgnoreCase(config.getStreamCachingSpoolUsedHeapMemoryLimit())) { + limit = StreamCachingStrategy.SpoolUsedHeapMemoryLimit.Max; + } else { + throw new IllegalArgumentException("Invalid option " + config.getStreamCachingSpoolUsedHeapMemoryLimit() + " must either be Committed or Max"); + } + camelContext.getStreamCachingStrategy().setSpoolUsedHeapMemoryLimit(limit); + } + if (config.getStreamCachingSpoolUsedHeapMemoryThreshold() != 0) { + camelContext.getStreamCachingStrategy().setSpoolUsedHeapMemoryThreshold(config.getStreamCachingSpoolUsedHeapMemoryThreshold()); + } + + camelContext.setMessageHistory(config.isMessageHistory()); + camelContext.setLogMask(config.isLogMask()); + camelContext.setLogExhaustedMessageBody(config.isLogExhaustedMessageBody()); + camelContext.setHandleFault(config.isHandleFault()); + camelContext.setAutoStartup(config.isAutoStartup()); + camelContext.setAllowUseOriginalMessage(config.isAllowUseOriginalMessage()); + camelContext.setUseBreadcrumb(config.isUseBreadcrumb()); + camelContext.setUseDataType(config.isUseDataType()); + camelContext.setUseMDCLogging(config.isUseMDCLogging()); + camelContext.setLoadTypeConverters(config.isLoadTypeConverters()); + + if (camelContext.getManagementStrategy().getManagementAgent() != null) { + camelContext.getManagementStrategy().getManagementAgent().setEndpointRuntimeStatisticsEnabled(config.isEndpointRuntimeStatisticsEnabled()); + camelContext.getManagementStrategy().getManagementAgent().setStatisticsLevel(config.getJmxManagementStatisticsLevel()); + camelContext.getManagementStrategy().getManagementAgent().setManagementNamePattern(config.getJmxManagementNamePattern()); + camelContext.getManagementStrategy().getManagementAgent().setCreateConnector(config.isJmxCreateConnector()); + } + + camelContext.setPackageScanClassResolver(new FatJarPackageScanClassResolver()); + + // tracing + camelContext.setTracing(config.isTracing()); + if (camelContext.getDefaultTracer() instanceof Tracer) { + Tracer tracer = (Tracer) camelContext.getDefaultTracer(); + if (tracer.getDefaultTraceFormatter() != null) { + DefaultTraceFormatter formatter = tracer.getDefaultTraceFormatter(); + if (config.getTracerFormatterBreadCrumbLength() != null) { + formatter.setBreadCrumbLength(config.getTracerFormatterBreadCrumbLength()); + } + if (config.getTracerFormatterMaxChars() != null) { + formatter.setMaxChars(config.getTracerFormatterMaxChars()); + } + if (config.getTracerFormatterNodeLength() != null) { + formatter.setNodeLength(config.getTracerFormatterNodeLength()); + } + formatter.setShowBody(config.isTraceFormatterShowBody()); + formatter.setShowBodyType(config.isTracerFormatterShowBodyType()); + formatter.setShowBreadCrumb(config.isTraceFormatterShowBreadCrumb()); + formatter.setShowException(config.isTraceFormatterShowException()); + formatter.setShowExchangeId(config.isTraceFormatterShowExchangeId()); + formatter.setShowExchangePattern(config.isTraceFormatterShowExchangePattern()); + formatter.setShowHeaders(config.isTraceFormatterShowHeaders()); + formatter.setShowNode(config.isTraceFormatterShowNode()); + formatter.setShowProperties(config.isTraceFormatterShowProperties()); + formatter.setShowRouteId(config.isTraceFormatterShowRouteId()); + formatter.setShowShortExchangeId(config.isTraceFormatterShowShortExchangeId()); + } + } + + if (config.getXmlRoutesReloadDirectory() != null) { + ReloadStrategy reload = new FileWatcherReloadStrategy(config.getXmlRoutesReloadDirectory()); + camelContext.setReloadStrategy(reload); + } + + // additional advanced configuration which is not configured using CamelConfigurationProperties + afterPropertiesSet(applicationContext, camelContext); + + return camelContext; + } + + @Bean + CamelSpringBootApplicationController applicationController(ApplicationContext applicationContext, CamelContext camelContext) { + return new CamelSpringBootApplicationController(applicationContext, camelContext); + } + + @Bean + @ConditionalOnMissingBean(RoutesCollector.class) + RoutesCollector routesCollector(ApplicationContext applicationContext, CamelConfigurationProperties config) { + Collection<CamelContextConfiguration> configurations = applicationContext.getBeansOfType(CamelContextConfiguration.class).values(); + return new RoutesCollector(applicationContext, new ArrayList<CamelContextConfiguration>(configurations), config); + } + + /** + * Default fluent producer template for the bootstrapped Camel context. + * Create the bean lazy as it should only be created if its in-use. + */ + // We explicitly declare the destroyMethod to be "" as the Spring @Bean + // annotation defaults to AbstractBeanDefinition.INFER_METHOD otherwise + // and in that case Service::close (FluentProducerTemplate implements Service) + // would be used for bean destruction. And we want Camel to handle the + // lifecycle. + @Bean(destroyMethod = "") + @ConditionalOnMissingBean(FluentProducerTemplate.class) + @Lazy + FluentProducerTemplate fluentProducerTemplate(CamelContext camelContext, + CamelConfigurationProperties config) throws Exception { + final FluentProducerTemplate fluentProducerTemplate = camelContext.createFluentProducerTemplate(config.getProducerTemplateCacheSize()); + // we add this fluentProducerTemplate as a Service to CamelContext so that it performs proper lifecycle (start and stop) + camelContext.addService(fluentProducerTemplate); + return fluentProducerTemplate; + } + + /** + * Default producer template for the bootstrapped Camel context. + * Create the bean lazy as it should only be created if its in-use. + */ + // We explicitly declare the destroyMethod to be "" as the Spring @Bean + // annotation defaults to AbstractBeanDefinition.INFER_METHOD otherwise + // and in that case Service::close (ProducerTemplate implements Service) + // would be used for bean destruction. And we want Camel to handle the + // lifecycle. + @Bean(destroyMethod = "") + @ConditionalOnMissingBean(ProducerTemplate.class) + @Lazy + ProducerTemplate producerTemplate(CamelContext camelContext, + CamelConfigurationProperties config) throws Exception { + final ProducerTemplate producerTemplate = camelContext.createProducerTemplate(config.getProducerTemplateCacheSize()); + // we add this producerTemplate as a Service to CamelContext so that it performs proper lifecycle (start and stop) + camelContext.addService(producerTemplate); + return producerTemplate; + } + + /** + * Default consumer template for the bootstrapped Camel context. + * Create the bean lazy as it should only be created if its in-use. + */ + // We explicitly declare the destroyMethod to be "" as the Spring @Bean + // annotation defaults to AbstractBeanDefinition.INFER_METHOD otherwise + // and in that case Service::close (ConsumerTemplate implements Service) + // would be used for bean destruction. And we want Camel to handle the + // lifecycle. + @Bean(destroyMethod = "") + @ConditionalOnMissingBean(ConsumerTemplate.class) + @Lazy + ConsumerTemplate consumerTemplate(CamelContext camelContext, + CamelConfigurationProperties config) throws Exception { + final ConsumerTemplate consumerTemplate = camelContext.createConsumerTemplate(config.getConsumerTemplateCacheSize()); + // we add this consumerTemplate as a Service to CamelContext so that it performs proper lifecycle (start and stop) + camelContext.addService(consumerTemplate); + return consumerTemplate; + } + + // SpringCamelContext integration + + @Bean + PropertiesParser propertiesParser() { + return new SpringPropertiesParser(); + } + + // We explicitly declare the destroyMethod to be "" as the Spring @Bean + // annotation defaults to AbstractBeanDefinition.INFER_METHOD otherwise + // and in that case ShutdownableService::shutdown/Service::close + // (PropertiesComponent extends ServiceSupport) would be used for bean + // destruction. And we want Camel to handle the lifecycle. + @Bean(destroyMethod = "") + PropertiesComponent properties(CamelContext camelContext, PropertiesParser parser) { + if (camelContext.hasComponent("properties") != null) { + return camelContext.getComponent("properties", PropertiesComponent.class); + } else { + PropertiesComponent pc = new PropertiesComponent(); + pc.setPropertiesParser(parser); + return pc; + } + } + + /** + * Camel post processor - required to support Camel annotations. + */ + @Bean + CamelBeanPostProcessor camelBeanPostProcessor(ApplicationContext applicationContext) { + CamelBeanPostProcessor processor = new CamelBeanPostProcessor(); + processor.setApplicationContext(applicationContext); + return processor; + } + + /** + * Performs additional configuration to lookup beans of Camel types to configure + * advanced configurations. + * <p/> + * Similar code in camel-core-xml module in class org.apache.camel.core.xml.AbstractCamelContextFactoryBean. + */ + static void afterPropertiesSet(ApplicationContext applicationContext, CamelContext camelContext) throws Exception { + Tracer tracer = getSingleBeanOfType(applicationContext, Tracer.class); + if (tracer != null) { + // use formatter if there is a TraceFormatter bean defined + TraceFormatter formatter = getSingleBeanOfType(applicationContext, TraceFormatter.class); + if (formatter != null) { + tracer.setFormatter(formatter); + } + LOG.info("Using custom Tracer: {}", tracer); + camelContext.addInterceptStrategy(tracer); + } + BacklogTracer backlogTracer = getSingleBeanOfType(applicationContext, BacklogTracer.class); + if (backlogTracer != null) { + LOG.info("Using custom BacklogTracer: {}", backlogTracer); + camelContext.addInterceptStrategy(backlogTracer); + } + HandleFault handleFault = getSingleBeanOfType(applicationContext, HandleFault.class); + if (handleFault != null) { + LOG.info("Using custom HandleFault: {}", handleFault); + camelContext.addInterceptStrategy(handleFault); + } + InflightRepository inflightRepository = getSingleBeanOfType(applicationContext, InflightRepository.class); + if (inflightRepository != null) { + LOG.info("Using custom InflightRepository: {}", inflightRepository); + camelContext.setInflightRepository(inflightRepository); + } + AsyncProcessorAwaitManager asyncProcessorAwaitManager = getSingleBeanOfType(applicationContext, AsyncProcessorAwaitManager.class); + if (asyncProcessorAwaitManager != null) { + LOG.info("Using custom AsyncProcessorAwaitManager: {}", asyncProcessorAwaitManager); + camelContext.setAsyncProcessorAwaitManager(asyncProcessorAwaitManager); + } + ManagementStrategy managementStrategy = getSingleBeanOfType(applicationContext, ManagementStrategy.class); + if (managementStrategy != null) { + LOG.info("Using custom ManagementStrategy: {}", managementStrategy); + camelContext.setManagementStrategy(managementStrategy); + } + ManagementNamingStrategy managementNamingStrategy = getSingleBeanOfType(applicationContext, ManagementNamingStrategy.class); + if (managementNamingStrategy != null) { + LOG.info("Using custom ManagementNamingStrategy: {}", managementNamingStrategy); + camelContext.getManagementStrategy().setManagementNamingStrategy(managementNamingStrategy); + } + EventFactory eventFactory = getSingleBeanOfType(applicationContext, EventFactory.class); + if (eventFactory != null) { + LOG.info("Using custom EventFactory: {}", eventFactory); + camelContext.getManagementStrategy().setEventFactory(eventFactory); + } + UnitOfWorkFactory unitOfWorkFactory = getSingleBeanOfType(applicationContext, UnitOfWorkFactory.class); + if (unitOfWorkFactory != null) { + LOG.info("Using custom UnitOfWorkFactory: {}", unitOfWorkFactory); + camelContext.setUnitOfWorkFactory(unitOfWorkFactory); + } + RuntimeEndpointRegistry runtimeEndpointRegistry = getSingleBeanOfType(applicationContext, RuntimeEndpointRegistry.class); + if (runtimeEndpointRegistry != null) { + LOG.info("Using custom RuntimeEndpointRegistry: {}", runtimeEndpointRegistry); + camelContext.setRuntimeEndpointRegistry(runtimeEndpointRegistry); + } + // custom type converters defined as <bean>s + Map<String, TypeConverters> typeConverters = applicationContext.getBeansOfType(TypeConverters.class); + if (typeConverters != null && !typeConverters.isEmpty()) { + for (Map.Entry<String, TypeConverters> entry : typeConverters.entrySet()) { + TypeConverters converter = entry.getValue(); + LOG.info("Adding custom TypeConverters with id: {} and implementation: {}", entry.getKey(), converter); + camelContext.getTypeConverterRegistry().addTypeConverters(converter); + } + } + // set the event notifier strategies if defined + Map<String, EventNotifier> eventNotifiers = applicationContext.getBeansOfType(EventNotifier.class); + if (eventNotifiers != null && !eventNotifiers.isEmpty()) { + for (Map.Entry<String, EventNotifier> entry : eventNotifiers.entrySet()) { + EventNotifier notifier = entry.getValue(); + // do not add if already added, for instance a tracer that is also an InterceptStrategy class + if (!camelContext.getManagementStrategy().getEventNotifiers().contains(notifier)) { + LOG.info("Using custom EventNotifier with id: {} and implementation: {}", entry.getKey(), notifier); + camelContext.getManagementStrategy().addEventNotifier(notifier); + } + } + } + // set endpoint strategies if defined + Map<String, EndpointStrategy> endpointStrategies = applicationContext.getBeansOfType(EndpointStrategy.class); + if (endpointStrategies != null && !endpointStrategies.isEmpty()) { + for (Map.Entry<String, EndpointStrategy> entry : endpointStrategies.entrySet()) { + EndpointStrategy strategy = entry.getValue(); + LOG.info("Using custom EndpointStrategy with id: {} and implementation: {}", entry.getKey(), strategy); + camelContext.addRegisterEndpointCallback(strategy); + } + } + // shutdown + ShutdownStrategy shutdownStrategy = getSingleBeanOfType(applicationContext, ShutdownStrategy.class); + if (shutdownStrategy != null) { + LOG.info("Using custom ShutdownStrategy: " + shutdownStrategy); + camelContext.setShutdownStrategy(shutdownStrategy); + } + // add global interceptors + Map<String, InterceptStrategy> interceptStrategies = applicationContext.getBeansOfType(InterceptStrategy.class); + if (interceptStrategies != null && !interceptStrategies.isEmpty()) { + for (Map.Entry<String, InterceptStrategy> entry : interceptStrategies.entrySet()) { + InterceptStrategy strategy = entry.getValue(); + // do not add if already added, for instance a tracer that is also an InterceptStrategy class + if (!camelContext.getInterceptStrategies().contains(strategy)) { + LOG.info("Using custom InterceptStrategy with id: {} and implementation: {}", entry.getKey(), strategy); + camelContext.addInterceptStrategy(strategy); + } + } + } + // set the lifecycle strategy if defined + Map<String, LifecycleStrategy> lifecycleStrategies = applicationContext.getBeansOfType(LifecycleStrategy.class); + if (lifecycleStrategies != null && !lifecycleStrategies.isEmpty()) { + for (Map.Entry<String, LifecycleStrategy> entry : lifecycleStrategies.entrySet()) { + LifecycleStrategy strategy = entry.getValue(); + // do not add if already added, for instance a tracer that is also an InterceptStrategy class + if (!camelContext.getLifecycleStrategies().contains(strategy)) { + LOG.info("Using custom LifecycleStrategy with id: {} and implementation: {}", entry.getKey(), strategy); + camelContext.addLifecycleStrategy(strategy); + } + } + } + // cluster service + CamelClusterService clusterService = getSingleBeanOfType(applicationContext, CamelClusterService.class); + if (clusterService != null) { + LOG.info("Using CamelClusterService: " + clusterService); + camelContext.addService(clusterService); + } + // add route policy factories + Map<String, RoutePolicyFactory> routePolicyFactories = applicationContext.getBeansOfType(RoutePolicyFactory.class); + if (routePolicyFactories != null && !routePolicyFactories.isEmpty()) { + for (Map.Entry<String, RoutePolicyFactory> entry : routePolicyFactories.entrySet()) { + RoutePolicyFactory factory = entry.getValue(); + LOG.info("Using custom RoutePolicyFactory with id: {} and implementation: {}", entry.getKey(), factory); + camelContext.addRoutePolicyFactory(factory); + } + } + // add SSL context parameters + GlobalSSLContextParametersSupplier sslContextParametersSupplier = getSingleBeanOfType(applicationContext, GlobalSSLContextParametersSupplier.class); + if (sslContextParametersSupplier != null) { + camelContext.setSSLContextParameters(sslContextParametersSupplier.get()); + } + + // Route controller + RouteController routeController = getSingleBeanOfType(applicationContext, RouteController.class); + if (routeController != null) { + LOG.info("Using RouteController: " + routeController); + camelContext.setRouteController(routeController); + } + + // set the default thread pool profile if defined + initThreadPoolProfiles(applicationContext, camelContext); + } + + private static void initThreadPoolProfiles(ApplicationContext applicationContext, CamelContext camelContext) { + Set<String> defaultIds = new HashSet<String>(); + + // lookup and use custom profiles from the registry + Map<String, ThreadPoolProfile> profiles = applicationContext.getBeansOfType(ThreadPoolProfile.class); + if (profiles != null && !profiles.isEmpty()) { + for (Map.Entry<String, ThreadPoolProfile> entry : profiles.entrySet()) { + ThreadPoolProfile profile = entry.getValue(); + // do not add if already added, for instance a tracer that is also an InterceptStrategy class + if (profile.isDefaultProfile()) { + LOG.info("Using custom default ThreadPoolProfile with id: {} and implementation: {}", entry.getKey(), profile); + camelContext.getExecutorServiceManager().setDefaultThreadPoolProfile(profile); + defaultIds.add(entry.getKey()); + } else { + camelContext.getExecutorServiceManager().registerThreadPoolProfile(profile); + } + } + } + + // validate at most one is defined + if (defaultIds.size() > 1) { + throw new IllegalArgumentException("Only exactly one default ThreadPoolProfile is allowed, was " + defaultIds.size() + " ids: " + defaultIds); + } + } + + private static <T> T getSingleBeanOfType(ApplicationContext applicationContext, Class<T> type) { + Map<String, T> beans = applicationContext.getBeansOfType(type); + if (beans.size() == 1) { + return beans.values().iterator().next(); + } else { + return null; + } + } + +}