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 2020/11/01 18:06:48 UTC

[camel] 01/04: CAMEL-15784: camel-core - Optimize with Bootstrap marker interface

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 13472b80b91960bd2aea5a20e22790b6e3961c4d
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sun Nov 1 16:57:08 2020 +0100

    CAMEL-15784: camel-core - Optimize with Bootstrap marker interface
---
 .../main/java/org/apache/camel/spring/Main.java    |  6 +-
 .../org/apache/camel/ExtendedCamelContext.java     | 11 +++
 .../org/apache/camel/spi/BootstrapCloseable.java   | 28 ++++++++
 .../camel/impl/engine/AbstractCamelContext.java    | 28 +++++++-
 .../engine/DefaultServiceBootstrapCloseable.java   | 64 ++++++++++++++++++
 .../camel/impl/lw/LightweightCamelContext.java     | 12 ++++
 .../impl/lw/LightweightRuntimeCamelContext.java    | 10 +++
 .../org/apache/camel/main/BaseMainSupport.java     | 62 +++++++++--------
 .../FaultToleranceConfigurationProperties.java     | 10 ++-
 .../camel/main/HealthConfigurationProperties.java  | 12 +++-
 .../camel/main/HystrixConfigurationProperties.java | 10 ++-
 .../camel/main/LraConfigurationProperties.java     | 10 ++-
 .../apache/camel/main/MainBootstrapCloseable.java  | 32 +++++++++
 .../apache/camel/main/MainCommandLineSupport.java  | 10 +++
 .../camel/main/MainConfigurationProperties.java    | 79 +++++++++++++++++++---
 .../java/org/apache/camel/main/MainSupport.java    | 28 ++++++--
 .../main/Resilience4jConfigurationProperties.java  | 10 ++-
 .../camel/main/RestConfigurationProperties.java    | 16 ++++-
 .../main/ThreadPoolConfigurationProperties.java    | 12 +++-
 .../camel/main/MainSupportCommandLineTest.java     |  7 +-
 .../main/java/org/apache/camel/util/TimeUtils.java |  4 ++
 21 files changed, 397 insertions(+), 64 deletions(-)

