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/11/11 18:33:39 UTC

[camel] branch master updated (7c7a983 -> 074ca0f)

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 7c7a983  Updating plexus utils
     new dad2e1f  CAMEL-14153: Add support for constructor parameters to property binding support - eg for camel-main configurations.
     new 074ca0f  camel-bean: Should assume Boolean/boolean parameter types are matching.

The 2 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:
 .../org/apache/camel/component/bean/BeanInfo.java  |   6 +
 .../java/org/apache/camel/support}/Animal.java     |  37 ++-
 .../camel/support/PropertyBindingSupportTest.java  |  34 +++
 .../camel/support/PropertyBindingSupport.java      | 258 +++++++++++++++++----
 4 files changed, 264 insertions(+), 71 deletions(-)
 copy {components/camel-groovy/src/test/java/org/apache/camel/language/groovy => core/camel-core/src/test/java/org/apache/camel/support}/Animal.java (68%)


[camel] 02/02: camel-bean: Should assume Boolean/boolean parameter types are matching.

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 074ca0fd652e69394d2db9e57065df413a6abca8
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Nov 11 19:21:09 2019 +0100

    camel-bean: Should assume Boolean/boolean parameter types are matching.
---
 .../src/main/java/org/apache/camel/component/bean/BeanInfo.java     | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/components/camel-bean/src/main/java/org/apache/camel/component/bean/BeanInfo.java b/components/camel-bean/src/main/java/org/apache/camel/component/bean/BeanInfo.java
index bd0f8cb..4cd5478 100644
--- a/components/camel-bean/src/main/java/org/apache/camel/component/bean/BeanInfo.java
+++ b/components/camel-bean/src/main/java/org/apache/camel/component/bean/BeanInfo.java
@@ -738,6 +738,12 @@ public class BeanInfo {
                 return true;
             }
         }
+        if (Boolean.class.equals(parameterType)) {
+            // boolean should match both Boolean and boolean
+            if (Boolean.class.isAssignableFrom(expectedType) || boolean.class.isAssignableFrom(expectedType)) {
+                return true;
+            }
+        }
         return parameterType.isAssignableFrom(expectedType);
     }
 


[camel] 01/02: CAMEL-14153: Add support for constructor parameters to property binding support - eg for camel-main configurations.

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 dad2e1f705acc816b933cb4c808403df9bdab575
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Nov 11 19:19:26 2019 +0100

    CAMEL-14153: Add support for constructor parameters to property binding support - eg for camel-main configurations.
---
 .../test/java/org/apache/camel/support/Animal.java |  50 ++++
 .../camel/support/PropertyBindingSupportTest.java  |  34 +++
 .../camel/support/PropertyBindingSupport.java      | 258 +++++++++++++++++----
 3 files changed, 293 insertions(+), 49 deletions(-)

diff --git a/core/camel-core/src/test/java/org/apache/camel/support/Animal.java b/core/camel-core/src/test/java/org/apache/camel/support/Animal.java
new file mode 100644
index 0000000..73ada4b
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/support/Animal.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+public class Animal {
+    private String name;
+    private boolean dangerous;
+
+    public Animal() {
+    }
+
+    public Animal(String name) {
+        this.name = name;
+    }
+
+    public Animal(boolean dangerous, int foo) {
+        throw new IllegalArgumentException("Should not be invoked");
+    }
+
+    public Animal(String name, boolean dangerous) {
+        this.name = name;
+        this.dangerous = dangerous;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public boolean isDangerous() {
+        return dangerous;
+    }
+
+    public void setDangerous(boolean dangerous) {
+        this.dangerous = dangerous;
+    }
+}
diff --git a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java
index eb995d7..f0b2bec 100644
--- a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java
@@ -346,9 +346,35 @@ public class PropertyBindingSupportTest extends ContextTestSupport {
         }
     }
 
