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 2024/08/29 14:17:19 UTC

(camel) 05/06: CAMEL-21140: camel-core - Add init/destroy method support for registry bind

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch init-destroy
in repository https://gitbox.apache.org/repos/asf/camel.git

commit d31aba86fb3ce66e8366018b29f4649ef3ee8d83
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Aug 29 15:46:25 2024 +0200

    CAMEL-21140: camel-core - Add init/destroy method support for registry bind
---
 .../main/java/org/apache/camel/BindToRegistry.java | 10 ++++++++
 .../CamelDependencyInjectionAnnotationFactory.java |  5 +++-
 .../impl/engine/DefaultCamelBeanPostProcessor.java | 29 ++++++++++++++--------
 ...efaultDependencyInjectionAnnotationFactory.java |  6 +++--
 .../org/apache/camel/support/DefaultRegistry.java  | 12 ++++-----
 .../camel/support/scan/PackageScanHelper.java      | 20 +++++++++++++--
 6 files changed, 60 insertions(+), 22 deletions(-)

diff --git a/core/camel-api/src/main/java/org/apache/camel/BindToRegistry.java b/core/camel-api/src/main/java/org/apache/camel/BindToRegistry.java
index 7cf2bd2d43f..15ea5a3cf65 100644
--- a/core/camel-api/src/main/java/org/apache/camel/BindToRegistry.java
+++ b/core/camel-api/src/main/java/org/apache/camel/BindToRegistry.java
@@ -52,4 +52,14 @@ public @interface BindToRegistry {
      * only need to create beans if they are explicit in-use.
      */
     boolean lazy() default false;
+
+    /**
+     * The optional name of a method to call on the bean instance during initialization.
+     */
+    String initMethod() default "";
+
+    /**
+     * The optional name of a method to call on the bean instance during destruction.
+     */
+    String destroyMethod() default "";
 }
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/CamelDependencyInjectionAnnotationFactory.java b/core/camel-api/src/main/java/org/apache/camel/spi/CamelDependencyInjectionAnnotationFactory.java
index 1eb23532078..5ea4d40770a 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/CamelDependencyInjectionAnnotationFactory.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/CamelDependencyInjectionAnnotationFactory.java
@@ -30,10 +30,13 @@ public interface CamelDependencyInjectionAnnotationFactory {
      * @param  beanName        the bean name
      * @param  beanType        the bean type (optional)
      * @param  beanPostProcess whether bean post processor should be performed
+     * @param  initMethod      optional init method (invoked at bind)
+     * @param  destroyMethod   optional destroy method (invoked at unbind or stopping Camel)
      * @return                 the created task to use for binding the bean
      */
     Runnable createBindToRegistryFactory(
             String id, Object bean, Class<?> beanType,
-            String beanName, boolean beanPostProcess);
+            String beanName, boolean beanPostProcess,
+            String initMethod, String destroyMethod);
 
 }
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelBeanPostProcessor.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelBeanPostProcessor.java
index e56ba6a462a..12c14e21507 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelBeanPostProcessor.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelBeanPostProcessor.java
@@ -300,7 +300,8 @@ public class DefaultCamelBeanPostProcessor implements CamelBeanPostProcessor, Ca
 
             BindToRegistry bind = field.getAnnotation(BindToRegistry.class);
             if (bind != null) {
-                bindToRegistry(field, bind.value(), bean, beanName, bind.beanPostProcess(), false);
+                bindToRegistry(field, bind.value(), bean, beanName, bind.beanPostProcess(), false, bind.initMethod(),
+                        bind.destroyMethod());
             }
         });
     }
@@ -405,7 +406,8 @@ public class DefaultCamelBeanPostProcessor implements CamelBeanPostProcessor, Ca
         // bind each method
         methods.forEach(method -> {
             BindToRegistry bind = method.getAnnotation(BindToRegistry.class);
-            bindToRegistry(method, bind.value(), bean, beanName, bind.beanPostProcess(), bind.lazy());
+            bindToRegistry(method, bind.value(), bean, beanName, bind.beanPostProcess(), bind.lazy(), bind.initMethod(),
+                    bind.destroyMethod());
         });
     }
 
@@ -413,7 +415,8 @@ public class DefaultCamelBeanPostProcessor implements CamelBeanPostProcessor, Ca
         Class<?> clazz = bean.getClass();
         BindToRegistry ann = clazz.getAnnotation(BindToRegistry.class);
         if (ann != null) {
-            bindToRegistry(clazz, ann.value(), bean, beanName, ann.beanPostProcess(), ann.lazy());
+            bindToRegistry(clazz, ann.value(), bean, beanName, ann.beanPostProcess(), ann.lazy(), ann.initMethod(),
+                    ann.destroyMethod());
         }
     }
 