diff --git a/components/camel-spring-main/src/main/java/org/apache/camel/spring/Main.java b/components/camel-spring-main/src/main/java/org/apache/camel/spring/Main.java
index 7507fd6..fe34866 100644
--- a/components/camel-spring-main/src/main/java/org/apache/camel/spring/Main.java
+++ b/components/camel-spring-main/src/main/java/org/apache/camel/spring/Main.java
@@ -64,6 +64,11 @@ public class Main extends MainCommandLineSupport {
     private String parentApplicationContextUri;
 
     public Main() {
+    }
+
+    @Override
+    protected void initOptions() {
+        super.initOptions();
 
         addOption(new ParameterOption(
                 "ac", "applicationContext",
@@ -80,7 +85,6 @@ public class Main extends MainCommandLineSupport {
                 setFileApplicationContextUri(parameter);
             }
         });
-
     }
 
     public static void main(String... args) throws Exception {
diff --git a/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java b/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java
index e463e66..f6b1557 100644
--- a/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java
+++ b/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java
@@ -27,6 +27,7 @@ import org.apache.camel.spi.AsyncProcessorAwaitManager;
 import org.apache.camel.spi.BeanIntrospection;
 import org.apache.camel.spi.BeanProcessorFactory;
 import org.apache.camel.spi.BeanProxyFactory;
+import org.apache.camel.spi.BootstrapCloseable;
 import org.apache.camel.spi.CamelBeanPostProcessor;
 import org.apache.camel.spi.ComponentNameResolver;
 import org.apache.camel.spi.ComponentResolver;
@@ -201,6 +202,16 @@ public interface ExtendedCamelContext extends CamelContext {
     List<RouteStartupOrder> getRouteStartupOrder();
 
     /**
+     * Adds a {@link BootstrapCloseable} task.
+     */
+    void addBootstrap(BootstrapCloseable bootstrap);
+
+    /**
+     * Returns an unmodifiable list of the services registered currently in this {@link CamelContext}.
+     */
+    List<Service> getServices();
+
+    /**
      * Returns the bean post processor used to do any bean customization.
      *
      * @return the bean post processor.
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/BootstrapCloseable.java b/core/camel-api/src/main/java/org/apache/camel/spi/BootstrapCloseable.java
new file mode 100644
index 0000000..cfab0a2
--- /dev/null
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/BootstrapCloseable.java
@@ -0,0 +1,28 @@
+/*
+ * 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.spi;
+
+import java.io.Closeable;
+
+/**
+ * A marker interface for a service, or other kind of process that is only used during bootstrapping Camel. After the
+ * bootstrap is complete the {@link #close()} method is invoked which allows to do some cleanup processes such as
+ * clearing internal caches, maps etc to clear up memory etc.
+ */
+public interface BootstrapCloseable extends Closeable {
+
+}
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
index e6d7fb5..b506a5f 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
@@ -79,6 +79,7 @@ import org.apache.camel.spi.AsyncProcessorAwaitManager;
 import org.apache.camel.spi.BeanIntrospection;
 import org.apache.camel.spi.BeanProcessorFactory;
 import org.apache.camel.spi.BeanProxyFactory;
+import org.apache.camel.spi.BootstrapCloseable;
 import org.apache.camel.spi.CamelBeanPostProcessor;
 import org.apache.camel.spi.CamelContextNameStrategy;
 import org.apache.camel.spi.CamelContextTracker;
@@ -189,6 +190,7 @@ public abstract class AbstractCamelContext extends BaseService
     private final Map<String, Component> components = new ConcurrentHashMap<>();
     private final Set<Route> routes = new LinkedHashSet<>();
     private final List<Service> servicesToStop = new CopyOnWriteArrayList<>();
+    private final List<BootstrapCloseable> bootstraps = new CopyOnWriteArrayList<>();
     private final List<StartupListener> startupListeners = new CopyOnWriteArrayList<>();
     private final DeferServiceStartupListener deferStartupListener = new DeferServiceStartupListener();
     private final Map<String, Language> languages = new ConcurrentHashMap<>();
@@ -331,7 +333,7 @@ public abstract class AbstractCamelContext extends BaseService
         // create a provisional (temporary) endpoint registry at first since end
         // users may access endpoints before CamelContext is started
         // we will later transfer the endpoints to the actual
-        // DefaultEndpointRegistry later, but we do this to starup Camel faster.
+        // DefaultEndpointRegistry later, but we do this to startup Camel faster.
         this.endpoints = new ProvisionalEndpointRegistry();
 
         // add the defer service startup listener
@@ -343,6 +345,9 @@ public abstract class AbstractCamelContext extends BaseService
         // add a default LifecycleStrategy to customize services using customizers from registry
         this.lifecycleStrategies.add(new CustomizersLifecycleStrategy(this));
 
+        // add the deafult bootstrap
+        this.bootstraps.add(new DefaultServiceBootstrapCloseable(this));
+
         if (build) {
             try {
                 build();
@@ -1438,6 +1443,16 @@ public abstract class AbstractCamelContext extends BaseService
     }
 
     @Override
+    public void addBootstrap(BootstrapCloseable bootstrap) {
+        bootstraps.add(bootstrap);
+    }
+
+    @Override
+    public List<Service> getServices() {
+        return Collections.unmodifiableList(servicesToStop);
+    }
+
+    @Override
     public boolean hasService(Object object) {
         if (servicesToStop.isEmpty()) {
             return false;
@@ -2706,12 +2721,21 @@ public abstract class AbstractCamelContext extends BaseService
             }
             LOG.info("Apache Camel {} ({}) started in {}", getVersion(), getName(), TimeUtils.printDuration(stopWatch.taken()));
 
+            // now Camel has been started/bootstrap is complete, then run cleanup to help free up memory etc
+            for (BootstrapCloseable bootstrap : bootstraps) {
+                try {
+                    bootstrap.close();
+                } catch (Exception e) {
+                    LOG.warn("Error during closing bootstrap. This exception is ignored.", e);
+                }
+            }
+            bootstraps.clear();
+
             if (isLightweight()) {
                 LOG.info(
                         "Lightweight enabled. Clearing services to free memory."
                          + " Danger this impacts the CamelContext not being able to add new routes or use reflection-free configuration, etc.");
                 ReifierStrategy.clearReifiers();
-                ConfigurerStrategy.clearBootstrapConfigurers();
                 ConfigurerStrategy.clearConfigurers();
             }
         }
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultServiceBootstrapCloseable.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultServiceBootstrapCloseable.java
new file mode 100644
index 0000000..411843d
--- /dev/null
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultServiceBootstrapCloseable.java
@@ -0,0 +1,64 @@
+/*
+ * 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.impl.engine;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.Service;
+import org.apache.camel.spi.BootstrapCloseable;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Default {@link BootstrapCloseable} which will collect all registered {@link Service} which is
+ * {@link BootstrapCloseable} and run their task and remove the service from {@link CamelContext}.
+ */
+public class DefaultServiceBootstrapCloseable implements BootstrapCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultServiceBootstrapCloseable.class);
+
+    private final ExtendedCamelContext camelContext;
+
+    public DefaultServiceBootstrapCloseable(CamelContext camelContext) {
+        this.camelContext = (ExtendedCamelContext) camelContext;
+    }
+
+    @Override
+    public void close() {
+        // clear bootstrap configurers
+        ConfigurerStrategy.clearBootstrapConfigurers();
+
+        Set<Service> set
+                = camelContext.getServices().stream().filter(s -> s instanceof BootstrapCloseable).collect(Collectors.toSet());
+        // its a bootstrap service
+        for (Service service : set) {
+            try {
+                if (service instanceof BootstrapCloseable) {
+                    ((BootstrapCloseable) service).close();
+                }
+                // service is no longer needed as it was only intended during bootstrap
+                camelContext.removeService(service);
+            } catch (Exception e) {
+                LOG.warn("Error during closing bootstrap service. This exception is ignored", e);
+            }
+        }
+    }
+}
diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
index b42f8ed..36ceab9 100644
--- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
+++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
@@ -43,6 +43,7 @@ import org.apache.camel.Processor;
 import org.apache.camel.ProducerTemplate;
 import org.apache.camel.Route;
 import org.apache.camel.RoutesBuilder;
+import org.apache.camel.Service;
 import org.apache.camel.ServiceStatus;
 import org.apache.camel.ShutdownRoute;
 import org.apache.camel.ShutdownRunningTask;
@@ -74,6 +75,7 @@ import org.apache.camel.spi.BeanIntrospection;
 import org.apache.camel.spi.BeanProcessorFactory;
 import org.apache.camel.spi.BeanProxyFactory;
 import org.apache.camel.spi.BeanRepository;
+import org.apache.camel.spi.BootstrapCloseable;
 import org.apache.camel.spi.CamelBeanPostProcessor;
 import org.apache.camel.spi.CamelContextNameStrategy;
 import org.apache.camel.spi.ClassResolver;
@@ -344,6 +346,11 @@ public class LightweightCamelContext implements ExtendedCamelContext, CatalogCam
     }
 
     @Override
+    public void addBootstrap(BootstrapCloseable bootstrap) {
+        getExtendedCamelContext().addBootstrap(bootstrap);
+    }
+
+    @Override
     public void addService(Object object) throws Exception {
         delegate.addService(object);
     }
@@ -369,6 +376,11 @@ public class LightweightCamelContext implements ExtendedCamelContext, CatalogCam
     }
 
     @Override
