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 2021/03/31 11:51:27 UTC

[camel] branch master updated: CAMEL-16353: camel-main - Add @EagerClassloaded to mark classes which should be eager loaded via camel-main. This optimize to load these classes before Camel is started and otherwise would load these classes on first message processed. A maven plugin scans the code and updates the source code to keep the list of classes to eager loaded automatic up-to-date.

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


The following commit(s) were added to refs/heads/master by this push:
     new 30c760e  CAMEL-16353: camel-main - Add @EagerClassloaded to mark classes which should be eager loaded via camel-main. This optimize to load these classes before Camel is started and otherwise would load these classes on first message processed. A maven plugin scans the code and updates the source code to keep the list of classes to eager loaded automatic up-to-date.
30c760e is described below

commit 30c760e15ad0aef26021408e484ede6851e21de8
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Mar 31 13:50:04 2021 +0200

    CAMEL-16353: camel-main - Add @EagerClassloaded to mark classes which should be eager loaded via camel-main. This optimize to load these classes before Camel is started and otherwise would load these classes on first message processed. A maven plugin scans the code and updates the source code to keep the list of classes to eager loaded automatic up-to-date.
---
 .../camel/spi/annotations/EagerClassloaded.java}   |  35 ++---
 .../org/apache/camel/spi/UnitOfWorkFactory.java    |   4 -
 .../camel/impl/engine/CamelInternalProcessor.java  |  22 ++-
 .../impl/engine/DefaultExchangeFactoryManager.java |  17 ---
 .../camel/impl/engine/DefaultUnitOfWork.java       |   6 +-
 .../impl/engine/DefaultUnitOfWorkFactory.java      |   6 -
 .../java/org/apache/camel/processor/Pipeline.java  |  23 +++-
 .../org/apache/camel/processor/PipelineHelper.java |   6 +-
 .../errorhandler/DefaultErrorHandler.java          |  17 +++
 .../errorhandler/RedeliveryErrorHandler.java       |  29 +++-
 core/camel-main/pom.xml                            |   7 +
 .../MainConfigurationPropertiesConfigurer.java     |   6 +
 .../camel-main-configuration-metadata.json         |   1 +
 core/camel-main/src/main/docs/main.adoc            |   1 +
 .../org/apache/camel/main/BaseMainSupport.java     |   8 +-
 .../camel/main/DefaultConfigurationProperties.java |  25 ++++
 .../apache/camel/main/EagerClassloadedHelper.java  |  56 ++++++++
 .../org/apache/camel/support/ExchangeHelper.java   |   6 +-
 .../org/apache/camel/support/MessageHelper.java    |   7 +
 .../org/apache/camel/support/UnitOfWorkHelper.java |   6 +-
 .../packaging/UpdateEagerClassloadedHelper.java    | 150 +++++++++++++++++++++
 .../camel/spi/annotations/EagerClassloaded.java    |  35 ++---
 22 files changed, 372 insertions(+), 101 deletions(-)

diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWorkFactory.java b/core/camel-api/src/generated/java/org/apache/camel/spi/annotations/EagerClassloaded.java
similarity index 54%
copy from core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWorkFactory.java
copy to core/camel-api/src/generated/java/org/apache/camel/spi/annotations/EagerClassloaded.java
index ab76ae1..65246b1 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWorkFactory.java
+++ b/core/camel-api/src/generated/java/org/apache/camel/spi/annotations/EagerClassloaded.java
@@ -14,32 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.spi;
+package org.apache.camel.spi.annotations;
 
-import org.apache.camel.AfterPropertiesConfigured;
-import org.apache.camel.CamelContext;
-import org.apache.camel.Exchange;
-import org.slf4j.Logger;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
- * Factory to create {@link org.apache.camel.spi.UnitOfWork}.
+ * Marks this class to be eager loaded by the JDK classloader so the class is already loaded when Camel is started.
+ *
+ * This is intended to assist required classes that Camel always uses.
  */
