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


The following commit(s) were added to refs/heads/master by this push:
     new 9b7c95c  CAMEL-13557: Add property binding support to make it 
convenient to configure components and whatnot.
9b7c95c is described below

commit 9b7c95c81bc2ba5a433529cad08eac45d138fa64
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Mon May 27 09:00:31 2019 +0200

    CAMEL-13557: Add property binding support to make it convenient to 
configure components and whatnot.
---
 .../camel/main/MainConfigurationProperties.java    |  16 +++
 .../java/org/apache/camel/main/MainSupport.java    |   8 +-
 ...opertyBindingSupportAutowireNestedDeepTest.java | 116 +++++++++++++++++++++
 .../camel/support/PropertyBindingSupport.java      |  41 ++++++--
 4 files changed, 167 insertions(+), 14 deletions(-)

diff --git 
a/core/camel-core/src/main/java/org/apache/camel/main/MainConfigurationProperties.java
 
b/core/camel-core/src/main/java/org/apache/camel/main/MainConfigurationProperties.java
index 9e7e418..c45b33a 100644
--- 
a/core/camel-core/src/main/java/org/apache/camel/main/MainConfigurationProperties.java
+++ 
b/core/camel-core/src/main/java/org/apache/camel/main/MainConfigurationProperties.java
@@ -26,6 +26,7 @@ public class MainConfigurationProperties {
 
     private boolean autoConfigurationEnabled = true;
     private boolean autowireComponentProperties = true;
+    private boolean autowireComponentPropertiesDeep;
     private String name;
     private int shutdownTimeout = 300;
     private boolean shutdownSuppressLoggingOnTimeout;
@@ -117,6 +118,21 @@ public class MainConfigurationProperties {
         this.autowireComponentProperties = autowireComponentProperties;
     }
 
+    public boolean isAutowireComponentPropertiesDeep() {
+        return autowireComponentPropertiesDeep;
+    }
+
+    /**
+     * Whether autowiring components (with deep nesting by attempting to walk 
as deep down the object graph by creating new empty objects on the way if 
needed)
+     * with properties that are of same type, which has been added to the 
Camel registry, as a singleton instance.
+     * This is used for convention over configuration to inject DataSource, 
AmazonLogin instances to the components.
+     * <p/>
+     * This option is default disabled.
+     */
+    public void setAutowireComponentPropertiesDeep(boolean 
autowireComponentPropertiesDeep) {
+        this.autowireComponentPropertiesDeep = autowireComponentPropertiesDeep;
+    }
+
     public String getName() {
         return name;
     }
diff --git 
a/core/camel-core/src/main/java/org/apache/camel/main/MainSupport.java 
b/core/camel-core/src/main/java/org/apache/camel/main/MainSupport.java
index ab9bcd6..7157fcc 100644
--- a/core/camel-core/src/main/java/org/apache/camel/main/MainSupport.java
+++ b/core/camel-core/src/main/java/org/apache/camel/main/MainSupport.java
@@ -851,8 +851,8 @@ public abstract class MainSupport extends ServiceSupport {
 
         // conventional configuration via properties to allow configuring 
options on
         // component, dataformat, and languages (like spring-boot 
auto-configuration)
-        if (mainConfigurationProperties.isAutowireComponentProperties()) {
-            autoConfigurationFromRegistry(camelContext);
+        if (mainConfigurationProperties.isAutowireComponentProperties() || 
mainConfigurationProperties.isAutowireComponentPropertiesDeep()) {
+            autoConfigurationFromRegistry(camelContext, 
mainConfigurationProperties.isAutowireComponentPropertiesDeep());
         }
         if (mainConfigurationProperties.isAutoConfigurationEnabled()) {
             autoConfigurationFromProperties(camelContext);
@@ -1266,11 +1266,11 @@ public abstract class MainSupport extends 
ServiceSupport {
         }
     }
 
-    protected void autoConfigurationFromRegistry(CamelContext camelContext) 
throws Exception {
+    protected void autoConfigurationFromRegistry(CamelContext camelContext, 
boolean deepNesting) throws Exception {
         camelContext.addLifecycleStrategy(new LifecycleStrategySupport() {
             @Override
             public void onComponentAdd(String name, Component component) {
-                
PropertyBindingSupport.autowireSingletonPropertiesFromRegistry(camelContext, 
component, false, (obj, propertyName, type, value) -> {
+                
PropertyBindingSupport.autowireSingletonPropertiesFromRegistry(camelContext, 
component, false, deepNesting, (obj, propertyName, type, value) -> {
                     LOG.info("Auto configuring option: {} on component: {} as 
one instance of type: {} registered in the Camel Registry",
                             propertyName, 
component.getClass().getSimpleName(), type.getName());
                 });
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportAutowireNestedDeepTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportAutowireNestedDeepTest.java
new file mode 100644
index 0000000..5c706fe
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportAutowireNestedDeepTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.ContextTestSupport;
+import org.junit.Test;
+
+/**
+ * Unit test for PropertyBindingSupport
+ */
+public class PropertyBindingSupportAutowireNestedDeepTest extends 
ContextTestSupport {
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+
+        Company work = new Company();
+        work.setId(456);
+        work.setName("Acme");
+        context.getRegistry().bind("myWork", work);
+
+        return context;
+    }
+
+    @Test
+    public void testAutowirePropertiesDeep() throws Exception {
+        Foo foo = new Foo();
+
+        PropertyBindingSupport.bindProperty(context, foo, "name", "James");
+        // use deep nesting
+        
PropertyBindingSupport.autowireSingletonPropertiesFromRegistry(context, foo, 
false, true, null);
+
+        assertEquals("James", foo.getName());
+        // should be auto wired (deep)
+        assertNotNull(foo.getBar().getWork());
+        assertEquals(456, foo.getBar().getWork().getId());
+        assertEquals("Acme", foo.getBar().getWork().getName());
+    }
+
+    public static class Foo {
+        private String name;
+        private Bar 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 Company work;
+        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 Company getWork() {
+            return work;
+        }
+
+        public void setWork(Company work) {
+            this.work = work;
+        }
+
+        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/PropertyBindingSupport.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
index 4876513..a8ac7d2 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
@@ -148,7 +148,7 @@ public final class PropertyBindingSupport {
      * @return              true if one ore more properties was auto wired
      */
     public static boolean autowireSingletonPropertiesFromRegistry(CamelContext 
camelContext, Object target) {
-        return autowireSingletonPropertiesFromRegistry(camelContext, target, 
false, null);
+        return autowireSingletonPropertiesFromRegistry(camelContext, target, 
false, false, null);
     }
 
     /**
@@ -160,14 +160,18 @@ public final class PropertyBindingSupport {
      * @param camelContext  the camel context
      * @param target        the target object
      * @param bindNullOnly  whether to only autowire if the property has no 
default value or has not been configured explicit
+     * @param deepNesting   whether to attempt to walk as deep down the object 
graph by creating new empty objects on the way if needed (Camel can only create
+     *                      new empty objects if they have a default no-arg 
constructor, also mind that this may lead to creating many empty objects, even
+     *                      if they will not have any objects autowired from 
the registry, so use this with caution)
      * @param callback      optional callback when a property was auto wired
      * @return              true if one ore more properties was auto wired
      */
-    public static boolean autowireSingletonPropertiesFromRegistry(CamelContext 
camelContext, Object target, boolean bindNullOnly, OnAutowiring callback) {
+    public static boolean autowireSingletonPropertiesFromRegistry(CamelContext 
camelContext, Object target,
+                                                                  boolean 
bindNullOnly, boolean deepNesting, OnAutowiring callback) {
         try {
             if (target != null) {
                 Set<Object> parents = new HashSet<>();
-                return doAutowireSingletonPropertiesFromRegistry(camelContext, 
target, parents, bindNullOnly, callback);
+                return doAutowireSingletonPropertiesFromRegistry(camelContext, 
target, parents, bindNullOnly, deepNesting, callback);
             }
         } catch (Exception e) {
             throw new PropertyBindingException(target, e);
@@ -177,7 +181,7 @@ public final class PropertyBindingSupport {
     }
 
     private static boolean 
doAutowireSingletonPropertiesFromRegistry(CamelContext camelContext, Object 
target, Set<Object> parents,
-                                                                     boolean 
bindNullOnly, OnAutowiring callback) throws Exception {
+                                                                     boolean 
bindNullOnly, boolean deepNesting, OnAutowiring callback) throws Exception {
         // when adding a component then support auto-configuring complex types
         // by looking up from registry, such as DataSource etc
         Map<String, Object> properties = new LinkedHashMap<>();
@@ -212,13 +216,30 @@ public final class PropertyBindingSupport {
                     }
                 }
 
-                // TODO: Support creating new instances to walk down the tree 
if its null (deepNesting option)
-
-                // remember this as parent and also autowire nested properties
-                // do not walk down if it point to our-selves (circular 
reference)
-                if (value != null) {
+                // attempt to create new instances to walk down the tree if 
its null (deepNesting option)
+                if (value == null && deepNesting) {
+                    // okay is there a setter so we can create a new instance 
and set it automatic
+                    Method method = findBestSetterMethod(target.getClass(), 
key, true);
+                    if (method != null) {
+                        Class<?> parameterType = method.getParameterTypes()[0];
+                        if (parameterType != null && 
org.apache.camel.util.ObjectHelper.hasDefaultPublicNoArgConstructor(parameterType))
 {
+                            Object instance = 
camelContext.getInjector().newInstance(parameterType);
+                            if (instance != null) {
+                                
org.apache.camel.support.ObjectHelper.invokeMethod(method, target, instance);
+                                target = instance;
+                                // remember this as parent and also autowire 
nested properties
+                                // do not walk down if it point to our-selves 
(circular reference)
+                                parents.add(target);
+                                value = instance;
+                                hit |= 
doAutowireSingletonPropertiesFromRegistry(camelContext, value, parents, 
bindNullOnly, deepNesting, callback);
+                            }
+                        }
+                    }
+                } else if (value != null) {
+                    // remember this as parent and also autowire nested 
properties
+                    // do not walk down if it point to our-selves (circular 
reference)
                     parents.add(target);
-                    hit |= 
doAutowireSingletonPropertiesFromRegistry(camelContext, value, parents, 
bindNullOnly, callback);
+                    hit |= 
doAutowireSingletonPropertiesFromRegistry(camelContext, value, parents, 
bindNullOnly, deepNesting, callback);
                 }
             }
         }

Reply via email to