+    public List<Service> getServices() {
+        return getExtendedCamelContext().getServices();
+    }
+
+    @Override
     public boolean hasService(Object object) {
         return delegate.hasService(object);
     }
diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java
index 387a4d9..93583d8 100644
--- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java
+++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java
@@ -69,6 +69,7 @@ import org.apache.camel.spi.AsyncProcessorAwaitManager;
 import org.apache.camel.spi.BeanIntrospection;
 import org.apache.camel.spi.BeanProcessorFactory;
 import org.apache.camel.spi.BeanProxyFactory;
+import org.apache.camel.spi.BootstrapCloseable;
 import org.apache.camel.spi.CamelBeanPostProcessor;
 import org.apache.camel.spi.CamelContextNameStrategy;
 import org.apache.camel.spi.ClassResolver;
@@ -653,6 +654,15 @@ public class LightweightRuntimeCamelContext implements ExtendedCamelContext, Cat
     }
 
     @Override
+    public void addBootstrap(BootstrapCloseable bootstrap) {
+    }
+
+    @Override
+    public List<Service> getServices() {
+        return null;
+    }
+
+    @Override
     public boolean hasService(Object object) {
         return false;
     }
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index 34d541b..9d2a59f 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -101,8 +101,8 @@ public abstract class BaseMainSupport extends BaseService {
     protected volatile CamelContext camelContext;
 
     protected final List<MainListener> listeners = new ArrayList<>();
-    protected final MainConfigurationProperties mainConfigurationProperties = new MainConfigurationProperties();
-    protected final Properties wildcardProperties = new OrderedProperties();
+    protected MainConfigurationProperties mainConfigurationProperties = new MainConfigurationProperties();
+    protected Properties wildcardProperties = new OrderedProperties();
     protected RoutesCollector routesCollector = new DefaultRoutesCollector();
     protected String propertyPlaceholderLocations;
     protected String defaultPropertyPlaceholderLocation = DEFAULT_PROPERTY_PLACEHOLDER_LOCATION;
@@ -726,54 +726,56 @@ public abstract class BaseMainSupport extends BaseService {
                     mainConfigurationProperties.isAutoConfigurationFailFast(), true, autoConfiguredProperties);
         }
 
-        HystrixConfigurationProperties hystrix = mainConfigurationProperties.hystrix();
         if (!hystrixProperties.isEmpty()) {
+            HystrixConfigurationProperties hystrix = mainConfigurationProperties.hystrix();
             LOG.debug("Auto-configuring Hystrix Circuit Breaker EIP from loaded properties: {}", hystrixProperties.size());
             setPropertiesOnTarget(camelContext, hystrix, hystrixProperties, "camel.hystrix.",
                     mainConfigurationProperties.isAutoConfigurationFailFast(), true, autoConfiguredProperties);
+            HystrixConfigurationDefinition hystrixModel = model.getHystrixConfiguration(null);
+            if (hystrixModel == null) {
+                hystrixModel = new HystrixConfigurationDefinition();
+                model.setHystrixConfiguration(hystrixModel);
+            }
+            if (hystrix != null) {
+                setPropertiesOnTarget(camelContext, hystrixModel, hystrix);
+            }
         }
-        HystrixConfigurationDefinition hystrixModel = model.getHystrixConfiguration(null);
-        if (hystrixModel == null) {
-            hystrixModel = new HystrixConfigurationDefinition();
-            model.setHystrixConfiguration(hystrixModel);
-        }
-        setPropertiesOnTarget(camelContext, hystrixModel, hystrix);
 
