Skip to content

Commit

Permalink
CAMEL-9720: camel-spring-boot - Make it easy to keep the JVM running
Browse files Browse the repository at this point in the history
  • Loading branch information
davsclaus committed Mar 17, 2016
1 parent 89218fd commit 28c83d5
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 23 deletions.
Expand Up @@ -16,19 +16,13 @@
*/ */
package sample.camel; package sample.camel;


import org.apache.camel.spring.boot.CamelSpringBootApplicationController;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;


@SpringBootApplication @SpringBootApplication
public class SampleCamelApplication { public class SampleCamelApplication {


public static void main(String[] args) { public static void main(String[] args) {
ConfigurableApplicationContext ctx = new SpringApplicationBuilder().sources(SampleCamelApplication.class).run(args); new SpringApplicationBuilder().sources(SampleCamelApplication.class).run(args).registerShutdownHook();

// keep the JVM running as Camel uses only daemon threads in the sample
CamelSpringBootApplicationController controller = ctx.getBean(CamelSpringBootApplicationController.class);
controller.blockMainThread();
} }
} }
Expand Up @@ -22,7 +22,7 @@ public class SampleCamelRouter extends RouteBuilder {


@Override @Override
public void configure() throws Exception { public void configure() throws Exception {
from("timer:{{timer.name}}?period={{timer.period}}") from("timer:hello?period={{timer.period}}")
.transform(method("myBean", "saySomething")) .transform(method("myBean", "saySomething"))
.to("stream:out"); .to("stream:out");
} }
Expand Down
Expand Up @@ -17,7 +17,20 @@


spring.main.sources: sample.camel.SampleCamelRouter 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 greeting = Hello World


timer.name = myTimer # how often to trigger the timer
timer.period = 2000 timer.period = 2000
Expand Up @@ -60,6 +60,12 @@ public class CamelConfigurationProperties {
*/ */
private String xmlRests = "classpath:camel-rest/*.xml"; 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 // Getters & setters


public boolean isJmxEnabled() { public boolean isJmxEnabled() {
Expand Down Expand Up @@ -118,4 +124,11 @@ public void setXmlRests(String xmlRests) {
this.xmlRests = xmlRests; this.xmlRests = xmlRests;
} }


public boolean isMainRunController() {
return mainRunController;
}

public void setMainRunController(boolean mainRunController) {
this.mainRunController = mainRunController;
}
} }
@@ -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();
}
}

}
Expand Up @@ -18,20 +18,25 @@


import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;

import java.util.concurrent.CountDownLatch;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;


import org.apache.camel.CamelContext; import org.apache.camel.CamelContext;
import org.apache.camel.ProducerTemplate; 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; import org.springframework.context.ApplicationContext;


public class CamelSpringBootApplicationController { 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) { public CamelSpringBootApplicationController(final ApplicationContext applicationContext, final CamelContext camelContext) {
this.mainSupport = new MainSupport() { this.main = new Main() {
@Override @Override
protected ProducerTemplate findOrCreateCamelTemplate() { protected ProducerTemplate findOrCreateCamelTemplate() {
return applicationContext.getBean(ProducerTemplate.class); return applicationContext.getBean(ProducerTemplate.class);
Expand All @@ -41,20 +46,46 @@ protected ProducerTemplate findOrCreateCamelTemplate() {
protected Map<String, CamelContext> getCamelContextMap() { protected Map<String, CamelContext> getCamelContextMap() {
return Collections.singletonMap("camelContext", camelContext); 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 { try {
mainSupport.run(); main.run();
// keep the daemon thread running
LOG.debug("Waiting for CamelContext to complete shutdown");
latch.await();
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
LOG.debug("CamelContext shutdown complete.");
}

/**
* @deprecated use {@link #run()}
*/
@Deprecated
public void blockMainThread() {
run();
} }


@PreDestroy @PreDestroy
private void destroy() { private void destroy() {
mainSupport.completed(); main.completed();
} }


} }
Expand Up @@ -26,7 +26,7 @@ public static void main(String... args) {
ApplicationContext applicationContext = new SpringApplication(FatJarRouter.class).run(args); ApplicationContext applicationContext = new SpringApplication(FatJarRouter.class).run(args);
CamelSpringBootApplicationController applicationController = CamelSpringBootApplicationController applicationController =
applicationContext.getBean(CamelSpringBootApplicationController.class); applicationContext.getBean(CamelSpringBootApplicationController.class);
applicationController.blockMainThread(); applicationController.run();
} }


@Override @Override
Expand Down
Expand Up @@ -64,11 +64,13 @@ public RoutesCollector(ApplicationContext applicationContext, List<CamelContextC
// Overridden // Overridden


@Override @Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext(); ApplicationContext applicationContext = event.getApplicationContext();

// only listen to context refresh of "my" applicationContext // only listen to context refresh of "my" applicationContext
if (this.applicationContext.equals(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) // only add and start Camel if its stopped (initial state)
if (camelContext.getStatus().isStopped()) { if (camelContext.getStatus().isStopped()) {
Expand Down Expand Up @@ -100,24 +102,34 @@ public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
} }


for (CamelContextConfiguration camelContextConfiguration : camelContextConfigurations) { for (CamelContextConfiguration camelContextConfiguration : camelContextConfigurations) {
LOG.debug("CamelContextConfiguration found. Invoking: {}", camelContextConfiguration); LOG.debug("CamelContextConfiguration found. Invoking beforeApplicationStart: {}", camelContextConfiguration);
camelContextConfiguration.beforeApplicationStart(camelContext); 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) { for (CamelContextConfiguration camelContextConfiguration : camelContextConfigurations) {
LOG.debug("CamelContextConfiguration found. Invoking afterApplicationStart: {}", camelContextConfiguration);
camelContextConfiguration.afterApplicationStart(camelContext); camelContextConfiguration.afterApplicationStart(camelContext);
} }



} catch (Exception e) { } catch (Exception e) {
throw new CamelSpringBootInitializationException(e); throw new CamelSpringBootInitializationException(e);
} }
} else { } else {
LOG.debug("Camel already started, not adding routes."); LOG.debug("Camel already started, not adding routes.");
} }
} else { } else {
LOG.debug("Ignore ContextRefreshedEvent: {}", contextRefreshedEvent); LOG.debug("Ignore ContextRefreshedEvent: {}", event);
} }
} }


Expand Down

0 comments on commit 28c83d5

Please sign in to comment.