You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by lb...@apache.org on 2020/08/12 10:37:05 UTC

[camel] branch master updated: Inprove PropertyBindingSupport (CAMEL-15396, CAMEL-15397)

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

lburgazzoli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/master by this push:
     new ab54bed  Inprove PropertyBindingSupport (CAMEL-15396, CAMEL-15397)
ab54bed is described below

commit ab54bed173977fa8d386a330002f2119b3441a3b
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Tue Aug 11 17:20:12 2020 +0200

    Inprove PropertyBindingSupport (CAMEL-15396, CAMEL-15397)
    
    This PR adds support for arrays in PropertyBindingSupport and fix issues when binding to list when properties have gaps.
    
    See tests in:
    - PropertyBindingSupportArrayTest
    - PropertyBindingSupportListTest
---
 .../camel/support/IntrospectionSupportTest.java    |  30 ++++++
 ...t.java => PropertyBindingSupportArrayTest.java} | 110 +++++++++++++++------
 .../support/PropertyBindingSupportListTest.java    |  51 +++++++++-
 .../support/PropertyBindingSupportMapTest.java     |   2 +-
 .../apache/camel/support/IntrospectionSupport.java |  55 +++++++++--
 .../camel/support/PropertyBindingSupport.java      |  63 +++++++++++-
 .../camel/support/PropertyConfigurerHelper.java    |  70 +++++++++++++
 7 files changed, 336 insertions(+), 45 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 07fbe38..18ae51e 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
@@ -564,4 +564,34 @@ public class IntrospectionSupportTest extends ContextTestSupport {
         assertEquals(ExampleBean.class, setters.get(0).getParameterTypes()[0]);
         assertEquals(String.class, setters.get(1).getParameterTypes()[0]);
     }
+
+
+    @Test
+    public void testArray() throws Exception {
+        MyBeanWithArray target = new MyBeanWithArray();
+        IntrospectionSupport.setProperty(context.getTypeConverter(), target, "names[0]", "James");
+        IntrospectionSupport.setProperty(context.getTypeConverter(), target, "names[1]", "Claus");
+        assertEquals("James", target.getNames()[0]);
+        assertEquals("Claus", target.getNames()[1]);
+
+        IntrospectionSupport.setProperty(context.getTypeConverter(), target, "names[0]", "JamesX");
+        assertEquals("JamesX", target.getNames()[0]);
+
+        IntrospectionSupport.setProperty(context.getTypeConverter(), target, "names[2]", "Andrea");
+        assertEquals("JamesX", target.getNames()[0]);
+        assertEquals("Claus", target.getNames()[1]);
+        assertEquals("Andrea", target.getNames()[2]);
+    }
+
+    public class MyBeanWithArray {
+        private String[] names = new String[10];
+
+        public String[] getNames() {
+            return names;
+        }
+
+        public void setNames(String[] names) {
+            this.names = names;
+        }
+    }
 }
diff --git a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportListTest.java b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportArrayTest.java
similarity index 59%
copy from core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportListTest.java
copy to core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportArrayTest.java
index 83a1acc..88bf95b 100644
--- a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportListTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportArrayTest.java
@@ -17,7 +17,6 @@
 package org.apache.camel.support;
 
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 
@@ -26,12 +25,15 @@ import org.apache.camel.ContextTestSupport;
 import org.apache.camel.PropertyBindingException;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.apache.camel.util.CollectionHelper.mapOf;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
 /**
  * Unit test for PropertyBindingSupport
  */
