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);
}