-        Resilience4jConfigurationProperties resilience4j = mainConfigurationProperties.resilience4j();
         if (!resilience4jProperties.isEmpty()) {
+            Resilience4jConfigurationProperties resilience4j = mainConfigurationProperties.resilience4j();
             LOG.debug("Auto-configuring Resilience4j Circuit Breaker EIP from loaded properties: {}",
                     resilience4jProperties.size());
             setPropertiesOnTarget(camelContext, resilience4j, resilience4jProperties, "camel.resilience4j.",
                     mainConfigurationProperties.isAutoConfigurationFailFast(), true, autoConfiguredProperties);
+            Resilience4jConfigurationDefinition resilience4jModel = model.getResilience4jConfiguration(null);
+            if (resilience4jModel == null) {
+                resilience4jModel = new Resilience4jConfigurationDefinition();
+                model.setResilience4jConfiguration(resilience4jModel);
+            }
+            setPropertiesOnTarget(camelContext, resilience4jModel, resilience4j);
         }
-        Resilience4jConfigurationDefinition resilience4jModel = model.getResilience4jConfiguration(null);
-        if (resilience4jModel == null) {
-            resilience4jModel = new Resilience4jConfigurationDefinition();
-            model.setResilience4jConfiguration(resilience4jModel);
-        }
-        setPropertiesOnTarget(camelContext, resilience4jModel, resilience4j);
 
-        FaultToleranceConfigurationProperties faultTolerance = mainConfigurationProperties.faultTolerance();
         if (!faultToleranceProperties.isEmpty()) {
+            FaultToleranceConfigurationProperties faultTolerance = mainConfigurationProperties.faultTolerance();
             LOG.debug("Auto-configuring MicroProfile Fault Tolerance Circuit Breaker EIP from loaded properties: {}",
                     faultToleranceProperties.size());
             setPropertiesOnTarget(camelContext, faultTolerance, faultToleranceProperties, "camel.faulttolerance.",
                     mainConfigurationProperties.isAutoConfigurationFailFast(), true, autoConfiguredProperties);
+            FaultToleranceConfigurationDefinition faultToleranceModel = model.getFaultToleranceConfiguration(null);
+            if (faultToleranceModel == null) {
+                faultToleranceModel = new FaultToleranceConfigurationDefinition();
+                model.setFaultToleranceConfiguration(faultToleranceModel);
+            }
+            setPropertiesOnTarget(camelContext, faultToleranceModel, faultTolerance);
         }
-        FaultToleranceConfigurationDefinition faultToleranceModel = model.getFaultToleranceConfiguration(null);
-        if (faultToleranceModel == null) {
-            faultToleranceModel = new FaultToleranceConfigurationDefinition();
-            model.setFaultToleranceConfiguration(faultToleranceModel);
-        }
-        setPropertiesOnTarget(camelContext, faultToleranceModel, faultTolerance);
 
-        RestConfigurationProperties rest = mainConfigurationProperties.rest();
         if (!restProperties.isEmpty()) {
+            RestConfigurationProperties rest = mainConfigurationProperties.rest();
             LOG.debug("Auto-configuring Rest DSL from loaded properties: {}", restProperties.size());
             setPropertiesOnTarget(camelContext, rest, restProperties, "camel.rest.",
                     mainConfigurationProperties.isAutoConfigurationFailFast(), true, autoConfiguredProperties);
+            camelContext.setRestConfiguration(rest);
         }