+    @Test
+    public void testNestedClassConstructorParameterOneParameter() throws Exception {
+        Foo foo = new Foo();
+
+        PropertyBindingSupport.build().bind(context, foo, "name", "James");
+        PropertyBindingSupport.build().bind(context, foo, "animal", "#class:org.apache.camel.support.Animal('Tony Tiger')");
+        PropertyBindingSupport.build().bind(context, foo, "animal.dangerous", "true");
+
+        assertEquals("James", foo.getName());
+        assertEquals("Tony Tiger", foo.getAnimal().getName());
+        assertEquals(true, foo.getAnimal().isDangerous());
+    }
+
+    @Test
+    public void testNestedClassConstructorParameterTwoParameter() throws Exception {
+        Foo foo = new Foo();
+
+        PropertyBindingSupport.build().bind(context, foo, "name", "James");
+        PropertyBindingSupport.build().bind(context, foo, "animal", "#class:org.apache.camel.support.Animal('Donald Duck', false)");
+
+        assertEquals("James", foo.getName());
+        assertEquals("Donald Duck", foo.getAnimal().getName());
+        assertEquals(false, foo.getAnimal().isDangerous());
+    }
+
     public static class Foo {
         private String name;
         private Bar bar = new Bar();
+        private Animal animal;
 
         public String getName() {
             return name;
@@ -365,6 +391,14 @@ public class PropertyBindingSupportTest extends ContextTestSupport {
         public void setBar(Bar bar) {
             this.bar = bar;
         }
+
+        public Animal getAnimal() {
+            return animal;
+        }
+
+        public void setAnimal(Animal animal) {
+            this.animal = animal;
+        }
     }
 
     public static class Bar {
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 f88621e..ce63ff1 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
@@ -16,7 +16,9 @@
  */
 package org.apache.camel.support;
 
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -32,6 +34,7 @@ import org.apache.camel.PropertyBindingException;
 import org.apache.camel.spi.GeneratedPropertyConfigurer;
 import org.apache.camel.spi.PropertyConfigurer;
 import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.StringQuoteHelper;
 
 import static org.apache.camel.util.ObjectHelper.isNotEmpty;
 
@@ -49,7 +52,9 @@ import static org.apache.camel.util.ObjectHelper.isNotEmpty;
  *     <li>autowire by type - Values can refer to singleton beans by auto wiring by setting the value to #autowired</li>
  *     <li>reference new class - Values can refer to creating new beans by their class name by prefixing with #class, eg #class:com.foo.MyClassType.
  *                               The class is created using a default no-arg constructor, however if you need to create the instance via a factory method
- *                               then you specify the method as shown: #class:com.foo.MyClassType#myFactoryMethod</li>.
+ *                               then you specify the method as shown: #class:com.foo.MyClassType#myFactoryMethod.
+ *                               Or if you need to create the instance via constructor parameters then you can specify the parameters as shown:
+ *                               #class:com.foo.MyClass('Hello World', 5, true)</li>.
  *     <li>ignore case - Whether to ignore case for property keys<li>
  * </ul>
  */
@@ -211,7 +216,7 @@ public final class PropertyBindingSupport {
         /**
          * Binds the properties to the target object, and removes the property that was bound from properties.
          *
-         * @return  true if one or more properties was bound
+         * @return true if one or more properties was bound
          */
         public boolean bind() {
             // mandatory parameters
@@ -226,10 +231,10 @@ public final class PropertyBindingSupport {
         /**
          * Binds the properties to the target object, and removes the property that was bound from properties.
          *
-         * @param camelContext  the camel context
-         * @param target        the target object
-         * @param properties    the properties where the bound properties will be removed from
-         * @return              true if one or more properties was bound
+         * @param camelContext the camel context
+         * @param target       the target object
+         * @param properties   the properties where the bound properties will be removed from
+         * @return true if one or more properties was bound
          */
         public boolean bind(CamelContext camelContext, Object target, Map<String, Object> properties) {
             CamelContext context = camelContext != null ? camelContext : this.camelContext;
@@ -248,11 +253,11 @@ public final class PropertyBindingSupport {
         /**
          * Binds the property to the target object.
          *
-         * @param camelContext  the camel context
-         * @param target        the target object
-         * @param key           the property key
-         * @param value         the property value
-         * @return              true if the property was bound
+         * @param camelContext the camel context
+         * @param target       the target object
+         * @param key          the property key
+         * @param value        the property value
+         * @return true if the property was bound
          */
         public boolean bind(CamelContext camelContext, Object target, String key, Object value) {
             org.apache.camel.util.ObjectHelper.notNull(camelContext, "camelContext");
@@ -282,10 +287,10 @@ public final class PropertyBindingSupport {
         /**
          * 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
+         * @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);
 
@@ -297,9 +302,9 @@ public final class PropertyBindingSupport {
      * 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
-     * @return              true if one ore more properties was auto wired
+     * @param camelContext the camel context
+     * @param target       the target object
+     * @return true if one ore more properties was auto wired
      */
     public static boolean autowireSingletonPropertiesFromRegistry(CamelContext camelContext, Object target) {
         return autowireSingletonPropertiesFromRegistry(camelContext, target, false, false, null);
@@ -311,14 +316,14 @@ public final class PropertyBindingSupport {
      * 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 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
+     * @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, boolean deepNesting, OnAutowiring callback) {
@@ -422,11 +427,10 @@ public final class PropertyBindingSupport {
      * the fluent builder {@link #build()} where each option can be customized, such as whether parameter
      * should be removed, or whether options are mandatory etc.
      *
-     * @param camelContext  the camel context
-     * @param target        the target object
-     * @param properties    the properties where the bound properties will be removed from
-     * @return              true if one or more properties was bound
-     *
+     * @param camelContext the camel context
+     * @param target       the target object
+     * @param properties   the properties where the bound properties will be removed from
+     * @return true if one or more properties was bound
      * @see #build()
      */
     public static boolean bindProperties(CamelContext camelContext, Object target, Map<String, Object> properties) {
@@ -442,22 +446,22 @@ public final class PropertyBindingSupport {
      * Binds the properties with the given prefix to the target object, and removes the property that was bound from properties.
      * Note that the prefix is removed from the key before the property is bound.
      *
-     * @param camelContext        the camel context
-     * @param target              the target object
-     * @param properties          the properties where the bound properties will be removed from
-     * @param optionPrefix        the prefix used to filter properties
-     * @param ignoreCase          whether to ignore case for property keys
-     * @param removeParameter     whether to remove bound parameters
-     * @param mandatory           whether all parameters must be bound
-     * @param nesting             whether nesting is in use
-     * @param deepNesting         whether deep nesting is in use, where Camel will attempt to walk as deep as possible by creating new objects in the OGNL graph if
-     *                            a property has a setter and the object can be created from a default no-arg constructor.
-     * @param fluentBuilder       whether fluent builder is allowed as a valid getter/setter
-     * @param allowPrivateSetter  whether autowiring components allows to use private setter method when setting the value
-     * @param reference           whether reference parameter (syntax starts with #) is in use
-     * @param placeholder         whether to use Camels property placeholder to resolve placeholders on keys and values
-     * @param configurer          to use an optional {@link org.apache.camel.spi.PropertyConfigurer} to configure the properties
-     * @return                    true if one or more properties was bound
+     * @param camelContext       the camel context
+     * @param target             the target object
+     * @param properties         the properties where the bound properties will be removed from
+     * @param optionPrefix       the prefix used to filter properties
+     * @param ignoreCase         whether to ignore case for property keys
+     * @param removeParameter    whether to remove bound parameters
+     * @param mandatory          whether all parameters must be bound
+     * @param nesting            whether nesting is in use
+     * @param deepNesting        whether deep nesting is in use, where Camel will attempt to walk as deep as possible by creating new objects in the OGNL graph if
+     *                           a property has a setter and the object can be created from a default no-arg constructor.
+     * @param fluentBuilder      whether fluent builder is allowed as a valid getter/setter
+     * @param allowPrivateSetter whether autowiring components allows to use private setter method when setting the value
+     * @param reference          whether reference parameter (syntax starts with #) is in use
+     * @param placeholder        whether to use Camels property placeholder to resolve placeholders on keys and values
+     * @param configurer         to use an optional {@link org.apache.camel.spi.PropertyConfigurer} to configure the properties
+     * @return true if one or more properties was bound
      */
     private static boolean doBindProperties(CamelContext camelContext, Object target, Map<String, Object> properties,
                                             String optionPrefix, boolean ignoreCase, boolean removeParameter, boolean mandatory,
@@ -486,7 +490,7 @@ public final class PropertyBindingSupport {
                     // not resolve property placeholders eventually defined in the value before invoking
                     // the setter.
                     if (value instanceof String) {
-                        value = camelContext.resolvePropertyPlaceholders((String)value);
+                        value = camelContext.resolvePropertyPlaceholders((String) value);
                     }
                     try {
                         value = resolveValue(camelContext, target, key, value, ignoreCase, fluentBuilder, allowPrivateSetter);
@@ -559,13 +563,22 @@ public final class PropertyBindingSupport {
                 // its a new class to be created
                 String className = value.toString().substring(7);
                 String factoryMethod = null;
-                if (className.indexOf('#') != -1) {
+                String parameters = null;
+                if (className.endsWith(")") && className.indexOf('(') != -1) {
+                    parameters = StringHelper.after(className, "(");
+                    parameters = parameters.substring(0, parameters.length() - 1); // clip last )
+                    className = StringHelper.before(className, "(");
+                }
+                if (className != null && className.indexOf('#') != -1) {
                     factoryMethod = StringHelper.after(className, "#");
                     className = StringHelper.before(className, "#");
                 }
                 Class<?> type = context.getClassResolver().resolveMandatoryClass(className);
                 if (factoryMethod != null) {
                     value = context.getInjector().newInstance(type, factoryMethod);
+                } else if (parameters != null) {
+                    // special to support constructor parameters
+                    value = newInstanceConstructorParameters(context, type, parameters);
                 } else {
                     value = context.getInjector().newInstance(type);
                 }
@@ -800,5 +813,152 @@ public final class PropertyBindingSupport {
         return parameter != null && parameter.trim().startsWith("#");
     }
 
+    private static Object newInstanceConstructorParameters(CamelContext camelContext, Class<?> type, String parameters) throws Exception {
+        String[] params = StringQuoteHelper.splitSafeQuote(parameters, ',');
+        Constructor found = findMatchingConstructor(type.getConstructors(), params);
+        if (found != null) {
+            Object[] arr = new Object[found.getParameterCount()];
+            for (int i = 0; i < found.getParameterCount(); i++) {
+                Class<?> paramType = found.getParameterTypes()[i];
+                Object param = params[i];
+                Object val = camelContext.getTypeConverter().convertTo(paramType, param);
+                // unquote text
+                if (val instanceof String) {
+                    val = StringHelper.removeLeadingAndEndingQuotes((String) val);
+                }
+                arr[i] = val;
+            }
+            return found.newInstance(arr);
+        }
+        return null;
+    }
+
+    /**
+     * Finds the best matching constructor for the given parameters.
+     * <p/>
+     * This implementation is similar to the logic in camel-bean.
+     *
+     * @param constructors the constructors
+     * @param params       the parameters
+     * @return the constructor, or null if no matching constructor can be found
+     */
+    private static Constructor findMatchingConstructor(Constructor<?>[] constructors, String[] params) {
+        List<Constructor> candidates = new ArrayList<>();
+        Constructor fallbackCandidate = null;
+
+        for (Constructor ctr : constructors) {
+            if (ctr.getParameterCount() != params.length) {
+                continue;
+            }
+
+            boolean matches = true;
+            for (int i = 0; i < ctr.getParameterCount(); i++) {
+                String parameter = params[i];
+                if (parameter != null) {
+                    // must trim
+                    parameter = parameter.trim();
+                }
+
+                Class<?> parameterType = getValidParameterType(parameter);
+                Class<?> expectedType = ctr.getParameterTypes()[i];
+
+                if (parameterType != null && expectedType != null) {
+                    // skip java.lang.Object type, when we have multiple possible methods we want to avoid it if possible
+                    if (Object.class.equals(expectedType)) {
+                        fallbackCandidate = ctr;
+                        matches = false;
+                        break;
+                    }
+
+                    boolean matchingTypes = isParameterMatchingType(parameterType, expectedType);
+                    if (!matchingTypes) {
+                        matches = false;
+                        break;
+                    }
+                }
+            }
+
+            if (matches) {
+                candidates.add(ctr);
+            }
+        }
+
+        return candidates.size() == 1 ? candidates.get(0) : fallbackCandidate;
+    }
+
+    /**
+     * Determines and maps the given value is valid according to the supported
+     * values by the bean component.
+     * <p/>
+     * This implementation is similar to the logic in camel-bean.
+     *
+     * @param value the value
+     * @return the parameter type the given value is being mapped as, or <tt>null</tt> if not valid.
+     */
+    private static Class<?> getValidParameterType(String value) {
+        if (org.apache.camel.util.ObjectHelper.isEmpty(value)) {
+            return null;
+        }
+
+        // trim value
+        value = value.trim();
+
+        // single quoted is valid
+        if (value.startsWith("'") && value.endsWith("'")) {
+            return String.class;
+        }
+
+        // double quoted is valid
+        if (value.startsWith("\"") && value.endsWith("\"")) {
+            return String.class;
+        }
+
+        // true or false is valid (boolean)
+        if (value.equals("true") || value.equals("false")) {
+            return Boolean.class;
+        }
+
+        // null is valid (to force a null value)
+        if (value.equals("null")) {
+            return Object.class;
+        }
+
+        // simple language tokens is valid
+        if (StringHelper.hasStartToken(value, "simple")) {
+            return Object.class;
+        }
+
+        // numeric is valid
+        boolean numeric = true;
+        for (char ch : value.toCharArray()) {
+            if (!Character.isDigit(ch)) {
+                numeric = false;
+                break;
+            }
+        }
+        if (numeric) {
+            return Number.class;
+        }
+
+        // not valid
+        return null;
+    }
+
+    private static boolean isParameterMatchingType(Class<?> parameterType, Class<?> expectedType) {
+        if (Number.class.equals(parameterType)) {
+            // number should match long/int/etc.
+            if (Integer.class.isAssignableFrom(expectedType) || Long.class.isAssignableFrom(expectedType)
+                    || int.class.isAssignableFrom(expectedType) || long.class.isAssignableFrom(expectedType)) {
+                return true;
+            }
+        }
+        if (Boolean.class.equals(parameterType)) {
+            // boolean should match both Boolean and boolean
+            if (Boolean.class.isAssignableFrom(expectedType) || boolean.class.isAssignableFrom(expectedType)) {
+                return true;
+            }
+        }
+        return parameterType.isAssignableFrom(expectedType);
+    }
 
 }