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 f1c6b5dc7e6bffeb3e57e99871936ff86730af96 Author: Claus Ibsen <[email protected]> AuthorDate: Fri Mar 11 18:34:17 2022 +0100 CAMEL-17571: camel-jbang - Support for spring @Bean annotations in custom beans --- .../impl/engine/CamelPostProcessorHelper.java | 89 ++++++++++++++++++++++ .../impl/engine/DefaultCamelBeanPostProcessor.java | 85 +-------------------- .../apache/camel/main/SpringAnnotationSupport.java | 22 +++++- 3 files changed, 114 insertions(+), 82 deletions(-) diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelPostProcessorHelper.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelPostProcessorHelper.java index 04b4f95..a6a5654 100644 --- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelPostProcessorHelper.java +++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelPostProcessorHelper.java @@ -16,6 +16,7 @@ */ package org.apache.camel.impl.engine; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.LinkedHashMap; import java.util.Locale; @@ -23,6 +24,8 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import org.apache.camel.BeanConfigInject; +import org.apache.camel.BeanInject; import org.apache.camel.CamelContext; import org.apache.camel.CamelContextAware; import org.apache.camel.Consume; @@ -38,12 +41,15 @@ import org.apache.camel.NoSuchBeanException; import org.apache.camel.PollingConsumer; import org.apache.camel.Producer; import org.apache.camel.ProducerTemplate; +import org.apache.camel.PropertyInject; import org.apache.camel.ProxyInstantiationException; import org.apache.camel.RuntimeCamelException; import org.apache.camel.Service; +import org.apache.camel.TypeConverter; import org.apache.camel.spi.BeanProxyFactory; import org.apache.camel.spi.PropertiesComponent; import org.apache.camel.spi.PropertyConfigurer; +import org.apache.camel.spi.Registry; import org.apache.camel.support.CamelContextHelper; import org.apache.camel.support.PropertyBindingSupport; import org.apache.camel.support.service.ServiceHelper; @@ -51,6 +57,9 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.support.ObjectHelper.invokeMethod; +import static org.apache.camel.util.ObjectHelper.isEmpty; + /** * A helper class for Camel based injector or bean post processing hooks. */ @@ -402,6 +411,86 @@ public class CamelPostProcessorHelper implements CamelContextAware { return bean; } + public Object getInjectionBeanMethodValue( + CamelContext context, + Method method, Object bean, String beanName) { + Class<?> returnType = method.getReturnType(); + if (returnType == Void.TYPE) { + throw new IllegalArgumentException( + "@BindToRegistry on class: " + method.getDeclaringClass() + + " method: " + method.getName() + " with void return type is not allowed"); + } + + Object value; + Object[] parameters = bindToRegistryParameterMapping(context, method); + if (parameters != null) { + value = invokeMethod(method, bean, parameters); + } else { + value = invokeMethod(method, bean); + } + return value; + } + + private Object[] bindToRegistryParameterMapping(CamelContext context, Method method) { + if (method.getParameterCount() == 0) { + return null; + } + + // map each parameter if possible + Object[] parameters = new Object[method.getParameterCount()]; + for (int i = 0; i < method.getParameterCount(); i++) { + Class<?> type = method.getParameterTypes()[i]; + if (type.isAssignableFrom(CamelContext.class)) { + parameters[i] = context; + } else if (type.isAssignableFrom(Registry.class)) { + parameters[i] = context.getRegistry(); + } else if (type.isAssignableFrom(TypeConverter.class)) { + parameters[i] = context.getTypeConverter(); + } else { + // we also support @BeanInject and @PropertyInject annotations + Annotation[] anns = method.getParameterAnnotations()[i]; + if (anns.length == 1) { + // we dont assume there are multiple annotations on the same parameter so grab first + Annotation ann = anns[0]; + if (ann.annotationType() == PropertyInject.class) { + PropertyInject pi = (PropertyInject) ann; + Object result = getInjectionPropertyValue(type, pi.value(), pi.defaultValue(), + null, null, null); + parameters[i] = result; + } else if (ann.annotationType() == BeanConfigInject.class) { + BeanConfigInject pi = (BeanConfigInject) ann; + Object result = getInjectionBeanConfigValue(type, pi.value()); + parameters[i] = result; + } else if (ann.annotationType() == BeanInject.class) { + BeanInject bi = (BeanInject) ann; + Object result = getInjectionBeanValue(type, bi.value()); + parameters[i] = result; + } + } else { + // okay attempt to default to singleton instances from the registry + Set<?> instances = context.getRegistry().findByType(type); + if (instances.size() == 1) { + parameters[i] = instances.iterator().next(); + } else if (instances.size() > 1) { + // there are multiple instances of the same type, so barf + throw new IllegalArgumentException( + "Multiple beans of the same type: " + type + + " exists in the Camel registry. Specify the bean name on @BeanInject to bind to a single bean, at the method: " + + method); + } + } + } + + // each parameter must be mapped + if (parameters[i] == null) { + int pos = i + 1; + throw new IllegalArgumentException("@BindToProperty cannot bind parameter #" + pos + " on method: " + method); + } + } + + return parameters; + } + /** * Factory method to create a {@link org.apache.camel.ProducerTemplate} to be injected into a POJO */ 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 15ad1af..6293b86 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 @@ -16,13 +16,11 @@ */ package org.apache.camel.impl.engine; -import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Set; import java.util.function.Function; import org.apache.camel.BeanConfigInject; @@ -35,10 +33,8 @@ import org.apache.camel.EndpointInject; import org.apache.camel.ExtendedCamelContext; import org.apache.camel.Produce; import org.apache.camel.PropertyInject; -import org.apache.camel.TypeConverter; import org.apache.camel.spi.CamelBeanPostProcessor; import org.apache.camel.spi.CamelBeanPostProcessorInjector; -import org.apache.camel.spi.Registry; import org.apache.camel.support.DefaultEndpoint; import org.apache.camel.util.ReflectionHelper; import org.slf4j.Logger; @@ -515,12 +511,11 @@ public class DefaultCamelBeanPostProcessor implements CamelBeanPostProcessor, Ca } Object value = ReflectionHelper.getField(field, bean); - // use dependency injection factory to perform the task of binding the bean to registry if (value != null) { - if (unbindEnabled) { getOrLookupCamelContext().getRegistry().unbind(name); } + // use dependency injection factory to perform the task of binding the bean to registry Runnable task = getOrLookupCamelContext().adapt(ExtendedCamelContext.class) .getDependencyInjectionAnnotationFactory() .createBindToRegistryFactory(name, value, beanName, beanPostProcess); @@ -532,26 +527,14 @@ public class DefaultCamelBeanPostProcessor implements CamelBeanPostProcessor, Ca if (isEmpty(name)) { name = method.getName(); } - Class<?> returnType = method.getReturnType(); - if (returnType == null || returnType == Void.TYPE) { - throw new IllegalArgumentException( - "@BindToRegistry on class: " + method.getDeclaringClass() - + " method: " + method.getName() + " with void return type is not allowed"); - } + Object value = getPostProcessorHelper() + .getInjectionBeanMethodValue(getOrLookupCamelContext(), method, bean, beanName); - Object value; - Object[] parameters = bindToRegistryParameterMapping(method); - if (parameters != null) { - value = invokeMethod(method, bean, parameters); - } else { - value = invokeMethod(method, bean); - } - // use dependency injection factory to perform the task of binding the bean to registry if (value != null) { - if (unbindEnabled) { getOrLookupCamelContext().getRegistry().unbind(name); } + // use dependency injection factory to perform the task of binding the bean to registry Runnable task = getOrLookupCamelContext().adapt(ExtendedCamelContext.class) .getDependencyInjectionAnnotationFactory() .createBindToRegistryFactory(name, value, beanName, beanPostProcess); @@ -559,66 +542,6 @@ public class DefaultCamelBeanPostProcessor implements CamelBeanPostProcessor, Ca } } - private Object[] bindToRegistryParameterMapping(Method method) { - if (method.getParameterCount() == 0) { - return null; - } - - // map each parameter if possible - Object[] parameters = new Object[method.getParameterCount()]; - for (int i = 0; i < method.getParameterCount(); i++) { - Class<?> type = method.getParameterTypes()[i]; - if (type.isAssignableFrom(CamelContext.class)) { - parameters[i] = getOrLookupCamelContext(); - } else if (type.isAssignableFrom(Registry.class)) { - parameters[i] = getOrLookupCamelContext().getRegistry(); - } else if (type.isAssignableFrom(TypeConverter.class)) { - parameters[i] = getOrLookupCamelContext().getTypeConverter(); - } else { - // we also support @BeanInject and @PropertyInject annotations - Annotation[] anns = method.getParameterAnnotations()[i]; - if (anns.length == 1) { - // we dont assume there are multiple annotations on the same parameter so grab first - Annotation ann = anns[0]; - if (ann.annotationType() == PropertyInject.class) { - PropertyInject pi = (PropertyInject) ann; - Object result = getPostProcessorHelper().getInjectionPropertyValue(type, pi.value(), pi.defaultValue(), - null, null, null); - parameters[i] = result; - } else if (ann.annotationType() == BeanConfigInject.class) { - BeanConfigInject pi = (BeanConfigInject) ann; - Object result = getPostProcessorHelper().getInjectionBeanConfigValue(type, pi.value()); - parameters[i] = result; - } else if (ann.annotationType() == BeanInject.class) { - BeanInject bi = (BeanInject) ann; - Object result = getPostProcessorHelper().getInjectionBeanValue(type, bi.value()); - parameters[i] = result; - } - } else { - // okay attempt to default to singleton instances from the registry - Set<?> instances = getOrLookupCamelContext().getRegistry().findByType(type); - if (instances.size() == 1) { - parameters[i] = instances.iterator().next(); - } else if (instances.size() > 1) { - // there are multiple instances of the same type, so barf - throw new IllegalArgumentException( - "Multiple beans of the same type: " + type - + " exists in the Camel registry. Specify the bean name on @BeanInject to bind to a single bean, at the method: " - + method); - } - } - } - - // each parameter must be mapped - if (parameters[i] == null) { - int pos = i + 1; - throw new IllegalArgumentException("@BindToProperty cannot bind parameter #" + pos + " on method: " + method); - } - } - - return parameters; - } - private static boolean isComplexUserType(Class type) { // lets consider all non java, as complex types return type != null && !type.isPrimitive() && !type.getName().startsWith("java."); 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 index 1186ccd..881e4b3 100644 --- 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 @@ -31,6 +31,7 @@ import org.apache.camel.util.ReflectionHelper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; @@ -114,7 +115,26 @@ public final class SpringAnnotationSupport { @Override public void onMethodInject(Method method, Object bean, String beanName) { - // TODO; @Bean + Bean bi = method.getAnnotation(Bean.class); + if (bi != null) { + Object instance = helper.getInjectionBeanMethodValue(context, method, bean, beanName); + if (instance != null) { + String name = method.getName(); + if (bi.name() != null && bi.name().length > 0) { + name = bi.name()[0]; + } + // to support hot reloading of beans then we need to enable unbind mode in bean post processor + CamelBeanPostProcessor bpp = context.adapt(ExtendedCamelContext.class).getBeanPostProcessor(); + bpp.setUnbindEnabled(true); + try { + // re-bind the bean to the registry + context.getRegistry().unbind(name); + context.getRegistry().bind(name, instance); + } finally { + bpp.setUnbindEnabled(false); + } + } + } } } }
