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
commit 9cf5e63f3aba656a2956f891f95a38ec49d0c473 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Thu Mar 14 05:44:45 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. --- ...nverter.java => InjectedTestTypeConverter.java} | 84 ++++++++-------- .../InjectedTypeConverterMultipleContextsTest.java | 4 +- .../camel/cdi/test/InjectedTypeConverterTest.java | 4 +- .../src/main/java/org/apache/camel/Converter.java | 8 ++ .../java/org/apache/camel/FallbackConverter.java | 9 ++ .../impl/converter/BaseTypeConverterRegistry.java | 1 + .../java/org/apache/camel/util}/DoubleMap.java | 2 +- .../camel/tools/apt/AnnotationProcessorHelper.java | 4 +- ...essor.java => ComponentConverterProcessor.java} | 110 ++++++++++++++++----- ...rProcessor.java => CoreConverterProcessor.java} | 3 +- .../services/javax.annotation.processing.Processor | 6 +- 11 files changed, 161 insertions(+), 74 deletions(-) diff --git a/components/camel-cdi/src/test/java/org/apache/camel/cdi/converter/InjectedTypeConverter.java b/components/camel-cdi/src/test/java/org/apache/camel/cdi/converter/InjectedTestTypeConverter.java similarity index 91% rename from components/camel-cdi/src/test/java/org/apache/camel/cdi/converter/InjectedTypeConverter.java rename to components/camel-cdi/src/test/java/org/apache/camel/cdi/converter/InjectedTestTypeConverter.java index ef0b245..2e7fc75 100644 --- a/components/camel-cdi/src/test/java/org/apache/camel/cdi/converter/InjectedTypeConverter.java +++ b/components/camel-cdi/src/test/java/org/apache/camel/cdi/converter/InjectedTestTypeConverter.java @@ -1,42 +1,42 @@ -/** - * 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.cdi.converter; - -import javax.inject.Inject; - -import org.apache.camel.CamelContext; -import org.apache.camel.Converter; -import org.apache.camel.cdi.pojo.TypeConverterInput; -import org.apache.camel.cdi.pojo.TypeConverterOutput; - -@Converter -public final class InjectedTypeConverter { - - private final CamelContext context; - - @Inject - InjectedTypeConverter(CamelContext context) { - this.context = context; - } - - @Converter - public TypeConverterOutput convert(TypeConverterInput input) throws Exception { - TypeConverterOutput output = new TypeConverterOutput(); - output.setProperty(context.resolvePropertyPlaceholders(input.getProperty())); - return output; - } -} +/** + * 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.cdi.converter; + +import javax.inject.Inject; + +import org.apache.camel.CamelContext; +import org.apache.camel.Converter; +import org.apache.camel.cdi.pojo.TypeConverterInput; +import org.apache.camel.cdi.pojo.TypeConverterOutput; + +@Converter +public final class InjectedTestTypeConverter { + + private final CamelContext context; + + @Inject + InjectedTestTypeConverter(CamelContext context) { + this.context = context; + } + + @Converter + public TypeConverterOutput convert(TypeConverterInput input) throws Exception { + TypeConverterOutput output = new TypeConverterOutput(); + output.setProperty(context.resolvePropertyPlaceholders(input.getProperty())); + return output; + } +} diff --git a/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/InjectedTypeConverterMultipleContextsTest.java b/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/InjectedTypeConverterMultipleContextsTest.java index ccd5796..4f65e73 100644 --- a/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/InjectedTypeConverterMultipleContextsTest.java +++ b/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/InjectedTypeConverterMultipleContextsTest.java @@ -59,7 +59,7 @@ public class InjectedTypeConverterMultipleContextsTest { .addClass(FirstCamelContextConvertingRoute.class) .addClass(SecondCamelContextConvertingRoute.class) // Type converter - .addClass(InjectedTypeConverter.class) + .addClass(InjectedTestTypeConverter.class) // No need as Camel CDI automatically registers the type converter bean //.addAsManifestResource(new StringAsset("org.apache.camel.cdi.se.converter"), ArchivePaths.create("services/org/apache/camel/TypeConverter")) // Bean archive deployment descriptor @@ -107,7 +107,7 @@ public class InjectedTypeConverterMultipleContextsTest { } @Converter - public static final class InjectedTypeConverter { + public static final class InjectedTestTypeConverter { @Converter public TypeConverterOutput convert(TypeConverterInput input) throws Exception { TypeConverterOutput output = new TypeConverterOutput(); diff --git a/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/InjectedTypeConverterTest.java b/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/InjectedTypeConverterTest.java index 6e43984..4f9342f 100644 --- a/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/InjectedTypeConverterTest.java +++ b/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/InjectedTypeConverterTest.java @@ -28,7 +28,7 @@ import org.apache.camel.TypeConverter; import org.apache.camel.cdi.CdiCamelExtension; import org.apache.camel.cdi.Uri; import org.apache.camel.cdi.bean.InjectedTypeConverterRoute; -import org.apache.camel.cdi.converter.InjectedTypeConverter; +import org.apache.camel.cdi.converter.InjectedTestTypeConverter; import org.apache.camel.cdi.pojo.TypeConverterInput; import org.apache.camel.cdi.pojo.TypeConverterOutput; import org.apache.camel.component.mock.MockEndpoint; @@ -58,7 +58,7 @@ public class InjectedTypeConverterTest { // Test class .addClass(InjectedTypeConverterRoute.class) // Type converter - .addClass(InjectedTypeConverter.class) + .addClass(InjectedTestTypeConverter.class) // No need as Camel CDI automatically registers the type converter bean //.addAsManifestResource(new StringAsset("org.apache.camel.cdi.se.converter"), ArchivePaths.create("services/org/apache/camel/TypeConverter")) // Bean archive deployment descriptor diff --git a/core/camel-api/src/main/java/org/apache/camel/Converter.java b/core/camel-api/src/main/java/org/apache/camel/Converter.java index 79b2234..9f08426 100644 --- a/core/camel-api/src/main/java/org/apache/camel/Converter.java +++ b/core/camel-api/src/main/java/org/apache/camel/Converter.java @@ -50,4 +50,12 @@ public @interface Converter { * Important this configuration must be set on the class-level, not on the method. */ boolean ignoreOnLoadError() default false; + + /** + * Whether to let the Camel compiler plugin to generate java source code + * for fast loading of the type converters. + * + * This option should only be configured on the top-level class. + */ + boolean loader() default false; } diff --git a/core/camel-api/src/main/java/org/apache/camel/FallbackConverter.java b/core/camel-api/src/main/java/org/apache/camel/FallbackConverter.java index f3e4dae..6b3f4e7 100644 --- a/core/camel-api/src/main/java/org/apache/camel/FallbackConverter.java +++ b/core/camel-api/src/main/java/org/apache/camel/FallbackConverter.java @@ -51,4 +51,13 @@ public @interface FallbackConverter { */ boolean canPromote() default false; + /** + * Whether to let the Camel compiler plugin to generate java source code + * for fast loading of the type converters. + * + * This option should only be configured on the top-level class. + */ + boolean loader() default false; + + } \ No newline at end of file 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 5a808b1..6380da8 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.spi.TypeConverterRegistry; import org.apache.camel.support.MessageHelper; import org.apache.camel.support.TypeConverterSupport; import org.apache.camel.support.service.ServiceSupport; +import org.apache.camel.util.DoubleMap; import org.apache.camel.util.ObjectHelper; /** diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/converter/DoubleMap.java b/core/camel-util/src/main/java/org/apache/camel/util/DoubleMap.java similarity index 99% rename from core/camel-base/src/main/java/org/apache/camel/impl/converter/DoubleMap.java rename to core/camel-util/src/main/java/org/apache/camel/util/DoubleMap.java index abeb236..9a8ff70 100644 --- a/core/camel-base/src/main/java/org/apache/camel/impl/converter/DoubleMap.java +++ b/core/camel-util/src/main/java/org/apache/camel/util/DoubleMap.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.impl.converter; +package org.apache.camel.util; import java.util.function.Predicate; diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/AnnotationProcessorHelper.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/AnnotationProcessorHelper.java index 66e4e86..09fd766 100644 --- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/AnnotationProcessorHelper.java +++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/AnnotationProcessorHelper.java @@ -23,7 +23,9 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.nio.file.Files; +import java.nio.file.OpenOption; import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; import java.util.Collections; import java.util.List; import java.util.Set; @@ -379,7 +381,7 @@ public final class AnnotationProcessorHelper { } public static void dumpExceptionToErrorFile(String fileName, String message, Throwable e) { - try (BufferedWriter w = Files.newBufferedWriter(Paths.get(fileName))) { + try (BufferedWriter w = Files.newBufferedWriter(Paths.get(fileName), StandardOpenOption.CREATE, StandardOpenOption.APPEND)) { w.append(message); w.append("\n\n"); PrintWriter pw = new PrintWriter(w); diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/ComponentConverterProcessor.java similarity index 76% copy from tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java copy to tooling/apt/src/main/java/org/apache/camel/tools/apt/ComponentConverterProcessor.java index 08e3322..19c9790 100644 --- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java +++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/ComponentConverterProcessor.java @@ -24,7 +24,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; - import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.element.AnnotationMirror; @@ -39,27 +38,60 @@ import javax.tools.Diagnostic; import javax.tools.JavaFileObject; @SupportedAnnotationTypes({"org.apache.camel.Converter"}) -public class ConverterProcessor extends AbstractCamelAnnotationProcessor { +public class ComponentConverterProcessor extends AbstractCamelAnnotationProcessor { - @Override - protected void doProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws Exception { - if (this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.impl.converter.CoreStaticTypeConverterLoader") != null) { - return; + private static final class ClassConverters { + + private final Map<String, Map<TypeMirror, ExecutableElement>> converters = new TreeMap<>(); + private final Comparator<TypeMirror> comparator; + private final List<ExecutableElement> fallbackConverters = new ArrayList<>(); + + ClassConverters(Comparator<TypeMirror> comparator) { + this.comparator = comparator; + } + + void addTypeConverter(TypeMirror to, TypeMirror from, ExecutableElement ee) { + converters.computeIfAbsent(toString(to), c -> new TreeMap<>(comparator)).put(from, ee); + } + + void addFallbackTypeConverter(ExecutableElement ee) { + fallbackConverters.add(ee); + } + + Map<String, Map<TypeMirror, ExecutableElement>> getConverters() { + return converters; + } + + List<ExecutableElement> getFallbackConverters() { + return fallbackConverters; } - // We're in tests, do not generate anything - if (this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.converter.ObjectConverter") == null) { - return; + private static String toString(TypeMirror type) { + return type.toString().replaceAll("<.*>", ""); } + } + + @Override + protected void doProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws Exception { + Map<String, ClassConverters> converters = new TreeMap<>(); + Comparator<TypeMirror> comparator = (o1, o2) -> processingEnv.getTypeUtils().isAssignable(o1, o2) ? -1 : processingEnv.getTypeUtils().isAssignable(o2, o1) ? +1 : o1.toString().compareTo(o2.toString()); - Map<String, Map<TypeMirror, ExecutableElement>> converters = new TreeMap<>(); TypeElement converterAnnotationType = this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.Converter"); + // the current class with type converters + String currentClass = null; for (Element element : roundEnv.getElementsAnnotatedWith(converterAnnotationType)) { - if (element.getKind() == ElementKind.METHOD) { - ExecutableElement ee = (ExecutableElement)element; + // we need a top level class first + if (element.getKind() == ElementKind.CLASS) { + TypeElement te = (TypeElement) element; + if (!te.getNestingKind().isNested() && isLoaderEnabled(te)) { + // we only accept top-level classes and if loader is enabled + currentClass = te.getQualifiedName().toString(); + } + } else if (currentClass != null && element.getKind() == ElementKind.METHOD) { + ExecutableElement ee = (ExecutableElement) element; TypeMirror to = ee.getReturnType(); TypeMirror from = ee.getParameters().get(0).asType(); String fromStr = toString(from); @@ -70,22 +102,53 @@ public class ConverterProcessor extends AbstractCamelAnnotationProcessor { } else { processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Could not retrieve type element for " + fromStr); } - } - converters.computeIfAbsent(toString(to), c -> new TreeMap<>(comparator)).put(from, ee); + converters.computeIfAbsent(currentClass, c -> new ClassConverters(comparator)).addTypeConverter(to, from, ee); } } + TypeElement fallbackAnnotationType = this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.FallbackConverter"); - List<ExecutableElement> fallbackConverters = new ArrayList<>(); + currentClass = null; for (Element element : roundEnv.getElementsAnnotatedWith(fallbackAnnotationType)) { - if (element.getKind() == ElementKind.METHOD) { - ExecutableElement ee = (ExecutableElement)element; - fallbackConverters.add(ee); + if (element.getKind() == ElementKind.CLASS) { + TypeElement te = (TypeElement) element; + if (!te.getNestingKind().isNested() && isLoaderEnabled(te)) { + // we only accept top-level classes and if loader is enabled + currentClass = te.getQualifiedName().toString(); + } + } else if (currentClass != null && element.getKind() == ElementKind.METHOD) { + ExecutableElement ee = (ExecutableElement) element; + converters.computeIfAbsent(currentClass, c -> new ClassConverters(comparator)).addFallbackTypeConverter(ee); + } + } + + // now write all the converters + for (Map.Entry<String, ClassConverters> entry : converters.entrySet()) { + String key = entry.getKey(); + ClassConverters value = entry.getValue(); + writeConverterLoader(key, value, converterAnnotationType, fallbackAnnotationType); + } + } + + private static boolean isLoaderEnabled(Element element) { + for (AnnotationMirror ann : element.getAnnotationMirrors()) { + for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : ann.getElementValues().entrySet()) { + if ("loader".equals(entry.getKey().getSimpleName().toString())) { + return (Boolean) entry.getValue().getValue(); + } } } + return false; + } + + private void writeConverterLoader(String fqn, ClassConverters converters, + TypeElement converterAnnotationType, + TypeElement fallbackAnnotationType) throws Exception { + + int pos = fqn.lastIndexOf('.'); + String p = fqn.substring(0, pos); + String c = fqn.substring(pos + 1) + "Loader"; - String p = "org.apache.camel.impl.converter"; - String c = "CoreStaticTypeConverterLoader"; JavaFileObject jfo = processingEnv.getFiler().createSourceFile(p + "." + c); Set<String> converterClasses = new LinkedHashSet<>(); try (Writer writer = jfo.openWriter()) { @@ -98,11 +161,12 @@ public class ConverterProcessor extends AbstractCamelAnnotationProcessor { writer.append("import org.apache.camel.spi.TypeConverterLoader;\n"); writer.append("import org.apache.camel.spi.TypeConverterRegistry;\n"); writer.append("import org.apache.camel.support.TypeConverterSupport;\n"); + writer.append("import org.apache.camel.util.DoubleMap;\n"); writer.append("\n"); writer.append("@SuppressWarnings(\"unchecked\")\n"); writer.append("public class ").append(c).append(" implements TypeConverterLoader {\n"); writer.append("\n"); - writer.append(" public static final CoreStaticTypeConverterLoader INSTANCE = new CoreStaticTypeConverterLoader();\n"); + writer.append(" public static final ").append(c).append(" INSTANCE = new ").append(c).append("();\n"); writer.append("\n"); writer.append(" static abstract class SimpleTypeConverter extends TypeConverterSupport {\n"); writer.append(" private final boolean allowNull;\n"); @@ -133,7 +197,7 @@ public class ConverterProcessor extends AbstractCamelAnnotationProcessor { writer.append("\n"); writer.append(" private ").append(c).append("() {\n"); - for (Map.Entry<String, Map<TypeMirror, ExecutableElement>> to : converters.entrySet()) { + for (Map.Entry<String, Map<TypeMirror, ExecutableElement>> to : converters.getConverters().entrySet()) { for (Map.Entry<TypeMirror, ExecutableElement> from : to.getValue().entrySet()) { boolean allowNull = false; for (AnnotationMirror ann : from.getValue().getAnnotationMirrors()) { @@ -163,7 +227,7 @@ public class ConverterProcessor extends AbstractCamelAnnotationProcessor { writer.append(" @Override\n"); writer.append(" public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {\n"); writer.append(" converters.forEach((k, v, c) -> registry.addTypeConverter(k, v, c));\n"); - for (ExecutableElement ee : fallbackConverters) { + for (ExecutableElement ee : converters.getFallbackConverters()) { boolean allowNull = false; boolean canPromote = false; for (AnnotationMirror ann : ee.getAnnotationMirrors()) { diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreConverterProcessor.java similarity index 98% rename from tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java rename to tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreConverterProcessor.java index 08e3322..dfa0c29 100644 --- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java +++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreConverterProcessor.java @@ -39,7 +39,7 @@ import javax.tools.Diagnostic; import javax.tools.JavaFileObject; @SupportedAnnotationTypes({"org.apache.camel.Converter"}) -public class ConverterProcessor extends AbstractCamelAnnotationProcessor { +public class CoreConverterProcessor extends AbstractCamelAnnotationProcessor { @Override protected void doProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws Exception { @@ -98,6 +98,7 @@ public class ConverterProcessor extends AbstractCamelAnnotationProcessor { writer.append("import org.apache.camel.spi.TypeConverterLoader;\n"); writer.append("import org.apache.camel.spi.TypeConverterRegistry;\n"); writer.append("import org.apache.camel.support.TypeConverterSupport;\n"); + writer.append("import org.apache.camel.util.DoubleMap;\n"); writer.append("\n"); writer.append("@SuppressWarnings(\"unchecked\")\n"); writer.append("public class ").append(c).append(" implements TypeConverterLoader {\n"); diff --git a/tooling/apt/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/tooling/apt/src/main/resources/META-INF/services/javax.annotation.processing.Processor index 264838a..790ceb0 100644 --- a/tooling/apt/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ b/tooling/apt/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -18,6 +18,8 @@ ### only specify ModelAnnotationProcessor as it delegates to CoreEip or Spring accordingly org.apache.camel.tools.apt.ModelAnnotationProcessor org.apache.camel.tools.apt.EndpointAnnotationProcessor -org.apache.camel.tools.apt.ConverterProcessor -org.apache.camel.tools.apt.TypeConverterProcessor org.apache.camel.tools.apt.SpiProcessor +org.apache.camel.tools.apt.TypeConverterProcessor +org.apache.camel.tools.apt.CoreConverterProcessor +org.apache.camel.tools.apt.ComponentConverterProcessor +