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 2020/02/16 15:55:34 UTC
[camel] 10/13: CAMEL-14572: camel-core - Optimize type converter
for some basic convertions
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
commit da1e828f21d569c7994be70a0a6fb2cb56dec45b
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sun Feb 16 14:21:32 2020 +0100
CAMEL-14572: camel-core - Optimize type converter for some basic convertions
---
.../component/couchdb/CouchDbComponentTest.java | 2 +-
.../debezium/DebeziumMySqlComponentTest.java | 4 +-
.../sql/stored/ProducerBatchInvalidTest.java | 3 +-
.../camel/spi/AnnotationScanTypeConverters.java | 17 ++++
.../camel/converter/TimePatternConverter.java | 1 -
.../impl/converter/BaseTypeConverterRegistry.java | 102 ++++++++++++++++++---
.../camel/impl/converter/DefaultTypeConverter.java | 44 ++++++---
.../camel/impl/engine/AbstractCamelContext.java | 5 +
.../main/java/org/apache/camel/util/TimeUtils.java | 12 +--
9 files changed, 154 insertions(+), 36 deletions(-)
diff --git a/components/camel-couchdb/src/test/java/org/apache/camel/component/couchdb/CouchDbComponentTest.java b/components/camel-couchdb/src/test/java/org/apache/camel/component/couchdb/CouchDbComponentTest.java
index 5d472a6..aca029b 100644
--- a/components/camel-couchdb/src/test/java/org/apache/camel/component/couchdb/CouchDbComponentTest.java
+++ b/components/camel-couchdb/src/test/java/org/apache/camel/component/couchdb/CouchDbComponentTest.java
@@ -46,7 +46,7 @@ public class CouchDbComponentTest extends CamelTestSupport {
params.put("createDatabase", true);
params.put("username", "coldplay");
params.put("password", "chrism");
- params.put("heartbeat", 1000);
+ params.put("heartbeat", "1000");
params.put("style", "gothic");
params.put("deletes", false);
params.put("updates", false);
diff --git a/components/camel-debezium-mysql/src/test/java/org/apache/camel/component/debezium/DebeziumMySqlComponentTest.java b/components/camel-debezium-mysql/src/test/java/org/apache/camel/component/debezium/DebeziumMySqlComponentTest.java
index e4a2d91..aa434f5 100644
--- a/components/camel-debezium-mysql/src/test/java/org/apache/camel/component/debezium/DebeziumMySqlComponentTest.java
+++ b/components/camel-debezium-mysql/src/test/java/org/apache/camel/component/debezium/DebeziumMySqlComponentTest.java
@@ -37,7 +37,7 @@ public class DebeziumMySqlComponentTest {
params.put("databaseUser", "dbz");
params.put("databasePassword", "pwd");
params.put("databaseServerName", "test");
- params.put("databaseServerId", 1234);
+ params.put("databaseServerId", "1234");
params.put("databaseHistoryFileFilename", "/db_history_file_test");
final String remaining = "test_name";
@@ -60,7 +60,7 @@ public class DebeziumMySqlComponentTest {
assertEquals("dbz", configuration.getDatabaseUser());
assertEquals("pwd", configuration.getDatabasePassword());
assertEquals("test", configuration.getDatabaseServerName());
- assertEquals(1234, configuration.getDatabaseServerId());
+ assertEquals(1234L, configuration.getDatabaseServerId());
assertEquals("/db_history_file_test", configuration.getDatabaseHistoryFileFilename());
}
diff --git a/components/camel-sql/src/test/java/org/apache/camel/component/sql/stored/ProducerBatchInvalidTest.java b/components/camel-sql/src/test/java/org/apache/camel/component/sql/stored/ProducerBatchInvalidTest.java
index 37fe02c..22bed0d 100644
--- a/components/camel-sql/src/test/java/org/apache/camel/component/sql/stored/ProducerBatchInvalidTest.java
+++ b/components/camel-sql/src/test/java/org/apache/camel/component/sql/stored/ProducerBatchInvalidTest.java
@@ -19,7 +19,6 @@ package org.apache.camel.component.sql.stored;
import org.apache.camel.FailedToCreateRouteException;
import org.apache.camel.PropertyBindingException;
import org.apache.camel.ResolveEndpointFailedException;
-import org.apache.camel.TypeConversionException;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.After;
@@ -72,7 +71,7 @@ public class ProducerBatchInvalidTest extends CamelTestSupport {
ResolveEndpointFailedException refe = assertIsInstanceOf(ResolveEndpointFailedException.class, e.getCause());
PropertyBindingException pbe = assertIsInstanceOf(PropertyBindingException.class, refe.getCause());
assertEquals("batch", pbe.getPropertyName());
- TypeConversionException tce = assertIsInstanceOf(TypeConversionException.class, pbe.getCause());
+ assertIsInstanceOf(IllegalArgumentException.class, pbe.getCause());
assertEquals("[true, true]", pbe.getValue().toString());
}
}
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/AnnotationScanTypeConverters.java b/core/camel-api/src/main/java/org/apache/camel/spi/AnnotationScanTypeConverters.java
new file mode 100644
index 0000000..41dd5fa
--- /dev/null
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/AnnotationScanTypeConverters.java
@@ -0,0 +1,17 @@
+package org.apache.camel.spi;
+
+/**
+ * A {@link org.apache.camel.TypeConverter} which is capable of annotation scanning for {@link org.apache.camel.Converter}
+ * classes and add these as type converters.
+ * <p/>
+ * This is using Camel 2.x style and its recommended to migrate to @Converter(loader = true) for fast type converter mode.
+ */
+public interface AnnotationScanTypeConverters {
+
+ /**
+ * Scan for {@link org.apache.camel.Converter} classes and add those as type converters.
+ *
+ * @throws Exception is thrown if error happened
+ */
+ void scanTypeConverters() throws Exception;
+}
diff --git a/core/camel-base/src/main/java/org/apache/camel/converter/TimePatternConverter.java b/core/camel-base/src/main/java/org/apache/camel/converter/TimePatternConverter.java
index 4ccb021..c809fed 100644
--- a/core/camel-base/src/main/java/org/apache/camel/converter/TimePatternConverter.java
+++ b/core/camel-base/src/main/java/org/apache/camel/converter/TimePatternConverter.java
@@ -21,7 +21,6 @@ import org.apache.camel.util.TimeUtils;
/**
* Converter from String syntax to milli seconds.
- * Code is copied to org.apache.camel.catalog.TimePatternConverter in camel-catalog
*/
@Converter(generateLoader = true)
public final class TimePatternConverter {
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/converter/BaseTypeConverterRegistry.java b/core/camel-base/src/main/java/org/apache/camel/impl/converter/BaseTypeConverterRegistry.java
index 6ef8380..0fbff03 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/converter/BaseTypeConverterRegistry.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/converter/BaseTypeConverterRegistry.java
@@ -48,6 +48,7 @@ import org.apache.camel.TypeConverterExists;
import org.apache.camel.TypeConverterExistsException;
import org.apache.camel.TypeConverterLoaderException;
import org.apache.camel.TypeConverters;
+import org.apache.camel.converter.ObjectConverter;
import org.apache.camel.spi.CamelLogger;
import org.apache.camel.spi.FactoryFinder;
import org.apache.camel.spi.Injector;
@@ -126,6 +127,46 @@ public abstract class BaseTypeConverterRegistry extends ServiceSupport implement
@SuppressWarnings("unchecked")
@Override
public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
+ // optimize for a few common conversions
+ if (value != null) {
+ if (type.isInstance(value)) {
+ // same instance
+ return (T) value;
+ }
+ if (type == boolean.class) {
+ // primitive boolean which must return a value so throw exception if not possible
+ Object answer = ObjectConverter.toBoolean(value);
+ if (answer == null) {
+ throw new IllegalArgumentException("Cannot convert type: " + value.getClass().getName() + " to boolean");
+ }
+ return (T) answer;
+ } else if (type == Boolean.class && (value instanceof String)) {
+ // String -> Boolean
+ String str = (String) value;
+ if ("true".equalsIgnoreCase(str)) {
+ return (T) Boolean.TRUE;
+ } else if ("false".equalsIgnoreCase(str)) {
+ return (T) Boolean.FALSE;
+ }
+ } else if (type.isPrimitive()) {
+ // okay its a wrapper -> primitive then return as-is for some common types
+ Class cls = value.getClass();
+ if (cls == Integer.class || cls == Long.class) {
+ return (T) value;
+ }
+ } else if (type == String.class) {
+ // okay its a primitive -> string then return as-is for some common types
+ Class cls = value.getClass();
+ if (cls.isPrimitive()
+ || cls == Boolean.class || cls == boolean.class
+ || cls == Integer.class || cls == int.class
+ || cls == Long.class || cls == long.class) {
+ return (T) value.toString();
+ }
+ }
+ // NOTE: we cannot optimize any more if value is String as it may be time pattern and other patterns
+ }
+
return (T) doConvertTo(type, exchange, value, false, false);
}
@@ -137,6 +178,46 @@ public abstract class BaseTypeConverterRegistry extends ServiceSupport implement
@SuppressWarnings("unchecked")
@Override
public <T> T mandatoryConvertTo(Class<T> type, Exchange exchange, Object value) throws NoTypeConversionAvailableException {
+ // optimize for a few common conversions
+ if (value != null) {
+ if (type.isInstance(value)) {
+ // same instance
+ return (T) value;
+ }
+ if (type == boolean.class) {
+ // primitive boolean which must return a value so throw exception if not possible
+ Object answer = ObjectConverter.toBoolean(value);
+ if (answer == null) {
+ throw new IllegalArgumentException("Cannot convert type: " + value.getClass().getName() + " to boolean");
+ }
+ return (T) answer;
+ } else if (type == Boolean.class && (value instanceof String)) {
+ // String -> Boolean
+ String str = (String) value;
+ if ("true".equalsIgnoreCase(str)) {
+ return (T) Boolean.TRUE;
+ } else if ("false".equalsIgnoreCase(str)) {
+ return (T) Boolean.FALSE;
+ }
+ } else if (type.isPrimitive()) {
+ // okay its a wrapper -> primitive then return as-is for some common types
+ Class cls = value.getClass();
+ if (cls == Integer.class || cls == Long.class) {
+ return (T) value;
+ }
+ } else if (type == String.class) {
+ // okay its a primitive -> string then return as-is for some common types
+ Class cls = value.getClass();
+ if (cls.isPrimitive()
+ || cls == Boolean.class || cls == boolean.class
+ || cls == Integer.class || cls == int.class
+ || cls == Long.class || cls == long.class) {
+ return (T) value.toString();
+ }
+ }
+ // NOTE: we cannot optimize any more if value is String as it may be time pattern and other patterns
+ }
+
Object answer = doConvertTo(type, exchange, value, true, false);
if (answer == null) {
// Could not find suitable conversion
@@ -156,7 +237,8 @@ public abstract class BaseTypeConverterRegistry extends ServiceSupport implement
return (T) doConvertTo(type, exchange, value, false, true);
}
- protected Object doConvertTo(final Class<?> type, final Exchange exchange, final Object value, final boolean mandatory, final boolean tryConvert) {
+ protected Object doConvertTo(final Class<?> type, final Exchange exchange, final Object value,
+ final boolean mandatory, final boolean tryConvert) {
Object answer;
try {
answer = doConvertTo(type, exchange, value, tryConvert);
@@ -192,7 +274,8 @@ public abstract class BaseTypeConverterRegistry extends ServiceSupport implement
}
}
- protected Object doConvertTo(final Class<?> type, final Exchange exchange, final Object value, final boolean tryConvert) throws Exception {
+ protected Object doConvertTo(final Class<?> type, final Exchange exchange, final Object value,
+ final boolean tryConvert) throws Exception {
boolean trace = LOG.isTraceEnabled();
boolean statisticsEnabled = statistics.isStatisticsEnabled();
@@ -411,7 +494,8 @@ public abstract class BaseTypeConverterRegistry extends ServiceSupport implement
}
}
- private void addCoreFallbackTypeConverterToList(TypeConverter typeConverter, boolean canPromote, List<FallbackTypeConverter> converters) {
+ private void addCoreFallbackTypeConverterToList(TypeConverter typeConverter, boolean canPromote, List<
+ FallbackTypeConverter> converters) {
LOG.trace("Adding core fallback type converter: {} which can promote: {}", typeConverter, canPromote);
// add in top of fallback as the toString() fallback will nearly always be able to convert
@@ -496,7 +580,7 @@ public abstract class BaseTypeConverterRegistry extends ServiceSupport implement
TypeConverter converter = typeMappings.getFirst(
toType::isAssignableFrom,
// skip Object based we do them last
- from -> !from.equals(Object.class) && from.isAssignableFrom(fromType));
+ from -> !from.equals(Object.class) && from.isAssignableFrom(fromType));
if (converter != null) {
return converter;
}
@@ -653,7 +737,8 @@ public abstract class BaseTypeConverterRegistry extends ServiceSupport implement
}
}
- protected TypeConversionException createTypeConversionException(Exchange exchange, Class<?> type, Object value, Throwable cause) {
+ protected TypeConversionException createTypeConversionException(Exchange exchange, Class<?> type, Object
+ value, Throwable cause) {
if (cause instanceof TypeConversionException) {
if (((TypeConversionException) cause).getToType() == type) {
return (TypeConversionException) cause;
@@ -708,7 +793,6 @@ public abstract class BaseTypeConverterRegistry extends ServiceSupport implement
if (resolver == null && camelContext != null) {
resolver = camelContext.adapt(ExtendedCamelContext.class).getPackageScanClassResolver();
}
- initTypeConverterLoaders();
List<FallbackTypeConverter> fallbacks = new ArrayList<>();
// add to string first as it will then be last in the last as to string can nearly
@@ -728,12 +812,6 @@ public abstract class BaseTypeConverterRegistry extends ServiceSupport implement
fallbackConverters.addAll(fallbacks);
}
- protected void initTypeConverterLoaders() {
- if (resolver != null) {
- typeConverterLoaders.add(new AnnotationTypeConverterLoader(resolver));
- }
- }
-
@Override
protected void doStart() throws Exception {
// noop
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/converter/DefaultTypeConverter.java b/core/camel-base/src/main/java/org/apache/camel/impl/converter/DefaultTypeConverter.java
index e3f5f57..086af3c 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/converter/DefaultTypeConverter.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/converter/DefaultTypeConverter.java
@@ -17,6 +17,7 @@
package org.apache.camel.impl.converter;
import org.apache.camel.CamelContext;
+import org.apache.camel.spi.AnnotationScanTypeConverters;
import org.apache.camel.spi.FactoryFinder;
import org.apache.camel.spi.Injector;
import org.apache.camel.spi.PackageScanClassResolver;
@@ -31,10 +32,11 @@ import org.slf4j.LoggerFactory;
* <p/>
* This implementation will load type converters up-front on startup.
*/
-public class DefaultTypeConverter extends BaseTypeConverterRegistry {
+public class DefaultTypeConverter extends BaseTypeConverterRegistry implements AnnotationScanTypeConverters {
private static final Logger LOG = LoggerFactory.getLogger(DefaultTypeConverter.class);
+ private volatile boolean loadTypeConvertersDone;
private final boolean loadTypeConverters;
public DefaultTypeConverter(PackageScanClassResolver resolver, Injector injector,
@@ -73,8 +75,35 @@ public class DefaultTypeConverter extends BaseTypeConverterRegistry {
// core type converters is always loaded which does not use any classpath scanning and therefore is fast
loadCoreAndFastTypeConverters();
+ String time = TimeUtils.printDuration(watch.taken());
+ LOG.debug("Loaded {} type converters in {}", typeMappings.size(), time);
+
+ if (!loadTypeConvertersDone && isLoadTypeConverters()) {
+ scanTypeConverters();
+ }
+ }
+
+ private boolean isLoadTypeConverters() {
+ boolean load = loadTypeConverters;
+ if (camelContext != null) {
+ // camel context can override
+ load = camelContext.isLoadTypeConverters();
+ }
+ return load;
+ }
+
+ @Override
+ public void scanTypeConverters() throws Exception {
+ StopWatch watch = new StopWatch();
+
// we are using backwards compatible legacy mode to detect additional converters
- if (loadTypeConverters) {
+ if (!loadTypeConvertersDone) {
+ loadTypeConvertersDone = true;
+
+ if (resolver != null) {
+ typeConverterLoaders.add(new AnnotationTypeConverterLoader(resolver));
+ }
+
int fast = typeMappings.size();
// load type converters up front
loadTypeConverters();
@@ -93,15 +122,6 @@ public class DefaultTypeConverter extends BaseTypeConverterRegistry {
}
String time = TimeUtils.printDuration(watch.taken());
- LOG.debug("Loaded {} type converters in {}", typeMappings.size(), time);
+ LOG.debug("Scanned {} type converters in {}", typeMappings.size(), time);
}
-
- @Override
- protected void initTypeConverterLoaders() {
- // only use Camel 2.x annotation based loaders if enabled
- if (resolver != null && loadTypeConverters) {
- typeConverterLoaders.add(new FastAnnotationTypeConverterLoader(resolver));
- }
- }
-
}
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java b/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
index fe96711..3473ef4 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
@@ -80,6 +80,7 @@ import org.apache.camel.catalog.RuntimeCamelCatalog;
import org.apache.camel.impl.transformer.TransformerKey;
import org.apache.camel.impl.validator.ValidatorKey;
import org.apache.camel.spi.AnnotationBasedProcessorFactory;
+import org.apache.camel.spi.AnnotationScanTypeConverters;
import org.apache.camel.spi.AsyncProcessorAwaitManager;
import org.apache.camel.spi.BeanIntrospection;
import org.apache.camel.spi.BeanProcessorFactory;
@@ -2512,6 +2513,10 @@ public abstract class AbstractCamelContext extends ServiceSupport implements Ext
}
protected void doStartCamel() throws Exception {
+ // ensure additional type converters is loaded
+ if (loadTypeConverters && typeConverter instanceof AnnotationScanTypeConverters) {
+ ((AnnotationScanTypeConverters) typeConverter).scanTypeConverters();
+ }
// custom properties may use property placeholders so resolve those
// early on
diff --git a/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java b/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java
index d6edf36..2773cc3 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java
@@ -99,7 +99,7 @@ public final class TimeUtils {
}
}
if (digit) {
- return Long.valueOf(source);
+ return Long.parseLong(source);
}
long milliseconds = 0;
@@ -112,17 +112,17 @@ public final class TimeUtils {
if (matcher.find()) {
// Note: This will also be used for regular numeric strings.
// This String -> long converter will be used for all strings.
- milliseconds = Long.valueOf(source);
+ milliseconds = Long.parseLong(source);
} else {
matcher = createMatcher(HOUR_REGEX_PATTERN, source);
if (matcher.find()) {
- milliseconds = milliseconds + (3600000 * Long.valueOf(matcher.group(1)));
+ milliseconds = milliseconds + (3600000 * Long.parseLong(matcher.group(1)));
foundFlag = true;
}
matcher = createMatcher(MINUTES_REGEX_PATTERN, source);
if (matcher.find()) {
- long minutes = Long.valueOf(matcher.group(1));
+ long minutes = Long.parseLong(matcher.group(1));
if ((minutes > 59) && foundFlag) {
throw new IllegalArgumentException("Minutes should contain a valid value between 0 and 59: " + source);
}
@@ -132,7 +132,7 @@ public final class TimeUtils {
matcher = createMatcher(SECONDS_REGEX_PATTERN, source);
if (matcher.find()) {
- long seconds = Long.valueOf(matcher.group(1));
+ long seconds = Long.parseLong(matcher.group(1));
if ((seconds > 59) && foundFlag) {
throw new IllegalArgumentException("Seconds should contain a valid value between 0 and 59: " + source);
}
@@ -143,7 +143,7 @@ public final class TimeUtils {
// No pattern matched... initiating fallback check and conversion (if required).
// The source at this point may contain illegal values or special characters
if (!foundFlag) {
- milliseconds = Long.valueOf(source);
+ milliseconds = Long.parseLong(source);
}
}