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

jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git


The following commit(s) were added to refs/heads/main by this push:
     new 00b72624363 Fixes #9390. Avoid eager loading of all Camel service 
classes at startup (#9391)
00b72624363 is described below

commit 00b72624363db876e0f6cbfb06a497db5bbe39c8
Author: James Netherton <[email protected]>
AuthorDate: Mon Nov 2 16:16:43 2026 +0000

    Fixes #9390. Avoid eager loading of all Camel service classes at startup 
(#9391)
    
    FastFactoryFinderResolver now records service class names and resolves
    each class on first lookup, instead of loading and initializing every
    discovered service class during static init. Service classes are still
    loaded at build time so that an unresolvable entry fails the build.
    
    Co-authored-by: Claude Opus 5.5 <[email protected]>
---
 .../core/deployment/CamelBootstrapProcessor.java   |  4 +-
 .../apache/camel/quarkus/core/CamelRecorder.java   |  4 +-
 .../quarkus/core/FastFactoryFinderResolver.java    | 52 +++++++++++++++-------
 3 files changed, 42 insertions(+), 18 deletions(-)

diff --git 
a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelBootstrapProcessor.java
 
b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelBootstrapProcessor.java
index 0fb42a8209f..51000ce29e3 100644
--- 
a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelBootstrapProcessor.java
+++ 
b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelBootstrapProcessor.java
@@ -134,10 +134,12 @@ class CamelBootstrapProcessor {
         RuntimeValue<Builder> builder = 
recorder.factoryFinderResolverBuilder();
 
         camelServices.forEach(service -> {
+            // Fail the build on unresolvable service classes, as they are 
only loaded on demand at runtime
+            CamelSupport.loadClass(service.type, TCCL);
             recorder.factoryFinderResolverEntry(
                     builder,
                     FileUtils.nixifyPath(service.path),
-                    CamelSupport.loadClass(service.type, TCCL));
+                    service.type);
         });
 
         return new 
CamelFactoryFinderResolverBuildItem(recorder.factoryFinderResolver(builder));
diff --git 
a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRecorder.java
 
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRecorder.java
index e182f313c4c..3a303d47a22 100644
--- 
a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRecorder.java
+++ 
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelRecorder.java
@@ -134,8 +134,8 @@ public class CamelRecorder {
         return new RuntimeValue<>(new FastFactoryFinderResolver.Builder());
     }
 
-    public void factoryFinderResolverEntry(RuntimeValue<Builder> builder, 
String resourcePath, Class<?> cl) {
-        builder.getValue().entry(resourcePath, cl);
+    public void factoryFinderResolverEntry(RuntimeValue<Builder> builder, 
String resourcePath, String className) {
+        builder.getValue().entry(resourcePath, className);
     }
 
     public RuntimeValue<FactoryFinderResolver> 
factoryFinderResolver(RuntimeValue<Builder> builder) {
diff --git 
a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastFactoryFinderResolver.java
 
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastFactoryFinderResolver.java
index 558daa58132..b3c1fc91a24 100644
--- 
a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastFactoryFinderResolver.java
+++ 
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastFactoryFinderResolver.java
@@ -16,11 +16,13 @@
  */
 package org.apache.camel.quarkus.core;
 
-import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
 
+import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.impl.engine.DefaultFactoryFinderResolver;
 import org.apache.camel.spi.ClassResolver;
 import org.apache.camel.spi.FactoryFinder;
@@ -29,14 +31,18 @@ import org.apache.camel.support.ObjectHelper;
 import org.jboss.logging.Logger;
 
 /**
- * A build time assembled {@link FactoryFinderResolver}.
+ * A build time assembled {@link FactoryFinderResolver}. Classes are loaded on 
first lookup so that service classes the
+ * application never uses are neither loaded nor initialized at startup.
  */
 public class FastFactoryFinderResolver extends DefaultFactoryFinderResolver {
     private static final Logger LOG = 
Logger.getLogger(FastFactoryFinderResolver.class);
-    private final Map<String, Class<?>> classMap;
+    private final Map<String, String> classNames;
+    private final Map<String, Class<?>> classMap = new ConcurrentHashMap<>();
+    private final ClassLoader classLoader;
 
-    FastFactoryFinderResolver(Map<String, Class<?>> classMap) {
-        this.classMap = classMap;
+    FastFactoryFinderResolver(Map<String, String> classNames, ClassLoader 
classLoader) {
+        this.classNames = classNames;
+        this.classLoader = classLoader;
     }
 
     static String mapKey(String resourcePath, String key) {
@@ -70,29 +76,45 @@ public class FastFactoryFinderResolver extends 
DefaultFactoryFinderResolver {
 
     // exposed for testing purpose
     public Map<String, Class<?>> getClassMap() {
-        return Collections.unmodifiableMap(this.classMap);
+        Map<String, Class<?>> result = new LinkedHashMap<>();
+        classNames.keySet().forEach(key -> result.put(key, resolve(key)));
+        return result;
+    }
+
+    private Class<?> resolve(String mapKey) {
+        final String className = classNames.get(mapKey);
+        if (className == null) {
+            return null;
+        }
+        return classMap.computeIfAbsent(mapKey, k -> {
+            try {
+                return Class.forName(className, false, classLoader);
+            } catch (ClassNotFoundException e) {
+                throw new RuntimeCamelException("Could not load " + className 
+ " for " + mapKey, e);
+            }
+        });
     }
 
     public static class Builder {
-        private Map<String, Class<?>> classMap = new HashMap<>();
+        private Map<String, String> classNames = new HashMap<>();
 
-        public Builder entry(String resourcePath, Class<?> cl) {
+        public Builder entry(String resourcePath, String className) {
             if (resourcePath.startsWith("/")) {
                 resourcePath = resourcePath.substring(1);
             }
-            classMap.put(resourcePath, cl);
+            classNames.put(resourcePath, className);
             return this;
         }
 
         public FastFactoryFinderResolver build() {
-            Map<String, Class<?>> cm = classMap;
-            classMap = null; // make sure the classMap does not leak through 
re-using the builder
+            Map<String, String> cn = classNames;
+            classNames = null; // make sure the classNames does not leak 
through re-using the builder
 
             if (LOG.isDebugEnabled()) {
-                cm.forEach((key, value) -> LOG.debugf("FactoryFinder entry " + 
key + ": " + value.getName()));
+                cn.forEach((key, value) -> LOG.debugf("FactoryFinder entry " + 
key + ": " + value));
             }
 
-            return new FastFactoryFinderResolver(cm);
+            return new FastFactoryFinderResolver(cn, 
Thread.currentThread().getContextClassLoader());
         }
     }
 
@@ -122,7 +144,7 @@ public class FastFactoryFinderResolver extends 
DefaultFactoryFinderResolver {
         @Override
         public Optional<Class<?>> findClass(String key) {
             final String mapKey = mapKey(path, key);
-            final Class<?> cl = classMap.get(mapKey);
+            final Class<?> cl = resolve(mapKey);
             LOG.tracef("Found a non-optional class for key %s: %s", mapKey, cl 
== null ? "null" : cl.getName());
             return Optional.ofNullable(cl);
         }
@@ -130,7 +152,7 @@ public class FastFactoryFinderResolver extends 
DefaultFactoryFinderResolver {
         @Override
         public Optional<Class<?>> findOptionalClass(String key) {
             final String mapKey = mapKey(path, key);
-            final Class<?> cl = classMap.get(mapKey);
+            final Class<?> cl = resolve(mapKey);
             LOG.tracef("Found an optional class for key %s: %s", mapKey, cl == 
null ? "null" : cl.getName());
             return Optional.ofNullable(cl);
         }

Reply via email to