You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2017/03/02 17:39:01 UTC

camel git commit: CAMEL-10596: Allow camel spring-boot to auto terminate JVM after being idle for N seconds.

Repository: camel
Updated Branches:
  refs/heads/master c4d845acf -> 00ffe70a9


CAMEL-10596: Allow camel spring-boot to auto terminate JVM after being idle for N seconds.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/00ffe70a
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/00ffe70a
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/00ffe70a

Branch: refs/heads/master
Commit: 00ffe70a97f0559ae3f42194b40aeefa323a8d18
Parents: c4d845a
Author: Claus Ibsen <da...@apache.org>
Authored: Thu Mar 2 18:23:31 2017 +0100
Committer: Claus Ibsen <da...@apache.org>
Committed: Thu Mar 2 18:38:53 2017 +0100

----------------------------------------------------------------------
 .../camel/main/MainDurationEventNotifier.java   | 98 +++++++++++++++++---
 .../java/org/apache/camel/main/MainSupport.java | 57 +++++++++---
 .../org/apache/camel/impl/MainSupportTest.java  |  4 +-
 .../boot/CamelConfigurationProperties.java      | 14 +++
 .../camel/spring/boot/RoutesCollector.java      | 36 ++++---
 .../src/main/resources/application.properties   |  3 +
 .../java/org/apache/camel/maven/RunMojo.java    | 19 +++-
 7 files changed, 191 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/00ffe70a/camel-core/src/main/java/org/apache/camel/main/MainDurationEventNotifier.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/main/MainDurationEventNotifier.java b/camel-core/src/main/java/org/apache/camel/main/MainDurationEventNotifier.java
index 0b43f1e..e8e6bbe 100644
--- a/camel-core/src/main/java/org/apache/camel/main/MainDurationEventNotifier.java
+++ b/camel-core/src/main/java/org/apache/camel/main/MainDurationEventNotifier.java
@@ -18,12 +18,16 @@ package org.apache.camel.main;
 
 import java.util.EventObject;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.management.event.ExchangeCompletedEvent;
+import org.apache.camel.management.event.ExchangeCreatedEvent;
 import org.apache.camel.management.event.ExchangeFailedEvent;
 import org.apache.camel.support.EventNotifierSupport;