-public interface UnitOfWorkFactory extends AfterPropertiesConfigured {
-
-    /**
-     * Creates a new {@link UnitOfWork}
-     *
-     * @param  exchange the exchange
-     * @return          the created {@link UnitOfWork}
-     */
-    UnitOfWork createUnitOfWork(Exchange exchange);
-
-    @Override
-    default void afterPropertiesConfigured(CamelContext camelContext) {
-        // noop
-    }
-
-    default void warmup(Logger log) {
-        // noop
-    }
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EagerClassloaded {
 }
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWorkFactory.java b/core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWorkFactory.java
index ab76ae1..12f116a 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWorkFactory.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWorkFactory.java
@@ -19,7 +19,6 @@ package org.apache.camel.spi;
 import org.apache.camel.AfterPropertiesConfigured;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
-import org.slf4j.Logger;
 
 /**
  * Factory to create {@link org.apache.camel.spi.UnitOfWork}.
@@ -39,7 +38,4 @@ public interface UnitOfWorkFactory extends AfterPropertiesConfigured {
         // noop
     }
 
-    default void warmup(Logger log) {
-        // noop
-    }
 }
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
index a37ff89..8cf3552 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
@@ -56,6 +56,7 @@ import org.apache.camel.spi.Tracer;
 import org.apache.camel.spi.Transformer;
 import org.apache.camel.spi.UnitOfWork;
 import org.apache.camel.spi.UnitOfWorkFactory;
+import org.apache.camel.spi.annotations.EagerClassloaded;
 import org.apache.camel.support.CamelContextHelper;
 import org.apache.camel.support.ExchangeHelper;
 import org.apache.camel.support.MessageHelper;
@@ -95,6 +96,7 @@ import org.slf4j.LoggerFactory;
  * <p/>
  * The added advices can implement {@link Ordered} to control in which order the advices are executed.
  */
+@EagerClassloaded
 public class CamelInternalProcessor extends DelegateAsyncProcessor implements InternalProcessor {
 
     private static final Logger LOG = LoggerFactory.getLogger(CamelInternalProcessor.class);
@@ -122,18 +124,26 @@ public class CamelInternalProcessor extends DelegateAsyncProcessor implements In
         this.shutdownStrategy = camelContext.getShutdownStrategy();
     }
 
+    private CamelInternalProcessor(Logger log) {
+        // used for eager loading
+        camelContext = null;
+        reactiveExecutor = null;
+        shutdownStrategy = null;
+        AsyncAfterTask task = new AsyncAfterTask(null);
+        log.trace("Loaded {}", task.getClass().getSimpleName());
+    }
+
+    public static void onClassloaded(Logger log) {
+        CamelInternalProcessor dummy = new CamelInternalProcessor(log);
+        log.trace("Loaded {}", dummy.getClass().getSimpleName());
+    }
+
     @Override
     protected void doBuild() throws Exception {
         boolean pooled = camelContext.adapt(ExtendedCamelContext.class).getExchangeFactory().isPooled();
 
         // only create pooled task factory
         if (pooled) {
-
-            // force to create and load the class during build time so the JVM does not
-            // load the class on first exchange to be created
-            AsyncAfterTask dummy = new AsyncAfterTask(null);
-            LOG.trace("Warming up CamelInternalPooledTaskFactory loaded class: {}", dummy.getClass().getName());
-
             taskFactory = new CamelInternalPooledTaskFactory();
             int capacity = camelContext.adapt(ExtendedCamelContext.class).getExchangeFactory().getCapacity();
             taskFactory.setCapacity(capacity);
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultExchangeFactoryManager.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultExchangeFactoryManager.java
index 1568966..eb3aa1e 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultExchangeFactoryManager.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultExchangeFactoryManager.java
@@ -24,19 +24,12 @@ import java.util.concurrent.ConcurrentHashMap;
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.Consumer;
-import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.spi.ExchangeFactory;
 import org.apache.camel.spi.ExchangeFactoryManager;
-import org.apache.camel.support.ExchangeHelper;
-import org.apache.camel.support.UnitOfWorkHelper;
 import org.apache.camel.support.service.ServiceSupport;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class DefaultExchangeFactoryManager extends ServiceSupport implements ExchangeFactoryManager, CamelContextAware {
 
-    private static final Logger LOG = LoggerFactory.getLogger(DefaultExchangeFactoryManager.class);
-
     private final Map<Consumer, ExchangeFactory> factories = new ConcurrentHashMap<>();
     private final UtilizationStatistics statistics = new UtilizationStatistics();
     private CamelContext camelContext;
@@ -173,16 +166,6 @@ public class DefaultExchangeFactoryManager extends ServiceSupport implements Exc
     }
 
     @Override
-    protected void doBuild() throws Exception {
-        super.doBuild();
-        // force to create and load the class during build time so the JVM does not
-        // load the class on first exchange to be created
-        ExchangeHelper.warmup(LOG);
-        UnitOfWorkHelper.warmup(LOG);
-        camelContext.adapt(ExtendedCamelContext.class).getUnitOfWorkFactory().warmup(LOG);
-    }
-
-    @Override
     protected void doShutdown() throws Exception {
         factories.clear();
     }
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUnitOfWork.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUnitOfWork.java
index f8f143a..10b4d21 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUnitOfWork.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUnitOfWork.java
@@ -37,6 +37,7 @@ import org.apache.camel.spi.InflightRepository;
 import org.apache.camel.spi.Synchronization;
 import org.apache.camel.spi.SynchronizationVetoable;
 import org.apache.camel.spi.UnitOfWork;
+import org.apache.camel.spi.annotations.EagerClassloaded;
 import org.apache.camel.support.DefaultMessage;
 import org.apache.camel.support.EventHelper;
 import org.apache.camel.support.MessageSupport;
@@ -47,6 +48,7 @@ import org.slf4j.LoggerFactory;
 /**
  * The default implementation of {@link org.apache.camel.spi.UnitOfWork}
  */
+@EagerClassloaded
 public class DefaultUnitOfWork implements UnitOfWork {
     private static final Logger LOG = LoggerFactory.getLogger(DefaultUnitOfWork.class);
 
@@ -84,8 +86,8 @@ public class DefaultUnitOfWork implements UnitOfWork {
         doOnPrepare(exchange);
     }
 
-    static void warmup(Logger log) {
-        log.trace("Warming up DefaultUnitOfWork");
+    public static void onClassloaded(Logger log) {
+        log.trace("Loaded DefaultUnitOfWork");
     }
 
     UnitOfWork newInstance(Exchange exchange) {
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUnitOfWorkFactory.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUnitOfWorkFactory.java
index 78a36eb..dd91419 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUnitOfWorkFactory.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUnitOfWorkFactory.java
@@ -21,7 +21,6 @@ import org.apache.camel.Exchange;
 import org.apache.camel.spi.InflightRepository;
 import org.apache.camel.spi.UnitOfWork;
 import org.apache.camel.spi.UnitOfWorkFactory;
-import org.slf4j.Logger;
 
 /**
  * Default {@link org.apache.camel.spi.UnitOfWorkFactory}
@@ -35,11 +34,6 @@ public class DefaultUnitOfWorkFactory implements UnitOfWorkFactory {
     private boolean useBreadcrumb;
 
     @Override
-    public void warmup(Logger log) {
-        DefaultUnitOfWork.warmup(log);
-    }
-
-    @Override
     public UnitOfWork createUnitOfWork(Exchange exchange) {
         UnitOfWork answer;
         if (usedMDCLogging) {
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/Pipeline.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/Pipeline.java
index 557afa3..06e69f6 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/Pipeline.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/Pipeline.java
@@ -32,6 +32,7 @@ import org.apache.camel.Traceable;
 import org.apache.camel.spi.IdAware;
 import org.apache.camel.spi.ReactiveExecutor;
 import org.apache.camel.spi.RouteIdAware;
+import org.apache.camel.spi.annotations.EagerClassloaded;
 import org.apache.camel.support.AsyncProcessorConverterHelper;
 import org.apache.camel.support.AsyncProcessorSupport;
 import org.apache.camel.support.ExchangeHelper;
@@ -45,6 +46,7 @@ import static org.apache.camel.processor.PipelineHelper.continueProcessing;
  * Creates a Pipeline pattern where the output of the previous step is sent as input to the next step, reusing the same
  * message exchanges
  */
+@EagerClassloaded
 public class Pipeline extends AsyncProcessorSupport implements Navigate<Processor>, Traceable, IdAware, RouteIdAware {
 
     private static final Logger LOG = LoggerFactory.getLogger(Pipeline.class);
@@ -130,6 +132,21 @@ public class Pipeline extends AsyncProcessorSupport implements Navigate<Processo
         this.size = processors.size();
     }
 
+    private Pipeline(Logger log) {
+        // used for eager loading
+        camelContext = null;
+        reactiveExecutor = null;
+        processors = null;
+        size = 0;
+        PipelineTask task = new PipelineTask();
+        log.trace("Loaded {}", task.getClass().getSimpleName());
+    }
+
+    public static void onClassloaded(Logger log) {
+        Pipeline dummy = new Pipeline(log);
+        log.trace("Loaded {}", dummy.getClass().getSimpleName());
+    }
+
     public static Processor newInstance(CamelContext camelContext, List<Processor> processors) {
         if (processors.isEmpty()) {
             return null;
@@ -171,12 +188,6 @@ public class Pipeline extends AsyncProcessorSupport implements Navigate<Processo
 
     @Override
     protected void doBuild() throws Exception {
-        // force to create and load the class during build time so the JVM does not
-        // load the class on first exchange to be created
-        PipelineHelper.warmup(LOG);
-        PipelineTask dummy = new PipelineTask();
-        LOG.trace("Warming up Pipeline loaded class: {}", dummy.getClass().getName());
-
         boolean pooled = camelContext.adapt(ExtendedCamelContext.class).getExchangeFactory().isPooled();
         if (pooled) {
             taskFactory = new PooledTaskFactory(getId()) {
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/PipelineHelper.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/PipelineHelper.java
index 6f4b0fd..2e15151 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/PipelineHelper.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/PipelineHelper.java
@@ -18,19 +18,21 @@ package org.apache.camel.processor;
 
 import org.apache.camel.Exchange;
 import org.apache.camel.ExtendedExchange;
+import org.apache.camel.spi.annotations.EagerClassloaded;
 import org.slf4j.Logger;
 
 /**
  * Helper for processing {@link org.apache.camel.Exchange} in a
  * <a href="http://camel.apache.org/pipes-and-filters.html">pipeline</a>.
  */
+@EagerClassloaded
 public final class PipelineHelper {
 
     private PipelineHelper() {
     }
 
-    public static void warmup(Logger log) {
-        log.trace("Warming up PipelineHelper");
+    public static void onClassloaded(Logger log) {
+        log.trace("Loaded PipelineHelper");
     }
 
     /**
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/DefaultErrorHandler.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/DefaultErrorHandler.java
index 7d5ffd5..0046fd7 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/DefaultErrorHandler.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/DefaultErrorHandler.java
@@ -24,11 +24,14 @@ import org.apache.camel.Predicate;
 import org.apache.camel.Processor;
 import org.apache.camel.spi.CamelLogger;
 import org.apache.camel.spi.ErrorHandler;
+import org.apache.camel.spi.annotations.EagerClassloaded;
+import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * Default error handler
  */
+@EagerClassloaded
 public class DefaultErrorHandler extends RedeliveryErrorHandler {
 
     private static final CamelLogger DEFAULT_LOGGER
@@ -61,6 +64,20 @@ public class DefaultErrorHandler extends RedeliveryErrorHandler {
               executorService, onPrepareProcessor, onExceptionOccurredProcessor);
     }
 
+    private DefaultErrorHandler(Logger log) {
+        // used for eager loading
+        super(log);
+        SimpleTask dummy = new SimpleTask();
+        log.trace("Loaded {}", dummy.getClass().getName());
+        RedeliveryTask dummy2 = new RedeliveryTask();
+        log.trace("Loaded {}", dummy2.getClass().getName());
+    }
+
+    public static void onClassloaded(Logger log) {
+        DefaultErrorHandler dummy = new DefaultErrorHandler(log);
+        log.trace("Loaded {}", dummy.getClass().getName());
+    }
+
     @Override
     public ErrorHandler clone(Processor output) {
         DefaultErrorHandler answer = new DefaultErrorHandler(
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/RedeliveryErrorHandler.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/RedeliveryErrorHandler.java
index 26f03b8..23f68a9 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/RedeliveryErrorHandler.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/RedeliveryErrorHandler.java
@@ -162,6 +162,30 @@ public abstract class RedeliveryErrorHandler extends ErrorHandlerSupport
         }
     }
 
+    RedeliveryErrorHandler(Logger log) {
+        // used for eager loading
+        camelContext = null;
+        reactiveExecutor = null;
+        awaitManager = null;
+        shutdownStrategy = null;
+        deadLetter = null;
+        deadLetterUri = null;
+        deadLetterHandleNewException = false;
+        redeliveryProcessor = null;
+        redeliveryPolicy = null;
+        retryWhilePolicy = null;
+        logger = null;
+        useOriginalMessagePolicy = false;
+        useOriginalBodyPolicy = false;
+        redeliveryEnabled = false;
+        simpleTask = false;
+        exchangeFormatter = null;
+        customExchangeFormatter = false;
+        onPrepareProcessor = null;
+        onExceptionProcessor = null;
+        log.trace("Loaded {}", RedeliveryErrorHandler.class.getName());
+    }
+
     @Override
     public void process(Exchange exchange) {
         if (output == null) {
@@ -1590,11 +1614,6 @@ public abstract class RedeliveryErrorHandler extends ErrorHandlerSupport
         simpleTask = deadLetter == null && !redeliveryEnabled && (exceptionPolicies == null || exceptionPolicies.isEmpty())
                 && onPrepareProcessor == null;
 
-        // force to create and load the class during build time so the JVM does not
-        // load the class on first exchange to be created
-        Object dummy = simpleTask ? new SimpleTask() : new RedeliveryTask();
-        LOG.trace("Warming up RedeliveryErrorHandler loaded class: {}", dummy.getClass().getName());
-
         boolean pooled = camelContext.adapt(ExtendedCamelContext.class).getExchangeFactory().isPooled();
         if (pooled) {
             String id = output instanceof IdAware ? ((IdAware) output).getId() : output.toString();
diff --git a/core/camel-main/pom.xml b/core/camel-main/pom.xml
index 4668558..43461bd 100644
--- a/core/camel-main/pom.xml
+++ b/core/camel-main/pom.xml
@@ -152,6 +152,13 @@
                         </goals>
                     </execution>
                     <execution>
+                        <id>update-classloaded</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>update-classloaded-helper</goal>
+                        </goals>
+                    </execution>
+                    <execution>
                         <id>generate-test-configurer</id>
                         <phase>process-test-classes</phase>
                         <goals>
diff --git a/core/camel-main/src/generated/java/org/apache/camel/main/MainConfigurationPropertiesConfigurer.java b/core/camel-main/src/generated/java/org/apache/camel/main/MainConfigurationPropertiesConfigurer.java
index 5d7b3a6..cdad189 100644
--- a/core/camel-main/src/generated/java/org/apache/camel/main/MainConfigurationPropertiesConfigurer.java
+++ b/core/camel-main/src/generated/java/org/apache/camel/main/MainConfigurationPropertiesConfigurer.java
@@ -61,6 +61,8 @@ public class MainConfigurationPropertiesConfigurer extends org.apache.camel.supp
         case "DurationMaxMessages": target.setDurationMaxMessages(property(camelContext, int.class, value)); return true;
         case "durationmaxseconds":
         case "DurationMaxSeconds": target.setDurationMaxSeconds(property(camelContext, int.class, value)); return true;
+        case "eagerclassloading":
+        case "EagerClassloading": target.setEagerClassloading(property(camelContext, boolean.class, value)); return true;
         case "endpointbridgeerrorhandler":
         case "EndpointBridgeErrorHandler": target.setEndpointBridgeErrorHandler(property(camelContext, boolean.class, value)); return true;
         case "endpointlazystartproducer":
@@ -250,6 +252,8 @@ public class MainConfigurationPropertiesConfigurer extends org.apache.camel.supp
         case "DurationMaxMessages": return int.class;
         case "durationmaxseconds":
         case "DurationMaxSeconds": return int.class;
+        case "eagerclassloading":
+        case "EagerClassloading": return boolean.class;
         case "endpointbridgeerrorhandler":
         case "EndpointBridgeErrorHandler": return boolean.class;
         case "endpointlazystartproducer":
@@ -440,6 +444,8 @@ public class MainConfigurationPropertiesConfigurer extends org.apache.camel.supp
         case "DurationMaxMessages": return target.getDurationMaxMessages();
         case "durationmaxseconds":
         case "DurationMaxSeconds": return target.getDurationMaxSeconds();
+        case "eagerclassloading":
+        case "EagerClassloading": return target.isEagerClassloading();
         case "endpointbridgeerrorhandler":
         case "EndpointBridgeErrorHandler": return target.isEndpointBridgeErrorHandler();
         case "endpointlazystartproducer":
diff --git a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
index b6089dc..953a13e 100644
--- a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
+++ b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
@@ -30,6 +30,7 @@
     { "name": "camel.main.durationMaxIdleSeconds", "description": "To specify for how long time in seconds Camel can be idle before automatic terminating the JVM. You can use this to run Camel for a short while.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "integer", "javaType": "int" },
     { "name": "camel.main.durationMaxMessages", "description": "To specify how many messages to process by Camel before automatic terminating the JVM. You can use this to run Camel for a short while.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "integer", "javaType": "int" },
     { "name": "camel.main.durationMaxSeconds", "description": "To specify for how long time in seconds to keep running the JVM before automatic terminating the JVM. You can use this to run Camel for a short while.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "integer", "javaType": "int" },
+    { "name": "camel.main.eagerClassloading", "description": "Whether to eager load a common set of Camel classes that would otherwise first be loaded on processing the first message. By eager loading these classes then the JVM has already loaded the classes during build phase, which allows Camel to process the first message faster.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean" },
     { "name": "camel.main.endpointBridgeErrorHandler", "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN\/ERROR level and ignored. The default va [...]
     { "name": "camel.main.endpointLazyStartProducer", "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first mes [...]
     { "name": "camel.main.endpointRuntimeStatisticsEnabled", "description": "Sets whether endpoint runtime statistics is enabled (gathers runtime usage of each incoming and outgoing endpoints). The default value is false.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean" },
diff --git a/core/camel-main/src/main/docs/main.adoc b/core/camel-main/src/main/docs/main.adoc
index 65547e6..a4fc685 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -42,6 +42,7 @@ The following table lists all the options:
 | *camel.main.durationMaxIdle{zwsp}Seconds* | To specify for how long time in seconds Camel can be idle before automatic terminating the JVM. You can use this to run Camel for a short while. |  | int
 | *camel.main.durationMaxMessages* | To specify how many messages to process by Camel before automatic terminating the JVM. You can use this to run Camel for a short while. |  | int
 | *camel.main.durationMaxSeconds* | To specify for how long time in seconds to keep running the JVM before automatic terminating the JVM. You can use this to run Camel for a short while. |  | int
+| *camel.main.eagerClassloading* | Whether to eager load a common set of Camel classes that would otherwise first be loaded on processing the first message. By eager loading these classes then the JVM has already loaded the classes during build phase, which allows Camel to process the first message faster. |  | boolean
 | *camel.main.endpointBridgeError{zwsp}Handler* | Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN/ERROR level and ignored. The default value is false. |  | boolean
 | *camel.main.endpointLazyStart{zwsp}Producer* | Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed the [...]
 | *camel.main.endpointRuntime{zwsp}StatisticsEnabled* | Sets whether endpoint runtime statistics is enabled (gathers runtime usage of each incoming and outgoing endpoints). The default value is false. |  | boolean
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 0b74bc0..fce220b 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
@@ -521,7 +521,7 @@ public abstract class BaseMainSupport extends BaseService {
         // setup startup recorder before building context
         configureStartupRecorder(camelContext);
 
-        // ensure camel is initialized
+        // ensure camel context is build
         camelContext.build();
 
         for (MainListener listener : listeners) {
@@ -540,6 +540,12 @@ public abstract class BaseMainSupport extends BaseService {
         autoconfigure(camelContext);
         recorder.endStep(step);
 
+        if (mainConfigurationProperties.isEagerClassloading()) {
+            step = recorder.beginStep(BaseMainSupport.class, "classloading", "Eager Classloading");
+            EagerClassloadedHelper.eagerLoadClasses();
+            recorder.endStep(step);
+        }
+
         configureLifecycle(camelContext);
 
         step = recorder.beginStep(BaseMainSupport.class, "configureRoutes", "Collect Routes");
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java
index 320588f..759ae3b 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java
@@ -89,6 +89,7 @@ public abstract class DefaultConfigurationProperties<T> {
     private String routesIncludePattern = "classpath:camel/*.xml,classpath:camel-template/*.xml,classpath:camel-rest/*.xml";
     private String routesExcludePattern;
     private boolean lightweight;
+    private boolean eagerClassloading;
     @Metadata(defaultValue = "default", enums = "default,prototype,pooled")
     private String exchangeFactory = "default";
     private int exchangeFactoryCapacity = 100;
@@ -108,6 +109,7 @@ public abstract class DefaultConfigurationProperties<T> {
     private long routeControllerBackOffMaxAttempts;
     private double routeControllerBackOffMultiplier;
     private boolean routeControllerUnhealthyOnExhausted;
+    // startup recorder
     @Metadata(enums = "false,off,java-flight-recorder,jfr,logging")
     private String startupRecorder;
     private int startupRecorderMaxDepth = -1;
@@ -943,6 +945,19 @@ public abstract class DefaultConfigurationProperties<T> {
         this.lightweight = lightweight;
     }
 
+    public boolean isEagerClassloading() {
+        return eagerClassloading;
+    }
+
+    /**
+     * Whether to eager load a common set of Camel classes that would otherwise first be loaded on processing the first
+     * message. By eager loading these classes then the JVM has already loaded the classes during build phase, which
+     * allows Camel to process the first message faster.
+     */
+    public void setEagerClassloading(boolean eagerClassloading) {
+        this.eagerClassloading = eagerClassloading;
+    }
+
     public String getExchangeFactory() {
         return exchangeFactory;
     }
@@ -1835,6 +1850,16 @@ public abstract class DefaultConfigurationProperties<T> {
     }
 
     /**
+     * Whether to eager load a common set of Camel classes that would otherwise first be loaded on processing the first
+     * message. By eager loading these classes then the JVM has already loaded the classes during build phase, which
+     * allows Camel to process the first message faster.
+     */
+    public T withEagerClassloading(boolean eagerClassloading) {
+        this.eagerClassloading = eagerClassloading;
+        return (T) this;
+    }
+
+    /**
      * Controls whether to pool (reuse) exchanges or create new fresh exchanges (default). Using pooled will reduce JVM
      * garbage collection overhead by avoiding to re-create Exchange instances per message each consumer receives.
      */
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/EagerClassloadedHelper.java b/core/camel-main/src/main/java/org/apache/camel/main/EagerClassloadedHelper.java
new file mode 100644
index 0000000..69d0ed5
--- /dev/null
+++ b/core/camel-main/src/main/java/org/apache/camel/main/EagerClassloadedHelper.java
@@ -0,0 +1,56 @@
+/*
+ * 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.main;
+
+import org.apache.camel.util.StopWatch;
+import org.apache.camel.util.TimeUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * To eager load a set of classes that Camel always uses.
+ *
+ * This is for optimization purposes to ensure the classes are loaded before Camel is started and would load the classes
+ * while processing the first messages.
+ */
+public final class EagerClassloadedHelper {
+
+    public static final Logger LOG = LoggerFactory.getLogger(EagerClassloadedHelper.class);
+
+    private EagerClassloadedHelper() {
+    }
+
+    public static void eagerLoadClasses() {
+        StopWatch watch = new StopWatch();
+
+        int count = 0;
+        // EAGER-CLASSLOADED: START
+        count = 8;
+        org.apache.camel.impl.engine.CamelInternalProcessor.onClassloaded(LOG);
+        org.apache.camel.impl.engine.DefaultUnitOfWork.onClassloaded(LOG);
+        org.apache.camel.processor.Pipeline.onClassloaded(LOG);
+        org.apache.camel.processor.PipelineHelper.onClassloaded(LOG);
+        org.apache.camel.processor.errorhandler.DefaultErrorHandler.onClassloaded(LOG);
+        org.apache.camel.support.ExchangeHelper.onClassloaded(LOG);
+        org.apache.camel.support.MessageHelper.onClassloaded(LOG);
+        org.apache.camel.support.UnitOfWorkHelper.onClassloaded(LOG);
+        // EAGER-CLASSLOADED: END
+
+        String time = TimeUtils.printDuration(watch.taken());
+        LOG.info("Eager loaded {} classes in {}", count, time);
+    }
+}
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/ExchangeHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/ExchangeHelper.java
index 7006e7e..7312518 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/ExchangeHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/ExchangeHelper.java
@@ -52,6 +52,7 @@ import org.apache.camel.WrappedFile;
 import org.apache.camel.spi.NormalizedEndpointUri;
 import org.apache.camel.spi.Synchronization;
 import org.apache.camel.spi.UnitOfWork;
+import org.apache.camel.spi.annotations.EagerClassloaded;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.Scanner;
@@ -61,6 +62,7 @@ import org.slf4j.Logger;
 /**
  * Some helper methods for working with {@link Exchange} objects
  */
+@EagerClassloaded
 public final class ExchangeHelper {
 
     /**
@@ -69,8 +71,8 @@ public final class ExchangeHelper {
     private ExchangeHelper() {
     }
 
-    public static void warmup(Logger log) {
-        log.trace("Warming up ExchangeHelper");
+    public static void onClassloaded(Logger log) {
+        log.trace("Loaded ExchangeHelper");
     }
 
     /**
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
index 6ac3d7b..29ff2cf 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
@@ -35,14 +35,17 @@ import org.apache.camel.StreamCache;
 import org.apache.camel.WrappedFile;
 import org.apache.camel.spi.ExchangeFormatter;
 import org.apache.camel.spi.HeaderFilterStrategy;
+import org.apache.camel.spi.annotations.EagerClassloaded;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.StopWatch;
 import org.apache.camel.util.StringHelper;
 import org.apache.camel.util.URISupport;
+import org.slf4j.Logger;
 
 /**
  * Some helper methods when working with {@link org.apache.camel.Message}.
  */
+@EagerClassloaded
 public final class MessageHelper {
 
     private static final String MESSAGE_HISTORY_HEADER = "%-20s %-20s %-80s %-12s";
@@ -54,6 +57,10 @@ public final class MessageHelper {
     private MessageHelper() {
     }
 
+    public static void onClassloaded(Logger log) {
+        log.trace("Loaded MessageHelper");
+    }
+
     /**
      * Extracts the given body and returns it as a String, that can be used for logging etc.
      * <p/>
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/UnitOfWorkHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/UnitOfWorkHelper.java
index 4d99365..47309a4 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/UnitOfWorkHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/UnitOfWorkHelper.java
@@ -25,12 +25,14 @@ import org.apache.camel.Route;
 import org.apache.camel.spi.Synchronization;
 import org.apache.camel.spi.SynchronizationRouteAware;
 import org.apache.camel.spi.UnitOfWork;
+import org.apache.camel.spi.annotations.EagerClassloaded;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * Utility methods for {@link org.apache.camel.spi.UnitOfWork}
  */
+@EagerClassloaded
 public final class UnitOfWorkHelper {
 
     private static final Logger LOG = LoggerFactory.getLogger(UnitOfWorkHelper.class);
@@ -38,8 +40,8 @@ public final class UnitOfWorkHelper {
     private UnitOfWorkHelper() {
     }
 
-    public static void warmup(Logger log) {
-        log.trace("Warming up UnitOfWorkHelper");
+    public static void onClassloaded(Logger log) {
+        log.trace("Loaded UnitOfWorkHelper");
     }
 
     /**
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateEagerClassloadedHelper.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateEagerClassloadedHelper.java
new file mode 100644
index 0000000..24bbc5e
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateEagerClassloadedHelper.java
@@ -0,0 +1,150 @@
+/*
+ * 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.maven.packaging;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Set;
+import java.util.StringJoiner;
+import java.util.TreeSet;
+
+import org.apache.camel.tooling.util.PackageHelper;
+import org.apache.camel.tooling.util.Strings;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.jboss.jandex.AnnotationInstance;
+import org.jboss.jandex.AnnotationTarget;
+import org.jboss.jandex.ClassInfo;
+import org.jboss.jandex.DotName;
+import org.jboss.jandex.Index;
+import org.jboss.jandex.IndexReader;
+
+import static org.apache.camel.tooling.util.PackageHelper.findCamelDirectory;
+
+/**
+ * Updates the EagerClassloadedHelper.java with the class names to eager load when using camel-main.
+ */
+@Mojo(name = "update-classloaded-helper", threadSafe = true)
+public class UpdateEagerClassloadedHelper extends AbstractGeneratorMojo {
+
+    public static final DotName EAGER_CLASSLOADED = DotName.createSimple("org.apache.camel.spi.annotations.EagerClassloaded");
+
+    private static final String[] MODULES = new String[] { "camel-base-engine", "camel-core-processor", "camel-support" };
+
+    private static final String START_TOKEN = "// EAGER-CLASSLOADED: START";
+    private static final String END_TOKEN = "// EAGER-CLASSLOADED: END";
+
+    @Parameter(defaultValue = "${project.basedir}/")
+    protected File baseDir;
+
+    @Override
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        File mainDir = findCamelDirectory(baseDir, "core/camel-main");
+        if (mainDir == null) {
+            getLog().debug("No core/camel-main folder found, skipping execution");
+            return;
+        }
+
+        Set<String> fqns = new TreeSet<>();
+        // discover classes from a set of known core modules
+        for (String p : MODULES) {
+            File dir = findCamelDirectory(baseDir, "core/" + p);
+            if (dir != null && dir.exists() && dir.isDirectory()) {
+                Path output = Paths.get(dir.getAbsolutePath() + "/target/classes");
+                discoverClasses(output, fqns);
+            }
+        }
+
+        if (fqns.isEmpty()) {
+            return;
+        }
+
+        getLog().info("There are " + fqns.size()
+                      + " classes to eager loaded across the Camel core modules");
+        try {
+            boolean updated = updateHelper(mainDir, fqns);
+            if (updated) {
+                getLog().info("Updated camel-main/src/main/java/org/apache/camel/main/EagerClassloadedHelper.java file");
+            } else {
+                getLog().debug("No changes to camel-main/src/main/java/org/apache/camel/main/EagerClassloadedHelper.java file");
+            }
+
+        } catch (Exception e) {
+            throw new MojoExecutionException("Error updating EagerClassloadedHelper.java", e);
+        }
+    }
+
+    private void discoverClasses(Path output, Set<String> fqns) {
+        Index index;
+        try (InputStream is = Files.newInputStream(output.resolve("META-INF/jandex.idx"))) {
+            index = new IndexReader(is).read();
+        } catch (IOException e) {
+            // ignore
+            return;
+        }
+
+        // discover all classes annotated with @EagerClassloaded
+        List<AnnotationInstance> annotations = index.getAnnotations(EAGER_CLASSLOADED);
+        annotations.stream()
+                .filter(annotation -> annotation.target().kind() == AnnotationTarget.Kind.CLASS)
+                .filter(annotation -> annotation.target().asClass().nestingType() == ClassInfo.NestingType.TOP_LEVEL)
+                .forEach(annotation -> {
+                    String fqn = annotation.target().asClass().name().toString();
+                    fqns.add(fqn);
+                });
+    }
+
+    private boolean updateHelper(File camelDir, Set<String> fqns) throws Exception {
+        // load source code and update
+        File java = new File(camelDir, "src/main/java/org/apache/camel/main/EagerClassloadedHelper.java");
+        String text = PackageHelper.loadText(java);
+        String spaces8 = "        ";
+
+        StringJoiner sb = new StringJoiner("\n");
+        sb.add(spaces8 + "count = " + fqns.size() + ";");
+        for (String name : fqns) {
+            sb.add(spaces8 + name + ".onClassloaded(LOG);");
+        }
+        String changed = sb.toString();
+
+        String existing = Strings.between(text, START_TOKEN, END_TOKEN);
+        if (existing != null) {
+            // remove leading line breaks etc
+            existing = existing.trim();
+            changed = changed.trim();
+            if (existing.equals(changed)) {
+                return false;
+            } else {
+                String before = Strings.before(text, START_TOKEN);
+                String after = Strings.after(text, END_TOKEN);
+                text = before + START_TOKEN + "\n" + spaces8 + changed + "\n" + spaces8 + END_TOKEN + after;
+                PackageHelper.writeText(java, text);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+}
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWorkFactory.java b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/annotations/EagerClassloaded.java
similarity index 54%
copy from core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWorkFactory.java
copy to tooling/spi-annotations/src/main/java/org/apache/camel/spi/annotations/EagerClassloaded.java
index ab76ae1..65246b1 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWorkFactory.java
+++ b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/annotations/EagerClassloaded.java
@@ -14,32 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.spi;
+package org.apache.camel.spi.annotations;
 
-import org.apache.camel.AfterPropertiesConfigured;
-import org.apache.camel.CamelContext;
-import org.apache.camel.Exchange;
-import org.slf4j.Logger;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
- * Factory to create {@link org.apache.camel.spi.UnitOfWork}.
+ * Marks this class to be eager loaded by the JDK classloader so the class is already loaded when Camel is started.
+ *
+ * This is intended to assist required classes that Camel always uses.
  */
-public interface UnitOfWorkFactory extends AfterPropertiesConfigured {
-
-    /**
-     * Creates a new {@link UnitOfWork}
-     *
-     * @param  exchange the exchange
-     * @return          the created {@link UnitOfWork}
-     */
-    UnitOfWork createUnitOfWork(Exchange exchange);
-
-    @Override
-    default void afterPropertiesConfigured(CamelContext camelContext) {
-        // noop
-    }
-
-    default void warmup(Logger log) {
-        // noop
-    }
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EagerClassloaded {
 }