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 2022/03/12 06:18:36 UTC

[camel] 01/11: CAMEL-17571: camel-dsl - Allow to register custom annotation processors that can do custom logic after a DSL has compiled source into Java object.

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 <cl...@gmail.com>
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 {
+}