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/05/24 10:12:41 UTC
[camel] 01/27: CAMEL-13557: Add property binding support to make it
convenient to configure components and whatnot.
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 f2e24c802005649520e6971b776caa22643b6a8a
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed May 22 15:47:57 2019 +0200
CAMEL-13557: Add property binding support to make it convenient to configure components and whatnot.
---
.../java/org/apache/camel/main/MainSupport.java | 31 +----
.../camel/support/PropertyBindingSupportTest.java | 146 +++++++++++++++++++++
.../apache/camel/support/IntrospectionSupport.java | 58 +++++++-
.../camel/support/PropertyBindingSupport.java | 47 +++++++
4 files changed, 254 insertions(+), 28 deletions(-)
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 0f3570e..fedb2cd 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
@@ -81,10 +81,9 @@ 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.DefaultExchange;
-import org.apache.camel.support.EndpointHelper;
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;
import org.apache.camel.support.service.ServiceHelper;
import org.apache.camel.support.service.ServiceSupport;
@@ -1261,6 +1260,8 @@ public abstract class MainSupport extends ServiceSupport {
Map<String, Object> properties = new LinkedHashMap<>();
IntrospectionSupport.getProperties(component, properties, null);
+ // TODO: Use PropertyBindingSupport to make it support this kind of use-case too
+
// lookup complex types
properties.forEach((k, v) -> {
// if the property has not been set and its a complex type (not simple or string etc)
@@ -1343,34 +1344,10 @@ public abstract class MainSupport extends ServiceSupport {
String name = entry.getKey();
Object value = entry.getValue();
- // if the name has dot's then its an OGNL expressions (so lets use simple language to walk down this ognl path)
- boolean ognl = name.contains(".");
- if (ognl) {
- Language method = context.resolveLanguage("simple");
- String path = name.substring(0, name.lastIndexOf('.'));
- Expression exp = method.createExpression("${body." + path + "}");
- Exchange dummy = new DefaultExchange(context);
- dummy.getMessage().setBody(target);
- Object newTarget = exp.evaluate(dummy, Object.class);
- if (newTarget != null) {
- target = newTarget;
- name = name.substring(name.lastIndexOf('.') + 1);
- }
- }
-
String stringValue = value != null ? value.toString() : null;
- boolean hit = false;
LOG.debug("Setting property {} on {} with value {}", name, target, stringValue);
- if (EndpointHelper.isReferenceParameter(stringValue)) {
- hit = IntrospectionSupport.setProperty(context, context.getTypeConverter(), target, name, null, stringValue, true);
- } else if (value != null) {
- try {
- hit = IntrospectionSupport.setProperty(context, context.getTypeConverter(), target, name, value);
- } catch (IllegalArgumentException var12) {
- hit = IntrospectionSupport.setProperty(context, context.getTypeConverter(), target, name, null, stringValue, true);
- }
- }
+ boolean hit = PropertyBindingSupport.bindProperty(context, target, name, stringValue);
if (hit) {
it.remove();
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
new file mode 100644
index 0000000..c55745f
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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 with nested properties
+ */
+public class PropertyBindingSupportTest 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 testNested() throws Exception {
+ Foo foo = new Foo();
+
+ PropertyBindingSupport.bindProperty(context, foo, "name", "James");
+ PropertyBindingSupport.bindProperty(context, foo, "bar.age", "33");
+ PropertyBindingSupport.bindProperty(context, foo, "bar.rider", "true");
+ PropertyBindingSupport.bindProperty(context, foo, "bar.work.id", "123");
+ PropertyBindingSupport.bindProperty(context, foo, "bar.work.name", "Acme");
+
+ assertEquals("James", foo.getName());
+ assertEquals(33, foo.getBar().getAge());
+ assertTrue(foo.getBar().isRider());
+ assertEquals(123, foo.getBar().getWork().getId());
+ assertEquals("Acme", foo.getBar().getWork().getName());
+ }
+
+ @Test
+ public void testNestedReference() throws Exception {
+ Foo foo = new Foo();
+
+ PropertyBindingSupport.bindProperty(context, foo, "name", "James");
+ PropertyBindingSupport.bindProperty(context, foo, "bar.age", "33");
+ PropertyBindingSupport.bindProperty(context, foo, "bar.rider", "true");
+ PropertyBindingSupport.bindProperty(context, foo, "bar.work", "#myWork");
+
+ assertEquals("James", foo.getName());
+ assertEquals(33, foo.getBar().getAge());
+ assertTrue(foo.getBar().isRider());
+ assertEquals(456, foo.getBar().getWork().getId());
+ assertEquals("Acme", foo.getBar().getWork().getName());
+ }
+
+ public static class Foo {
+ private String name;
+ private Bar bar = new 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; // has no default value but Camel can automatic create one if there is a setter
+
+ 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 static class Company {
+ private int id;
+ private String name;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+
+}
+
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/IntrospectionSupport.java b/core/camel-support/src/main/java/org/apache/camel/support/IntrospectionSupport.java
index 55f2612..28a1e3f 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/IntrospectionSupport.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/IntrospectionSupport.java
@@ -513,12 +513,68 @@ public final class IntrospectionSupport {
* 2. Setting a property that has not yet been resolved, the property will be resolved based on the suitable methods
* found matching the property name on the {@code target} bean. For this mode to be triggered the parameters
* {@code context} and {@code refName} must NOT be NULL, and {@code value} MUST be NULL.
+ */
+ public static boolean setProperty(CamelContext context, TypeConverter typeConverter, Object target, String name, Object value, String refName,
+ boolean allowBuilderPattern) throws Exception {
+ return setProperty(context, typeConverter, target, name, value, refName, allowBuilderPattern, false);
+ }
+ /**
+ * This method supports two modes to set a property:
+ *
+ * 1. Setting a property that has already been resolved, this is the case when {@code context} and {@code refName} are
+ * NULL and {@code value} is non-NULL.
*
+ * 2. Setting a property that has not yet been resolved, the property will be resolved based on the suitable methods
+ * found matching the property name on the {@code target} bean. For this mode to be triggered the parameters
+ * {@code context} and {@code refName} must NOT be NULL, and {@code value} MUST be NULL.
*/
- public static boolean setProperty(CamelContext context, TypeConverter typeConverter, Object target, String name, Object value, String refName, boolean allowBuilderPattern) throws Exception {
+ public static boolean setProperty(CamelContext context, TypeConverter typeConverter, Object target, String name, Object value, String refName,
+ boolean allowBuilderPattern, boolean allowNestedProperties) throws Exception {
Class<?> clazz = target.getClass();
Collection<Method> setters;
+ // if name has dot then we need to OGNL walk it
+ if (allowNestedProperties && name.indexOf('.') > 0) {
+ String[] parts = name.split("\\.");
+ Object newTarget = target;
+ Class<?> newClass = clazz;
+ // we should only iterate until until 2nd last so we use -1 in the for loop
+ for (int i = 0; i < parts.length - 1; i++) {
+ String part = parts[i];
+ Object prop = getOrElseProperty(newTarget, part, null);
+ if (prop == null) {
+ // okay is there a setter so we can create a new instance and set it automatic
+ Set<Method> newSetters = findSetterMethods(newClass, part, true);
+ if (newSetters.size() == 1) {
+ Method method = newSetters.iterator().next();
+ Class<?> parameterType = method.getParameterTypes()[0];
+ if (parameterType != null && org.apache.camel.util.ObjectHelper.hasDefaultPublicNoArgConstructor(parameterType)) {
+ Object instance = context.getInjector().newInstance(parameterType);
+ if (instance != null) {
+ org.apache.camel.support.ObjectHelper.invokeMethod(method, newTarget, instance);
+ newTarget = instance;
+ newClass = newTarget.getClass();
+ }
+ }
+ }
+ } else {
+ newTarget = prop;
+ newClass = newTarget.getClass();
+ }
+ }
+ // okay we found a nested property, then lets change to use that
+ target = newTarget;
+ clazz = newTarget.getClass();
+ name = parts[parts.length - 1];
+ if (value instanceof String) {
+ if (EndpointHelper.isReferenceParameter(value.toString())) {
+ // okay its a reference so swap to lookup this
+ refName = value.toString();
+ value = null;
+ }
+ }
+ }
+
// we need to lookup the value from the registry
if (context != null && refName != null && value == null) {
setters = findSetterMethodsOrderedByParameterType(clazz, name, allowBuilderPattern);
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
new file mode 100644
index 0000000..b9d98bf
--- /dev/null
+++ b/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
@@ -0,0 +1,47 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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 java.util.Map;
+
+/**
+ * A convenient support class for binding String valued properties to an instance which
+ * uses a set of conventions:
+ * <ul>
+ * <li>nested - Properties can be nested using the dot syntax (OGNL)</li>
+ * <li>reference by id - Values can refer to other beans by their id using # syntax</li>
+ * </ul>
+ */
+public final class PropertyBindingSupport {
+
+ private PropertyBindingSupport() {
+ }
+
+ public static boolean bindProperties(CamelContext camelContext, Object target, Map<String, Object> properties) throws Exception {
+ boolean answer = true;
+ for (Map.Entry<String, Object> entry : properties.entrySet()) {
+ answer &= bindProperty(camelContext, target, entry.getKey(), entry.getValue());
+ }
+ return answer;
+ }
+
+ public static boolean bindProperty(CamelContext camelContext, Object target, String name, Object value) throws Exception {
+ return IntrospectionSupport.setProperty(camelContext, camelContext.getTypeConverter(), target, name, value, null, true, true);
+ }
+}