This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch CAMEL-17571 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 347d531a6a8532beb24fa5f2e1143b44885656b1 Author: Claus Ibsen <[email protected]> AuthorDate: Fri Mar 11 10:24:38 2022 +0100 CAMEL-17571: camel-dsl - Allow to register custom annotation processors that can do custom logic after a DSL has compiled source into Java object. --- .../camel/dsl/support/AnnotationPreProcessor.java | 35 +++++++ .../dsl/support/RouteBuilderLoaderSupport.java | 35 +++++++ .../dsl/java/joor/JavaRoutesBuilderLoader.java | 106 +++++++++++++-------- .../apache/camel/main/SpringAnnotationSupport.java | 2 + 4 files changed, 137 insertions(+), 41 deletions(-) diff --git a/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/AnnotationPreProcessor.java b/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/AnnotationPreProcessor.java new file mode 100644 index 0000000..2c0499a --- /dev/null +++ b/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/AnnotationPreProcessor.java @@ -0,0 +1,35 @@ +/* + * 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.dsl.support; + +import org.apache.camel.CamelContext; + +/** + * Allows to plugin custom annotation pre-processors that are processed after the DSL has loaded the source and compiled + * into a Java object. + * <p/> + * This is used to detect and handle {@link org.apache.camel.BindToRegistry} and {@link org.apache.camel.TypeConverter} + * classes. + */ +public interface AnnotationPreProcessor { + + void handleAnnotation( + CamelContext camelContext, String name, + Class<?> clazz, Object instance) + throws Exception; + +} diff --git a/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java b/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java index 11fbc8f..5e93d15 100644 --- a/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java +++ b/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java @@ -16,6 +16,10 @@ */ package org.apache.camel.dsl.support; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + import org.apache.camel.CamelContextAware; import org.apache.camel.ExtendedCamelContext; import org.apache.camel.RoutesBuilder; @@ -35,6 +39,7 @@ public abstract class RouteBuilderLoaderSupport extends RoutesBuilderLoaderSuppo private final String extension; private StartupStepRecorder recorder; + private final List<AnnotationPreProcessor> annotationPreProcessors = new ArrayList<>(); protected RouteBuilderLoaderSupport(String extension) { this.extension = extension; @@ -46,6 +51,21 @@ public abstract class RouteBuilderLoaderSupport extends RoutesBuilderLoaderSuppo return extension; } + /** + * Gets the registered {@link AnnotationPreProcessor}. + */ + public List<AnnotationPreProcessor> getAnnotationPreProcessors() { + return annotationPreProcessors; + } + + /** + * Add a custom {@link AnnotationPreProcessor} to handle specific annotations after compiling the source into a Java + * object. + */ + public void addAnnotationPreProcessor(AnnotationPreProcessor preProcessor) { + this.annotationPreProcessors.add(preProcessor); + } + @Override protected void doBuild() throws Exception { super.doBuild(); @@ -56,6 +76,21 @@ public abstract class RouteBuilderLoaderSupport extends RoutesBuilderLoaderSuppo } @Override + protected void doStart() throws Exception { + super.doStart(); + + if (getCamelContext() != null) { + // discover optional pre-processors to be used + Set<AnnotationPreProcessor> pres = getCamelContext().getRegistry().findByType(AnnotationPreProcessor.class); + if (pres != null && !pres.isEmpty()) { + for (AnnotationPreProcessor pre : pres) { + addAnnotationPreProcessor(pre); + } + } + } + } + + @Override public RoutesBuilder loadRoutesBuilder(Resource resource) throws Exception { final RouteBuilder builder = doLoadRouteBuilder(resource); if (builder != null) { diff --git a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java index dd149db..baad4ee 100644 --- a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java +++ b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java @@ -23,6 +23,7 @@ import java.util.regex.Pattern; import org.apache.camel.BindToRegistry; import org.apache.camel.CamelConfiguration; +import org.apache.camel.CamelContext; import org.apache.camel.Configuration; import org.apache.camel.Converter; import org.apache.camel.ExtendedCamelContext; @@ -30,6 +31,7 @@ import org.apache.camel.LoggingLevel; import org.apache.camel.TypeConverterExists; import org.apache.camel.api.management.ManagedResource; import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.dsl.support.AnnotationPreProcessor; import org.apache.camel.dsl.support.RouteBuilderLoaderSupport; import org.apache.camel.spi.CamelBeanPostProcessor; import org.apache.camel.spi.Resource; @@ -50,6 +52,9 @@ public class JavaRoutesBuilderLoader extends RouteBuilderLoaderSupport { public JavaRoutesBuilderLoader() { super(EXTENSION); + + addAnnotationPreProcessor(new ConverterAnnotationPreProcessor()); + addAnnotationPreProcessor(new BindToRegistryAnnotationPreProcessor()); } @Override @@ -64,51 +69,16 @@ public class JavaRoutesBuilderLoader extends RouteBuilderLoaderSupport { Reflect ref = Reflect.compile(name, content).create(); Class<?> clazz = ref.type(); - if (clazz.getAnnotation(Converter.class) != null) { - TypeConverterRegistry tcr = getCamelContext().getTypeConverterRegistry(); - TypeConverterExists exists = tcr.getTypeConverterExists(); - LoggingLevel level = tcr.getTypeConverterExistsLoggingLevel(); - // force type converter to override as we could be re-loading - tcr.setTypeConverterExists(TypeConverterExists.Override); - tcr.setTypeConverterExistsLoggingLevel(LoggingLevel.OFF); - try { - tcr.addTypeConverters(clazz); - } finally { - tcr.setTypeConverterExists(exists); - tcr.setTypeConverterExistsLoggingLevel(level); - } - return null; - } - Object obj = ref.get(); if (obj instanceof RouteBuilder) { return (RouteBuilder) obj; - } else if (obj != null) { - BindToRegistry bir = obj.getClass().getAnnotation(BindToRegistry.class); - Configuration cfg = obj.getClass().getAnnotation(Configuration.class); - if (bir != null || cfg != null || obj instanceof CamelConfiguration) { - CamelBeanPostProcessor bpp = getCamelContext().adapt(ExtendedCamelContext.class).getBeanPostProcessor(); - if (bir != null && ObjectHelper.isNotEmpty(bir.value())) { - name = bir.value(); - } else if (cfg != null && ObjectHelper.isNotEmpty(cfg.value())) { - name = cfg.value(); - } - // to support hot reloading of beans then we need to enable unbind mode in bean post processor - bpp.setUnbindEnabled(true); - try { - // this class is a bean service which needs to be post processed and registered which happens - // automatic by the bean post processor - bpp.postProcessBeforeInitialization(obj, name); - bpp.postProcessAfterInitialization(obj, name); - } finally { - bpp.setUnbindEnabled(false); - } - if (obj instanceof CamelConfiguration) { - ((CamelConfiguration) obj).configure(getCamelContext()); - } - return null; - } } + + // not a route builder but we support annotation scan to register custom beans, type converters, etc. + for (AnnotationPreProcessor pre : getAnnotationPreProcessors()) { + pre.handleAnnotation(getCamelContext(), name, clazz, obj); + } + return null; } } @@ -127,4 +97,58 @@ public class JavaRoutesBuilderLoader extends RouteBuilderLoaderSupport { ? matcher.group(1) + "." + name : name; } + + private static class ConverterAnnotationPreProcessor implements AnnotationPreProcessor { + + @Override + public void handleAnnotation(CamelContext camelContext, String name, Class<?> clazz, Object instance) { + if (clazz.getAnnotation(Converter.class) != null) { + TypeConverterRegistry tcr = camelContext.getTypeConverterRegistry(); + TypeConverterExists exists = tcr.getTypeConverterExists(); + LoggingLevel level = tcr.getTypeConverterExistsLoggingLevel(); + // force type converter to override as we could be re-loading + tcr.setTypeConverterExists(TypeConverterExists.Override); + tcr.setTypeConverterExistsLoggingLevel(LoggingLevel.OFF); + try { + tcr.addTypeConverters(clazz); + } finally { + tcr.setTypeConverterExists(exists); + tcr.setTypeConverterExistsLoggingLevel(level); + } + } + } + } + + private static class BindToRegistryAnnotationPreProcessor implements AnnotationPreProcessor { + + @Override + public void handleAnnotation(CamelContext camelContext, String name, Class<?> clazz, Object instance) + throws Exception { + BindToRegistry bir = instance.getClass().getAnnotation(BindToRegistry.class); + Configuration cfg = instance.getClass().getAnnotation(Configuration.class); + if (bir != null || cfg != null || instance instanceof CamelConfiguration) { + CamelBeanPostProcessor bpp = camelContext.adapt(ExtendedCamelContext.class).getBeanPostProcessor(); + if (bir != null && ObjectHelper.isNotEmpty(bir.value())) { + name = bir.value(); + } else if (cfg != null && ObjectHelper.isNotEmpty(cfg.value())) { + name = cfg.value(); + } + // to support hot reloading of beans then we need to enable unbind mode in bean post processor + bpp.setUnbindEnabled(true); + try { + // this class is a bean service which needs to be post processed and registered which happens + // automatic by the bean post processor + bpp.postProcessBeforeInitialization(instance, name); + bpp.postProcessAfterInitialization(instance, name); + } finally { + bpp.setUnbindEnabled(false); + } + if (instance instanceof CamelConfiguration) { + ((CamelConfiguration) instance).configure(camelContext); + } + } + } + + } + } diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/SpringAnnotationSupport.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/SpringAnnotationSupport.java new file mode 100644 index 0000000..1682402 --- /dev/null +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/SpringAnnotationSupport.java @@ -0,0 +1,2 @@ +package org.apache.camel.main;public class SpringAnnotationSupport { +}
