This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch tc-loader in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/tc-loader by this push: new 1197136 CAMEL-13313: Add support for generating type converter loader source code to be able to load component type converters in a faster way 1197136 is described below commit 11971366e43bae5d9f05481d125859e122aa1936 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Sat Mar 16 10:53:35 2019 +0100 CAMEL-13313: Add support for generating type converter loader source code to be able to load component type converters in a faster way --- .../main/java/org/apache/camel/CamelContext.java | 7 + .../impl/converter/BaseTypeConverterRegistry.java | 14 +- .../converter/TypeConverterLoaderRegistry.java | 149 +++++++++++++++++++++ .../apache/camel/impl/AbstractCamelContext.java | 4 + ...BindingWithTypeConverterLoaderRegistryTest.java | 31 +++++ .../tools/apt/AbstractTypeConverterGenerator.java | 8 +- .../tools/apt/CoreTypeConverterProcessor.java | 2 +- .../tools/apt/TypeConverterLoaderGenerator.java | 2 +- 8 files changed, 207 insertions(+), 10 deletions(-) diff --git a/core/camel-api/src/main/java/org/apache/camel/CamelContext.java b/core/camel-api/src/main/java/org/apache/camel/CamelContext.java index 7f617c3..e53eea9 100644 --- a/core/camel-api/src/main/java/org/apache/camel/CamelContext.java +++ b/core/camel-api/src/main/java/org/apache/camel/CamelContext.java @@ -691,6 +691,13 @@ public interface CamelContext extends SuspendableService, RuntimeConfiguration { TypeConverterRegistry getTypeConverterRegistry(); /** + * Configures the type converter registry to use, where type converters can be added or looked up. + * + * @param typeConverterRegistry the registry to use + */ + void setTypeConverterRegistry(TypeConverterRegistry typeConverterRegistry); + + /** * Returns the registry used to lookup components by name and type such as SimpleRegistry, Spring ApplicationContext, * JNDI, or the OSGi Service Registry. * 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 6380da8..fb23326 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 @@ -77,7 +77,6 @@ public abstract class BaseTypeConverterRegistry extends ServiceSupport implement protected final LongAdder noopCounter = new LongAdder(); protected final LongAdder attemptCounter = new LongAdder(); protected final LongAdder missCounter = new LongAdder(); - protected final LongAdder baseHitCounter = new LongAdder(); protected final LongAdder hitCounter = new LongAdder(); protected final LongAdder failedCounter = new LongAdder(); @@ -85,7 +84,10 @@ public abstract class BaseTypeConverterRegistry extends ServiceSupport implement this.resolver = resolver; this.injector = injector; this.factoryFinder = factoryFinder; - this.typeConverterLoaders.add(new AnnotationTypeConverterLoader(resolver)); + if (resolver != null) { + // we only have annotation based package scanning if we have a resolver + this.typeConverterLoaders.add(new AnnotationTypeConverterLoader(resolver)); + } List<FallbackTypeConverter> fallbacks = new ArrayList<>(); // add to string first as it will then be last in the last as to string can nearly @@ -547,9 +549,11 @@ public abstract class BaseTypeConverterRegistry extends ServiceSupport implement } protected void loadFallbackTypeConverters() throws IOException, ClassNotFoundException { - List<TypeConverter> converters = factoryFinder.newInstances("FallbackTypeConverter", getInjector(), TypeConverter.class); - for (TypeConverter converter : converters) { - addFallbackTypeConverter(converter, false); + if (factoryFinder != null) { + List<TypeConverter> converters = factoryFinder.newInstances("FallbackTypeConverter", getInjector(), TypeConverter.class); + for (TypeConverter converter : converters) { + addFallbackTypeConverter(converter, false); + } } } diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/converter/TypeConverterLoaderRegistry.java b/core/camel-base/src/main/java/org/apache/camel/impl/converter/TypeConverterLoaderRegistry.java new file mode 100644 index 0000000..d3e716b --- /dev/null +++ b/core/camel-base/src/main/java/org/apache/camel/impl/converter/TypeConverterLoaderRegistry.java @@ -0,0 +1,149 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.impl.converter; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; +import java.util.StringTokenizer; + +import org.apache.camel.RuntimeCamelException; +import org.apache.camel.spi.TypeConverterLoader; +import org.apache.camel.util.IOHelper; +import org.apache.camel.util.StringHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +// TODO: We can automatic detect this for example like headersmap-factory by having this on the classpath + +/** + * An optimized {@link org.apache.camel.spi.TypeConverterRegistry} which loads + * the type converters up-front on startup in a faster way by leveraging + * source generated type converter loaders (<tt>@Converter(loader = true)</tt>, + * and will not perform slower package scanning. + */ +public class TypeConverterLoaderRegistry extends BaseTypeConverterRegistry { + + public static final String META_INF_SERVICES = "META-INF/services/org/apache/camel/TypeConverterLoader"; + + private static final Logger LOG = LoggerFactory.getLogger(TypeConverterLoaderRegistry.class); + private static final Charset UTF8 = Charset.forName("UTF-8"); + + public TypeConverterLoaderRegistry() { + super(null, null, null); // pass in null to base class as we load all type converters without package scanning + } + + @Override + public boolean allowNull() { + return false; + } + + @Override + public boolean isRunAllowed() { + // as type converter is used during initialization then allow it to always run + return true; + } + + @Override + protected void doInit() { + try { + // core type converters is always loaded which does not use any classpath scanning and therefore is fast + loadCoreTypeConverters(); + int core = typeMappings.size(); + // load type converters up front + loadTypeConverters(); + int additional = typeMappings.size() - core; + + // report how many type converters we have loaded + log.info("Type converters loaded (core: {}, classpath: {})", core, additional); + } catch (Exception e) { + throw RuntimeCamelException.wrapRuntimeCamelException(e); + } + } + + @Override + public void loadTypeConverters() throws Exception { + String[] lines = findTypeConverterLoaderClasses(); + for (String line : lines) { + String name = StringHelper.after(line, "class="); + if (name != null) { + LOG.debug("Resolving TypeConverterLoader: {}", name); + Class clazz = getCamelContext().getClassResolver().resolveMandatoryClass(name); + Object obj = getCamelContext().getInjector().newInstance(clazz); + if (obj instanceof TypeConverterLoader) { + TypeConverterLoader loader = (TypeConverterLoader) obj; + LOG.debug("TypeConverterLoader: {} loading converters", name); + loader.load(this); + } + } + } + } + + /** + * Finds the type converter loader classes from the classpath looking + * for text files on the classpath at the {@link #META_INF_SERVICES} location. + */ + protected String[] findTypeConverterLoaderClasses() throws IOException { + Set<String> classes = new HashSet<>(); + findLoaders(classes, getClass().getClassLoader()); + return classes.toArray(new String[classes.size()]); + } + + protected void findLoaders(Set<String> packages, ClassLoader classLoader) throws IOException { + Enumeration<URL> resources = classLoader.getResources(META_INF_SERVICES); + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + LOG.debug("Loading file {} to retrieve list of type converters, from url: {}", META_INF_SERVICES, url); + BufferedReader reader = IOHelper.buffered(new InputStreamReader(url.openStream(), UTF8)); + try { + while (true) { + String line = reader.readLine(); + if (line == null) { + break; + } + line = line.trim(); + if (line.startsWith("#") || line.length() == 0) { + continue; + } + tokenize(packages, line); + } + } finally { + IOHelper.close(reader, null, LOG); + } + } + } + + /** + * Tokenizes the line from the META-IN/services file using commas and + * ignoring whitespace between packages + */ + private void tokenize(Set<String> packages, String line) { + StringTokenizer iter = new StringTokenizer(line, ","); + while (iter.hasMoreTokens()) { + String name = iter.nextToken().trim(); + if (name.length() > 0) { + packages.add(name); + } + } + } + +} diff --git a/core/camel-core/src/main/java/org/apache/camel/impl/AbstractCamelContext.java b/core/camel-core/src/main/java/org/apache/camel/impl/AbstractCamelContext.java index 503a924..e056f5c 100644 --- a/core/camel-core/src/main/java/org/apache/camel/impl/AbstractCamelContext.java +++ b/core/camel-core/src/main/java/org/apache/camel/impl/AbstractCamelContext.java @@ -2446,6 +2446,10 @@ public abstract class AbstractCamelContext extends ServiceSupport implements Mod public void setTypeConverterRegistry(TypeConverterRegistry typeConverterRegistry) { this.typeConverterRegistry = doAddService(typeConverterRegistry); + // some registries are also a type converter implementation + if (typeConverterRegistry instanceof TypeConverter) { + this.typeConverter = (TypeConverter) typeConverterRegistry; + } } public Injector getInjector() { diff --git a/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanProxyNoBindingWithTypeConverterLoaderRegistryTest.java b/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanProxyNoBindingWithTypeConverterLoaderRegistryTest.java new file mode 100644 index 0000000..46cffa6 --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanProxyNoBindingWithTypeConverterLoaderRegistryTest.java @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.bean; + +import org.apache.camel.CamelContext; +import org.apache.camel.impl.converter.TypeConverterLoaderRegistry; + +public class BeanProxyNoBindingWithTypeConverterLoaderRegistryTest extends BeanProxyNoBindingTest { + + @Override + protected CamelContext createCamelContext() throws Exception { + CamelContext context = super.createCamelContext(); + // switch to using the faster type converter loader + context.setTypeConverterRegistry(new TypeConverterLoaderRegistry()); + return context; + } +} diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/AbstractTypeConverterGenerator.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/AbstractTypeConverterGenerator.java index b54d14a..b2c509c 100644 --- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/AbstractTypeConverterGenerator.java +++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/AbstractTypeConverterGenerator.java @@ -201,7 +201,7 @@ public abstract class AbstractTypeConverterGenerator extends AbstractCamelAnnota return false; } - void writeConverters(String fqn, String suffix, ClassConverters converters) throws Exception { + void writeConverters(String fqn, String suffix, boolean staticInstance, ClassConverters converters) throws Exception { int pos = fqn.lastIndexOf('.'); String p = fqn.substring(0, pos); @@ -224,7 +224,9 @@ public abstract class AbstractTypeConverterGenerator extends AbstractCamelAnnota writer.append("@SuppressWarnings(\"unchecked\")\n"); writer.append("public class ").append(c).append(" implements TypeConverterLoader {\n"); writer.append("\n"); - writer.append(" public static final ").append(c).append(" INSTANCE = new ").append(c).append("();\n"); + if (staticInstance) { + writer.append(" public static final ").append(c).append(" INSTANCE = new ").append(c).append("();\n"); + } writer.append("\n"); if (converters.size() > 0) { @@ -256,7 +258,7 @@ public abstract class AbstractTypeConverterGenerator extends AbstractCamelAnnota writer.append(" private final DoubleMap<Class<?>, Class<?>, BaseTypeConverter> converters = new DoubleMap<>(").append(String.valueOf(converters.size())).append(");\n"); writer.append("\n"); } - writer.append(" private ").append(c).append("() {\n"); + writer.append(" ").append(staticInstance ? "private " : "public ").append(c).append("() {\n"); for (Map.Entry<String, Map<TypeMirror, ExecutableElement>> to : converters.getConverters().entrySet()) { for (Map.Entry<TypeMirror, ExecutableElement> from : to.getValue().entrySet()) { diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreTypeConverterProcessor.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreTypeConverterProcessor.java index efd7d54..e42b5d6 100644 --- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreTypeConverterProcessor.java +++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreTypeConverterProcessor.java @@ -60,7 +60,7 @@ public class CoreTypeConverterProcessor extends AbstractTypeConverterGenerator { for (Map.Entry<String, ClassConverters> entry : converters.entrySet()) { String key = entry.getKey(); ClassConverters value = entry.getValue(); - writeConverters(key, null, value); + writeConverters(key, null, true, value); } } diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/TypeConverterLoaderGenerator.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/TypeConverterLoaderGenerator.java index ecf2aa7..75f4765 100644 --- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/TypeConverterLoaderGenerator.java +++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/TypeConverterLoaderGenerator.java @@ -46,7 +46,7 @@ public class TypeConverterLoaderGenerator extends AbstractTypeConverterGenerator for (Map.Entry<String, ClassConverters> entry : converters.entrySet()) { String key = entry.getKey(); ClassConverters value = entry.getValue(); - writeConverters(key, "Loader", value); + writeConverters(key, "Loader", false, value); } writeConverterLoaderMetaInfo(converters); }