-        camelContext.setRestConfiguration(rest);
 
         if (!threadPoolProperties.isEmpty()) {
             LOG.debug("Auto-configuring Thread Pool from loaded properties: {}", threadPoolProperties.size());
@@ -804,27 +806,27 @@ public abstract class BaseMainSupport extends BaseService {
         }
         if (!contextProperties.isEmpty()) {
             contextProperties.forEach((k, v) -> {
-                LOG.warn("Property not auto-configured: camel.context.{}={} on bean: {}", k, v, camelContext);
+                LOG.warn("Property not auto-configured: camel.context.{}={}", k, v);
             });
         }
         if (!hystrixProperties.isEmpty()) {
             hystrixProperties.forEach((k, v) -> {
-                LOG.warn("Property not auto-configured: camel.hystrix.{}={} on bean: {}", k, v, hystrix);
+                LOG.warn("Property not auto-configured: camel.hystrix.{}={}", k, v);
             });
         }
         if (!resilience4jProperties.isEmpty()) {
             resilience4jProperties.forEach((k, v) -> {
-                LOG.warn("Property not auto-configured: camel.resilience4j.{}={} on bean: {}", k, v, resilience4j);
+                LOG.warn("Property not auto-configured: camel.resilience4j.{}={}", k, v);
             });
         }
         if (!faultToleranceProperties.isEmpty()) {
             faultToleranceProperties.forEach((k, v) -> {
-                LOG.warn("Property not auto-configured: camel.faulttolerance.{}={} on bean: {}", k, v, faultTolerance);
+                LOG.warn("Property not auto-configured: camel.faulttolerance.{}={}", k, v);
             });
         }
         if (!restProperties.isEmpty()) {
             restProperties.forEach((k, v) -> {
-                LOG.warn("Property not auto-configured: camel.rest.{}={} on bean: {}", k, v, rest);
+                LOG.warn("Property not auto-configured: camel.rest.{}={}", k, v);
             });
         }
         if (!threadPoolProperties.isEmpty()) {
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/FaultToleranceConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/FaultToleranceConfigurationProperties.java
index 601a25b..a242748 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/FaultToleranceConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/FaultToleranceConfigurationProperties.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.main;
 
+import org.apache.camel.spi.BootstrapCloseable;
 import org.apache.camel.spi.Configurer;
 import org.apache.camel.spi.Metadata;
 
@@ -23,9 +24,9 @@ import org.apache.camel.spi.Metadata;
  * Global configuration for MicroProfile Fault Tolerance EIP circuit breaker.
  */
 @Configurer(bootstrap = true)
-public class FaultToleranceConfigurationProperties {
+public class FaultToleranceConfigurationProperties implements BootstrapCloseable {
 
-    private final MainConfigurationProperties parent;
+    private MainConfigurationProperties parent;
 
     private String circuitBreakerRef;
     @Metadata(defaultValue = "5")
@@ -59,6 +60,11 @@ public class FaultToleranceConfigurationProperties {
         return parent;
     }
 
+    @Override
+    public void close() {
+        parent = null;
+    }
+
     // getter and setters
     // --------------------------------------------------------------
 
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/HealthConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/HealthConfigurationProperties.java
index 1c212f0..b652291 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/HealthConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/HealthConfigurationProperties.java
@@ -19,6 +19,7 @@ package org.apache.camel.main;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.camel.spi.BootstrapCloseable;
 import org.apache.camel.spi.Configurer;
 import org.apache.camel.spi.Metadata;
 
@@ -26,9 +27,9 @@ import org.apache.camel.spi.Metadata;
  * Global configuration for Health Check
  */
 @Configurer(bootstrap = true)
-public class HealthConfigurationProperties {
+public class HealthConfigurationProperties implements BootstrapCloseable {
 
-    private final MainConfigurationProperties parent;
+    private MainConfigurationProperties parent;
 
     @Metadata(defaultValue = "true")
     private Boolean enabled;
@@ -48,6 +49,13 @@ public class HealthConfigurationProperties {
         return parent;
     }
 
+    @Override
+    public void close() {
+        config.clear();
+        config = null;
+        parent = null;
+    }
+
     public Boolean getEnabled() {
         return enabled;
     }
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/HystrixConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/HystrixConfigurationProperties.java
index c725f73..fcb2f53 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/HystrixConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/HystrixConfigurationProperties.java
@@ -21,6 +21,7 @@ import java.util.concurrent.Future;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.camel.spi.BootstrapCloseable;
 import org.apache.camel.spi.Configurer;
 
 /**
@@ -28,9 +29,9 @@ import org.apache.camel.spi.Configurer;
  */
 @Configurer(bootstrap = true)
 @Deprecated
-public class HystrixConfigurationProperties {
+public class HystrixConfigurationProperties implements BootstrapCloseable {
 
-    private final MainConfigurationProperties parent;
+    private MainConfigurationProperties parent;
 
     private String groupKey;
     private String threadPoolKey;
@@ -73,6 +74,11 @@ public class HystrixConfigurationProperties {
         return parent;
     }
 
+    @Override
+    public void close() {
+        parent = null;
+    }
+
     // getter and setters
     // --------------------------------------------------------------
 
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/LraConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/LraConfigurationProperties.java
index 134f71f..c0106dd 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/LraConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/LraConfigurationProperties.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.main;
 
+import org.apache.camel.spi.BootstrapCloseable;
 import org.apache.camel.spi.Configurer;
 import org.apache.camel.spi.Metadata;
 
@@ -23,9 +24,9 @@ import org.apache.camel.spi.Metadata;
  * Global configuration for Saga LRA
  */
 @Configurer(bootstrap = true)
-public class LraConfigurationProperties {
+public class LraConfigurationProperties implements BootstrapCloseable {
 
-    private final MainConfigurationProperties parent;
+    private MainConfigurationProperties parent;
 
     private String coordinatorUrl;
     @Metadata(defaultValue = "/lra-coordinator")
@@ -42,6 +43,11 @@ public class LraConfigurationProperties {
         return parent;
     }
 
+    @Override
+    public void close() {
+        parent = null;
+    }
+
     public String getCoordinatorUrl() {
         return coordinatorUrl;
     }
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/MainBootstrapCloseable.java b/core/camel-main/src/main/java/org/apache/camel/main/MainBootstrapCloseable.java
new file mode 100644
index 0000000..8d7ba90
--- /dev/null
+++ b/core/camel-main/src/main/java/org/apache/camel/main/MainBootstrapCloseable.java
@@ -0,0 +1,32 @@
+package org.apache.camel.main;
+
+import org.apache.camel.spi.BootstrapCloseable;
+
+public class MainBootstrapCloseable implements BootstrapCloseable {
+
+    private final MainSupport main;
+
+    public MainBootstrapCloseable(MainSupport main) {
+        this.main = main;
+    }
+
+    @Override
+    public void close() {
+        // we are now bootstrapped and can clear up memory
+        if (main.initialProperties != null) {
+            main.initialProperties.clear();
+            main.initialProperties = null;
+        }
+        if (main.overrideProperties != null) {
+            main.overrideProperties.clear();
+            main.overrideProperties = null;
+        }
+        main.wildcardProperties.clear();
+        main.wildcardProperties = null;
+
+        // no longer in use
+        main.mainConfigurationProperties.close();
+        main.mainConfigurationProperties = null;
+        main.routesCollector = null;
+    }
+}
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/MainCommandLineSupport.java b/core/camel-main/src/main/java/org/apache/camel/main/MainCommandLineSupport.java
index 231b020..e82a7f0 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/MainCommandLineSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/MainCommandLineSupport.java
@@ -27,12 +27,19 @@ import java.util.List;
 public abstract class MainCommandLineSupport extends MainSupport {
 
     protected final List<Option> options = new ArrayList<>();
+    private volatile boolean initOptionsDone;
 
     public MainCommandLineSupport(Class... configurationClasses) {
         super(configurationClasses);
     }
 
     public MainCommandLineSupport() {
+    }
+
+    protected void initOptions() {
+        if (initOptionsDone) {
+            return;
+        }
         addOption(new Option("h", "help", "Displays the help screen") {
             protected void doProcess(String arg, LinkedList<String> remainingArgs) {
                 showOptions();
@@ -101,12 +108,14 @@ public abstract class MainCommandLineSupport extends MainSupport {
                 setPropertyPlaceholderLocations(parameter);
             }
         });
+        initOptionsDone = true;
     }
 
     /**
      * Displays the command line options.
      */
     public void showOptions() {
+        initOptions();
         showOptionsHeader();
 
         for (Option option : options) {
@@ -122,6 +131,7 @@ public abstract class MainCommandLineSupport extends MainSupport {
 
         boolean valid = true;
         while (!args.isEmpty()) {
+            initOptions();
             String arg = args.removeFirst();
 
             boolean handled = false;
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java
index 02b09ac..ba4298a 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java
@@ -22,13 +22,15 @@ import java.util.List;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.LambdaRouteBuilder;
 import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.BootstrapCloseable;
 import org.apache.camel.spi.Configurer;
 
 /**
  * Global configuration for Camel Main to setup context name, stream caching and other global configurations.
  */
 @Configurer(bootstrap = true)
-public class MainConfigurationProperties extends DefaultConfigurationProperties<MainConfigurationProperties> {
+public class MainConfigurationProperties extends DefaultConfigurationProperties<MainConfigurationProperties>
+        implements BootstrapCloseable {
 
     private boolean autoConfigurationEnabled = true;
     private boolean autoConfigurationEnvironmentVariablesEnabled = true;
@@ -48,15 +50,49 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties<
     private List<Object> configurations = new ArrayList<>();
 
     // extended configuration
-    private final HealthConfigurationProperties healthConfigurationProperties = new HealthConfigurationProperties(this);
-    private final LraConfigurationProperties lraConfigurationProperties = new LraConfigurationProperties(this);
-    private final ThreadPoolConfigurationProperties threadPool = new ThreadPoolConfigurationProperties(this);
-    private final HystrixConfigurationProperties hystrixConfigurationProperties = new HystrixConfigurationProperties(this);
-    private final Resilience4jConfigurationProperties resilience4jConfigurationProperties
-            = new Resilience4jConfigurationProperties(this);
-    private final FaultToleranceConfigurationProperties faultToleranceConfigurationProperties
-            = new FaultToleranceConfigurationProperties(this);
-    private final RestConfigurationProperties restConfigurationProperties = new RestConfigurationProperties(this);
+    private HealthConfigurationProperties healthConfigurationProperties;
+    private LraConfigurationProperties lraConfigurationProperties;
+    private ThreadPoolConfigurationProperties threadPool;
+    private HystrixConfigurationProperties hystrixConfigurationProperties;
+    private Resilience4jConfigurationProperties resilience4jConfigurationProperties;
+    private FaultToleranceConfigurationProperties faultToleranceConfigurationProperties;
+    private RestConfigurationProperties restConfigurationProperties;
+
+    @Override
+    public void close() {
+        if (healthConfigurationProperties != null) {
+            healthConfigurationProperties.close();
+            healthConfigurationProperties = null;
+        }
+        if (lraConfigurationProperties != null) {
+            lraConfigurationProperties.close();
+            lraConfigurationProperties = null;
+        }
+        if (threadPool != null) {
+            threadPool.close();
+            threadPool = null;
+        }
+        if (hystrixConfigurationProperties != null) {
+            hystrixConfigurationProperties.close();
+            hystrixConfigurationProperties = null;
+        }
+        if (resilience4jConfigurationProperties != null) {
+            resilience4jConfigurationProperties.close();
+            resilience4jConfigurationProperties = null;
+        }
+        if (faultToleranceConfigurationProperties != null) {
+            faultToleranceConfigurationProperties.close();
+            faultToleranceConfigurationProperties = null;
+        }
+        if (restConfigurationProperties != null) {
+            restConfigurationProperties.close();
+            restConfigurationProperties = null;
+        }
+        routesBuilders.clear();
+        routesBuilders = null;
+        configurations.clear();
+        configurations = null;
+    }
 
     // extended
     // --------------------------------------------------------------
@@ -65,6 +101,9 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties<
      * To configure Health Check
      */
     public HealthConfigurationProperties health() {
+        if (healthConfigurationProperties == null) {
+            healthConfigurationProperties = new HealthConfigurationProperties(this);
+        }
         return healthConfigurationProperties;
     }
 
@@ -72,6 +111,9 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties<
      * To configure Saga LRA
      */
     public LraConfigurationProperties lra() {
+        if (lraConfigurationProperties == null) {
+            lraConfigurationProperties = new LraConfigurationProperties(this);
+        }
         return lraConfigurationProperties;
     }
 
@@ -79,6 +121,9 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties<
      * To configure thread pools
      */
     public ThreadPoolConfigurationProperties threadPool() {
+        if (threadPool == null) {
+            threadPool = new ThreadPoolConfigurationProperties(this);
+        }
         return threadPool;
     }
 
@@ -87,6 +132,9 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties<
      */
     @Deprecated
     public HystrixConfigurationProperties hystrix() {
+        if (hystrixConfigurationProperties == null) {
+            hystrixConfigurationProperties = new HystrixConfigurationProperties(this);
+        }
         return hystrixConfigurationProperties;
     }
 
@@ -94,6 +142,9 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties<
      * To configure Circuit Breaker EIP with Resilience4j
      */
     public Resilience4jConfigurationProperties resilience4j() {
+        if (resilience4jConfigurationProperties == null) {
+            resilience4jConfigurationProperties = new Resilience4jConfigurationProperties(this);
+        }
         return resilience4jConfigurationProperties;
     }
 
@@ -101,6 +152,9 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties<
      * To configure Circuit Breaker EIP with MicroProfile Fault Tolerance
      */
     public FaultToleranceConfigurationProperties faultTolerance() {
+        if (faultToleranceConfigurationProperties == null) {
+            faultToleranceConfigurationProperties = new FaultToleranceConfigurationProperties(this);
+        }
         return faultToleranceConfigurationProperties;
     }
 
@@ -108,6 +162,9 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties<
      * To configure Rest DSL
      */
     public RestConfigurationProperties rest() {
+        if (restConfigurationProperties == null) {
+            restConfigurationProperties = new RestConfigurationProperties(this);
+        }
         return restConfigurationProperties;
     }
 
@@ -355,7 +412,7 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties<
             existing = "";
         }
         if (routeBuilder != null) {
-            for (Class clazz : routeBuilder) {
+            for (Class<?> clazz : routeBuilder) {
                 if (!existing.isEmpty()) {
                     existing = existing + ",";
                 }
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/MainSupport.java b/core/camel-main/src/main/java/org/apache/camel/main/MainSupport.java
index 3176f8d..ee4618e 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/MainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/MainSupport.java
@@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.ProducerTemplate;
 import org.apache.camel.spi.EventNotifier;
 import org.apache.camel.support.service.ServiceHelper;
@@ -41,6 +42,11 @@ public abstract class MainSupport extends BaseMainSupport {
 
     protected volatile ProducerTemplate camelTemplate;
 
+    private int durationMaxIdleSeconds;
+    private int durationMaxMessages;
+    private long durationMaxSeconds;
+    private int durationHitExitCode;
+
     protected MainSupport(Class<?>... configurationClasses) {
         this();
         configure().addConfigurationClass(configurationClasses);
@@ -97,6 +103,17 @@ public abstract class MainSupport extends BaseMainSupport {
     }
 
     private void internalBeforeStart() {
+        // used while waiting to be done
+        durationMaxIdleSeconds = mainConfigurationProperties.getDurationMaxIdleSeconds();
+        durationMaxMessages = mainConfigurationProperties.getDurationMaxMessages();
+        durationMaxSeconds = mainConfigurationProperties.getDurationMaxSeconds();
+        durationHitExitCode = mainConfigurationProperties.getDurationHitExitCode();
+
+        // register main as bootstrap
+        CamelContext context = getCamelContext();
+        if (context != null) {
+            context.adapt(ExtendedCamelContext.class).addBootstrap(new MainBootstrapCloseable(this));
+        }
     }
 
     /**
@@ -271,13 +288,14 @@ public abstract class MainSupport extends BaseMainSupport {
     protected void waitUntilCompleted() {
         while (shutdownStrategy.isRunAllowed()) {
             try {
-                int idle = mainConfigurationProperties.getDurationMaxIdleSeconds();
-                int max = mainConfigurationProperties.getDurationMaxMessages();
-                long sec = mainConfigurationProperties.getDurationMaxSeconds();
+                int idle = durationMaxIdleSeconds;
+                int max = durationMaxMessages;
+                long sec = durationMaxSeconds;
+                int exit = durationHitExitCode;
                 if (sec > 0) {
                     LOG.info("Waiting for: {} seconds", sec);
                     shutdownStrategy.await(sec, TimeUnit.SECONDS);
-                    exitCode.compareAndSet(UNINITIALIZED_EXIT_CODE, mainConfigurationProperties.getDurationHitExitCode());
+                    exitCode.compareAndSet(UNINITIALIZED_EXIT_CODE, exit);
                     shutdownStrategy.shutdown();
                 } else if (idle > 0 || max > 0) {
                     if (idle > 0 && max > 0) {
@@ -287,7 +305,7 @@ public abstract class MainSupport extends BaseMainSupport {
                     } else {
                         LOG.info("Waiting until: {} messages has been processed", max);
                     }
-                    exitCode.compareAndSet(UNINITIALIZED_EXIT_CODE, mainConfigurationProperties.getDurationHitExitCode());
+                    exitCode.compareAndSet(UNINITIALIZED_EXIT_CODE, exit);
                     shutdownStrategy.await();
                     shutdownStrategy.shutdown();
                 } else {
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/Resilience4jConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/Resilience4jConfigurationProperties.java
index b619e27..caeca39 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/Resilience4jConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/Resilience4jConfigurationProperties.java
@@ -18,6 +18,7 @@ package org.apache.camel.main;
 
 import java.util.concurrent.ForkJoinPool;
 
+import org.apache.camel.spi.BootstrapCloseable;
 import org.apache.camel.spi.Configurer;
 import org.apache.camel.spi.Metadata;
 
@@ -25,9 +26,9 @@ import org.apache.camel.spi.Metadata;
  * Global configuration for Resilience EIP circuit breaker.
  */
 @Configurer(bootstrap = true)
-public class Resilience4jConfigurationProperties {
+public class Resilience4jConfigurationProperties implements BootstrapCloseable {
 
-    private final MainConfigurationProperties parent;
+    private MainConfigurationProperties parent;
 
     private String circuitBreakerRef;
     private String configRef;
@@ -69,6 +70,11 @@ public class Resilience4jConfigurationProperties {
         return parent;
     }
 
+    @Override
+    public void close() {
+        parent = null;
+    }
+
     // getter and setters
     // --------------------------------------------------------------
 
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/RestConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/RestConfigurationProperties.java
index 3f7e762..959e60e 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/RestConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/RestConfigurationProperties.java
@@ -18,6 +18,7 @@ package org.apache.camel.main;
 
 import java.util.HashMap;
 
+import org.apache.camel.spi.BootstrapCloseable;
 import org.apache.camel.spi.Configurer;
 import org.apache.camel.spi.RestConfiguration;
 import org.apache.camel.support.PatternHelper;
@@ -26,9 +27,9 @@ import org.apache.camel.support.PatternHelper;
  * Global configuration for Rest DSL.
  */
 @Configurer(bootstrap = true)
-public class RestConfigurationProperties extends RestConfiguration {
+public class RestConfigurationProperties extends RestConfiguration implements BootstrapCloseable {
 
-    private final MainConfigurationProperties parent;
+    private MainConfigurationProperties parent;
 
     public RestConfigurationProperties(MainConfigurationProperties parent) {
         this.parent = parent;
@@ -38,6 +39,17 @@ public class RestConfigurationProperties extends RestConfiguration {
         return parent;
     }
 
+    @Override
+    public void close() {
+        parent = null;
+        setComponentProperties(null);
+        setEndpointProperties(null);
+        setConsumerProperties(null);
+        setDataFormatProperties(null);
+        setApiProperties(null);
+        setCorsHeaders(null);
+    }
+
     // getter and setters
     // --------------------------------------------------------------
 
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/ThreadPoolConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/ThreadPoolConfigurationProperties.java
index 136c55a..d37d140 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/ThreadPoolConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/ThreadPoolConfigurationProperties.java
@@ -20,6 +20,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.camel.spi.BootstrapCloseable;
 import org.apache.camel.spi.Configurer;
 import org.apache.camel.util.concurrent.ThreadPoolRejectedPolicy;
 
@@ -27,9 +28,9 @@ import org.apache.camel.util.concurrent.ThreadPoolRejectedPolicy;
  * Global configuration for thread pools
  */
 @Configurer(bootstrap = true)
-public class ThreadPoolConfigurationProperties {
+public class ThreadPoolConfigurationProperties implements BootstrapCloseable {
 
-    private final MainConfigurationProperties parent;
+    private MainConfigurationProperties parent;
 
     // default values
     private Integer poolSize;
@@ -51,6 +52,13 @@ public class ThreadPoolConfigurationProperties {
         return parent;
     }
 
+    @Override
+    public void close() {
+        parent = null;
+        config.clear();
+        config = null;
+    }
+
     public Integer getPoolSize() {
         return poolSize;
     }
diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MainSupportCommandLineTest.java b/core/camel-main/src/test/java/org/apache/camel/main/MainSupportCommandLineTest.java
index 50e7b53..f483187 100644
--- a/core/camel-main/src/test/java/org/apache/camel/main/MainSupportCommandLineTest.java
+++ b/core/camel-main/src/test/java/org/apache/camel/main/MainSupportCommandLineTest.java
@@ -34,7 +34,12 @@ public class MainSupportCommandLineTest {
 
         @Override
         protected CamelContext createCamelContext() {
-            return null;
+            return context;
+        }
+
+        @Override
+        public CamelContext getCamelContext() {
+            return context;
         }
     }
 
diff --git a/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java b/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java
index aacd7c7..13daedc 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java
@@ -57,6 +57,10 @@ public final class TimeUtils {
      * @return         the time used for displaying on screen or in logs
      */
     public static String printDuration(long uptime, boolean precise) {
+        if (uptime <= 0) {
+            return "0ms";
+        }
+
         StringBuilder sb = new StringBuilder();
 
         long seconds = uptime / 1000;