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 2019/05/30 12:44:17 UTC

[camel] branch master updated (8d714fa -> e86896f)

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

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


    from 8d714fa  CAMEL-13599: Claim check - Allow to use dynamic key via simple language
     new 7680c2a  CAMEL-13557: Add property binding support to make it convenient to configure components and whatnot.
     new db1738e  CAMEL-13557: Add property binding support to make it convenient to configure components and whatnot.
     new e86896f  CAMEL-13557: Add property binding support to make it convenient to configure components and whatnot.

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../support/PropertyBindingSupportListTest.java    | 206 +++++++++++++++++++++
 .../support/PropertyBindingSupportMapTest.java     | 205 ++++++++++++++++++++
 .../apache/camel/support/IntrospectionSupport.java |  62 ++++++-
 .../camel/support/PropertyBindingSupport.java      |  43 ++++-
 4 files changed, 511 insertions(+), 5 deletions(-)
 create mode 100644 core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportListTest.java
 create mode 100644 core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportMapTest.java


[camel] 03/03: CAMEL-13557: Add property binding support to make it convenient to configure components and whatnot.

Posted by da...@apache.org.
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 e86896fa284842d5e66120d1390ab1888aa408e2
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu May 30 14:28:17 2019 +0200

    CAMEL-13557: Add property binding support to make it convenient to configure components and whatnot.
---
 ...st.java => PropertyBindingSupportListTest.java} | 67 +++++++++++-----------
 .../support/PropertyBindingSupportMapTest.java     |  2 +-
 .../apache/camel/support/IntrospectionSupport.java | 40 ++++++++++---
 .../camel/support/PropertyBindingSupport.java      | 29 ++++++++--
 4 files changed, 91 insertions(+), 47 deletions(-)

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/PropertyBindingSupportListTest.java
similarity index 70%
copy from core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportMapTest.java
copy to core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportListTest.java
index f46e976..5579c64 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/PropertyBindingSupportListTest.java
@@ -17,6 +17,7 @@
 package org.apache.camel.support;
 
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 
@@ -28,7 +29,7 @@ import org.junit.Test;
 /**
  * Unit test for PropertyBindingSupport
  */
