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