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); } } }