This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
commit 9abf0ff27a5183f3b09616062932829ec29d3a38 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Mon Jun 17 09:33:26 2019 +0200 CAMEL-13647: Allow to do autowrire by classpath. Quick and dirty prototype --- .../camel/support/IntrospectionSupportTest.java | 2 +- .../camel/main/MainConfigurationProperties.java | 26 ++++++ .../apache/camel/support/IntrospectionSupport.java | 45 +++++---- .../camel/support/PropertyBindingSupport.java | 103 +++++++++++---------- .../src/main/resources/application.properties | 2 +- 5 files changed, 112 insertions(+), 66 deletions(-) diff --git a/core/camel-core/src/test/java/org/apache/camel/support/IntrospectionSupportTest.java b/core/camel-core/src/test/java/org/apache/camel/support/IntrospectionSupportTest.java index 0088941..debe26b 100644 --- a/core/camel-core/src/test/java/org/apache/camel/support/IntrospectionSupportTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/support/IntrospectionSupportTest.java @@ -552,7 +552,7 @@ public class IntrospectionSupportTest extends ContextTestSupport { @Test public void testFindSetterMethodsOrderedByParameterType() throws Exception { - List<Method> setters = IntrospectionSupport.findSetterMethodsOrderedByParameterType(MyOverloadedBean.class, "bean", false); + List<Method> setters = IntrospectionSupport.findSetterMethodsOrderedByParameterType(MyOverloadedBean.class, "bean", false, false); assertNotNull(setters); assertEquals(2, setters.size()); diff --git a/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java index c43ced7..7af382b 100644 --- a/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java +++ b/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java @@ -24,6 +24,7 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties< private boolean autoConfigurationEnabled = true; private boolean autowireComponentProperties = true; private boolean autowireComponentPropertiesDeep; + private boolean autowireComponentPropertiesAllowPrivateSetter = true; private long duration = -1; private int durationHitExitCode; private boolean hangupInterceptorEnabled = true; @@ -109,6 +110,19 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties< this.autowireComponentPropertiesDeep = autowireComponentPropertiesDeep; } + public boolean isAutowireComponentPropertiesAllowPrivateSetter() { + return autowireComponentPropertiesAllowPrivateSetter; + } + + /** + * Whether autowiring components allows to use private setter method when setting the value. This may be needed + * in some rare situations when some configuration classes may configure via constructors over setters. But + * constructor configuration is more cumbersome to use via .properties files etc. + */ + public void setAutowireComponentPropertiesAllowPrivateSetter(boolean autowireComponentPropertiesAllowPrivateSetter) { + this.autowireComponentPropertiesAllowPrivateSetter = autowireComponentPropertiesAllowPrivateSetter; + } + public long getDuration() { return duration; } @@ -195,6 +209,18 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties< } /** + * Whether autowiring components (with deep nesting by attempting to walk as deep down the object graph by creating new empty objects on the way if needed) + * with properties that are of same type, which has been added to the Camel registry, as a singleton instance. + * This is used for convention over configuration to inject DataSource, AmazonLogin instances to the components. + * <p/> + * This option is default enabled. + */ + public MainConfigurationProperties withAutowireComponentPropertiesAllowPrivateSetter(boolean autowireComponentPropertiesAllowPrivateSetter) { + this.autowireComponentPropertiesAllowPrivateSetter = autowireComponentPropertiesAllowPrivateSetter; + return this; + } + + /** * Sets the duration (in seconds) to run the application until it * should be terminated. Defaults to -1. Any value <= 0 will run forever. */ diff --git a/core/camel-support/src/main/java/org/apache/camel/support/IntrospectionSupport.java b/core/camel-support/src/main/java/org/apache/camel/support/IntrospectionSupport.java index e82255f..d2b325a 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/IntrospectionSupport.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/IntrospectionSupport.java @@ -539,6 +539,23 @@ public final class IntrospectionSupport { */ public static boolean setProperty(CamelContext context, TypeConverter typeConverter, Object target, String name, Object value, String refName, boolean allowBuilderPattern) throws Exception { + return setProperty(context, typeConverter, target, name, value, refName, allowBuilderPattern, false); + } + + /** + * This method supports three modes to set a property: + * + * 1. Setting a Map property where the property name refers to a map via name[aKey] where aKey is the map key to use. + * + * 2. Setting a property that has already been resolved, this is the case when {@code context} and {@code refName} are + * NULL and {@code value} is non-NULL. + * + * 3. Setting a property that has not yet been resolved, the property will be resolved based on the suitable methods + * found matching the property name on the {@code target} bean. For this mode to be triggered the parameters + * {@code context} and {@code refName} must NOT be NULL, and {@code value} MUST be NULL. + */ + public static boolean setProperty(CamelContext context, TypeConverter typeConverter, Object target, String name, Object value, String refName, + boolean allowBuilderPattern, boolean allowPrivateSetter) throws Exception { // does the property name include a lookup key, then we need to set the property as a map or list if (name.contains("[") && name.endsWith("]")) { @@ -599,10 +616,10 @@ public final class IntrospectionSupport { // we need to lookup the value from the registry if (context != null && refName != null && value == null) { - setters = findSetterMethodsOrderedByParameterType(clazz, name, allowBuilderPattern); + setters = findSetterMethodsOrderedByParameterType(clazz, name, allowBuilderPattern, allowPrivateSetter); } else { // find candidates of setter methods as there can be overloaded setters - setters = findSetterMethods(clazz, name, value, allowBuilderPattern); + setters = findSetterMethods(clazz, name, value, allowBuilderPattern, allowPrivateSetter); } if (setters.isEmpty()) { return false; @@ -723,22 +740,22 @@ public final class IntrospectionSupport { public static boolean setProperty(CamelContext context, Object target, String name, Object value) throws Exception { // allow build pattern as a setter as well - return setProperty(context, context != null ? context.getTypeConverter() : null, target, name, value, null, true); + return setProperty(context, context != null ? context.getTypeConverter() : null, target, name, value, null, true, false); } public static boolean setProperty(CamelContext context, TypeConverter typeConverter, Object target, String name, Object value) throws Exception { // allow build pattern as a setter as well - return setProperty(context, typeConverter, target, name, value, null, true); + return setProperty(context, typeConverter, target, name, value, null, true, false); } public static boolean setProperty(TypeConverter typeConverter, Object target, String name, Object value) throws Exception { // allow build pattern as a setter as well - return setProperty(null, typeConverter, target, name, value, null, true); + return setProperty(null, typeConverter, target, name, value, null, true, false); } @Deprecated public static boolean setProperty(Object target, String name, Object value, boolean allowBuilderPattern) throws Exception { - return setProperty(null, null, target, name, value, null, allowBuilderPattern); + return setProperty(null, null, target, name, value, null, allowBuilderPattern, false); } @Deprecated @@ -747,13 +764,9 @@ public final class IntrospectionSupport { return setProperty(target, name, value, true); } - public static Set<Method> findSetterMethods(Class<?> clazz, String name, boolean allowBuilderPattern) { + public static Set<Method> findSetterMethods(Class<?> clazz, String name, boolean allowBuilderPattern, boolean allowPrivateSetter) { Set<Method> candidates = new LinkedHashSet<>(); - boolean allowPrivate = name.startsWith("#private#"); - if (allowPrivate) { - name = name.substring(9); - } // Build the method name String builderName = "with" + StringHelper.capitalize(name, true); String builderName2 = StringHelper.capitalize(name, true); @@ -763,7 +776,7 @@ public final class IntrospectionSupport { // Since Object.class.isInstance all the objects, // here we just make sure it will be add to the bottom of the set. Method objectSetMethod = null; - Method[] methods = allowPrivate ? clazz.getDeclaredMethods() : clazz.getMethods(); + Method[] methods = allowPrivateSetter ? clazz.getDeclaredMethods() : clazz.getMethods(); for (Method method : methods) { boolean validName = method.getName().equals(setName) || allowBuilderPattern && method.getName().equals(builderName) || allowBuilderPattern && method.getName().equals(builderName2); if (validName) { @@ -785,8 +798,8 @@ public final class IntrospectionSupport { return candidates; } - static Set<Method> findSetterMethods(Class<?> clazz, String name, Object value, boolean allowBuilderPattern) { - Set<Method> candidates = findSetterMethods(clazz, name, allowBuilderPattern); + static Set<Method> findSetterMethods(Class<?> clazz, String name, Object value, boolean allowBuilderPattern, boolean allowPrivateSetter) { + Set<Method> candidates = findSetterMethods(clazz, name, allowBuilderPattern, allowPrivateSetter); if (candidates.isEmpty()) { return candidates; @@ -811,10 +824,10 @@ public final class IntrospectionSupport { } } - static List<Method> findSetterMethodsOrderedByParameterType(Class<?> target, String propertyName, boolean allowBuilderPattern) { + static List<Method> findSetterMethodsOrderedByParameterType(Class<?> target, String propertyName, boolean allowBuilderPattern, boolean allowPrivateSetter) { List<Method> answer = new LinkedList<>(); List<Method> primitives = new LinkedList<>(); - Set<Method> setters = findSetterMethods(target, propertyName, allowBuilderPattern); + Set<Method> setters = findSetterMethods(target, propertyName, allowBuilderPattern, allowPrivateSetter); for (Method setter : setters) { Class<?> parameterType = setter.getParameterTypes()[0]; if (PRIMITIVE_CLASSES.contains(parameterType)) { diff --git a/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java b/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java index c91bb5e..a4367d0 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java @@ -19,18 +19,13 @@ package org.apache.camel.support; import java.io.File; import java.io.IOException; import java.lang.reflect.Method; -import java.net.JarURLConnection; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; -import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.apache.camel.CamelContext; @@ -55,9 +50,6 @@ import static org.apache.camel.util.ObjectHelper.isNotEmpty; * <li>autowire by type - Values can refer to singleton beans by auto wiring by setting the value to #autowired</li> * <li>reference new class - Values can refer to creating new beans by their class name by prefixing with #class, eg #class:com.foo.MyClassType</li> * </ul> - * When setting the property then by default only public setter methods is supported, however you can - * prefix the name with #private# to allow using private/protected setters, eg to use the private setBrokerURL setter method: - * camel.component.jms.configuration.connectionFactory.#private#brokerURL=tcp://localhost:61616. * <p/> * This implementations reuses parts of {@link IntrospectionSupport}. */ @@ -73,6 +65,7 @@ public final class PropertyBindingSupport { private boolean reference = true; private boolean placeholder = true; private boolean fluentBuilder = true; + private boolean allowPrivateSetter = true; private String optionPrefix; /** @@ -126,6 +119,15 @@ public final class PropertyBindingSupport { } /** + * Whether properties should be filtered by prefix. * + * Note that the prefix is removed from the key before the property is bound. + */ + public Builder withAllowPrivateSetter(boolean allowPrivateSetter) { + this.allowPrivateSetter = allowPrivateSetter; + return this; + } + + /** * Binds the properties to the target object, and removes the property that was bound from properties. * * @param camelContext the camel context @@ -138,7 +140,7 @@ public final class PropertyBindingSupport { org.apache.camel.util.ObjectHelper.notNull(target, "target"); org.apache.camel.util.ObjectHelper.notNull(properties, "properties"); - return bindProperties(camelContext, target, properties, optionPrefix, nesting, deepNesting, fluentBuilder, reference, placeholder); + return bindProperties(camelContext, target, properties, optionPrefix, nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder); } } @@ -246,7 +248,7 @@ public final class PropertyBindingSupport { // attempt to create new instances to walk down the tree if its null (deepNesting option) if (value == null && deepNesting) { // okay is there a setter so we can create a new instance and set it automatic - Method method = findBestSetterMethod(target.getClass(), key, true); + Method method = findBestSetterMethod(target.getClass(), key, true, true); if (method != null) { Class<?> parameterType = method.getParameterTypes()[0]; if (parameterType != null && org.apache.camel.util.ObjectHelper.hasDefaultPublicNoArgConstructor(parameterType)) { @@ -373,7 +375,7 @@ public final class PropertyBindingSupport { // attempt to create new instances to walk down the tree if its null (deepNesting option) if (value == null && deepNesting) { // okay is there a setter so we can create a new instance and set it automatic - Method method = findBestSetterMethod(target.getClass(), key, true); + Method method = findBestSetterMethod(target.getClass(), key, true, true); if (method != null) { Class<?> parameterType = method.getParameterTypes()[0]; if (parameterType != null && org.apache.camel.util.ObjectHelper.hasDefaultPublicNoArgConstructor(parameterType)) { @@ -424,48 +426,52 @@ public final class PropertyBindingSupport { * @return true if one or more properties was bound */ public static boolean bindProperties(CamelContext camelContext, Object target, Map<String, Object> properties, String optionPrefix) { - return bindProperties(camelContext, target, properties, optionPrefix, true, true, true, true, true); + return bindProperties(camelContext, target, properties, optionPrefix, true, true, true, true, true, true); } /** * Binds the properties with the given prefix to the target object, and removes the property that was bound from properties. * - * @param camelContext the camel context - * @param target the target object - * @param properties the properties where the bound properties will be removed from - * @param nesting whether nesting is in use - * @param deepNesting whether deep nesting is in use, where Camel will attempt to walk as deep as possible by creating new objects in the OGNL graph if - * a property has a setter and the object can be created from a default no-arg constructor. - * @param fluentBuilder whether fluent builder is allowed as a valid getter/setter - * @param reference whether reference parameter (syntax starts with #) is in use - * @param placeholder whether to use Camels property placeholder to resolve placeholders on keys and values - * @return true if one or more properties was bound + * @param camelContext the camel context + * @param target the target object + * @param properties the properties where the bound properties will be removed from + * @param nesting whether nesting is in use + * @param deepNesting whether deep nesting is in use, where Camel will attempt to walk as deep as possible by creating new objects in the OGNL graph if + * a property has a setter and the object can be created from a default no-arg constructor. + * @param fluentBuilder whether fluent builder is allowed as a valid getter/setter + * @param allowPrivateSetter whether autowiring components allows to use private setter method when setting the value + * @param reference whether reference parameter (syntax starts with #) is in use + * @param placeholder whether to use Camels property placeholder to resolve placeholders on keys and values + * @return true if one or more properties was bound */ public static boolean bindProperties(CamelContext camelContext, Object target, Map<String, Object> properties, - boolean nesting, boolean deepNesting, boolean fluentBuilder, boolean reference, boolean placeholder) { + boolean nesting, boolean deepNesting, boolean fluentBuilder, boolean allowPrivateSetter, + boolean reference, boolean placeholder) { - return bindProperties(camelContext, target, properties, null, nesting, deepNesting, fluentBuilder, reference, placeholder); + return bindProperties(camelContext, target, properties, null, nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder); } /** * Binds the properties with the given prefix to the target object, and removes the property that was bound from properties. * Note that the prefix is removed from the key before the property is bound. * - * @param camelContext the camel context - * @param target the target object - * @param properties the properties where the bound properties will be removed from - * @param optionPrefix the prefix used to filter properties - * @param nesting whether nesting is in use - * @param deepNesting whether deep nesting is in use, where Camel will attempt to walk as deep as possible by creating new objects in the OGNL graph if - * a property has a setter and the object can be created from a default no-arg constructor. - * @param fluentBuilder whether fluent builder is allowed as a valid getter/setter - * @param reference whether reference parameter (syntax starts with #) is in use - * @param placeholder whether to use Camels property placeholder to resolve placeholders on keys and values - * @return true if one or more properties was bound + * @param camelContext the camel context + * @param target the target object + * @param properties the properties where the bound properties will be removed from + * @param optionPrefix the prefix used to filter properties + * @param nesting whether nesting is in use + * @param deepNesting whether deep nesting is in use, where Camel will attempt to walk as deep as possible by creating new objects in the OGNL graph if + * a property has a setter and the object can be created from a default no-arg constructor. + * @param fluentBuilder whether fluent builder is allowed as a valid getter/setter + * @param allowPrivateSetter whether autowiring components allows to use private setter method when setting the value + * @param reference whether reference parameter (syntax starts with #) is in use + * @param placeholder whether to use Camels property placeholder to resolve placeholders on keys and values + * @return true if one or more properties was bound */ public static boolean bindProperties(CamelContext camelContext, Object target, Map<String, Object> properties, String optionPrefix, - boolean nesting, boolean deepNesting, boolean fluentBuilder, boolean reference, boolean placeholder) { + boolean nesting, boolean deepNesting, boolean fluentBuilder, boolean allowPrivateSetter, + boolean reference, boolean placeholder) { org.apache.camel.util.ObjectHelper.notNull(camelContext, "camelContext"); org.apache.camel.util.ObjectHelper.notNull(target, "target"); org.apache.camel.util.ObjectHelper.notNull(properties, "properties"); @@ -486,7 +492,7 @@ public final class PropertyBindingSupport { key = key.substring(optionPrefix.length()); } - if (bindProperty(camelContext, target, key, value, nesting, deepNesting, fluentBuilder, reference, placeholder)) { + if (bindProperty(camelContext, target, key, value, nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder)) { iter.remove(); rc = true; } @@ -507,7 +513,7 @@ public final class PropertyBindingSupport { public static boolean bindProperty(CamelContext camelContext, Object target, String name, Object value) { try { if (target != null && name != null) { - return setProperty(camelContext, target, name, value, false, true, true, true, true, true); + return setProperty(camelContext, target, name, value, false, true, true, true, true, true, true); } } catch (Exception e) { throw new PropertyBindingException(target, name, e); @@ -517,10 +523,10 @@ public final class PropertyBindingSupport { } private static boolean bindProperty(CamelContext camelContext, Object target, String name, Object value, - boolean nesting, boolean deepNesting, boolean fluentBuilder, boolean reference, boolean placeholder) { + boolean nesting, boolean deepNesting, boolean fluentBuilder, boolean allowPrivateSetter, boolean reference, boolean placeholder) { try { if (target != null && name != null) { - return setProperty(camelContext, target, name, value, false, nesting, deepNesting, fluentBuilder, reference, placeholder); + return setProperty(camelContext, target, name, value, false, nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder); } } catch (Exception e) { throw new PropertyBindingException(target, name, e); @@ -540,7 +546,7 @@ public final class PropertyBindingSupport { public static void bindMandatoryProperty(CamelContext camelContext, Object target, String name, Object value) { try { if (target != null && name != null) { - boolean bound = setProperty(camelContext, target, name, value, true, true, true, true, true, true); + boolean bound = setProperty(camelContext, target, name, value, true, true, true, true, true, true, true); if (!bound) { throw new PropertyBindingException(target, name); } @@ -551,7 +557,8 @@ public final class PropertyBindingSupport { } private static boolean setProperty(CamelContext context, Object target, String name, Object value, boolean mandatory, - boolean nesting, boolean deepNesting, boolean fluentBuilder, boolean reference, boolean placeholder) throws Exception { + boolean nesting, boolean deepNesting, boolean fluentBuilder, boolean allowPrivateSetter, + boolean reference, boolean placeholder) throws Exception { String refName = null; if (placeholder) { @@ -581,7 +588,7 @@ public final class PropertyBindingSupport { break; } // okay is there a setter so we can create a new instance and set it automatic - Method method = findBestSetterMethod(newClass, part, fluentBuilder); + Method method = findBestSetterMethod(newClass, part, fluentBuilder, allowPrivateSetter); if (method != null) { Class<?> parameterType = method.getParameterTypes()[0]; Object instance = null; @@ -632,7 +639,7 @@ public final class PropertyBindingSupport { } } else if (value.toString().equals("#autowired")) { // we should get the type from the setter - Method method = findBestSetterMethod(target.getClass(), name, fluentBuilder); + Method method = findBestSetterMethod(target.getClass(), name, fluentBuilder, allowPrivateSetter); if (method != null) { Class<?> parameterType = method.getParameterTypes()[0]; if (parameterType != null) { @@ -649,7 +656,7 @@ public final class PropertyBindingSupport { } } - boolean hit = IntrospectionSupport.setProperty(context, context.getTypeConverter(), target, name, value, refName, fluentBuilder); + boolean hit = IntrospectionSupport.setProperty(context, context.getTypeConverter(), target, name, value, refName, fluentBuilder, allowPrivateSetter); if (!hit && mandatory) { // there is no setter with this given name, so lets report this as a problem throw new IllegalArgumentException("Cannot find setter method: " + name + " on bean: " + target + " when binding property: " + ognlPath); @@ -689,16 +696,16 @@ public final class PropertyBindingSupport { return answer != null ? answer : defaultValue; } - private static Method findBestSetterMethod(Class clazz, String name, boolean fluentBuilder) { + private static Method findBestSetterMethod(Class clazz, String name, boolean fluentBuilder, boolean allowPrivateSetter) { // is there a direct setter? - Set<Method> candidates = findSetterMethods(clazz, name, false); + Set<Method> candidates = findSetterMethods(clazz, name, fluentBuilder, allowPrivateSetter); if (candidates.size() == 1) { return candidates.iterator().next(); } // okay now try with builder pattern if (fluentBuilder) { - candidates = findSetterMethods(clazz, name, true); + candidates = findSetterMethods(clazz, name, fluentBuilder, allowPrivateSetter); if (candidates.size() == 1) { return candidates.iterator().next(); } diff --git a/examples/camel-example-main/src/main/resources/application.properties b/examples/camel-example-main/src/main/resources/application.properties index e76d2a2..b0c1717 100644 --- a/examples/camel-example-main/src/main/resources/application.properties +++ b/examples/camel-example-main/src/main/resources/application.properties @@ -43,7 +43,7 @@ camel.component.quartz2.start-delayed-seconds = 3 ### camel.component.properties.environment-variable-mode=1 # setup JMS component with connection to ActiveMQ Artemis broker -camel.component.jms.configuration.connectionFactory.#private#brokerURL=tcp://localhost:61616 +camel.component.jms.configuration.connectionFactory.brokerURL=tcp://localhost:61616 # properties used in the route myCron = 0/2 * * * * ?