-public class PropertyBindingSupportMapTest extends ContextTestSupport {
+public class PropertyBindingSupportListTest extends ContextTestSupport {
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
@@ -52,7 +53,7 @@ public class PropertyBindingSupportMapTest extends ContextTestSupport {
     }
 
     @Test
-    public void testPropertiesMap() throws Exception {
+    public void testPropertiesList() throws Exception {
         Foo foo = new Foo();
 
         Map<String, Object> prop = new LinkedHashMap<>();
@@ -60,8 +61,8 @@ public class PropertyBindingSupportMapTest extends ContextTestSupport {
         prop.put("bar.age", "33");
         prop.put("bar.{{committer}}", "true");
         prop.put("bar.gold-customer", "true");
-        prop.put("bar.works[acme]", "#bean:company1");
-        prop.put("bar.works[burger]", "#bean:company2");
+        prop.put("bar.works[0]", "#bean:company1");
+        prop.put("bar.works[1]", "#bean:company2");
 
         PropertyBindingSupport.bindProperties(context, foo, prop);
 
@@ -70,14 +71,14 @@ public class PropertyBindingSupportMapTest extends ContextTestSupport {
         assertTrue(foo.getBar().isRider());
         assertTrue(foo.getBar().isGoldCustomer());
         assertEquals(2, foo.getBar().getWorks().size());
-        assertEquals(123, foo.getBar().getWorks().get("acme").getId());
-        assertEquals("Acme", foo.getBar().getWorks().get("acme").getName());
-        assertEquals(456, foo.getBar().getWorks().get("burger").getId());
-        assertEquals("Acme 2", foo.getBar().getWorks().get("burger").getName());
+        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());
     }
 
     @Test
-    public void testPropertiesMapNested() throws Exception {
+    public void testPropertiesListNested() throws Exception {
         Foo foo = new Foo();
 
         Map<String, Object> prop = new LinkedHashMap<>();
@@ -85,10 +86,10 @@ public class PropertyBindingSupportMapTest extends ContextTestSupport {
         prop.put("bar.age", "33");
         prop.put("bar.{{committer}}", "true");
         prop.put("bar.gold-customer", "true");
-        prop.put("bar.works[acme]", "#bean:company1");
-        prop.put("bar.works[acme].id", "666");
-        prop.put("bar.works[burger]", "#bean:company2");
-        prop.put("bar.works[burger].name", "I changed this");
+        prop.put("bar.works[0]", "#bean:company1");
+        prop.put("bar.works[0].id", "666");
+        prop.put("bar.works[1]", "#bean:company2");
+        prop.put("bar.works[1].name", "I changed this");
 
         PropertyBindingSupport.bindProperties(context, foo, prop);
 
@@ -97,47 +98,47 @@ public class PropertyBindingSupportMapTest extends ContextTestSupport {
         assertTrue(foo.getBar().isRider());
         assertTrue(foo.getBar().isGoldCustomer());
         assertEquals(2, foo.getBar().getWorks().size());
-        assertEquals(666, foo.getBar().getWorks().get("acme").getId());
-        assertEquals("Acme", foo.getBar().getWorks().get("acme").getName());
-        assertEquals(456, foo.getBar().getWorks().get("burger").getId());
-        assertEquals("I changed this", foo.getBar().getWorks().get("burger").getName());
+        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());
     }
 
     @Test
-    public void testPropertiesMapFirst() throws Exception {
+    public void testPropertiesListFirst() throws Exception {
         Bar bar = new Bar();
 
         Map<String, Object> prop = new LinkedHashMap<>();
-        prop.put("works[acme]", "#bean:company1");
-        prop.put("works[acme].id", "666");
-        prop.put("works[burger]", "#bean:company2");
-        prop.put("works[burger].name", "I changed this");
+        prop.put("works[0]", "#bean:company1");
+        prop.put("works[0].id", "666");
+        prop.put("works[1]", "#bean:company2");
+        prop.put("works[1].name", "I changed this");
 
         PropertyBindingSupport.bindProperties(context, bar, prop);
 
         assertEquals(2, bar.getWorks().size());
-        assertEquals(666, bar.getWorks().get("acme").getId());
-        assertEquals("Acme", bar.getWorks().get("acme").getName());
-        assertEquals(456, bar.getWorks().get("burger").getId());
-        assertEquals("I changed this", bar.getWorks().get("burger").getName());
+        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());
     }
 
     @Test
-    public void testPropertiesNotMap() throws Exception {
+    public void testPropertiesNotList() throws Exception {
         Foo foo = new Foo();
 
         Map<String, Object> prop = new LinkedHashMap<>();
         prop.put("name", "James");
         prop.put("bar.age", "33");
-        prop.put("bar.gold-customer[foo]", "true");
+        prop.put("bar.gold-customer[]", "true");
 
         try {
             PropertyBindingSupport.bindProperties(context, foo, prop);
             fail("Should have thrown exception");
         } catch (PropertyBindingException e) {
-            assertEquals("bar.gold-customer[foo]", e.getPropertyName());
+            assertEquals("bar.gold-customer[]", e.getPropertyName());
             IllegalArgumentException iae = assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
-            assertTrue(iae.getMessage().startsWith("Cannot set property: gold-customer[foo] as a Map because target bean is not a Map"));
+            assertTrue(iae.getMessage().startsWith("Cannot set property: gold-customer[] as either a Map/List because target bean is not a Map or List type"));
         }
     }
 
@@ -165,7 +166,7 @@ public class PropertyBindingSupportMapTest extends ContextTestSupport {
     public static class Bar {
         private int age;
         private boolean rider;
-        private Map<String, Company> works; // should auto-create this via the setter
+        private List<Company> works; // should auto-create this via the setter
         private boolean goldCustomer;
 
         public int getAge() {
@@ -184,11 +185,11 @@ public class PropertyBindingSupportMapTest extends ContextTestSupport {
             this.rider = rider;
         }
 
-        public Map<String, Company> getWorks() {
+        public List<Company> getWorks() {
             return works;
         }
 
-        public void setWorks(Map<String, Company> works) {
+        public void setWorks(List<Company> works) {
             this.works = works;
         }
 
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 f46e976..0acaa24 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
@@ -137,7 +137,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 a Map because target bean is not a Map"));
+            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"));
         }
     }
 
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 700260a..05893a7 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
@@ -44,6 +44,8 @@ import org.apache.camel.util.StringHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.camel.util.ObjectHelper.isNotEmpty;
+
 /**
  * Helper for introspections of beans.
  * <p/>
@@ -538,16 +540,28 @@ public final class IntrospectionSupport {
     public static boolean setProperty(CamelContext context, TypeConverter typeConverter, Object target, String name, Object value, String refName,
                                       boolean allowBuilderPattern) throws Exception {
 
-        // does the property name include a mapped key, then we need to set the property as a map
+        // 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("]")) {
             int pos = name.indexOf('[');
-            String mapKey = name.substring(pos + 1, name.length() - 1);
+            String lookupKey = name.substring(pos + 1, name.length() - 1);
             String key = name.substring(0, pos);
 
             Object obj = IntrospectionSupport.getOrElseProperty(target, key, null);
             if (obj == null) {
-                // it was supposed to be a map, but its null, so lets create a new map and set it automatically
-                obj = new LinkedHashMap<>();
+                // it was supposed to be a list or map, but its null, so lets create a new list or map and set it automatically
+                Method getter = IntrospectionSupport.getPropertyGetter(target.getClass(), key);
+                if (getter != null) {
+                    // what type does it have
+                    Class<?> returnType = getter.getReturnType();
+                    if (Map.class.isAssignableFrom(returnType)) {
+                        obj = new LinkedHashMap<>();
+                    } else if (Collection.class.isAssignableFrom(returnType)) {
+                        obj = new ArrayList<>();
+                    }
+                } else {
+                    // fallback as map type
+                    obj = new LinkedHashMap<>();
+                }
                 boolean hit = IntrospectionSupport.setProperty(context, target, key, obj);
                 if (!hit) {
                     throw new IllegalArgumentException("Cannot set property: " + name + " as a Map because target bean has no setter method for the Map");
@@ -558,11 +572,23 @@ public final class IntrospectionSupport {
                 if (context != null && refName != null && value == null) {
                     value = CamelContextHelper.lookup(context, refName);
                 }
-                map.put(mapKey, value);
+                map.put(lookupKey, value);
+                return true;
+            } else if (obj instanceof List) {
+                List list = (List) obj;
+                if (context != null && refName != null && value == null) {
+                    value = CamelContextHelper.lookup(context, refName);
+                }
+                if (isNotEmpty(lookupKey)) {
+                    int idx = Integer.valueOf(lookupKey);
+                    list.add(idx, value);
+                } else {
+                    list.add(value);
+                }
                 return true;
             } else {
-                // not a map
-                throw new IllegalArgumentException("Cannot set property: " + name + " as a Map because target bean is not a Map: " + target);
+                // 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);
             }
         }
 
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 760029c..9741b80 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
@@ -20,6 +20,7 @@ import java.lang.reflect.Method;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.regex.Matcher;
@@ -29,6 +30,8 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.PropertyBindingException;
 
 import static org.apache.camel.support.IntrospectionSupport.findSetterMethods;
+import static org.apache.camel.util.ObjectHelper.isNotEmpty;
+import static org.apache.camel.util.StringHelper.notEmpty;
 
 /**
  * A convenient support class for binding String valued properties to an instance which
@@ -36,7 +39,9 @@ import static org.apache.camel.support.IntrospectionSupport.findSetterMethods;
  * <ul>
  *     <li>property placeholders - Keys and values using Camels property placeholder will be resolved</li>
  *     <li>nested - Properties can be nested using the dot syntax (OGNL and builder pattern using with as prefix), eg foo.bar=123</li>
- *     <li>keys with map</li> - Properties can lookup in Map's using map syntax, eg foo[bar] where foo is the name of the property that is a Map instance, and bar is the name of the key.</li>
+ *     <li>map</li> - Properties can lookup in Map's using map syntax, eg foo[bar] where foo is the name of the property that is a Map instance, and bar is the name of the key.</li>
+ *     <li>list</li> - Properties can refer or add to in List's using list syntax, eg foo[0] where foo is the name of the property that is a
+ *                     List instance, and 0 is the index. To refer to the last element, then use last as key.</li>
  *     <li>reference by bean id - Values can refer to other beans in the registry by prefixing with #nean: eg #bean:myBean</li>
  *     <li>reference by type - Values can refer to singleton beans by their type in the registry by prefixing with #type: syntax, eg #type:com.foo.MyClassType</li>
  *     <li>autowire by type - Values can refer to singleton beans by auto wiring by setting the value to #autowired</li>
@@ -444,22 +449,34 @@ public final class PropertyBindingSupport {
 
     private static Object getOrElseProperty(Object target, String property, Object defaultValue) {
         String key = property;
-        String mapKey = null;
+        String lookupKey = null;
 
         // support maps in keys
         if (property.contains("[") && property.endsWith("]")) {
             int pos = property.indexOf('[');
-            mapKey = property.substring(pos + 1, property.length() - 1);
+            lookupKey = property.substring(pos + 1, property.length() - 1);
             key = property.substring(0, pos);
         }
 
         Object answer = IntrospectionSupport.getOrElseProperty(target, key, defaultValue);
-        if (answer instanceof Map && mapKey != null) {
+        if (answer instanceof Map && lookupKey != null) {
             Map map = (Map) answer;
-            answer = map.getOrDefault(mapKey, defaultValue);
+            answer = map.getOrDefault(lookupKey, defaultValue);
+        } else if (answer instanceof List) {
+            List list = (List) answer;
+            if (isNotEmpty(lookupKey)) {
+                int idx = Integer.valueOf(lookupKey);
+                answer = list.get(idx);
+            } else {
+                if (list.isEmpty()) {
+                    answer = null;
+                } else {
+                    answer = list.get(list.size() - 1);
+                }
+            }
         }
 
-        return answer;
+        return answer != null ? answer : defaultValue;
     }
 
     private static Method findBestSetterMethod(Class clazz, String name, boolean fluentBuilder) {


[camel] 02/03: CAMEL-13557: Add property binding support to make it convenient to configure components and whatnot.

Posted by da...@apache.org.
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 db1738e407c0159c5fe9181d3811a62c85377b69
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu May 30 13:56:34 2019 +0200

    CAMEL-13557: Add property binding support to make it convenient to configure components and whatnot.
---
 .../camel/support/PropertyBindingSupportMapTest.java  | 19 +++++++++++++++++++
 .../apache/camel/support/PropertyBindingSupport.java  |  5 ++---
 2 files changed, 21 insertions(+), 3 deletions(-)

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 561d9dc..f46e976 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
@@ -104,6 +104,25 @@ public class PropertyBindingSupportMapTest extends ContextTestSupport {
     }
 
     @Test
+    public void testPropertiesMapFirst() throws Exception {
+        Bar bar = new Bar();
+
+        Map<String, Object> prop = new LinkedHashMap<>();
+        prop.put("works[acme]", "#bean:company1");
+        prop.put("works[acme].id", "666");
+        prop.put("works[burger]", "#bean:company2");
+        prop.put("works[burger].name", "I changed this");
+
+        PropertyBindingSupport.bindProperties(context, bar, prop);
+
+        assertEquals(2, bar.getWorks().size());
+        assertEquals(666, bar.getWorks().get("acme").getId());
+        assertEquals("Acme", bar.getWorks().get("acme").getName());
+        assertEquals(456, bar.getWorks().get("burger").getId());
+        assertEquals("I changed this", bar.getWorks().get("burger").getName());
+    }
+
+    @Test
     public void testPropertiesNotMap() 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 1781c79..760029c 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
@@ -36,6 +36,7 @@ import static org.apache.camel.support.IntrospectionSupport.findSetterMethods;
  * <ul>
  *     <li>property placeholders - Keys and values using Camels property placeholder will be resolved</li>
  *     <li>nested - Properties can be nested using the dot syntax (OGNL and builder pattern using with as prefix), eg foo.bar=123</li>
+ *     <li>keys with map</li> - Properties can lookup in Map's using map syntax, eg foo[bar] where foo is the name of the property that is a Map instance, and bar is the name of the key.</li>
  *     <li>reference by bean id - Values can refer to other beans in the registry by prefixing with #nean: eg #bean:myBean</li>
  *     <li>reference by type - Values can refer to singleton beans by their type in the registry by prefixing with #type: syntax, eg #type:com.foo.MyClassType</li>
  *     <li>autowire by type - Values can refer to singleton beans by auto wiring by setting the value to #autowired</li>
@@ -45,7 +46,7 @@ import static org.apache.camel.support.IntrospectionSupport.findSetterMethods;
  */
 public final class PropertyBindingSupport {
 
-    // TODO: Add support for Map/List in keys
+    // TODO: Add support for List in keys
 
     /**
      * To use a fluent builder style to configure this property binding support.
@@ -360,8 +361,6 @@ public final class PropertyBindingSupport {
             }
         }
 
-        // TODO: support key without nested dots
-
         // if name has dot then we need to OGNL walk it
         if (nesting) {
             if (name.indexOf('.') > 0) {


[camel] 01/03: CAMEL-13557: Add property binding support to make it convenient to configure components and whatnot.

Posted by da...@apache.org.
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 7680c2a6ab930564c18bdea2070a9d1dc98b2bb6
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu May 30 12:10:42 2019 +0200

    CAMEL-13557: Add property binding support to make it convenient to configure components and whatnot.
---
 .../support/PropertyBindingSupportMapTest.java     | 186 +++++++++++++++++++++
 .../apache/camel/support/IntrospectionSupport.java |  36 +++-
 .../camel/support/PropertyBindingSupport.java      |  27 ++-
 3 files changed, 244 insertions(+), 5 deletions(-)

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
new file mode 100644
index 0000000..561d9dc
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportMapTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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 java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.PropertyBindingException;
+import org.junit.Test;
+
+/**
+ * Unit test for PropertyBindingSupport
+ */
+public class PropertyBindingSupportMapTest extends ContextTestSupport {
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+
+        Company work1 = new Company();
+        work1.setId(123);
+        work1.setName("Acme");
+        context.getRegistry().bind("company1", work1);
+        Company work2 = new Company();
+        work2.setId(456);
+        work2.setName("Acme 2");
+        context.getRegistry().bind("company2", work2);
+
+        Properties placeholders = new Properties();
+        placeholders.put("companyName", "Acme");
+        placeholders.put("committer", "rider");
+        context.getPropertiesComponent().setInitialProperties(placeholders);
+
+        return context;
+    }
+
+    @Test
+    public void testPropertiesMap() 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[acme]", "#bean:company1");
+        prop.put("bar.works[burger]", "#bean:company2");
+
+        PropertyBindingSupport.bindProperties(context, foo, prop);
+
+        assertEquals("James", foo.getName());
+        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("acme").getId());
+        assertEquals("Acme", foo.getBar().getWorks().get("acme").getName());
+        assertEquals(456, foo.getBar().getWorks().get("burger").getId());
+        assertEquals("Acme 2", foo.getBar().getWorks().get("burger").getName());
+    }
+
+    @Test
+    public void testPropertiesMapNested() 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[acme]", "#bean:company1");
+        prop.put("bar.works[acme].id", "666");
+        prop.put("bar.works[burger]", "#bean:company2");
+        prop.put("bar.works[burger].name", "I changed this");
+
+        PropertyBindingSupport.bindProperties(context, foo, prop);
+
+        assertEquals("James", foo.getName());
+        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("acme").getId());
+        assertEquals("Acme", foo.getBar().getWorks().get("acme").getName());
+        assertEquals(456, foo.getBar().getWorks().get("burger").getId());
+        assertEquals("I changed this", foo.getBar().getWorks().get("burger").getName());
+    }
+
+    @Test
+    public void testPropertiesNotMap() throws Exception {
+        Foo foo = new Foo();
+
+        Map<String, Object> prop = new LinkedHashMap<>();
+        prop.put("name", "James");
+        prop.put("bar.age", "33");
+        prop.put("bar.gold-customer[foo]", "true");
+
+        try {
+            PropertyBindingSupport.bindProperties(context, foo, prop);
+            fail("Should have thrown exception");
+        } 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 a Map because target bean is not a Map"));
+        }
+    }
+
+    public static class Foo {
+        private String name;
+        private Bar bar = new Bar();
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public Bar getBar() {
+            return bar;
+        }
+
+        public void setBar(Bar bar) {
+            this.bar = bar;
+        }
+    }
+
+    public static class Bar {
+        private int age;
+        private boolean rider;
+        private Map<String, Company> works; // should auto-create this via the setter
+        private boolean goldCustomer;
+
+        public int getAge() {
+            return age;
+        }
+
+        public void setAge(int age) {
+            this.age = age;
+        }
+
+        public boolean isRider() {
+            return rider;
+        }
+
+        public void setRider(boolean rider) {
+            this.rider = rider;
+        }
+
+        public Map<String, Company> getWorks() {
+            return works;
+        }
+
+        public void setWorks(Map<String, Company> works) {
+            this.works = works;
+        }
+
+        public boolean isGoldCustomer() {
+            return goldCustomer;
+        }
+
+        public void setGoldCustomer(boolean goldCustomer) {
+            this.goldCustomer = goldCustomer;
+        }
+    }
+
+}
+
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 fc62a37..700260a 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
@@ -524,18 +524,48 @@ public final class IntrospectionSupport {
     }
 
     /**
-     * This method supports two modes to set a property:
+     * This method supports three modes to set a property:
      *
-     * 1. Setting a property that has already been resolved, this is the case when {@code context} and {@code refName} are
+     * 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.
      *
-     * 2. Setting a property that has not yet been resolved, the property will be resolved based on the suitable methods
+     * 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) throws Exception {
 
+        // does the property name include a mapped key, then we need to set the property as a map
+        if (name.contains("[") && name.endsWith("]")) {
+            int pos = name.indexOf('[');
+            String mapKey = name.substring(pos + 1, name.length() - 1);
+            String key = name.substring(0, pos);
+
+            Object obj = IntrospectionSupport.getOrElseProperty(target, key, null);
+            if (obj == null) {
+                // it was supposed to be a map, but its null, so lets create a new map and set it automatically
+                obj = new LinkedHashMap<>();
+                boolean hit = IntrospectionSupport.setProperty(context, target, key, obj);
+                if (!hit) {
+                    throw new IllegalArgumentException("Cannot set property: " + name + " as a Map because target bean has no setter method for the Map");
+                }
+            }
+            if (obj instanceof Map) {
+                Map map = (Map) obj;
+                if (context != null && refName != null && value == null) {
+                    value = CamelContextHelper.lookup(context, refName);
+                }
+                map.put(mapKey, value);
+                return true;
+            } else {
+                // not a map
+                throw new IllegalArgumentException("Cannot set property: " + name + " as a Map because target bean is not a Map: " + target);
+            }
+        }
+
         Class<?> clazz = target.getClass();
         Collection<Method> setters;
 
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 a8ac7d2..1781c79 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
@@ -22,12 +22,13 @@ import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.PropertyBindingException;
 
 import static org.apache.camel.support.IntrospectionSupport.findSetterMethods;
-import static org.apache.camel.support.IntrospectionSupport.getOrElseProperty;
 
 /**
  * A convenient support class for binding String valued properties to an instance which
@@ -44,7 +45,7 @@ import static org.apache.camel.support.IntrospectionSupport.getOrElseProperty;
  */
 public final class PropertyBindingSupport {
 
-    // TODO: Add support for Map/List
+    // TODO: Add support for Map/List in keys
 
     /**
      * To use a fluent builder style to configure this property binding support.
@@ -359,6 +360,8 @@ public final class PropertyBindingSupport {
             }
         }
 
+        // TODO: support key without nested dots
+
         // if name has dot then we need to OGNL walk it
         if (nesting) {
             if (name.indexOf('.') > 0) {
@@ -440,6 +443,26 @@ public final class PropertyBindingSupport {
         return IntrospectionSupport.setProperty(context, context.getTypeConverter(), target, name, value, refName, fluentBuilder);
     }
 
+    private static Object getOrElseProperty(Object target, String property, Object defaultValue) {
+        String key = property;
+        String mapKey = null;
+
+        // support maps in keys
+        if (property.contains("[") && property.endsWith("]")) {
+            int pos = property.indexOf('[');
+            mapKey = property.substring(pos + 1, property.length() - 1);
+            key = property.substring(0, pos);
+        }
+
+        Object answer = IntrospectionSupport.getOrElseProperty(target, key, defaultValue);
+        if (answer instanceof Map && mapKey != null) {
+            Map map = (Map) answer;
+            answer = map.getOrDefault(mapKey, defaultValue);
+        }
+
+        return answer;
+    }
+
     private static Method findBestSetterMethod(Class clazz, String name, boolean fluentBuilder) {
         // is there a direct setter?
         Set<Method> candidates = findSetterMethods(clazz, name, false);