This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fast-sc in repository https://gitbox.apache.org/repos/asf/camel.git
commit 8e997d0f51989af383556f597a1c3ef0de0b1c62 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Tue May 27 12:09:30 2025 +0200 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 | 59 +++++++++++++++++----- .../impl/converter/CoreTypeConverterRegistry.java | 13 +++++ ...TypeConverterRegistryStatisticsEnabledTest.java | 4 +- .../ManagedTypeConverterRegistryTest.java | 4 +- .../ROOT/pages/camel-4x-upgrade-guide-4_13.adoc | 5 ++ 6 files changed, 79 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..a16f2a217c9 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 @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; @@ -33,6 +34,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; @@ -52,6 +54,7 @@ public class DefaultStreamCachingStrategy extends ServiceSupport implements Came private boolean enabled; private String allowClassNames; private String denyClassNames; + private Map<Class<?>, TypeConverter> coreConverters; private Collection<Class<?>> allowClasses; private Collection<Class<?>> denyClasses; private boolean spoolEnabled; @@ -269,15 +272,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 +313,33 @@ public class DefaultStreamCachingStrategy extends ServiceSupport implements Came return cache; } - private boolean checkAllowDenyList(Object body) { + private TypeConverter lookupTypeConverter(Class<?> type) { + if (coreConverters != null) { + for (var tc : coreConverters.entrySet()) { + Class<?> clazz = tc.getKey(); + if (clazz.isAssignableFrom(type)) { + return tc.getValue(); + } + } + } + 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,9 @@ public class DefaultStreamCachingStrategy extends ServiceSupport implements Came return; } + // find core type converters that can convert to StreamCache + this.coreConverters = getCamelContext().getTypeConverterRegistry().lookup(StreamCache.class); + 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..375b314d6d2 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.HashMap; 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 HashMap<>(); + 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 04865c0397c..797274e7990 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`.