CAMEL-9720: camel-spring-boot - Make it easy to keep the JVM running
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/28c83d58 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/28c83d58 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/28c83d58 Branch: refs/heads/master Commit: 28c83d58db7c50be9aecbcd81b45af5004bcb304 Parents: 89218fd Author: Claus Ibsen <[email protected]> Authored: Thu Mar 17 11:15:22 2016 +0100 Committer: Claus Ibsen <[email protected]> Committed: Thu Mar 17 11:54:32 2016 +0100 ---------------------------------------------------------------------- .../sample/camel/SampleCamelApplication.java | 8 +--- .../java/sample/camel/SampleCamelRouter.java | 2 +- .../src/main/resources/application.properties | 15 ++++++- .../boot/CamelConfigurationProperties.java | 13 ++++++ .../spring/boot/CamelMainRunController.java | 47 ++++++++++++++++++++ .../CamelSpringBootApplicationController.java | 45 ++++++++++++++++--- .../apache/camel/spring/boot/FatJarRouter.java | 2 +- .../camel/spring/boot/RoutesCollector.java | 24 +++++++--- 8 files changed, 133 insertions(+), 23 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/28c83d58/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/java/sample/camel/SampleCamelApplication.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/java/sample/camel/SampleCamelApplication.java b/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/java/sample/camel/SampleCamelApplication.java index fe4e4dd..8a68b69 100644 --- a/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/java/sample/camel/SampleCamelApplication.java +++ b/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/java/sample/camel/SampleCamelApplication.java @@ -16,19 +16,13 @@ */ package sample.camel; -import org.apache.camel.spring.boot.CamelSpringBootApplicationController; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class SampleCamelApplication { public static void main(String[] args) { - ConfigurableApplicationContext ctx = new SpringApplicationBuilder().sources(SampleCamelApplication.class).run(args); - - // keep the JVM running as Camel uses only daemon threads in the sample - CamelSpringBootApplicationController controller = ctx.getBean(CamelSpringBootApplicationController.class); - controller.blockMainThread(); + new SpringApplicationBuilder().sources(SampleCamelApplication.class).run(args).registerShutdownHook(); } } http://git-wip-us.apache.org/repos/asf/camel/blob/28c83d58/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/java/sample/camel/SampleCamelRouter.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/java/sample/camel/SampleCamelRouter.java b/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/java/sample/camel/SampleCamelRouter.java index d3c2dd4..5ce9cb7 100644 --- a/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/java/sample/camel/SampleCamelRouter.java +++ b/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/java/sample/camel/SampleCamelRouter.java @@ -22,7 +22,7 @@ public class SampleCamelRouter extends RouteBuilder { @Override public void configure() throws Exception { - from("timer:{{timer.name}}?period={{timer.period}}") + from("timer:hello?period={{timer.period}}") .transform(method("myBean", "saySomething")) .to("stream:out"); } http://git-wip-us.apache.org/repos/asf/camel/blob/28c83d58/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/resources/application.properties ---------------------------------------------------------------------- diff --git a/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/resources/application.properties b/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/resources/application.properties index eb5bdee..9270963 100644 --- a/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/resources/application.properties +++ b/components/camel-spring-boot-starter/camel-spring-boot-sample/src/main/resources/application.properties @@ -17,7 +17,20 @@ spring.main.sources: sample.camel.SampleCamelRouter + +# the name of Camel +camel.springboot.name = SampleCamel + +# we want the main thread to keep running +camel.springboot.main-run-controller = true + + + +# properties used in the Camel route and beans +# -------------------------------------------- + +# what to say greeting = Hello World -timer.name = myTimer +# how often to trigger the timer timer.period = 2000 http://git-wip-us.apache.org/repos/asf/camel/blob/28c83d58/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelConfigurationProperties.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelConfigurationProperties.java b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelConfigurationProperties.java index c8f1793..41b90df 100644 --- a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelConfigurationProperties.java +++ b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelConfigurationProperties.java @@ -60,6 +60,12 @@ public class CamelConfigurationProperties { */ private String xmlRests = "classpath:camel-rest/*.xml"; + /** + * Whether to use the main run controller to ensure the Spring-Boot application + * keeps running until being stopped or the JVM terminated. + */ + private boolean mainRunController; + // Getters & setters public boolean isJmxEnabled() { @@ -118,4 +124,11 @@ public class CamelConfigurationProperties { this.xmlRests = xmlRests; } + public boolean isMainRunController() { + return mainRunController; + } + + public void setMainRunController(boolean mainRunController) { + this.mainRunController = mainRunController; + } } http://git-wip-us.apache.org/repos/asf/camel/blob/28c83d58/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelMainRunController.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelMainRunController.java b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelMainRunController.java new file mode 100644 index 0000000..a09ade4 --- /dev/null +++ b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelMainRunController.java @@ -0,0 +1,47 @@ +/** + * 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 org.apache.camel.CamelContext; +import org.springframework.context.ApplicationContext; + +/** + * Controller to keep the main running and perform graceful shutdown when the JVM is stopped. + */ +public class CamelMainRunController { + + private final CamelSpringBootApplicationController controller; + private final Thread daemon; + + public CamelMainRunController(ApplicationContext applicationContext, CamelContext camelContext) { + controller = new CamelSpringBootApplicationController(applicationContext, camelContext); + daemon = new Thread(new DaemonTask(), "CamelMainRunController"); + } + + public void start() { + daemon.run(); + } + + private final class DaemonTask implements Runnable { + + @Override + public void run() { + controller.run(); + } + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/28c83d58/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelSpringBootApplicationController.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelSpringBootApplicationController.java b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelSpringBootApplicationController.java index abafd8a..bbca474 100644 --- a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelSpringBootApplicationController.java +++ b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelSpringBootApplicationController.java @@ -18,20 +18,25 @@ package org.apache.camel.spring.boot; import java.util.Collections; import java.util.Map; - +import java.util.concurrent.CountDownLatch; import javax.annotation.PreDestroy; import org.apache.camel.CamelContext; import org.apache.camel.ProducerTemplate; -import org.apache.camel.main.MainSupport; +import org.apache.camel.main.Main; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; public class CamelSpringBootApplicationController { - private final MainSupport mainSupport; + private static final Logger LOG = LoggerFactory.getLogger(CamelSpringBootApplicationController.class); + + private final Main main; + private final CountDownLatch latch = new CountDownLatch(1); public CamelSpringBootApplicationController(final ApplicationContext applicationContext, final CamelContext camelContext) { - this.mainSupport = new MainSupport() { + this.main = new Main() { @Override protected ProducerTemplate findOrCreateCamelTemplate() { return applicationContext.getBean(ProducerTemplate.class); @@ -41,20 +46,46 @@ public class CamelSpringBootApplicationController { protected Map<String, CamelContext> getCamelContextMap() { return Collections.singletonMap("camelContext", camelContext); } + + @Override + protected void doStop() throws Exception { + LOG.debug("Controller is shutting down CamelContext"); + try { + super.doStop(); + } finally { + latch.countDown(); + } + } }; } - public void blockMainThread() { + /** + * Runs the application and blocks the main thread and shutdown Camel graceful when the JVM is stopping. + */ + public void run() { + LOG.debug("Controller is starting and waiting for Spring-Boot to stop or JVM to terminate"); try { - mainSupport.run(); + main.run(); + // keep the daemon thread running + LOG.debug("Waiting for CamelContext to complete shutdown"); + latch.await(); } catch (Exception e) { throw new RuntimeException(e); } + LOG.debug("CamelContext shutdown complete."); + } + + /** + * @deprecated use {@link #run()} + */ + @Deprecated + public void blockMainThread() { + run(); } @PreDestroy private void destroy() { - mainSupport.completed(); + main.completed(); } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/28c83d58/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/FatJarRouter.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/FatJarRouter.java b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/FatJarRouter.java index 94458ef..f5a376c 100644 --- a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/FatJarRouter.java +++ b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/FatJarRouter.java @@ -26,7 +26,7 @@ public class FatJarRouter extends RouteBuilder { ApplicationContext applicationContext = new SpringApplication(FatJarRouter.class).run(args); CamelSpringBootApplicationController applicationController = applicationContext.getBean(CamelSpringBootApplicationController.class); - applicationController.blockMainThread(); + applicationController.run(); } @Override http://git-wip-us.apache.org/repos/asf/camel/blob/28c83d58/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/RoutesCollector.java ---------------------------------------------------------------------- diff --git a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/RoutesCollector.java b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/RoutesCollector.java index 61b5378..6d589e5 100644 --- a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/RoutesCollector.java +++ b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/RoutesCollector.java @@ -64,11 +64,13 @@ public class RoutesCollector implements ApplicationListener<ContextRefreshedEven // Overridden @Override - public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { - ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext(); + public void onApplicationEvent(ContextRefreshedEvent event) { + ApplicationContext applicationContext = event.getApplicationContext(); + // only listen to context refresh of "my" applicationContext if (this.applicationContext.equals(applicationContext)) { - CamelContext camelContext = contextRefreshedEvent.getApplicationContext().getBean(CamelContext.class); + + CamelContext camelContext = event.getApplicationContext().getBean(CamelContext.class); // only add and start Camel if its stopped (initial state) if (camelContext.getStatus().isStopped()) { @@ -100,16 +102,26 @@ public class RoutesCollector implements ApplicationListener<ContextRefreshedEven } for (CamelContextConfiguration camelContextConfiguration : camelContextConfigurations) { - LOG.debug("CamelContextConfiguration found. Invoking: {}", camelContextConfiguration); + LOG.debug("CamelContextConfiguration found. Invoking beforeApplicationStart: {}", camelContextConfiguration); camelContextConfiguration.beforeApplicationStart(camelContext); } - camelContext.start(); + if (configurationProperties.isMainRunController()) { + LOG.info("Starting CamelMainRunController to ensure the main thread keeps running"); + CamelMainRunController controller = new CamelMainRunController(applicationContext, camelContext); + // controller will start Camel + controller.start(); + } else { + // start camel manually + camelContext.start(); + } for (CamelContextConfiguration camelContextConfiguration : camelContextConfigurations) { + LOG.debug("CamelContextConfiguration found. Invoking afterApplicationStart: {}", camelContextConfiguration); camelContextConfiguration.afterApplicationStart(camelContext); } + } catch (Exception e) { throw new CamelSpringBootInitializationException(e); } @@ -117,7 +129,7 @@ public class RoutesCollector implements ApplicationListener<ContextRefreshedEven LOG.debug("Camel already started, not adding routes."); } } else { - LOG.debug("Ignore ContextRefreshedEvent: {}", contextRefreshedEvent); + LOG.debug("Ignore ContextRefreshedEvent: {}", event); } }
