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 86222649961f03b5d1d242ed8ef4cd46ad9163da Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Sun Aug 23 15:33:39 2020 +0200 CAMEL-15452: PropertyBindingSupport - Add reflection on|off option --- .../PropertyBindingSupportConfigurerTest.java | 35 ++++++++++- .../camel/support/PropertyBindingSupportTest.java | 24 ++++++-- .../camel/support/PropertyBindingSupport.java | 68 +++++++++++++--------- 3 files changed, 95 insertions(+), 32 deletions(-) diff --git a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportConfigurerTest.java b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportConfigurerTest.java index f6f44b4..b2b2332 100644 --- a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportConfigurerTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportConfigurerTest.java @@ -172,7 +172,7 @@ public class PropertyBindingSupportConfigurerTest extends ContextTestSupport { } @Test - public void testPropertiesOptionallKeyMandatory() throws Exception { + public void testPropertiesOptionalKeyMandatory() throws Exception { Bar bar = new Bar(); Map<String, Object> prop = new HashMap<>(); @@ -220,6 +220,39 @@ public class PropertyBindingSupportConfigurerTest extends ContextTestSupport { } @Test + public void testPropertiesNoReflection() throws Exception { + BeanIntrospection bi = context.adapt(ExtendedCamelContext.class).getBeanIntrospection(); + bi.setExtendedStatistics(true); + bi.setLoggingLevel(LoggingLevel.WARN); + + Bar bar = new Bar(); + + Map<String, Object> prop = new HashMap<>(); + prop.put("age", "33"); + prop.put("{{committer}}", "true"); + prop.put("gold-customer", "true"); + prop.put("work.id", "123"); + prop.put("work.name", "{{companyName}}"); + + myConfigurer.reset(); + PropertyBindingSupport.build().withReflection(false).withConfigurer(myConfigurer).withIgnoreCase(true).bind(context, bar, prop); + assertEquals(6, myConfigurer.getCounter()); + + assertEquals(33, bar.getAge()); + assertTrue(bar.isRider()); + assertTrue(bar.isGoldCustomer()); + assertEquals(0, bar.getWork().getId()); + assertNull(bar.getWork().getName()); + + assertEquals(2, prop.size()); + assertEquals("123", prop.get("work.id")); + assertEquals("{{companyName}}", prop.get("work.name")); + + // reflection is turned off + assertEquals(0, bi.getInvokedCounter()); + } + + @Test public void testConfigurerShouldNotFailForAnonymousClasses() throws Exception { PropertyBindingSupport.autowireSingletonPropertiesFromRegistry(context, new Bar() { @Override diff --git a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java index 371eccf..39c512c 100644 --- a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java @@ -27,11 +27,8 @@ import org.apache.camel.spi.Injector; import org.apache.camel.spi.PropertiesComponent; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; + +import static org.junit.jupiter.api.Assertions.*; /** * Unit test for PropertyBindingSupport @@ -141,6 +138,23 @@ public class PropertyBindingSupportTest extends ContextTestSupport { } @Test + public void testPropertiesNoReflection() throws Exception { + Foo foo = new Foo(); + + Map<String, Object> prop = new HashMap<>(); + prop.put("name", "James"); + prop.put("bar.AGE", "33"); + + PropertyBindingSupport.build().withReflection(false).bind(context, foo, prop); + + assertNull(foo.getName()); + assertEquals(0, foo.getBar().getAge()); + + // should not bind any properties as reflection is off + assertEquals(2, prop.size()); + } + + @Test public void testPropertiesIgnoreCase() throws Exception { Foo foo = new Foo(); 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 37306e2..b08f65b 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 @@ -104,6 +104,7 @@ public final class PropertyBindingSupport { private boolean allowPrivateSetter = true; private boolean ignoreCase; private String optionPrefix; + private boolean reflection = true; private PropertyConfigurer configurer; /** @@ -240,6 +241,14 @@ public final class PropertyBindingSupport { } /** + * Whether to allow using reflection (when there is no configurer available). + */ + public Builder withReflection(boolean reflection) { + this.reflection = reflection; + return this; + } + + /** * Binds the properties to the target object, and removes the property that was bound from properties. * * @return true if one or more properties was bound @@ -255,7 +264,7 @@ public final class PropertyBindingSupport { return doBindProperties(camelContext, target, removeParameters ? properties : new HashMap<>(properties), optionPrefix, ignoreCase, true, mandatory, - nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder, configurer); + nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder, reflection, configurer); } /** @@ -273,7 +282,7 @@ public final class PropertyBindingSupport { return doBindProperties(context, obj, removeParameters ? prop : new HashMap<>(prop), optionPrefix, ignoreCase, true, mandatory, - nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder, configurer); + nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder, reflection, configurer); } /** @@ -290,7 +299,7 @@ public final class PropertyBindingSupport { properties.put(key, value); return doBindProperties(camelContext, target, properties, optionPrefix, ignoreCase, true, mandatory, - nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder, configurer); + nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, placeholder, reflection, configurer); } } @@ -621,7 +630,8 @@ public final class PropertyBindingSupport { * 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 - * @param configurer to use an optional {@link org.apache.camel.spi.PropertyConfigurer} to configure the + * @param reflection whether to allow using reflection (when there is no configurer available). + * @param configurer to use an optional {@link PropertyConfigurer} to configure the * properties * @return true if one or more properties was bound */ @@ -630,7 +640,7 @@ public final class PropertyBindingSupport { String optionPrefix, boolean ignoreCase, boolean removeParameter, boolean mandatory, boolean nesting, boolean deepNesting, boolean fluentBuilder, boolean allowPrivateSetter, boolean reference, boolean placeholder, - PropertyConfigurer configurer) { + boolean reflection, PropertyConfigurer configurer) { if (properties == null || properties.isEmpty()) { return false; @@ -642,24 +652,30 @@ public final class PropertyBindingSupport { properties = new OptionPrefixMap(properties, optionPrefix); } - // need to process them in specific order so use a sorted map - // and use our comparator - SortedMap<String, Object> sorted = new TreeMap<>(new PropertyBindingKeyComparator(properties)); - sorted.putAll(properties); + Map<String, Object> sorted; + if (properties.size() > 1) { + // need to process them in specific order so use a sorted map + // and use our comparator + sorted = new TreeMap<>(new PropertyBindingKeyComparator(properties)); + sorted.putAll(properties); + } else { + // no need to sort as there is only 1 element + sorted = properties; + } // process each property and bind it for (Map.Entry<String, Object> entry : sorted.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); - // if nesting is not allowed, then only bind properties without dots (ONGL graph) + // if nesting is not allowed, then only bind properties without dots (OGNL graph) if (!nesting && key.indexOf('.') != -1) { continue; } // attempt to bind the property boolean hit = doBuildPropertyOgnlPath(camelContext, target, key, value, deepNesting, fluentBuilder, - allowPrivateSetter, ignoreCase, reference, placeholder, mandatory, configurer); + allowPrivateSetter, ignoreCase, reference, placeholder, mandatory, reflection, configurer); if (hit && removeParameter) { properties.remove(key); } @@ -673,7 +689,7 @@ public final class PropertyBindingSupport { final CamelContext camelContext, final Object originalTarget, String name, final Object value, boolean deepNesting, boolean fluentBuilder, boolean allowPrivateSetter, boolean ignoreCase, boolean reference, boolean placeholder, boolean mandatory, - PropertyConfigurer configurer) { + boolean reflection, PropertyConfigurer configurer) { boolean optional = name.startsWith("?"); if (optional) { @@ -705,7 +721,7 @@ public final class PropertyBindingSupport { if (configurer != null) { prop = getOrCreatePropertyOgnlPathViaConfigurer(camelContext, newTarget, part, ignoreCase, configurer); } - if (prop == null) { + if (prop == null && reflection) { // no configurer or not possible with configurer so fallback and use reflection prop = getOrCreatePropertyOgnlPathViaReflection(camelContext, newTarget, part, ignoreCase); } @@ -719,7 +735,7 @@ public final class PropertyBindingSupport { prop = attemptCreateNewInstanceViaConfigurer(camelContext, newTarget, part, ignoreCase, configurer); } - if (prop == null) { + if (prop == null && reflection) { // no configurer or not possible with configurer so fallback and use reflection prop = attemptCreateNewInstanceViaReflection(camelContext, newTarget, newClass, part, fluentBuilder, allowPrivateSetter, @@ -767,7 +783,7 @@ public final class PropertyBindingSupport { // we have walked down to the last part of the ognl path and are ready to set the last piece with the value // now this is actually also a bit complex so lets use another method for that return doSetPropertyValue(camelContext, newTarget, newName, value, ignoreCase, mandatory, - fluentBuilder, allowPrivateSetter, reference, placeholder, optional, configurer); + fluentBuilder, allowPrivateSetter, reference, placeholder, optional, reflection, configurer); } private static Object attemptCreateNewInstanceViaReflection( @@ -849,7 +865,7 @@ public final class PropertyBindingSupport { boolean ignoreCase, boolean mandatory, boolean fluentBuilder, boolean allowPrivateSetter, boolean reference, boolean placeholder, boolean optional, - PropertyConfigurer configurer) { + boolean reflection, PropertyConfigurer configurer) { String key = name; Object text = value; @@ -866,7 +882,7 @@ public final class PropertyBindingSupport { // prepare the value before it is bound try { Object str = resolveValue(camelContext, target, key, text, ignoreCase, fluentBuilder, - allowPrivateSetter, configurer); + allowPrivateSetter, reflection, configurer); // resolve property placeholders if (str instanceof String) { // resolve property placeholders @@ -887,7 +903,7 @@ public final class PropertyBindingSupport { if (configurer != null) { bound = setPropertyCollectionViaConfigurer(camelContext, target, key, value, ignoreCase, configurer); } - if (!bound) { + if (!bound && reflection) { // fallback to reflection based bound = setPropertyCollectionViaReflection(camelContext, target, key, value, ignoreCase, reference); } @@ -896,10 +912,10 @@ public final class PropertyBindingSupport { if (configurer != null) { bound = setSimplePropertyViaConfigurer(camelContext, target, key, value, ignoreCase, configurer); } - if (!bound) { + if (!bound && reflection) { // fallback to reflection based bound = setSimplePropertyViaReflection(camelContext, target, key, value, fluentBuilder, allowPrivateSetter, - reference, ignoreCase); + reflection, ignoreCase); } } @@ -961,7 +977,7 @@ public final class PropertyBindingSupport { if (value instanceof String) { String str = value.toString(); if (reference && isReferenceParameter(str)) { - Object bean = CamelContextHelper.lookup(context, str.toString().substring(1)); + Object bean = CamelContextHelper.lookup(context, str.substring(1)); if (bean != null) { value = bean; } @@ -1157,7 +1173,7 @@ public final class PropertyBindingSupport { private static Object resolveAutowired( CamelContext context, Object target, String name, Object value, boolean ignoreCase, boolean fluentBuilder, boolean allowPrivateSetter, - PropertyConfigurer configurer) + boolean reflection, PropertyConfigurer configurer) throws Exception { if (value instanceof String) { @@ -1169,7 +1185,7 @@ public final class PropertyBindingSupport { // favour using configurer parameterType = (Class<?>) ((PropertyConfigurerGetter) configurer).getAllOptions(target).get(name); } - if (parameterType == null) { + if (parameterType == null && reflection) { // fallback to reflection Method method = findBestSetterMethod(context, target.getClass(), name, fluentBuilder, allowPrivateSetter, @@ -1204,7 +1220,7 @@ public final class PropertyBindingSupport { private static Object resolveValue( CamelContext context, Object target, String name, Object value, boolean ignoreCase, boolean fluentBuilder, boolean allowPrivateSetter, - PropertyConfigurer configurer) + boolean reflection, PropertyConfigurer configurer) throws Exception { if (value instanceof String) { String str = value.toString(); @@ -1220,7 +1236,7 @@ public final class PropertyBindingSupport { } } else if (str.equals("#autowired")) { value = resolveAutowired(context, target, name, value, ignoreCase, fluentBuilder, allowPrivateSetter, - configurer); + reflection, configurer); } else { value = resolveBean(context, name, value); } @@ -1242,7 +1258,7 @@ public final class PropertyBindingSupport { refName = "#" + ((String) value).substring(6); value = null; } else if (str.equals("#autowired")) { - value = resolveAutowired(context, target, name, value, ignoreCase, fluentBuilder, allowPrivateSetter, null); + value = resolveAutowired(context, target, name, value, ignoreCase, fluentBuilder, allowPrivateSetter, true, null); } else if (isReferenceParameter(str)) { // special for reference (we should not do this for options that are String type) // this is only required for reflection (as configurer does this automatic in a more safe way)