+import org.apache.camel.util.StopWatch;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -36,15 +40,20 @@ public class MainDurationEventNotifier extends EventNotifierSupport {
     private static final Logger LOG = LoggerFactory.getLogger(MainLifecycleStrategy.class);
     private final CamelContext camelContext;
     private final int maxMessages;
+    private final long maxIdleSeconds;
     private final AtomicBoolean completed;
     private final CountDownLatch latch;
     private final boolean stopCamelContext;
 
     private volatile int doneMessages;
+    private volatile StopWatch watch;
+    private volatile ScheduledExecutorService executorService;
 
-    public MainDurationEventNotifier(CamelContext camelContext, int maxMessages, AtomicBoolean completed, CountDownLatch latch, boolean stopCamelContext) {
+    public MainDurationEventNotifier(CamelContext camelContext, int maxMessages, long maxIdleSeconds,
+                                     AtomicBoolean completed, CountDownLatch latch, boolean stopCamelContext) {
         this.camelContext = camelContext;
         this.maxMessages = maxMessages;
+        this.maxIdleSeconds = maxIdleSeconds;
         this.completed = completed;
         this.latch = latch;
         this.stopCamelContext = stopCamelContext;
@@ -52,19 +61,38 @@ public class MainDurationEventNotifier extends EventNotifierSupport {
 
     @Override
     public void notify(EventObject event) throws Exception {
-        doneMessages++;
-
-        if (maxMessages > 0 && doneMessages >= maxMessages) {
-            if (completed.compareAndSet(false, true)) {
-                LOG.info("Duration max messages triggering shutdown of the JVM.");
-                // shutting down CamelContext
-                if (stopCamelContext) {
-                    camelContext.stop();
+        boolean begin = event instanceof ExchangeCreatedEvent;
+        boolean complete = event instanceof ExchangeCompletedEvent || event instanceof ExchangeFailedEvent;
+
+        if (maxMessages > 0 && complete) {
+            doneMessages++;
+
+            boolean result = doneMessages >= maxMessages;
+            LOG.trace("Duration max messages check {} >= {} -> {}", doneMessages, maxMessages, result);
+
+            if (result) {
+                if (completed.compareAndSet(false, true)) {
+                    LOG.info("Duration max messages triggering shutdown of the JVM.");
+                    try {
+                        // shutting down CamelContext
+                        if (stopCamelContext) {
+                            camelContext.stop();
+                        }
+                    } catch (Exception e) {
+                        LOG.warn("Error during stopping CamelContext. This exception is ignored.", e);
+                    } finally {
+                        // trigger stopping the Main
+                        latch.countDown();
+                    }
                 }
-                // trigger stopping the Main
-                latch.countDown();
             }
         }
+
+        // idle reacts on both incoming and complete messages
+        if (maxIdleSeconds > 0 && (begin || complete)) {
+            LOG.trace("Message activity so restarting stop watch");
+            watch.restart();
+        }
     }
 
     @Override
@@ -76,4 +104,52 @@ public class MainDurationEventNotifier extends EventNotifierSupport {
     public String toString() {
         return "MainDurationEventNotifier[" + maxMessages + " max messages]";
     }
+
+    @Override
+    protected void doStart() throws Exception {
+        if (maxIdleSeconds > 0) {
+
+            // we only start watch when Camel is started
+            camelContext.addStartupListener((context, alreadyStarted) -> watch = new StopWatch());
+
+            // okay we need to trigger on idle after X period, and therefore we need a background task that checks this
+            executorService = camelContext.getExecutorServiceManager().newSingleThreadScheduledExecutor(this, "MainDurationIdleChecker");
+            Runnable task = () -> {
+                if (watch == null) {
+                    // camel has not been started yet
+                    return;
+                }
+
+                // any inflight messages currently
+                int inflight = camelContext.getInflightRepository().size();
+                if (inflight > 0) {
+                    LOG.trace("Duration max idle check is skipped due {} inflight messages", inflight);
+                    return;
+                }
+
+                long seconds = watch.taken() / 1000;
+                boolean result = seconds >= maxIdleSeconds;
+                LOG.trace("Duration max idle check {} >= {} -> {}", seconds, maxIdleSeconds, result);
+
+                if (result) {
+                    if (completed.compareAndSet(false, true)) {
+                        LOG.info("Duration max idle triggering shutdown of the JVM.");
+                        try {
+                            // shutting down CamelContext
+                            if (stopCamelContext) {
+                                camelContext.stop();
+                            }
+                        } catch (Exception e) {
+                            LOG.warn("Error during stopping CamelContext. This exception is ignored.", e);
+                        } finally {
+                            // trigger stopping the Main
+                            latch.countDown();
+                        }
+                    }
+                }
+            };
+            executorService.scheduleAtFixedRate(task, 1,1, TimeUnit.SECONDS);
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/00ffe70a/camel-core/src/main/java/org/apache/camel/main/MainSupport.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/main/MainSupport.java b/camel-core/src/main/java/org/apache/camel/main/MainSupport.java
index 5baa669..6c57d82 100644
--- a/camel-core/src/main/java/org/apache/camel/main/MainSupport.java
+++ b/camel-core/src/main/java/org/apache/camel/main/MainSupport.java
@@ -20,7 +20,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
@@ -32,7 +31,6 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.ProducerTemplate;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.impl.DefaultModelJAXBContextFactory;
-import org.apache.camel.impl.DurationRoutePolicyFactory;
 import org.apache.camel.impl.FileWatcherReloadStrategy;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.spi.EventNotifier;
@@ -58,8 +56,9 @@ public abstract class MainSupport extends ServiceSupport {
     protected final AtomicBoolean completed = new AtomicBoolean(false);
     protected final AtomicInteger exitCode = new AtomicInteger(UNINITIALIZED_EXIT_CODE);
     protected long duration = -1;
+    protected long durationIdle = -1;
     protected int durationMaxMessages;
-    protected TimeUnit timeUnit = TimeUnit.MILLISECONDS;
+    protected TimeUnit timeUnit = TimeUnit.SECONDS;
     protected boolean trace;
     protected List<RouteBuilder> routeBuilders = new ArrayList<RouteBuilder>();
     protected String routeBuilderClasses;
@@ -108,15 +107,14 @@ public abstract class MainSupport extends ServiceSupport {
             }
         });
         addOption(new ParameterOption("d", "duration",
-                "Sets the time duration that the application will run for, by default in milliseconds. You can use '10s' for 10 seconds etc",
+                "Sets the time duration (seconds) that the application will run for before terminating.",
                 "duration") {
             protected void doProcess(String arg, String parameter, LinkedList<String> remainingArgs) {
-                String value = parameter.toUpperCase(Locale.ENGLISH);
-                if (value.endsWith("S")) {
-                    value = value.substring(0, value.length() - 1);
-                    setTimeUnit(TimeUnit.SECONDS);
+                // skip second marker to be backwards compatible
+                if (parameter.endsWith("s") || parameter.endsWith("S")) {
+                    parameter = parameter.substring(0, parameter.length() - 1);
                 }
-                setDuration(Integer.parseInt(value));
+                setDuration(Integer.parseInt(parameter));
             }
         });
         addOption(new ParameterOption("dm", "durationMaxMessages",
@@ -126,6 +124,17 @@ public abstract class MainSupport extends ServiceSupport {
                 setDurationMaxMessages(Integer.parseInt(parameter));
             }
         });
+        addOption(new ParameterOption("di", "durationIdle",
+                "Sets the idle time duration (seconds) duration that the application can be idle before terminating.",
+                "durationIdle") {
+            protected void doProcess(String arg, String parameter, LinkedList<String> remainingArgs) {
+                // skip second marker to be backwards compatible
+                if (parameter.endsWith("s") || parameter.endsWith("S")) {
+                    parameter = parameter.substring(0, parameter.length() - 1);
+                }
+                setDurationIdle(Integer.parseInt(parameter));
+            }
+        });
         addOption(new Option("t", "trace", "Enables tracing") {
             protected void doProcess(String arg, LinkedList<String> remainingArgs) {
                 enableTrace();
@@ -327,13 +336,27 @@ public abstract class MainSupport extends ServiceSupport {
     }
 
     /**
-     * Sets the duration to run the application for in milliseconds until it
+     * Sets the duration (in seconds) to run the application until it
      * should be terminated. Defaults to -1. Any value <= 0 will run forever.
      */
     public void setDuration(long duration) {
         this.duration = duration;
     }
 
+    public long getDurationIdle() {
+        return durationIdle;
+    }
+
+    /**
+     * Sets the maximum idle duration (in seconds) when running the application, and
+     * if there has been no message processed after being idle for more than this duration
+     * then the application should be terminated.
+     * Defaults to -1. Any value <= 0 will run forever.
+     */
+    public void setDurationIdle(long durationIdle) {
+        this.durationIdle = durationIdle;
+    }
+
     public int getDurationMaxMessages() {
         return durationMaxMessages;
     }
@@ -351,7 +374,7 @@ public abstract class MainSupport extends ServiceSupport {
     }
 
     /**
-     * Sets the time unit duration.
+     * Sets the time unit duration (seconds by default).
      */
     public void setTimeUnit(TimeUnit timeUnit) {
         this.timeUnit = timeUnit;
@@ -431,6 +454,12 @@ public abstract class MainSupport extends ServiceSupport {
                     latch.await(duration, unit);
                     exitCode.compareAndSet(UNINITIALIZED_EXIT_CODE, durationHitExitCode);
                     completed.set(true);
+                } else if (durationIdle > 0) {
+                    TimeUnit unit = getTimeUnit();
+                    LOG.info("Waiting to be idle for: " + duration + " " + unit);
+                    exitCode.compareAndSet(UNINITIALIZED_EXIT_CODE, durationHitExitCode);
+                    latch.await();
+                    completed.set(true);
                 } else if (durationMaxMessages > 0) {
                     LOG.info("Waiting until: " + durationMaxMessages + " messages has been processed");
                     exitCode.compareAndSet(UNINITIALIZED_EXIT_CODE, durationHitExitCode);
@@ -548,9 +577,11 @@ public abstract class MainSupport extends ServiceSupport {
             }
         }
 
-        if (durationMaxMessages > 0) {
+        if (durationMaxMessages > 0 || durationIdle > 0) {
+            // convert to seconds as that is what event notifier uses
+            long seconds = timeUnit.toSeconds(durationIdle);
             // register lifecycle so we can trigger to shutdown the JVM when maximum number of messages has been processed
-            EventNotifier notifier = new MainDurationEventNotifier(camelContext, durationMaxMessages, completed, latch, true);
+            EventNotifier notifier = new MainDurationEventNotifier(camelContext, durationMaxMessages, seconds, completed, latch, true);
             // register our event notifier
             ServiceHelper.startService(notifier);
             camelContext.getManagementStrategy().addEventNotifier(notifier);

http://git-wip-us.apache.org/repos/asf/camel/blob/00ffe70a/camel-core/src/test/java/org/apache/camel/impl/MainSupportTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/impl/MainSupportTest.java b/camel-core/src/test/java/org/apache/camel/impl/MainSupportTest.java
index 2607c81..a9d78ec 100644
--- a/camel-core/src/test/java/org/apache/camel/impl/MainSupportTest.java
+++ b/camel-core/src/test/java/org/apache/camel/impl/MainSupportTest.java
@@ -41,12 +41,12 @@ public class MainSupportTest extends ContextTestSupport {
 
     public void testMainSupport() throws Exception {
         MyMainSupport my = new MyMainSupport();
-        my.run(new String[]{"-d", "1s"});
+        my.run(new String[]{"-d", "1"});
     }
 
     public void testMainSupportMaxMessages() throws Exception {
         MyMainSupport my = new MyMainSupport();
-        my.run(new String[]{"-d", "1s", "-dm", "2"});
+        my.run(new String[]{"-d", "1", "-dm", "2"});
     }
 
     public void testMainSupportHelp() throws Exception {

http://git-wip-us.apache.org/repos/asf/camel/blob/00ffe70a/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 6673917..a798012 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
@@ -134,6 +134,12 @@ public class CamelConfigurationProperties {
     private int durationMaxSeconds;
 
     /**
+     * To specify for how long time in seconds Camel can be idle before automatic terminating the JVM.
+     * You can use this to run Spring Boot for a short while.
+     */
+    private int durationMaxIdleSeconds;
+
+    /**
      * To specify how many messages to process by Camel before automatic terminating the JVM.
      * You can use this to run Spring Boot for a short while.
      */
@@ -501,6 +507,14 @@ public class CamelConfigurationProperties {
         this.durationMaxSeconds = durationMaxSeconds;
     }
 
+    public int getDurationMaxIdleSeconds() {
+        return durationMaxIdleSeconds;
+    }
+
+    public void setDurationMaxIdleSeconds(int durationMaxIdleSeconds) {
+        this.durationMaxIdleSeconds = durationMaxIdleSeconds;
+    }
+
     public int getDurationMaxMessages() {
         return durationMaxMessages;
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/00ffe70a/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 4084b14..ce78b01 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
@@ -116,10 +116,16 @@ public class RoutesCollector implements ApplicationListener<ContextRefreshedEven
                     if (configurationProperties.isMainRunController()) {
                         CamelMainRunController controller = new CamelMainRunController(applicationContext, camelContext);
 
-                        if (configurationProperties.getDurationMaxMessages() > 0) {
-                            LOG.info("CamelSpringBoot will terminate after processing {} messages", configurationProperties.getDurationMaxMessages());
+                        if (configurationProperties.getDurationMaxMessages() > 0 || configurationProperties.getDurationMaxIdleSeconds() > 0) {
+                            if (configurationProperties.getDurationMaxMessages() > 0) {
+                                LOG.info("CamelSpringBoot will terminate after processing {} messages", configurationProperties.getDurationMaxMessages());
+                            }
+                            if (configurationProperties.getDurationMaxIdleSeconds() > 0) {
+                                LOG.info("CamelSpringBoot will terminate after being idle for more {} seconds", configurationProperties.getDurationMaxIdleSeconds());
+                            }
                             // register lifecycle so we can trigger to shutdown the JVM when maximum number of messages has been processed
-                            EventNotifier notifier = new MainDurationEventNotifier(camelContext, configurationProperties.getDurationMaxMessages(),
+                            EventNotifier notifier = new MainDurationEventNotifier(camelContext,
+                                configurationProperties.getDurationMaxMessages(), configurationProperties.getDurationMaxIdleSeconds(),
                                 controller.getCompleted(), controller.getLatch(), true);
                             // register our event notifier
                             ServiceHelper.startService(notifier);
@@ -144,19 +150,27 @@ public class RoutesCollector implements ApplicationListener<ContextRefreshedEven
                                 terminateApplicationContext(cac, camelContext, configurationProperties.getDurationMaxSeconds());
                             }
 
-                            if (configurationProperties.getDurationMaxMessages() > 0) {
+                            if (configurationProperties.getDurationMaxMessages() > 0 || configurationProperties.getDurationMaxIdleSeconds() > 0) {
+
+                                if (configurationProperties.getDurationMaxMessages() > 0) {
+                                    LOG.info("CamelSpringBoot will terminate after processing {} messages", configurationProperties.getDurationMaxMessages());
+                                }
+                                if (configurationProperties.getDurationMaxIdleSeconds() > 0) {
+                                    LOG.info("CamelSpringBoot will terminate after being idle for more {} seconds", configurationProperties.getDurationMaxIdleSeconds());
+                                }
                                 // needed by MainDurationEventNotifier to signal when we have processed the max messages
                                 final AtomicBoolean completed = new AtomicBoolean();
                                 final CountDownLatch latch = new CountDownLatch(1);
 
                                 // register lifecycle so we can trigger to shutdown the JVM when maximum number of messages has been processed
-                                EventNotifier notifier = new MainDurationEventNotifier(camelContext, configurationProperties.getDurationMaxMessages(), completed, latch, false);
+                                EventNotifier notifier = new MainDurationEventNotifier(camelContext,
+                                    configurationProperties.getDurationMaxMessages(), configurationProperties.getDurationMaxIdleSeconds(),
+                                    completed, latch, false);
                                 // register our event notifier
                                 ServiceHelper.startService(notifier);
                                 camelContext.getManagementStrategy().addEventNotifier(notifier);
 
-                                LOG.info("CamelSpringBoot will terminate after processing {} messages", configurationProperties.getDurationMaxMessages());
-                                terminateApplicationContext(cac, camelContext, configurationProperties.getDurationMaxMessages(), latch);
+                                terminateApplicationContext(cac, camelContext, latch);
                             }
                         }
 
@@ -228,7 +242,7 @@ public class RoutesCollector implements ApplicationListener<ContextRefreshedEven
     private void terminateMainControllerAfter(final CamelContext camelContext, int seconds, final AtomicBoolean completed, final CountDownLatch latch) {
         ScheduledExecutorService executorService = camelContext.getExecutorServiceManager().newSingleThreadScheduledExecutor(this, "CamelSpringBootTerminateTask");
         Runnable task = () -> {
-            LOG.info("CamelSpringBoot max seconds triggering shutdown of the JVM.");
+            LOG.info("CamelSpringBoot triggering shutdown of the JVM.");
             try {
                 camelContext.stop();
             } catch (Throwable e) {
@@ -244,19 +258,19 @@ public class RoutesCollector implements ApplicationListener<ContextRefreshedEven
     private void terminateApplicationContext(final ConfigurableApplicationContext applicationContext, final CamelContext camelContext, int seconds) {
         ScheduledExecutorService executorService = camelContext.getExecutorServiceManager().newSingleThreadScheduledExecutor(this, "CamelSpringBootTerminateTask");
         Runnable task = () -> {
-            LOG.info("CamelSpringBoot max seconds triggering shutdown of the JVM.");
+            LOG.info("CamelSpringBoot triggering shutdown of the JVM.");
             // we need to run a daemon thread to stop ourselves so this thread pool can be stopped nice also
             new Thread(applicationContext::close).start();
         };
         executorService.schedule(task, seconds, TimeUnit.SECONDS);
     }
 
-    private void terminateApplicationContext(final ConfigurableApplicationContext applicationContext, final CamelContext camelContext, int messages, final CountDownLatch latch) {
+    private void terminateApplicationContext(final ConfigurableApplicationContext applicationContext, final CamelContext camelContext, final CountDownLatch latch) {
         ExecutorService executorService = camelContext.getExecutorServiceManager().newSingleThreadExecutor(this, "CamelSpringBootTerminateTask");
         Runnable task = () -> {
             try {
                 latch.await();
-                LOG.info("CamelSpringBoot max messages " + messages + " triggering shutdown of the JVM.");
+                LOG.info("CamelSpringBoot triggering shutdown of the JVM.");
                 // we need to run a daemon thread to stop ourselves so this thread pool can be stopped nice also
                 new Thread(applicationContext::close).start();
             } catch (Throwable e) {

http://git-wip-us.apache.org/repos/asf/camel/blob/00ffe70a/examples/camel-example-spring-boot/src/main/resources/application.properties
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot/src/main/resources/application.properties b/examples/camel-example-spring-boot/src/main/resources/application.properties
index 5a036ed..514e3a6 100644
--- a/examples/camel-example-spring-boot/src/main/resources/application.properties
+++ b/examples/camel-example-spring-boot/src/main/resources/application.properties
@@ -22,6 +22,9 @@ camel.springboot.name = SampleCamel
 #camel.springboot.duration-max-seconds=60
 #camel.springboot.duration-max-messages=100
 
+# add for example: &repeatCount=5 to the timer endpoint to make Camel idle
+#camel.springboot.duration-max-idle-seconds=15
+
 # properties used in the Camel route and beans
 # --------------------------------------------
 

http://git-wip-us.apache.org/repos/asf/camel/blob/00ffe70a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/RunMojo.java
----------------------------------------------------------------------
diff --git a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/RunMojo.java b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/RunMojo.java
index 1f132e3..c375e45 100644
--- a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/RunMojo.java
+++ b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/RunMojo.java
@@ -84,9 +84,8 @@ public class RunMojo extends AbstractExecMojo {
     protected MavenProject project;
 
     /**
-     * The duration to run the application for which by default is in
-     * milliseconds. A value <= 0 will run forever.
-     * Adding a s indicates seconds - eg "5s" means 5 seconds.
+     * Sets the time duration (seconds) that the application will run for before terminating.
+     * A value <= 0 will run forever.
      *
      * @parameter property="camel.duration"
      *            default-value="-1"
@@ -95,6 +94,16 @@ public class RunMojo extends AbstractExecMojo {
     protected String duration;
 
     /**
+     * Sets the idle time duration (seconds) duration that the application can be idle before terminating.
+     * A value <= 0 will run forever.
+     *
+     * @parameter property="camel.durationIdle"
+     *            default-value="-1"
+     *
+     */
+    protected String durationIdle;
+
+    /**
      * Sets the duration of maximum number of messages that the application will process before terminating.
      *
      * @parameter property="camel.duration.maxMessages"
@@ -438,6 +447,10 @@ public class RunMojo extends AbstractExecMojo {
             args.add("-d");
             args.add(duration);
         }
+        if (!durationIdle.equals("-1")) {
+            args.add("-di");
+            args.add(durationIdle);
+        }
         if (!durationMaxMessages.equals("-1")) {
             args.add("-dm");
             args.add(durationMaxMessages);