This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 4f1c8337a09 CAMEL-19898: camel-core - Optimize StreamCaching strategy
to skip con… (#18191)
4f1c8337a09 is described below
commit 4f1c8337a096fbb7f5d032cff273bda68d8b15bd
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue May 27 14:54:30 2025 +0200
CAMEL-19898: camel-core - Optimize StreamCaching strategy to skip con…
(#18191)
* CAMEL-19898: camel-core - Optimize StreamCaching strategy to skip
conversion for well known types and do fast lookup of dedicated StreamCache
converters, to bypass regular type converter.
---
.../apache/camel/spi/TypeConverterRegistry.java | 13 +++--
.../impl/engine/DefaultStreamCachingStrategy.java | 60 +++++++++++++++++-----
.../impl/converter/CoreTypeConverterRegistry.java | 13 +++++
...TypeConverterRegistryStatisticsEnabledTest.java | 4 +-
.../ManagedTypeConverterRegistryTest.java | 4 +-
.../ROOT/pages/camel-4x-upgrade-guide-4_13.adoc | 5 ++
6 files changed, 80 insertions(+), 19 deletions(-)
diff --git
a/core/camel-api/src/main/java/org/apache/camel/spi/TypeConverterRegistry.java
b/core/camel-api/src/main/java/org/apache/camel/spi/TypeConverterRegistry.java
index 979a37acfe3..58667314a9a 100644
---
a/core/camel-api/src/main/java/org/apache/camel/spi/TypeConverterRegistry.java
+++
b/core/camel-api/src/main/java/org/apache/camel/spi/TypeConverterRegistry.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.spi;
+import java.util.Map;
import java.util.function.LongConsumer;
import java.util.function.LongSupplier;
@@ -125,6 +126,14 @@ public interface TypeConverterRegistry extends
StaticService, CamelContextAware
*/
TypeConverter lookup(Class<?> toType, Class<?> fromType);
+ /**
+ * Lookup the type converters that can convert to a given type
+ *
+ * @param toType the type to convert to
+ * @return the type converters that can convert from
+ */
+ Map<Class<?>, TypeConverter> lookup(Class<?> toType);
+
/**
* Sets the injector to be used for creating new instances during type
conversions.
*
@@ -189,8 +198,6 @@ public interface TypeConverterRegistry extends
StaticService, CamelContextAware
* @param typeConvertible A type convertible pair
* @param typeConverter The type converter to associate with the type
convertible pair
*/
- default void addConverter(TypeConvertible<?, ?> typeConvertible,
TypeConverter typeConverter) {
-
- }
+ void addConverter(TypeConvertible<?, ?> typeConvertible, TypeConverter
typeConverter);
}
diff --git
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultStreamCachingStrategy.java
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultStreamCachingStrategy.java
index d22e9aa0712..c335a76eb5e 100644
---
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultStreamCachingStrategy.java
+++
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultStreamCachingStrategy.java
@@ -33,6 +33,7 @@ import org.apache.camel.CamelContextAware;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.StreamCache;
+import org.apache.camel.TypeConverter;
import org.apache.camel.spi.StreamCachingStrategy;
import org.apache.camel.support.TempDirHelper;
import org.apache.camel.support.service.ServiceSupport;
@@ -46,12 +47,17 @@ import org.slf4j.LoggerFactory;
*/
public class DefaultStreamCachingStrategy extends ServiceSupport implements
CamelContextAware, StreamCachingStrategy {
+ // stream cache type converters
+ private record CoreConverter(Class<?> from, TypeConverter converter) {
+ }
+
private static final Logger LOG =
LoggerFactory.getLogger(DefaultStreamCachingStrategy.class);
private CamelContext camelContext;
private boolean enabled;
private String allowClassNames;
private String denyClassNames;
+ private final Collection<CoreConverter> coreConverters = new ArrayList<>();
private Collection<Class<?>> allowClasses;
private Collection<Class<?>> denyClasses;
private boolean spoolEnabled;
@@ -269,15 +275,33 @@ public class DefaultStreamCachingStrategy extends
ServiceSupport implements Came
StreamCache cache = null;
// try convert to stream cache
if (body != null) {
+ // fast skip some common types that should never be converted
+ if (body instanceof String) {
+ return null;
+ } else if (body instanceof byte[]) {
+ return null;
+ } else if (body instanceof Boolean) {
+ return null;
+ } else if (body instanceof Number) {
+ return null;
+ }
+ Class<?> type = body.getClass();
+ if (type.isPrimitive() || type.isEnum()) {
+ return null;
+ }
+
boolean allowed = allowClasses == null && denyClasses == null;
if (!allowed) {
- allowed = checkAllowDenyList(body);
+ allowed = checkAllowDenyList(type);
}
if (allowed) {
- if (exchange != null) {
- cache =
camelContext.getTypeConverter().convertTo(StreamCache.class, exchange, body);
- } else {
- cache =
camelContext.getTypeConverter().convertTo(StreamCache.class, body);
+ TypeConverter tc = lookupTypeConverter(type);
+ if (tc != null) {
+ if (exchange != null) {
+ cache = tc.convertTo(StreamCache.class, exchange,
body);
+ } else {
+ cache = tc.convertTo(StreamCache.class, body);
+ }
}
}
}
@@ -292,22 +316,30 @@ public class DefaultStreamCachingStrategy extends
ServiceSupport implements Came
return cache;
}
- private boolean checkAllowDenyList(Object body) {
+ private TypeConverter lookupTypeConverter(Class<?> type) {
+ for (var tc : coreConverters) {
+ if (tc.from().isAssignableFrom(type)) {
+ return tc.converter();
+ }
+ }
+ return null;
+ }
+
+ private boolean checkAllowDenyList(Class<?> type) {
boolean allowed;
- Class<?> source = body.getClass();
if (denyClasses != null && allowClasses != null) {
// deny takes precedence
- allowed = !isAssignableFrom(source, denyClasses);
+ allowed = !isAssignableFrom(type, denyClasses);
if (allowed) {
- allowed = isAssignableFrom(source, allowClasses);
+ allowed = isAssignableFrom(type, allowClasses);
}
} else if (denyClasses != null) {
- allowed = !isAssignableFrom(source, denyClasses);
+ allowed = !isAssignableFrom(type, denyClasses);
} else {
- allowed = isAssignableFrom(source, allowClasses);
+ allowed = isAssignableFrom(type, allowClasses);
}
if (LOG.isTraceEnabled()) {
- LOG.trace("Cache stream from class: {} is {}", source, allowed ?
"allowed" : "denied");
+ LOG.trace("Cache stream from class: {} is {}", type, allowed ?
"allowed" : "denied");
}
return allowed;
}
@@ -340,6 +372,10 @@ public class DefaultStreamCachingStrategy extends
ServiceSupport implements Came
return;
}
+ // find core type converters that can convert to StreamCache
+ var set =
getCamelContext().getTypeConverterRegistry().lookup(StreamCache.class).entrySet();
+ set.forEach(e -> coreConverters.add(new CoreConverter(e.getKey(),
e.getValue())));
+
if (allowClassNames != null) {
if (allowClasses == null) {
allowClasses = new ArrayList<>();
diff --git
a/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java
b/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java
index e6cc5048980..47494e5efc3 100644
---
a/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java
+++
b/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.impl.converter;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -565,6 +566,18 @@ public abstract class CoreTypeConverterRegistry extends
ServiceSupport implement
return doLookup(toType, fromType);
}
+ @Override
+ public Map<Class<?>, TypeConverter> lookup(Class<?> toType) {
+ Map<Class<?>, TypeConverter> answer = new LinkedHashMap<>();
+ for (var e : converters.entrySet()) {
+ Class<?> target = e.getKey().getTo();
+ if (target == toType) {
+ answer.put(e.getKey().getFrom(), e.getValue());
+ }
+ }
+ return answer;
+ }
+
@Deprecated(since = "4.0.0")
protected TypeConverter getOrFindTypeConverter(Class<?> toType, Class<?>
fromType) {
TypeConvertible<?, ?> typeConvertible = new
TypeConvertible<>(fromType, toType);
diff --git
a/core/camel-core/src/test/java/org/apache/camel/impl/TypeConverterRegistryStatisticsEnabledTest.java
b/core/camel-core/src/test/java/org/apache/camel/impl/TypeConverterRegistryStatisticsEnabledTest.java
index 0a9dec58add..25a71a1b1fa 100644
---
a/core/camel-core/src/test/java/org/apache/camel/impl/TypeConverterRegistryStatisticsEnabledTest.java
+++
b/core/camel-core/src/test/java/org/apache/camel/impl/TypeConverterRegistryStatisticsEnabledTest.java
@@ -48,7 +48,7 @@ public class TypeConverterRegistryStatisticsEnabledTest
extends ContextTestSuppo
long failed = reg.getStatistics().getFailedCounter();
assertEquals(0, (int) failed);
long miss = reg.getStatistics().getMissCounter();
- assertEquals(4, (int) miss); // stream caching misses
+ assertEquals(0, (int) miss);
assertThrows(Exception.class, () -> template.sendBody("direct:start",
"foo"),
"Should have thrown exception");
@@ -57,7 +57,7 @@ public class TypeConverterRegistryStatisticsEnabledTest
extends ContextTestSuppo
failed = reg.getStatistics().getFailedCounter();
assertEquals(1, (int) failed);
miss = reg.getStatistics().getMissCounter();
- assertEquals(5, (int) miss); // stream caching misses
+ assertEquals(0, (int) miss);
// reset
reg.getStatistics().reset();
diff --git
a/core/camel-management/src/test/java/org/apache/camel/management/ManagedTypeConverterRegistryTest.java
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedTypeConverterRegistryTest.java
index 5f91f325353..979cb676be0 100644
---
a/core/camel-management/src/test/java/org/apache/camel/management/ManagedTypeConverterRegistryTest.java
+++
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedTypeConverterRegistryTest.java
@@ -80,7 +80,7 @@ public class ManagedTypeConverterRegistryTest extends
ManagementTestSupport {
Long failed = (Long) mbeanServer.getAttribute(name, "FailedCounter");
assertEquals(0, failed.intValue());
Long miss = (Long) mbeanServer.getAttribute(name, "MissCounter");
- assertEquals(2, miss.intValue()); // stream caching misses
+ assertEquals(0, miss.intValue());
// reset
mbeanServer.invoke(name, "resetTypeConversionCounters", null, null);
@@ -96,7 +96,7 @@ public class ManagedTypeConverterRegistryTest extends
ManagementTestSupport {
failed = (Long) mbeanServer.getAttribute(name, "FailedCounter");
assertEquals(1, failed.intValue());
miss = (Long) mbeanServer.getAttribute(name, "MissCounter");
- assertEquals(1, miss.intValue()); // stream caching misses
+ assertEquals(0, miss.intValue());
// reset
mbeanServer.invoke(name, "resetTypeConversionCounters", null, null);
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_13.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_13.adoc
index 0049ee16f5a..d417a21ba1e 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_13.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_13.adoc
@@ -6,6 +6,11 @@ from both 4.0 to 4.1 and 4.1 to 4.2.
== Upgrading Camel 4.12 to 4.13
+=== camel-core
+
+Added a 2nd `lookup` method to `org.apache.camel.spi.TypeConverterRegistry`
and changed the `addConverter` to no longer have
+an empty default noop implementation in the interface.
+
=== camel-http
Renamed class
`org.apache.camel.component.http.BasicAuthenticationHttpClientConfigurer` to
`org.apache.camel.component.http.DefaultAuthenticationHttpClientConfigurer`.