This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch 13557 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 46d568f11e7018f856205316f84b769b054ad7bb Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Thu May 23 11:51:39 2019 +0200 CAMEL-13557: Add property binding support to make it convenient to configure components and whatnot. --- .../org/apache/camel/PropertyBindingException.java | 6 ++ .../java/org/apache/camel/main/MainSupport.java | 58 +---------- .../PropertyBindingSupportAutowireTest.java | 107 +++++++++++++++++++++ .../camel/support/PropertyBindingSupport.java | 89 ++++++++++++++++- 4 files changed, 203 insertions(+), 57 deletions(-) diff --git a/core/camel-api/src/main/java/org/apache/camel/PropertyBindingException.java b/core/camel-api/src/main/java/org/apache/camel/PropertyBindingException.java index ed6e61b..fdc9005 100644 --- a/core/camel-api/src/main/java/org/apache/camel/PropertyBindingException.java +++ b/core/camel-api/src/main/java/org/apache/camel/PropertyBindingException.java @@ -36,6 +36,12 @@ public class PropertyBindingException extends RuntimeCamelException { this.propertyName = propertyName; } + public PropertyBindingException(Object target, Exception e) { + super("Error binding properties on bean: " + target, e); + this.target = target; + this.propertyName = null; + } + public Object getTarget() { return target; } 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 4a249dc..a20623b 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 @@ -21,7 +21,6 @@ import java.io.FileInputStream; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; @@ -81,7 +80,6 @@ import org.apache.camel.spi.StreamCachingStrategy; import org.apache.camel.spi.ThreadPoolProfile; import org.apache.camel.spi.UnitOfWorkFactory; import org.apache.camel.spi.UuidGenerator; -import org.apache.camel.support.IntrospectionSupport; import org.apache.camel.support.LifecycleStrategySupport; import org.apache.camel.support.PropertyBindingSupport; import org.apache.camel.support.jsse.GlobalSSLContextParametersSupplier; @@ -1255,62 +1253,10 @@ public abstract class MainSupport extends ServiceSupport { camelContext.addLifecycleStrategy(new LifecycleStrategySupport() { @Override public void onComponentAdd(String name, Component component) { - // 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<>(); - IntrospectionSupport.getProperties(component, properties, null); - - // TODO: Use PropertyBindingSupport to make it support this kind of use-case too - // TODO: Allow nested properties too - // TODO: Allow fluent builders - - // lookup complex types - properties.forEach((k, v) -> { - // if the property has not been set and its a complex type (not simple or string etc) - Class type = getGetterType(component, k); - if (isComplexType(type)) { - Set lookup = findExplicitBindingByType(camelContext, type); - if (lookup.size() == 1) { - v = lookup.iterator().next(); - try { - LOG.info("Auto configuring option: {} on component: {} as one instance of type: {} registered in the Camel Registry", k, name, type.getName()); - IntrospectionSupport.setProperty(camelContext, component, k, v); - } catch (Exception e) { - LOG.warn("Cannot auto configure option: " + k + " on component: " + name + " due to " + e.getMessage()); - } - } - } + PropertyBindingSupport.autowireSingletonPropertiesFromRegistry(camelContext, component, false, (obj, propertyName, type, value) -> { + LOG.info("Auto configuring option: {} on component: {} as one instance of type: {} registered in the Camel Registry", propertyName, obj, type.getName()); }); } - - /** - * Finds any explicit bean bindings that has been added to the registry. - * This means that if there are any, then they have been added by the end user - * and we should favour using the bean if there is a single instance bound for the type. - */ - private Set findExplicitBindingByType(CamelContext camelContext, Class type) { - if (camelContext.getRegistry() instanceof MainRegistry) { - return ((MainRegistry) camelContext.getRegistry()).findBindingsByType(type); - } - return Collections.EMPTY_SET; - } - - private boolean isComplexType(Class type) { - // lets consider all non java as complex types - return type != null && !type.isPrimitive() && !type.getName().startsWith("java"); - } - - private Class getGetterType(Component component, String key) { - try { - Method getter = IntrospectionSupport.getPropertyGetter(component.getClass(), key); - if (getter != null) { - return getter.getReturnType(); - } - } catch (NoSuchMethodException e) { - // ignore - } - return null; - } }); } diff --git a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportAutowireTest.java b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportAutowireTest.java new file mode 100644 index 0000000..5b33d61 --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportAutowireTest.java @@ -0,0 +1,107 @@ +/* + * 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 PropertyBindingSupportAutowireTest extends ContextTestSupport { + + @Override + protected CamelContext createCamelContext() throws Exception { + CamelContext context = super.createCamelContext(); + + Bar bar = new Bar(); + bar.setAge(33); + bar.setGoldCustomer(true); + bar.setRider(true); + context.getRegistry().bind("myBar", bar); + + return context; + } + + @Test + public void testAutowireProperties() throws Exception { + Foo foo = new Foo(); + + PropertyBindingSupport.bindProperty(context, foo, "name", "James"); + PropertyBindingSupport.autowireSingletonPropertiesFromRegistry(context, foo); + + assertEquals("James", foo.getName()); + // should be auto wired + assertEquals(33, foo.getBar().getAge()); + assertTrue(foo.getBar().isRider()); + assertTrue(foo.getBar().isGoldCustomer()); + } + + 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 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 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 d4a6980..f336d75 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 @@ -17,6 +17,7 @@ package org.apache.camel.support; import java.lang.reflect.Method; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; @@ -46,6 +47,20 @@ public final class PropertyBindingSupport { private PropertyBindingSupport() { } + @FunctionalInterface + public interface OnAutowiring { + + /** + * Callback when a property was autowired on a bean + * + * @param target the targeted bean + * @param propertyName the name of the property + * @param propertyType the type of the property + * @param value the property value + */ + void onAutowire(Object target, String propertyName, Class propertyType, Object value); + + } /** * This will discover all the properties on the target, and automatic bind the properties that are null by * looking up in the registry to see if there is a single instance of the same type as the property. @@ -57,10 +72,65 @@ public final class PropertyBindingSupport { * @return true if one ore more properties was auto wired */ public static boolean autowireSingletonPropertiesFromRegistry(CamelContext camelContext, Object target) { - // TODO: implement me + return autowireSingletonPropertiesFromRegistry(camelContext, target, false,null); + } + + /** + * This will discover all the properties on the target, and automatic bind the properties by + * looking up in the registry to see if there is a single instance of the same type as the property. + * This is used for convention over configuration to automatic configure resources such as DataSource, Amazon Logins and + * so on. + * + * @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 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) { + try { + if (target != null) { + return doAutowireSingletonPropertiesFromRegistry(camelContext, target, bindNullOnly, callback); + } + } catch (Exception e) { + throw new PropertyBindingException(target, e); + } + return false; } + private static boolean doAutowireSingletonPropertiesFromRegistry(CamelContext camelContext, Object target, boolean bindNullOnly, 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<>(); + IntrospectionSupport.getProperties(target, properties, null); + + boolean hit = false; + + for (Map.Entry<String, Object> entry : properties.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + Class<?> type = getGetterType(target, key); + if (isComplexUserType(type)) { + // if the property has not been set and its a complex type (not simple or string etc) + if (!bindNullOnly || value == null) { + Set lookup = camelContext.getRegistry().findByType(type); + if (lookup.size() == 1) { + value = lookup.iterator().next(); + if (value != null) { + hit |= IntrospectionSupport.setProperty(camelContext, target, key, value); + if (hit && callback != null) { + callback.onAutowire(target, key, type, value); + } + } + } + } + } + }; + + return hit; + } + /** * Binds the properties to the target object. * @@ -222,4 +292,21 @@ public final class PropertyBindingSupport { return null; } + private static Class getGetterType(Object target, String name) { + try { + Method getter = IntrospectionSupport.getPropertyGetter(target.getClass(), name); + if (getter != null) { + return getter.getReturnType(); + } + } catch (NoSuchMethodException e) { + // ignore + } + return null; + } + + private static boolean isComplexUserType(Class type) { + // lets consider all non java, as complex types + return type != null && !type.isPrimitive() && !type.getName().startsWith("java"); + } + }