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:42 UTC
[camel] 07/11: CAMEL-17571: camel-jbang - Support for spring @Bean annotations in custom beans
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 <cl...@gmail.com>
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);
+ }
+ }
+ }
}
}
}