-public class PropertyBindingSupportListTest extends ContextTestSupport {
+public class PropertyBindingSupportArrayTest extends ContextTestSupport {
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
@@ -55,7 +57,7 @@ public class PropertyBindingSupportListTest extends ContextTestSupport {
     }
 
     @Test
-    public void testPropertiesList() throws Exception {
+    public void testPropertiesArray() throws Exception {
         Foo foo = new Foo();
 
         Map<String, Object> prop = new LinkedHashMap<>();
@@ -72,15 +74,40 @@ public class PropertyBindingSupportListTest extends ContextTestSupport {
         assertEquals(33, foo.getBar().getAge());
         assertTrue(foo.getBar().isRider());
         assertTrue(foo.getBar().isGoldCustomer());
-        assertEquals(2, foo.getBar().getWorks().size());
-        assertEquals(123, foo.getBar().getWorks().get(0).getId());
-        assertEquals("Acme", foo.getBar().getWorks().get(0).getName());
-        assertEquals(456, foo.getBar().getWorks().get(1).getId());
-        assertEquals("Acme 2", foo.getBar().getWorks().get(1).getName());
+        assertEquals(2, foo.getBar().getWorks().length);
+        assertEquals(123, foo.getBar().getWorks()[0].getId());
+        assertEquals("Acme", foo.getBar().getWorks()[0].getName());
+        assertEquals(456, foo.getBar().getWorks()[1].getId());
+        assertEquals("Acme 2", foo.getBar().getWorks()[1].getName());
     }
 
     @Test
-    public void testPropertiesListNested() throws Exception {
+    public void testPropertiesArrayWithGaps() throws Exception {
+        Foo foo = new Foo();
+
+        Map<String, Object> prop = new LinkedHashMap<>();
+        prop.put("name", "James");
+        prop.put("bar.age", "33");
+        prop.put("bar.{{committer}}", "true");
+        prop.put("bar.gold-customer", "true");
+        prop.put("bar.works[5]", "#bean:company1");
+        prop.put("bar.works[9]", "#bean:company2");
+
+        PropertyBindingSupport.build().bind(context, foo, prop);
+
+        assertEquals("James", foo.getName());
+        assertEquals(33, foo.getBar().getAge());
+        assertTrue(foo.getBar().isRider());
+        assertTrue(foo.getBar().isGoldCustomer());
+        assertEquals(10, foo.getBar().getWorks().length);
+        assertEquals(123, foo.getBar().getWorks()[5].getId());
+        assertEquals("Acme", foo.getBar().getWorks()[5].getName());
+        assertEquals(456, foo.getBar().getWorks()[9].getId());
+        assertEquals("Acme 2", foo.getBar().getWorks()[9].getName());
+    }
+
+    @Test
+    public void testPropertiesArrayNested() throws Exception {
         Foo foo = new Foo();
 
         Map<String, Object> prop = new LinkedHashMap<>();
@@ -99,15 +126,36 @@ public class PropertyBindingSupportListTest extends ContextTestSupport {
         assertEquals(33, foo.getBar().getAge());
         assertTrue(foo.getBar().isRider());
         assertTrue(foo.getBar().isGoldCustomer());
-        assertEquals(2, foo.getBar().getWorks().size());
-        assertEquals(666, foo.getBar().getWorks().get(0).getId());
-        assertEquals("Acme", foo.getBar().getWorks().get(0).getName());
-        assertEquals(456, foo.getBar().getWorks().get(1).getId());
-        assertEquals("I changed this", foo.getBar().getWorks().get(1).getName());
+        assertEquals(2, foo.getBar().getWorks().length);
+        assertEquals(666, foo.getBar().getWorks()[0].getId());
+        assertEquals("Acme", foo.getBar().getWorks()[0].getName());
+        assertEquals(456, foo.getBar().getWorks()[1].getId());
+        assertEquals("I changed this", foo.getBar().getWorks()[1].getName());
     }
 
     @Test
-    public void testPropertiesListFirst() throws Exception {
+    public void testPropertiesArrayNestedSimple() throws Exception {
+        Foo foo = new Foo();
+
+        PropertyBindingSupport.build().bind(context, foo, mapOf(
+            "bar.works[0].id", "666",
+            "bar.works[1].name", "I changed this"
+        ));
+
+        assertEquals(666, foo.bar.works[0].getId());
+        assertEquals("I changed this", foo.bar.works[1].getName());
+
+        PropertyBindingSupport.build().bind(context, foo, mapOf(
+            "bar.works[0].id", "999",
+            "bar.works[1].name", "I changed this again"
+        ));
+
+        assertEquals(999, foo.bar.works[0].getId());
+        assertEquals("I changed this again", foo.bar.works[1].getName());
+    }
+
+    @Test
+    public void testPropertiesArrayFirst() throws Exception {
         Bar bar = new Bar();
 
         Map<String, Object> prop = new LinkedHashMap<>();
@@ -118,15 +166,15 @@ public class PropertyBindingSupportListTest extends ContextTestSupport {
 
         PropertyBindingSupport.build().bind(context, bar, prop);
 
-        assertEquals(2, bar.getWorks().size());
-        assertEquals(666, bar.getWorks().get(0).getId());
-        assertEquals("Acme", bar.getWorks().get(0).getName());
-        assertEquals(456, bar.getWorks().get(1).getId());
-        assertEquals("I changed this", bar.getWorks().get(1).getName());
+        assertEquals(2, bar.getWorks().length);
+        assertEquals(666, bar.getWorks()[0].getId());
+        assertEquals("Acme", bar.getWorks()[0].getName());
+        assertEquals(456, bar.getWorks()[1].getId());
+        assertEquals("I changed this", bar.getWorks()[1].getName());
     }
 
     @Test
-    public void testPropertiesNotList() throws Exception {
+    public void testPropertiesNotArray() throws Exception {
         Foo foo = new Foo();
 
         Map<String, Object> prop = new LinkedHashMap<>();
@@ -140,13 +188,13 @@ public class PropertyBindingSupportListTest extends ContextTestSupport {
         } catch (PropertyBindingException e) {
             assertEquals("bar.gold-customer[]", e.getPropertyName());
             IllegalArgumentException iae = assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
-            assertTrue(iae.getMessage().startsWith("Cannot set property: gold-customer[] as either a Map/List because target bean is not a Map or List type"));
+            assertTrue(iae.getMessage().startsWith("Cannot set property: gold-customer[] as either a Map/List/array because target bean is not a Map, List or array type"));
         }
     }
 
     public static class Foo {
-        private String name;
-        private Bar bar = new Bar();
+        String name;
+        Bar bar = new Bar();
 
         public String getName() {
             return name;
@@ -166,10 +214,10 @@ public class PropertyBindingSupportListTest extends ContextTestSupport {
     }
 
     public static class Bar {
-        private int age;
-        private boolean rider;
-        private List<Company> works; // should auto-create this via the setter
-        private boolean goldCustomer;
+        int age;
+        boolean rider;
+        Company[] works; // should auto-create this via the setter
+        boolean goldCustomer;
 
         public int getAge() {
             return age;
@@ -187,11 +235,11 @@ public class PropertyBindingSupportListTest extends ContextTestSupport {
             this.rider = rider;
         }
 
-        public List<Company> getWorks() {
+        public Company[] getWorks() {
             return works;
         }
 
-        public void setWorks(List<Company> works) {
+        public void setWorks(Company[] works) {
             this.works = works;
         }
 
diff --git a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportListTest.java b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportListTest.java
index 83a1acc..11c4892 100644
--- a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportListTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportListTest.java
@@ -26,7 +26,10 @@ import org.apache.camel.ContextTestSupport;
 import org.apache.camel.PropertyBindingException;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.apache.camel.util.CollectionHelper.mapOf;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
 /**
  * Unit test for PropertyBindingSupport
@@ -80,6 +83,31 @@ public class PropertyBindingSupportListTest extends ContextTestSupport {
     }
 
     @Test
+    public void testPropertiesListWithGaps() throws Exception {
+        Foo foo = new Foo();
+
+        Map<String, Object> prop = new LinkedHashMap<>();
+        prop.put("name", "James");
+        prop.put("bar.age", "33");
+        prop.put("bar.{{committer}}", "true");
+        prop.put("bar.gold-customer", "true");
+        prop.put("bar.works[5]", "#bean:company1");
+        prop.put("bar.works[9]", "#bean:company2");
+
+        PropertyBindingSupport.build().bind(context, foo, prop);
+
+        assertEquals("James", foo.getName());
+        assertEquals(33, foo.getBar().getAge());
+        assertTrue(foo.getBar().isRider());
+        assertTrue(foo.getBar().isGoldCustomer());
+        assertEquals(10, foo.getBar().getWorks().size());
+        assertEquals(123, foo.getBar().getWorks().get(5).getId());
+        assertEquals("Acme", foo.getBar().getWorks().get(5).getName());
+        assertEquals(456, foo.getBar().getWorks().get(9).getId());
+        assertEquals("Acme 2", foo.getBar().getWorks().get(9).getName());
+    }
+
+    @Test
     public void testPropertiesListNested() throws Exception {
         Foo foo = new Foo();
 
@@ -107,6 +135,25 @@ public class PropertyBindingSupportListTest extends ContextTestSupport {
     }
 
     @Test
+    public void testPropertiesListNestedWithType() throws Exception {
+        Foo foo = new Foo();
+
+        // use CollectionHelper::mapOf to avoid insertion ordered iteration
+        PropertyBindingSupport.build().bind(context, foo, mapOf(
+            "bar.works[0]", "#class:" + Company.class.getName(),
+            "bar.works[0].name", "first",
+            "bar.works[1]", "#class:" + Company.class.getName(),
+            "bar.works[1].name", "second"
+        ));
+
+        assertEquals(2, foo.getBar().getWorks().size());
+        assertEquals(0, foo.getBar().getWorks().get(0).getId());
+        assertEquals("first", foo.getBar().getWorks().get(0).getName());
+        assertEquals(0, foo.getBar().getWorks().get(1).getId());
+        assertEquals("second", foo.getBar().getWorks().get(1).getName());
+    }
+
+    @Test
     public void testPropertiesListFirst() throws Exception {
         Bar bar = new Bar();
 
@@ -140,7 +187,7 @@ public class PropertyBindingSupportListTest extends ContextTestSupport {
         } catch (PropertyBindingException e) {
             assertEquals("bar.gold-customer[]", e.getPropertyName());
             IllegalArgumentException iae = assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
-            assertTrue(iae.getMessage().startsWith("Cannot set property: gold-customer[] as either a Map/List because target bean is not a Map or List type"));
+            assertTrue(iae.getMessage().startsWith("Cannot set property: gold-customer[] as either a Map/List/array because target bean is not a Map, List or array type"));
         }
     }
 
diff --git a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportMapTest.java b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportMapTest.java
index 3f18035..4b3b688 100644
--- a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportMapTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportMapTest.java
@@ -139,7 +139,7 @@ public class PropertyBindingSupportMapTest extends ContextTestSupport {
         } catch (PropertyBindingException e) {
             assertEquals("bar.gold-customer[foo]", e.getPropertyName());
             IllegalArgumentException iae = assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
-            assertTrue(iae.getMessage().startsWith("Cannot set property: gold-customer[foo] as either a Map/List because target bean is not a Map or List type"));
+            assertTrue(iae.getMessage().startsWith("Cannot set property: gold-customer[foo] as either a Map/List/array because target bean is not a Map, List or array type"));
         }
     }
 
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 178e193..5dcefab 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
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.support;
 
+import java.lang.reflect.Array;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
@@ -199,7 +200,7 @@ public final class IntrospectionSupport {
 
         return false;
     }
-    
+
     public static boolean isSetter(Method method) {
         return isSetter(method, false);
     }
@@ -471,7 +472,7 @@ public final class IntrospectionSupport {
                 }
             }
         }
-        
+
         return rc;
     }
 
@@ -621,6 +622,8 @@ public final class IntrospectionSupport {
                         obj = new LinkedHashMap<>();
                     } else if (Collection.class.isAssignableFrom(returnType)) {
                         obj = new ArrayList<>();
+                    } else if (returnType.isArray()) {
+                        obj = Array.newInstance(returnType.getComponentType(), 0);
                     }
                 } else {
                     // fallback as map type
@@ -646,15 +649,53 @@ public final class IntrospectionSupport {
                     value = CamelContextHelper.lookup(context, s);
                 }
                 if (isNotEmpty(lookupKey)) {
-                    int idx = Integer.valueOf(lookupKey);
-                    list.add(idx, value);
+                    int idx = Integer.parseInt(lookupKey);
+                    if (idx < list.size()) {
+                        list.set(idx, value);
+                    } else if (idx == list.size()) {
+                        list.add(value);
+                    } else {
+                        // If the list implementation is based on an array, we
+                        // can increase tha capacity to the required value to
+                        // avoid potential re-allocation weh invoking List::add.
+                        //
+                        // Note that ArrayList is the default List impl that
+                        // is automatically created if the property is null.
+                        if (list instanceof ArrayList) {
+                            ((ArrayList) list).ensureCapacity(idx + 1);
+                        }
+                        while (list.size() < idx) {
+                            list.add(null);
+                        }
+                        list.add(idx, value);
+                    }
                 } else {
                     list.add(value);
                 }
                 return true;
+            } else if (obj.getClass().isArray() && lookupKey != null) {
+                if (context != null && refName != null && value == null) {
+                    String s = StringHelper.replaceAll(refName, "#", "");
+                    value = CamelContextHelper.lookup(context, s);
+                }
+                int idx = Integer.parseInt(lookupKey);
+                int size = Array.getLength(obj);
+                if (idx >= size) {
+                    obj = Arrays.copyOf((Object[])obj, idx + 1);
+
+                    // replace array
+                    boolean hit = IntrospectionSupport.setProperty(context, target, key, obj);
+                    if (!hit) {
+                        throw new IllegalArgumentException("Cannot set property: " + name + " as an array because target bean has no setter method for the array");
+                    }
+                }
+
+                Array.set(obj, idx, value);
+
+                return true;
             } else {
                 // not a map or list
-                throw new IllegalArgumentException("Cannot set property: " + name + " as either a Map/List because target bean is not a Map or List type: " + target);
+                throw new IllegalArgumentException("Cannot set property: " + name + " as either a Map/List/array because target bean is not a Map, List or array type: " + target);
             }
         }
 
@@ -799,7 +840,7 @@ public final class IntrospectionSupport {
         // allow build pattern as a setter as well
         return setProperty(context, typeConverter, target, name, value, null, true, false, 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, false, false);
@@ -873,7 +914,7 @@ public final class IntrospectionSupport {
             // find the best match if possible
             LOG.trace("Found {} suitable setter methods for setting {}", candidates.size(), name);
             // prefer to use the one with the same instance if any exists
-            for (Method method : candidates) {                               
+            for (Method method : candidates) {
                 if (method.getParameterTypes()[0].isInstance(value)) {
                     LOG.trace("Method {} is the best candidate as it has parameter with same instance type", method);
                     // retain only this method in the answer
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 650b0f4..5a8308b 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
@@ -16,10 +16,12 @@
  */
 package org.apache.camel.support;
 
+import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -35,6 +37,7 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.Component;
 import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.PropertyBindingException;
+import org.apache.camel.spi.BeanIntrospection;
 import org.apache.camel.spi.GeneratedPropertyConfigurer;
 import org.apache.camel.spi.PropertyConfigurer;
 import org.apache.camel.spi.PropertyConfigurerGetter;
@@ -778,17 +781,37 @@ public final class PropertyBindingSupport {
 
         // use configurer if possible
         Object answer = null;
-        GeneratedPropertyConfigurer configurer = context.adapt(ExtendedCamelContext.class).getConfigurerResolver().resolvePropertyConfigurer(target.getClass().getSimpleName(), context);
+        Class<?> type = null;
+
+        GeneratedPropertyConfigurer configurer = PropertyConfigurerHelper.resolvePropertyConfigurer(context, target);
         if (configurer instanceof PropertyConfigurerGetter) {
-            answer = ((PropertyConfigurerGetter) configurer).getOptionValue(target, key, ignoreCase);
+            answer = ((PropertyConfigurerGetter)configurer).getOptionValue(target, key, ignoreCase);
             if (answer == null) {
                 answer = defaultValue;
             }
         }
         if (answer == null) {
+            BeanIntrospection introspection = context.adapt(ExtendedCamelContext.class).getBeanIntrospection();
             // fallback to reflection based
-            answer = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getOrElseProperty(target, key, defaultValue, ignoreCase);
+            answer = introspection.getOrElseProperty(target, key, defaultValue, ignoreCase);
+            if (answer == null) {
+                try {
+                    Method method = introspection.getPropertyGetter(target.getClass(), key, ignoreCase);
+                    if (method != null) {
+                        type = method.getReturnType();
+                    }
+                } catch (NoSuchMethodException e) {
+                    // ignore
+                }
+            }
+        }
+
+        if (answer != null) {
+            type = answer.getClass();
+        } else if (configurer instanceof PropertyConfigurerGetter) {
+            type = (Class<?>)((PropertyConfigurerGetter)configurer).getAllOptions(target).get(key);
         }
+
         if (answer instanceof Map && lookupKey != null) {
             Map map = (Map) answer;
             answer = map.getOrDefault(lookupKey, defaultValue);
@@ -804,6 +827,38 @@ public final class PropertyBindingSupport {
                     answer = list.get(list.size() - 1);
                 }
             }
+        } else if (type != null && type.isArray() && lookupKey != null) {
+            int idx = Integer.parseInt(lookupKey);
+            int size = answer != null ? Array.getLength(answer) : 0;
+            if (idx >= size) {
+                answer = answer != null ? Arrays.copyOf((Object[]) answer, idx + 1) : Array.newInstance(Object.class, idx + 1);
+            }
+
+            Object result = Array.get(answer, idx);
+            if (result == null) {
+                result = context.getInjector().newInstance(type.getComponentType());
+                Array.set(answer, idx, result);
+            }
+
+            if (idx >= size) {
+                // replace array
+                if (configurer != null) {
+                    configurer.configure(context, target, key, answer, true);
+                } else {
+                    // fallback to reflection
+                    boolean hit;
+                    try {
+                        hit = IntrospectionSupport.setProperty(context.getTypeConverter(), target, key, answer);
+                    } catch (Exception e) {
+                        throw new IllegalArgumentException("Cannot set property: " + key + " as an array because target bean has no setter method for the array");
+                    }
+                    if (!hit) {
+                        throw new IllegalArgumentException("Cannot set property: " + key + " as an array because target bean has no setter method for the array");
+                    }
+                }
+            }
+
+            answer = result;
         }
 
         return answer != null ? answer : defaultValue;
@@ -1035,7 +1090,7 @@ public final class PropertyBindingSupport {
                 continue;
             }
             // must be a public static method that returns something
-            if (!Modifier.isStatic(method.getModifiers()) 
+            if (!Modifier.isStatic(method.getModifiers())
                 || !Modifier.isPublic(method.getModifiers())
                 || method.getReturnType() == Void.TYPE) {
                 continue;
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/PropertyConfigurerHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/PropertyConfigurerHelper.java
new file mode 100644
index 0000000..df104d5
--- /dev/null
+++ b/core/camel-support/src/main/java/org/apache/camel/support/PropertyConfigurerHelper.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.support;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * Helper class for dealing with configurers.
+ *
+ * @see org.apache.camel.spi.PropertyConfigurer
+ * @see org.apache.camel.spi.PropertyConfigurerGetter
+ */
+public final class PropertyConfigurerHelper {
+
+    private PropertyConfigurerHelper() {
+    }
+
+    /**
+     * Resolves the given configurer.
+     *
+     * @param context the camel context
+     * @param target the target object for which we need a {@link org.apache.camel.spi.PropertyConfigurer}
+     * @return the resolved configurer, or <tt>null</tt> if no configurer could be found
+     */
+    public static GeneratedPropertyConfigurer resolvePropertyConfigurer(CamelContext context, Object target) {
+        ObjectHelper.notNull(target, "target");
+        ObjectHelper.notNull(context, "context");
+
+        return context.adapt(ExtendedCamelContext.class)
+            .getConfigurerResolver()
+            .resolvePropertyConfigurer(target.getClass().getSimpleName(), context);
+    }
+
+    /**
+     * Resolves the given configurer.
+     *
+     * @param context the camel context
+     * @param target the target object for which we need a {@link org.apache.camel.spi.PropertyConfigurer}
+     * @param type the specific type of {@link org.apache.camel.spi.PropertyConfigurer}
+     * @return the resolved configurer, or <tt>null</tt> if no configurer could be found
+     */
+    public static <T> T resolvePropertyConfigurer(CamelContext context, Object target, Class<T> type) {
+        ObjectHelper.notNull(target, "target");
+        ObjectHelper.notNull(context, "context");
+
+        GeneratedPropertyConfigurer configurer = resolvePropertyConfigurer(context, target);
+        if (type.isInstance(configurer)) {
+            return type.cast(configurer);
+        }
+
+        return null;
+    }
+}