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

Reply via email to