@@ -422,7 +425,8 @@ public class DefaultCamelBeanPostProcessor implements CamelBeanPostProcessor, Ca
             BindToRegistry ann = clazz.getAnnotation(BindToRegistry.class);
             if (ann != null) {
                 // it is a nested class so we don't have a bean instance for it
-                bindToRegistry(clazz, ann.value(), null, null, ann.beanPostProcess(), ann.lazy());
+                bindToRegistry(clazz, ann.value(), null, null, ann.beanPostProcess(), ann.lazy(), ann.initMethod(),
+                        ann.destroyMethod());
             }
         });
     }
@@ -505,7 +509,8 @@ public class DefaultCamelBeanPostProcessor implements CamelBeanPostProcessor, Ca
 
     private void bindToRegistry(
             Class<?> clazz, String name, Object bean, String beanName,
-            boolean beanPostProcess, boolean lazy) {
+            boolean beanPostProcess, boolean lazy,
+            String initMethod, String destroyMethod) {
         if (isEmpty(name)) {
             name = clazz.getSimpleName();
         }
@@ -536,13 +541,14 @@ public class DefaultCamelBeanPostProcessor implements CamelBeanPostProcessor, Ca
         }
         // use dependency injection factory to perform the task of binding the bean to registry
         Runnable task = PluginHelper.getDependencyInjectionAnnotationFactory(getOrLookupCamelContext())
-                .createBindToRegistryFactory(name, bean, clazz, beanName, postProcess);
+                .createBindToRegistryFactory(name, bean, clazz, beanName, postProcess, initMethod, destroyMethod);
         task.run();
     }
 
     private void bindToRegistry(
             Field field, String name, Object bean, String beanName,
-            boolean beanPostProcess, boolean lazy) {
+            boolean beanPostProcess, boolean lazy,
+            String initMethod, String destroyMethod) {
         if (isEmpty(name)) {
             name = field.getName();
         }
@@ -572,14 +578,16 @@ public class DefaultCamelBeanPostProcessor implements CamelBeanPostProcessor, Ca
             }
             // use dependency injection factory to perform the task of binding the bean to registry
             Runnable task = PluginHelper.getDependencyInjectionAnnotationFactory(getOrLookupCamelContext())
-                    .createBindToRegistryFactory(name, value, field.getType(), beanName, postProcess);
+                    .createBindToRegistryFactory(name, value, field.getType(), beanName, postProcess, initMethod,
+                            destroyMethod);
             task.run();
         }
     }
 
     private void bindToRegistry(
             Method method, String name, Object bean, String beanName,
-            boolean beanPostProcess, boolean lazy) {
+            boolean beanPostProcess, boolean lazy,
+            String initMethod, String destroyMethod) {
         if (isEmpty(name)) {
             name = method.getName();
         }
@@ -611,7 +619,8 @@ public class DefaultCamelBeanPostProcessor implements CamelBeanPostProcessor, Ca
             }
             // use dependency injection factory to perform the task of binding the bean to registry
             Runnable task = PluginHelper.getDependencyInjectionAnnotationFactory(getOrLookupCamelContext())
