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 10683c6  CAMEL-14268: Component auto configuration fails in Java 11
10683c6 is described below

commit 10683c64385dc9c3bc82710b9996068e20319751
Author: lburgazzoli <[email protected]>
AuthorDate: Mon Dec 9 10:37:06 2019 +0100

    CAMEL-14268: Component auto configuration fails in Java 11
---
 .../org/apache/camel/main/BaseMainSupport.java     | 130 ++++++++++-----------
 .../camel/main/MainComponentConfigurationTest.java |  54 +++++++++
 .../camel/main/support/MyDummyComponent.java       |  67 +++++++++++
 .../main/support/MyDummyComponentConfigurer.java   |  52 +++++++++
 .../camel/main/support/MyDummyConfiguration.java   |  52 +++++++++
 .../camel/support/PropertyBindingSupport.java      |  74 ++++++------
 6 files changed, 329 insertions(+), 100 deletions(-)

diff --git 
a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java 
b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index 991e6ab..7b5e8ea 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -21,7 +21,6 @@ import java.io.FileInputStream;
 import java.io.InputStream;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
@@ -122,15 +121,13 @@ public abstract class BaseMainSupport extends 
ServiceSupport {
     }
 
     protected static boolean setPropertiesOnTarget(CamelContext context, 
Object target, Map<String, Object> properties,
-                                                   String optionKey, String 
optionPrefix, boolean failIfNotSet, boolean ignoreCase,
+                                                   String optionPrefix, 
boolean failIfNotSet, boolean ignoreCase,
                                                    Map<String, String> 
autoConfiguredProperties) throws Exception {
         ObjectHelper.notNull(context, "context");
         ObjectHelper.notNull(target, "target");
         ObjectHelper.notNull(properties, "properties");
 
         boolean rc = false;
-        Iterator it = properties.entrySet().iterator();
-
         PropertyConfigurer configurer = null;
         if (target instanceof Component) {
             // the component needs to be initialized to have the configurer 
ready
@@ -138,41 +135,47 @@ public abstract class BaseMainSupport extends 
ServiceSupport {
             configurer = ((Component) target).getComponentPropertyConfigurer();
         }
 
-        while (it.hasNext()) {
-            Map.Entry<String, Object> entry = (Map.Entry) it.next();
-            String name = entry.getKey();
-            Object value = entry.getValue();
-            String stringValue = value != null ? value.toString() : null;
-            String key = name;
-            if (optionPrefix != null && optionKey != null) {
-                key = optionPrefix + optionKey;
-            } else if (optionPrefix != null) {
-                key = optionPrefix + name;
-            }
+        try {
+            // keep a reference of the original keys
+            Map<String, Object> backup = new LinkedHashMap<>(properties);
+
+            rc = PropertyBindingSupport.build()
+                .withMandatory(failIfNotSet)
+                .withRemoveParameters(true)
+                .withConfigurer(configurer)
+                .withIgnoreCase(ignoreCase)
+                .bind(context, target, properties);
+
+            for (Map.Entry<String, Object> entry: backup.entrySet()) {
+                if (entry.getValue() != null && 
!properties.containsKey(entry.getKey())) {
+                    String prefix = optionPrefix;
+                    if (prefix != null && !prefix.endsWith(".")) {
+                        prefix = "." + prefix;
+                    }
 
-            LOG.debug("Configuring property: {}={} on bean: {}", key, 
stringValue, target);
-            try {
-                boolean hit;
-                if (failIfNotSet) {
-                    
PropertyBindingSupport.build().withMandatory(true).withConfigurer(configurer).withIgnoreCase(ignoreCase).bind(context,
 target, name, stringValue);
-                    hit = true;
-                } else {
-                    hit = 
PropertyBindingSupport.build().withConfigurer(configurer).withIgnoreCase(true).bind(context,
 target, name, stringValue);
-                }
-                if (hit) {
-                    it.remove();
-                    rc = true;
-                    LOG.debug("Configured property: {}={} on bean: {}", key, 
stringValue, target);
-                    autoConfiguredProperties.put(key, stringValue);
+                    LOG.debug("Configured property: {}{}={} on bean: {}", 
prefix, entry.getKey(), entry.getValue(), target);
+                    autoConfiguredProperties.put(prefix + entry.getKey(), 
entry.getValue().toString());
                 }
-            } catch (PropertyBindingException e) {
-                if (failIfNotSet) {
-                    // enrich the error with more precise details with option 
prefix and key
-                    throw new PropertyBindingException(e.getTarget(), 
e.getPropertyName(), e.getValue(), optionPrefix, optionKey, e.getCause());
-                } else {
-                    LOG.debug("Error configuring property (" + key + ") with 
name: " + name + ") on bean: " + target
-                            + " with value: " + stringValue + ". This 
exception is ignored as failIfNotSet=false.", e);
+            }
+        } catch (PropertyBindingException e) {
+            String key = e.getOptionKey();
+            if (key == null) {
+                String prefix = e.getOptionPrefix();
+                if (prefix != null && !prefix.endsWith(".")) {
+                    prefix = "." + prefix;
                 }
+
+                key = prefix != null
+                    ? prefix + "." + e.getPropertyName()
+                    : e.getPropertyName();
+            }
+
+            if (failIfNotSet) {
+                // enrich the error with more precise details with option 
prefix and key
+                throw new PropertyBindingException(e.getTarget(), 
e.getPropertyName(), e.getValue(), optionPrefix, key, e.getCause());
+            } else {
+                LOG.debug("Error configuring property (" + key + ") with name: 
" + e.getPropertyName() + ") on bean: " + target
+                    + " with value: " + e.getValue() + ". This exception is 
ignored as failIfNotSet=false.", e);
             }
         }
 
@@ -707,7 +710,7 @@ public abstract class BaseMainSupport extends 
ServiceSupport {
         }
         if (!contextProperties.isEmpty()) {
             LOG.debug("Auto-configuring CamelContext from loaded properties: 
{}", contextProperties.size());
-            setPropertiesOnTarget(camelContext, camelContext, 
contextProperties, null, "camel.context.",
+            setPropertiesOnTarget(camelContext, camelContext, 
contextProperties, "camel.context.",
                     mainConfigurationProperties.isAutoConfigurationFailFast(), 
true, autoConfiguredProperties);
         }
         if (!hystrixProperties.isEmpty()) {
@@ -718,7 +721,7 @@ public abstract class BaseMainSupport extends 
ServiceSupport {
                 hystrix = new HystrixConfigurationDefinition();
                 model.setHystrixConfiguration(hystrix);
             }
-            setPropertiesOnTarget(camelContext, hystrix, hystrixProperties, 
null, "camel.hystrix.",
+            setPropertiesOnTarget(camelContext, hystrix, hystrixProperties, 
"camel.hystrix.",
                     mainConfigurationProperties.isAutoConfigurationFailFast(), 
true, autoConfiguredProperties);
         }
         if (!resilience4jProperties.isEmpty()) {
@@ -729,7 +732,7 @@ public abstract class BaseMainSupport extends 
ServiceSupport {
                 resilience4j = new Resilience4jConfigurationDefinition();
                 model.setResilience4jConfiguration(resilience4j);
             }
-            setPropertiesOnTarget(camelContext, resilience4j, 
resilience4jProperties, null, "camel.resilience4j.",
+            setPropertiesOnTarget(camelContext, resilience4j, 
resilience4jProperties, "camel.resilience4j.",
                     mainConfigurationProperties.isAutoConfigurationFailFast(), 
true, autoConfiguredProperties);
         }
         if (!restProperties.isEmpty()) {
@@ -740,7 +743,7 @@ public abstract class BaseMainSupport extends 
ServiceSupport {
                 rest = new RestConfiguration();
                 model.setRestConfiguration(rest);
             }
-            setPropertiesOnTarget(camelContext, rest, restProperties, null, 
"camel.rest.",
+            setPropertiesOnTarget(camelContext, rest, restProperties, 
"camel.rest.",
                     mainConfigurationProperties.isAutoConfigurationFailFast(), 
true, autoConfiguredProperties);
         }
 
@@ -799,9 +802,8 @@ public abstract class BaseMainSupport extends 
ServiceSupport {
 
         if (!properties.isEmpty()) {
             LOG.debug("Auto-configuring properties component from loaded 
properties: {}", properties.size());
-            setPropertiesOnTarget(camelContext, 
camelContext.getPropertiesComponent(), properties, null,
-                    "camel.component.properties.", 
mainConfigurationProperties.isAutoConfigurationFailFast(),
-                    true, autoConfiguredProperties);
+            setPropertiesOnTarget(camelContext, 
camelContext.getPropertiesComponent(), properties, 
"camel.component.properties.",
+                    mainConfigurationProperties.isAutoConfigurationFailFast(), 
true, autoConfiguredProperties);
         }
 
         // log which options was not set
@@ -838,7 +840,7 @@ public abstract class BaseMainSupport extends 
ServiceSupport {
 
         if (!properties.isEmpty()) {
             LOG.debug("Auto-configuring main from loaded properties: {}", 
properties.size());
-            setPropertiesOnTarget(camelContext, config, properties, null, 
"camel.main.",
+            setPropertiesOnTarget(camelContext, config, properties, 
"camel.main.",
                     mainConfigurationProperties.isAutoConfigurationFailFast(), 
true, autoConfiguredProperties);
         }
 
@@ -923,11 +925,15 @@ public abstract class BaseMainSupport extends 
ServiceSupport {
             LOG.debug("Auto-configuring {} components/dataformat/languages 
from loaded properties: {}", properties.size(), total);
         }
 
-        for (PropertyOptionKey pok : properties.keySet()) {
-            Map<String, Object> values = properties.get(pok);
-            String optionKey = 
pok.getKey().substring(pok.getOptionPrefix().length());
-            setPropertiesOnTarget(camelContext, pok.getInstance(), values, 
optionKey, pok.getOptionPrefix(),
-                    mainConfigurationProperties.isAutoConfigurationFailFast(), 
true, autoConfiguredProperties);
+        for (Map.Entry<PropertyOptionKey, Map<String, Object>> entry: 
properties.entrySet()) {
+            setPropertiesOnTarget(
+                    camelContext,
+                    entry.getKey().getInstance(),
+                    entry.getValue(),
+                    entry.getKey().getOptionPrefix(),
+                    mainConfigurationProperties.isAutoConfigurationFailFast(),
+                    true,
+                    autoConfiguredProperties);
         }
 
         // log which options was not set
@@ -936,7 +942,7 @@ public abstract class BaseMainSupport extends 
ServiceSupport {
                 Map<String, Object> values = properties.get(pok);
                 values.forEach((k, v) -> {
                     String stringValue = v != null ? v.toString() : null;
-                    LOG.warn("Property ({}={}) not auto-configured with name: 
{} on bean: {} with value: {}", pok.getKey(), stringValue, k, 
pok.getInstance(), stringValue);
+                    LOG.warn("Property ({}={}) not auto-configured with name: 
{} on bean: {} with value: {}", pok.getOptionPrefix() +  "." + k, stringValue, 
k, pok.getInstance(), stringValue);
                 });
             }
         }
@@ -1010,19 +1016,12 @@ public abstract class BaseMainSupport extends 
ServiceSupport {
     }
 
     private static final class PropertyOptionKey {
-
-        private final String key;
         private final Object instance;
         private final String optionPrefix;
 
-        private PropertyOptionKey(String key, Object instance, String 
optionPrefix) {
-            this.key = key;
-            this.instance = instance;
-            this.optionPrefix = optionPrefix;
-        }
-
-        public String getKey() {
-            return key;
+        private PropertyOptionKey(Object instance, String optionPrefix) {
+            this.instance = ObjectHelper.notNull(instance, "instance");
+            this.optionPrefix = ObjectHelper.notNull(optionPrefix, 
"optionPrefix");
         }
 
         public Object getInstance() {
@@ -1038,16 +1037,17 @@ public abstract class BaseMainSupport extends 
ServiceSupport {
             if (this == o) {
                 return true;
             }
-            if (o == null || getClass() != o.getClass()) {
+            if (!(o instanceof PropertyOptionKey)) {
                 return false;
             }
-            PropertyOptionKey that = (PropertyOptionKey) o;
-            return key.equals(that.key) && instance.equals(that.instance);
+            PropertyOptionKey key = (PropertyOptionKey) o;
+            return Objects.equals(instance, key.instance)
+                && Objects.equals(optionPrefix, key.optionPrefix);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(key, instance);
+            return Objects.hash(instance, optionPrefix);
         }
     }
 
@@ -1093,7 +1093,7 @@ public abstract class BaseMainSupport extends 
ServiceSupport {
 
             validateOptionAndValue(key, option, value);
 
-            PropertyOptionKey pok = new PropertyOptionKey(key, target, prefix);
+            PropertyOptionKey pok = new PropertyOptionKey(target, prefix);
             Map<String, Object> values = properties.computeIfAbsent(pok, k -> 
new LinkedHashMap<>());
 
             // we ignore case for property keys (so we should store them in 
canonical style
diff --git 
a/core/camel-main/src/test/java/org/apache/camel/main/MainComponentConfigurationTest.java
 
b/core/camel-main/src/test/java/org/apache/camel/main/MainComponentConfigurationTest.java
new file mode 100644
index 0000000..a2360ba
--- /dev/null
+++ 
b/core/camel-main/src/test/java/org/apache/camel/main/MainComponentConfigurationTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.main;
+
+import java.util.Properties;
+
+import org.apache.camel.main.support.MyDummyComponent;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class MainComponentConfigurationTest extends Assert {
+    @Test
+    public void testComponentConfiguration() {
+        Properties properties = new Properties();
+        properties.setProperty("camel.component.dummy.configuration.log", 
"true");
+        properties.setProperty("camel.component.dummy.component-value", 
"component-value");
+        
properties.setProperty("camel.component.dummy.configuration.nested.value", 
"nested-value");
+        properties.setProperty("camel.component.dummy.configuration", 
"#class:org.apache.camel.main.support.MyDummyConfiguration");
+
+        Main main = new Main();
+        try {
+            MyDummyComponent dummy = new MyDummyComponent(false);
+
+            main.bind("dummy", dummy);
+            main.setOverrideProperties(properties);
+            main.setDefaultPropertyPlaceholderLocation("false");
+            main.start();
+
+            assertEquals("component-value", dummy.getComponentValue());
+
+            assertNotNull(dummy.getConfiguration());
+            assertTrue(dummy.getConfiguration().isLog());
+            assertNotNull(dummy.getConfiguration().getNested());
+            assertEquals("nested-value", 
dummy.getConfiguration().getNested().getValue());
+        } finally {
+            main.stop();
+        }
+    }
+}
+
diff --git 
a/core/camel-main/src/test/java/org/apache/camel/main/support/MyDummyComponent.java
 
b/core/camel-main/src/test/java/org/apache/camel/main/support/MyDummyComponent.java
new file mode 100644
index 0000000..9b3d73f
--- /dev/null
+++ 
b/core/camel-main/src/test/java/org/apache/camel/main/support/MyDummyComponent.java
@@ -0,0 +1,67 @@
+/*
+ * 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.main.support;
+
+import java.util.Map;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.spi.PropertyConfigurer;
+import org.apache.camel.spi.annotations.Component;
+import org.apache.camel.support.DefaultComponent;
+
+@Component("dummy")
+public class MyDummyComponent extends DefaultComponent {
+    private MyDummyConfiguration configuration;
+    private boolean configurer;
+    private String componentValue;
+
+    public MyDummyComponent(boolean configurer) {
+        this.configurer = configurer;
+    }
+
+    public MyDummyConfiguration getConfiguration() {
+        return configuration;
+    }
+
+    public void setConfiguration(MyDummyConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    // this method makes camel no able to find a suitable setter
+    public void setConfiguration(Object configuration) {
+        this.configuration = (MyDummyConfiguration)configuration;
+    }
+
+    public String getComponentValue() {
+        return componentValue;
+    }
+
+    public MyDummyComponent setComponentValue(String componentValue) {
+        this.componentValue = componentValue;
+        return this;
+    }
+
+    @Override
+    protected Endpoint createEndpoint(String uri, String remaining, 
Map<String, Object> parameters) throws Exception {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public PropertyConfigurer getComponentPropertyConfigurer() {
+        return configurer ? new MyDummyComponentConfigurer() : null;
+    }
+}
diff --git 
a/core/camel-main/src/test/java/org/apache/camel/main/support/MyDummyComponentConfigurer.java
 
b/core/camel-main/src/test/java/org/apache/camel/main/support/MyDummyComponentConfigurer.java
new file mode 100644
index 0000000..9751c7b
--- /dev/null
+++ 
b/core/camel-main/src/test/java/org/apache/camel/main/support/MyDummyComponentConfigurer.java
@@ -0,0 +1,52 @@
+/*
+ * 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.main.support;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+public class MyDummyComponentConfigurer extends PropertyConfigurerSupport 
implements GeneratedPropertyConfigurer {
+    @Override
+    public boolean configure(CamelContext camelContext, Object component, 
String name, Object value, boolean ignoreCase) {
+        if (ignoreCase) {
+            return doConfigureIgnoreCase(camelContext, component, name, value);
+        } else {
+            return doConfigure(camelContext, component, name, value);
+        }
+    }
+
+    private static boolean doConfigure(CamelContext camelContext, Object 
component, String name, Object value) {
+        switch (name) {
+        case "configuration":
+            ((MyDummyComponent) 
component).setConfiguration(property(camelContext, MyDummyConfiguration.class, 
value));
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    private static boolean doConfigureIgnoreCase(CamelContext camelContext, 
Object component, String name, Object value) {
+        switch (name.toLowerCase()) {
+        case "configuration":
+            ((MyDummyComponent) 
component).setConfiguration(property(camelContext, MyDummyConfiguration.class, 
value));
+            return true;
+        default:
+            return false;
+        }
+    }
+}
diff --git 
a/core/camel-main/src/test/java/org/apache/camel/main/support/MyDummyConfiguration.java
 
b/core/camel-main/src/test/java/org/apache/camel/main/support/MyDummyConfiguration.java
new file mode 100644
index 0000000..153dadf
--- /dev/null
+++ 
b/core/camel-main/src/test/java/org/apache/camel/main/support/MyDummyConfiguration.java
@@ -0,0 +1,52 @@
+/*
+ * 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.main.support;
+
+public class MyDummyConfiguration {
+    private boolean log;
+    private NestedConfig nested;
+
+    public boolean isLog() {
+        return log;
+    }
+
+    public MyDummyConfiguration setLog(boolean log) {
+        this.log = log;
+        return this;
+    }
+
+    public NestedConfig getNested() {
+        return nested;
+    }
+
+    public void setNested(NestedConfig nested) {
+        this.nested = nested;
+    }
+
+    public static class NestedConfig {
+        private String value;
+
+        public String getValue() {
+            return value;
+        }
+
+        public NestedConfig setValue(String value) {
+            this.value = value;
+            return this;
+        }
+    }
+}
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 4d101c1..1c8205d 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,6 +19,7 @@ package org.apache.camel.support;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -471,7 +472,9 @@ public final class PropertyBindingSupport {
         org.apache.camel.util.ObjectHelper.notNull(camelContext, 
"camelContext");
         org.apache.camel.util.ObjectHelper.notNull(target, "target");
         org.apache.camel.util.ObjectHelper.notNull(properties, "properties");
-        boolean rc = false;
+
+        final String uOptionPrefix = ignoreCase && isNotEmpty(optionPrefix) ? 
optionPrefix.toUpperCase(Locale.US) : "";
+        final int size = properties.size();
 
         if (configurer instanceof GeneratedPropertyConfigurer) {
             GeneratedPropertyConfigurer gen = (GeneratedPropertyConfigurer) 
configurer;
@@ -480,12 +483,9 @@ public final class PropertyBindingSupport {
                 Map.Entry<String, Object> entry = iter.next();
                 String key = entry.getKey();
                 Object value = entry.getValue();
-                boolean valid = true;
-                if (nesting) {
-                    // property configurer does not support nested names so 
skip if the name has a dot
-                    valid = key.indexOf('.') == -1;
-                }
-                if (valid) {
+
+                // property configurer does not support nested names so skip 
if the name has a dot
+                if (key.indexOf('.') != -1) {
                     try {
                         // GeneratedPropertyConfigurer works by invoking the 
methods directly but it does
                         // not resolve property placeholders eventually 
defined in the value before invoking
@@ -497,7 +497,6 @@ public final class PropertyBindingSupport {
                         boolean hit = gen.configure(camelContext, target, key, 
value, ignoreCase);
                         if (removeParameter && hit) {
                             iter.remove();
-                            rc = true;
                         }
                     } catch (Exception e) {
                         throw new PropertyBindingException(target, key, value, 
e);
@@ -507,39 +506,44 @@ public final class PropertyBindingSupport {
         }
 
         // must set reference parameters first before the other bindings
-        int size = properties.size();
         setReferenceProperties(camelContext, target, properties);
-        rc |= properties.size() != size;
-
-        String uOptionPrefix = "";
-        if (ignoreCase && isNotEmpty(optionPrefix)) {
-            uOptionPrefix = optionPrefix.toUpperCase(Locale.US);
-        }
-
-        for (Iterator<Map.Entry<String, Object>> iter = 
properties.entrySet().iterator(); iter.hasNext();) {
-            Map.Entry<String, Object> entry = iter.next();
-            String key = entry.getKey();
-            Object value = entry.getValue();
 
-            if (isNotEmpty(optionPrefix)) {
-                boolean match = key.startsWith(optionPrefix) || ignoreCase && 
key.toUpperCase(Locale.US).startsWith(uOptionPrefix);
-                if (!match) {
-                    continue;
+        // sort the keys by nesting level so when moving to the nest level all 
the
+        // propertis of the curent level are bound to the target. This allow 
the 
+        // properties binging engine to be indipendent to the order of 
properrties.
+        //
+        // As example:
+        //
+        //     configuration.my-property = myCustomValue
+        //     configuration = #class:my.custom.Config
+        //
+        // 'configuration.my-property' has lower precedence over 
'configuration' as
+        // it is one level deep and will be set after all the properties of 
the current
+        // level are set which allow `my-property` to be set of the right 
instance.
+        properties.keySet().stream()
+            .sorted(Comparator.comparingInt(s -> StringHelper.countChar(s, 
'.')))
+            .forEach(key -> {
+                final Object value = properties.get(key);
+
+                if (isNotEmpty(optionPrefix)) {
+                    boolean match = key.startsWith(optionPrefix) || ignoreCase 
&& key.toUpperCase(Locale.US).startsWith(uOptionPrefix);
+                    if (!match) {
+                        return;
+                    }
+                    key = key.substring(optionPrefix.length());
                 }
-                key = key.substring(optionPrefix.length());
-            }
 
-            boolean bound = bindProperty(camelContext, target, key, value, 
ignoreCase, nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, 
placeholder);
-            if (bound && removeParameter) {
-                iter.remove();
-                rc = true;
-            }
-            if (mandatory && !bound) {
-                throw new PropertyBindingException(target, key, value);
+                boolean bound = bindProperty(camelContext, target, key, value, 
ignoreCase, nesting, deepNesting, fluentBuilder, allowPrivateSetter, reference, 
placeholder);
+                if (bound && removeParameter) {
+                    properties.remove(key);
+                }
+                if (mandatory && !bound) {
+                    throw new PropertyBindingException(target, key, value);
+                }
             }
-        }
+        );
 
-        return rc;
+        return properties.size() != size;
     }
 
     private static boolean bindProperty(CamelContext camelContext, Object 
target, String name, Object value,

Reply via email to