-                    .createBindToRegistryFactory(name, value, method.getReturnType(), beanName, postProcess);
+                    .createBindToRegistryFactory(name, value, method.getReturnType(), beanName, postProcess,
+                            initMethod, destroyMethod);
             task.run();
         }
     }
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDependencyInjectionAnnotationFactory.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDependencyInjectionAnnotationFactory.java
index 1e1dcf70d11..fe6899f5b48 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDependencyInjectionAnnotationFactory.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDependencyInjectionAnnotationFactory.java
@@ -50,7 +50,8 @@ public class DefaultDependencyInjectionAnnotationFactory
     @Override
     @SuppressWarnings("unchecked")
     public Runnable createBindToRegistryFactory(
-            String id, Object bean, Class<?> beanType, String beanName, boolean beanPostProcess) {
+            String id, Object bean, Class<?> beanType, String beanName, boolean beanPostProcess,
+            String initMethod, String destroyMethod) {
         return () -> {
             if (beanPostProcess) {
                 try {
@@ -63,11 +64,12 @@ public class DefaultDependencyInjectionAnnotationFactory
             }
             CamelContextAware.trySetCamelContext(bean, camelContext);
             if (bean instanceof Supplier) {
+                // TODO: supplier/lazy does not support init/destroy
                 // must be Supplier<Object> to ensure correct binding
                 Supplier<Object> sup = (Supplier<Object>) bean;
                 camelContext.getRegistry().bind(id, beanType, sup);
             } else {
-                camelContext.getRegistry().bind(id, bean);
+                camelContext.getRegistry().bind(id, bean, initMethod, destroyMethod);
             }
         };
     }
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java b/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java
index 521762ea480..b9036124e6e 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java
@@ -42,6 +42,8 @@ import org.apache.camel.util.function.Suppliers;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.camel.util.ObjectHelper.isNotEmpty;
+
 /**
  * The default {@link Registry} which supports using a given first-choice repository to lookup the beans, such as
  * Spring, JNDI, OSGi etc. And to use a secondary {@link SimpleRegistry} as the fallback repository to lookup and bind
@@ -180,11 +182,7 @@ public class DefaultRegistry extends ServiceSupport implements Registry, LocalBe
 
     @Override
     public void bind(String id, Class<?> type, Object bean) throws RuntimeCamelException {
-        if (bean != null) {
-            // automatic inject camel context in bean if its aware
-            CamelContextAware.trySetCamelContext(bean, camelContext);
-            fallbackRegistry.bind(id, type, bean);
-        }
+        bind(id, type, bean, null, null);
     }
 
     @Override
@@ -193,7 +191,7 @@ public class DefaultRegistry extends ServiceSupport implements Registry, LocalBe
         if (bean != null) {
             // automatic inject camel context in bean if its aware
             CamelContextAware.trySetCamelContext(bean, camelContext);
-            if (initMethod != null) {
+            if (isNotEmpty(initMethod)) {
                 try {
                     org.apache.camel.support.ObjectHelper.invokeMethodSafe(initMethod, bean);
                 } catch (Exception e) {
@@ -201,7 +199,7 @@ public class DefaultRegistry extends ServiceSupport implements Registry, LocalBe
                 }
             }
             fallbackRegistry.bind(id, type, bean);
-            if (destroyMethod != null) {
+            if (isNotEmpty(destroyMethod)) {
                 beansToDestroy.put(id, new KeyValueHolder<>(bean, destroyMethod));
             }
         }
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/scan/PackageScanHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/scan/PackageScanHelper.java
index 4e927d221d8..e405e4550cd 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/scan/PackageScanHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/scan/PackageScanHelper.java
@@ -33,6 +33,7 @@ import org.apache.camel.spi.Registry;
 import org.apache.camel.support.PluginHelper;
 
 import static org.apache.camel.util.ObjectHelper.isEmpty;
+import static org.apache.camel.util.ObjectHelper.isNotEmpty;
 
 /**
  * Helper for Camel package scanning.
@@ -58,6 +59,7 @@ public class PackageScanHelper {
                 Injector injector = camelContext.getInjector();
                 if (scanner != null && injector != null) {
                     Map<Class<?>, Object> created = new HashMap<>();
+                    Map<Object, String> initMethods = new HashMap<>();
                     Set<Class<?>> lazy = new HashSet<>();
                     for (String pkg : packages) {
                         Set<Class<?>> classes = scanner.findAnnotated(BindToRegistry.class, pkg);
@@ -100,7 +102,7 @@ public class PackageScanHelper {
                                 // - bind to registry if @org.apache.camel.BindToRegistry is present
                                 // use dependency injection factory to perform the task of binding the bean to registry
                                 Runnable task = PluginHelper.getDependencyInjectionAnnotationFactory(camelContext)
-                                        .createBindToRegistryFactory(name, bean, c, beanName, false);
+                                        .createBindToRegistryFactory(name, bean, c, beanName, false, null, null);
                                 task.run();
                             }
                         }
@@ -118,7 +120,11 @@ public class PackageScanHelper {
                                 // - bind to registry if @org.apache.camel.BindToRegistry is present
                                 // use dependency injection factory to perform the task of binding the bean to registry
                                 Runnable task = PluginHelper.getDependencyInjectionAnnotationFactory(camelContext)
-                                        .createBindToRegistryFactory(name, bean, c, beanName, false);
+                                        .createBindToRegistryFactory(name, bean, c, beanName, false, null, ann.destroyMethod());
+                                // defer calling init methods until dependency injection in phase-4 is complete
+                                if (isNotEmpty(ann.initMethod())) {
+                                    initMethods.put(bean, ann.initMethod());
+                                }
                                 task.run();
                             }
                         }
@@ -138,6 +144,16 @@ public class PackageScanHelper {
                                 throw new RuntimeCamelException("Error post-processing bean: " + beanName, e);
                             }
                         }
+                        for (Entry<Object, String> entry : initMethods.entrySet()) {
+                            Object bean = entry.getKey();
+                            String method = entry.getValue();
+                            // phase-5: now call init method on created beans
+                            try {
+                                org.apache.camel.support.ObjectHelper.invokeMethodSafe(method, bean);
+                            } catch (Exception e) {
+                                throw RuntimeCamelException.wrapRuntimeCamelException(e);
+                            }
+                        }
                     }
                 }
             }