Repository: camel
Updated Branches:
  refs/heads/master 443f6b769 -> 5db786a28


CAMEL-11718: camel-spring-boot auto configuration should support bean reference 
lookup to make it easy to configure in application.properties|yaml file.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/d5a362b8
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/d5a362b8
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/d5a362b8

Branch: refs/heads/master
Commit: d5a362b809a14a9e35393e8efb2c1f4e318142cc
Parents: 443f6b7
Author: Claus Ibsen <[email protected]>
Authored: Mon Sep 4 16:20:59 2017 +0200
Committer: Claus Ibsen <[email protected]>
Committed: Mon Sep 4 16:58:19 2017 +0200

----------------------------------------------------------------------
 .../spring/boot/util/CamelPropertiesHelper.java |  95 +++++++++++
 .../boot/util/CamelPropertiesHelperTest.java    | 170 +++++++++++++++++++
 .../SpringBootAutoConfigurationMojo.java        |  22 ++-
 3 files changed, 282 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/d5a362b8/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/util/CamelPropertiesHelper.java
----------------------------------------------------------------------
diff --git 
a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/util/CamelPropertiesHelper.java
 
b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/util/CamelPropertiesHelper.java
new file mode 100644
index 0000000..ddfb090
--- /dev/null
+++ 
b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/util/CamelPropertiesHelper.java
@@ -0,0 +1,95 @@
+/**
+ * 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.spring.boot.util;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.util.IntrospectionSupport;
+import org.apache.camel.util.ObjectHelper;
+
+import static org.apache.camel.util.EndpointHelper.isReferenceParameter;
+
+/**
+ * To help configuring Camel properties that have been defined in Spring Boot 
configuration files.
+ */
+public final class CamelPropertiesHelper {
+
+    private CamelPropertiesHelper() {
+    }
+
+    /**
+     * Sets the properties on the target bean.
+     * <p/>
+     * This implementation sets the properties using the following algorithm:
+     * <ul>
+     *     <li>Value as reference lookup - If the value uses Camel reference 
syntax, eg #beanId then the bean is looked up from Registry and set on the 
target</li>
+     *     <li>Value as-is - The value is attempted to be converted to the 
class type of the bean setter method; this is for regular types like String, 
numbers etc</li>
+     *     <li>Value as lookup - the bean is looked up from Registry and if 
there is a bean then its set on the target</li>
+     * </ul>
+     * When an option has been set on the target bean, then its removed from 
the given properties map. If all the options has been set, then the map will be 
empty.
+     *
+     * @param context        the CamelContext
+     * @param target         the target bean
+     * @param properties     the properties
+     * @param failOnNotSet   whether to fail if an option cannot be set on the 
target bean.
+     * @return <tt>true</tt> if at least one option was configured
+     * @throws IllegalArgumentException is thrown if an option cannot be 
configured on the bean because there is no suitable setter method and 
failOnNoSet is true.
+     * @throws Exception for any other kind of error
+     */
+    public static boolean setCamelProperties(CamelContext context, Object 
target, Map<String, Object> properties, boolean failOnNotSet) throws Exception {
+        ObjectHelper.notNull(context, "context");
+        ObjectHelper.notNull(target, "target");
+        ObjectHelper.notNull(properties, "properties");
+        boolean rc = false;
+
+        Iterator<Map.Entry<String, Object>> it = 
properties.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<String, Object> entry = it.next();
+            String name = entry.getKey();
+            Object value = entry.getValue();
+            String stringValue = value != null ? value.toString() : null;
+            boolean hit = false;
+            if (stringValue != null && isReferenceParameter(stringValue)) {
+                // its a #beanId reference lookup
+                hit = IntrospectionSupport.setProperty(context, 
context.getTypeConverter(), target, name, null, stringValue, true);
+            } else if (value != null) {
+                // its a value to be used as-is (or type converted)
+                try {
+                    hit = IntrospectionSupport.setProperty(context, 
context.getTypeConverter(), target, name, value);
+                } catch (IllegalArgumentException e) {
+                    // no we could not and this would be thrown if we 
attempted to set a value on a property which we cannot do type conversion as
+                    // then maybe the value refers to a spring bean in the 
registry so try this
+                    hit = IntrospectionSupport.setProperty(context, 
context.getTypeConverter(), target, name, null, stringValue, true);
+                }
+            }
+
+            if (hit) {
+                // must remove as its a valid option and we could configure it
+                it.remove();
+                rc = true;
+            } else if (failOnNotSet) {
+                throw new IllegalArgumentException("Cannot configure option [" 
+ name + "] with value [" + stringValue
+                    + "] as the bean class [" + 
ObjectHelper.classCanonicalName(target)
+                    + "] has no suitable setter method, or not possible to 
lookup a bean with the id [" + stringValue + "] in Spring Boot registry");
+            }
+        }
+
+        return rc;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d5a362b8/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/util/CamelPropertiesHelperTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/util/CamelPropertiesHelperTest.java
 
b/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/util/CamelPropertiesHelperTest.java
new file mode 100644
index 0000000..9af219f
--- /dev/null
+++ 
b/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/util/CamelPropertiesHelperTest.java
@@ -0,0 +1,170 @@
+/**
+ * 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.spring.boot.util;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@DirtiesContext
+@SpringBootApplication
+@SpringBootTest(
+    classes = {
+        CamelPropertiesHelperTest.TestConfiguration.class
+    }
+)
+public class CamelPropertiesHelperTest {
+
+    @Autowired
+    ApplicationContext context;
+
+    @Autowired
+    CamelContext camelContext;
+
+    @Configuration
+    static class TestConfiguration {
+        @Bean(name = "myCoolOption")
+        MyOption myCoolBean() {
+            return new MyOption();
+        }
+    }
+
+    static class MyOption {
+    }
+
+    static class MyClass {
+
+        private int id;
+        private String name;
+        private MyOption option;
+
+        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;
+        }
+
+        public MyOption getOption() {
+            return option;
+        }
+
+        public void setOption(MyOption option) {
+            this.option = option;
+        }
+    }
+
+    @Test
+    public void testSetCamelProperties() throws Exception {
+        MyClass target = new MyClass();
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("id", "123");
+        map.put("name", "Donald Duck");
+        map.put("option", "myCoolOption");
+
+        CamelPropertiesHelper.setCamelProperties(camelContext, target, map, 
true);
+
+        Assert.assertEquals("Should configure all options", 0, map.size());
+        Assert.assertEquals(123, target.getId());
+        Assert.assertEquals("Donald Duck", target.getName());
+        Assert.assertSame(context.getBean("myCoolOption"), target.getOption());
+    }
+
+    @Test
+    public void testSetCamelPropertiesReference() throws Exception {
+        MyClass target = new MyClass();
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("id", "123");
+        map.put("name", "Donald Duck");
+        map.put("option", "#myCoolOption");
+
+        CamelPropertiesHelper.setCamelProperties(camelContext, target, map, 
true);
+
+        Assert.assertEquals("Should configure all options", 0, map.size());
+        Assert.assertEquals(123, target.getId());
+        Assert.assertEquals("Donald Duck", target.getName());
+        Assert.assertSame(context.getBean("myCoolOption"), target.getOption());
+    }
+
+    @Test
+    public void testSetCamelPropertiesUnknownOption() throws Exception {
+        MyClass target = new MyClass();
+
+        Map<String, Object> map = new LinkedHashMap<>();
+        map.put("id", "123");
+        map.put("name", "Donald Duck");
+        map.put("option", "#myCoolOption");
+        map.put("unknown", "foo");
+
+        try {
+            CamelPropertiesHelper.setCamelProperties(camelContext, target, 
map, true);
+            Assert.fail("Should have thrown exception");
+        } catch (IllegalArgumentException e) {
+            // expected
+            Assert.assertTrue(e.getMessage().startsWith("Cannot configure 
option [unknown] with value [foo]"));
+        }
+
+        Assert.assertEquals("Should configure the three first options", 1, 
map.size());
+        Assert.assertEquals(123, target.getId());
+        Assert.assertEquals("Donald Duck", target.getName());
+        Assert.assertSame(context.getBean("myCoolOption"), target.getOption());
+    }
+
+    @Test
+    public void testSetCamelPropertiesUnknownOptionIgnore() throws Exception {
+        MyClass target = new MyClass();
+
+        Map<String, Object> map = new LinkedHashMap<>();
+        map.put("id", "123");
+        map.put("name", "Donald Duck");
+        map.put("option", "#myCoolOption");
+        map.put("unknown", "foo");
+
+        CamelPropertiesHelper.setCamelProperties(camelContext, target, map, 
false);
+
+        Assert.assertEquals("Should configure the three first options", 1, 
map.size());
+        Assert.assertEquals(123, target.getId());
+        Assert.assertEquals("Donald Duck", target.getName());
+        Assert.assertSame(context.getBean("myCoolOption"), target.getOption());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d5a362b8/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootAutoConfigurationMojo.java
----------------------------------------------------------------------
diff --git 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootAutoConfigurationMojo.java
 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootAutoConfigurationMojo.java
index cf01ab9..976010e 100644
--- 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootAutoConfigurationMojo.java
+++ 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootAutoConfigurationMojo.java
@@ -473,6 +473,7 @@ public class SpringBootAutoConfigurationMojo extends 
AbstractMojo {
         javaClass.addImport("java.util.HashMap");
         javaClass.addImport("org.apache.camel.util.CollectionHelper");
         javaClass.addImport("org.apache.camel.util.IntrospectionSupport");
+        
javaClass.addImport("org.apache.camel.spring.boot.util.CamelPropertiesHelper");
         javaClass.addImport("org.apache.camel.CamelContext");
         javaClass.addImport("org.apache.camel.model.rest.RestConstants");
         javaClass.addImport("org.apache.camel.spi.RestConfiguration");
@@ -503,9 +504,17 @@ public class SpringBootAutoConfigurationMojo extends 
AbstractMojo {
         method.setBody(""
             + "Map<String, Object> properties = new HashMap<>();\n"
             + "IntrospectionSupport.getProperties(config, properties, null, 
false);\n"
+            + "// These options is configured specially further below, so 
remove them first\n"
+            + "properties.remove(\"enableCors\");\n"
+            + "properties.remove(\"apiProperty\");\n"
+            + "properties.remove(\"componentProperty\");\n"
+            + "properties.remove(\"consumerProperty\");\n"
+            + "properties.remove(\"dataFormatProperty\");\n"
+            + "properties.remove(\"endpointProperty\");\n"
+            + "properties.remove(\"corsHeaders\");\n"
             + "\n"
             + "RestConfiguration definition = new RestConfiguration();\n"
-            + "IntrospectionSupport.setProperties(camelContext, 
camelContext.getTypeConverter(), definition, properties);\n"
+            + "CamelPropertiesHelper.setCamelProperties(camelContext, 
definition, properties, true);\n"
             + "\n"
             + "// Workaround for spring-boot properties name as It would 
appear\n"
             + "// as enable-c-o-r-s if left uppercase in Configuration\n"
@@ -1228,6 +1237,7 @@ public class SpringBootAutoConfigurationMojo extends 
AbstractMojo {
         javaClass.addImport("org.apache.camel.spi.ComponentCustomizer");
         
javaClass.addImport("org.apache.camel.spring.boot.CamelAutoConfiguration");
         
javaClass.addImport("org.apache.camel.spring.boot.ComponentConfigurationProperties");
+        
javaClass.addImport("org.apache.camel.spring.boot.util.CamelPropertiesHelper");
         
javaClass.addImport("org.apache.camel.spring.boot.util.ConditionalOnCamelContextAndAutoConfigurationBeans");
         
javaClass.addImport("org.apache.camel.spring.boot.util.GroupCondition");
         
javaClass.addImport("org.apache.camel.spring.boot.util.HierarchicalPropertiesEvaluator");
@@ -1335,6 +1345,7 @@ public class SpringBootAutoConfigurationMojo extends 
AbstractMojo {
         javaClass.addImport("org.apache.camel.CamelContextAware");
         
javaClass.addImport("org.apache.camel.spring.boot.CamelAutoConfiguration");
         
javaClass.addImport("org.apache.camel.spring.boot.DataFormatConfigurationProperties");
+        
javaClass.addImport("org.apache.camel.spring.boot.util.CamelPropertiesHelper");
         
javaClass.addImport("org.apache.camel.spring.boot.util.ConditionalOnCamelContextAndAutoConfigurationBeans");
         
javaClass.addImport("org.apache.camel.spring.boot.util.GroupCondition");
         
javaClass.addImport("org.apache.camel.spring.boot.util.HierarchicalPropertiesEvaluator");
@@ -1447,6 +1458,7 @@ public class SpringBootAutoConfigurationMojo extends 
AbstractMojo {
         javaClass.addImport("org.apache.camel.CamelContextAware");
         
javaClass.addImport("org.apache.camel.spring.boot.CamelAutoConfiguration");
         
javaClass.addImport("org.apache.camel.spring.boot.LanguageConfigurationProperties");
+        
javaClass.addImport("org.apache.camel.spring.boot.util.CamelPropertiesHelper");
         
javaClass.addImport("org.apache.camel.spring.boot.util.ConditionalOnCamelContextAndAutoConfigurationBeans");
         
javaClass.addImport("org.apache.camel.spring.boot.util.GroupCondition");
         
javaClass.addImport("org.apache.camel.spring.boot.util.HierarchicalPropertiesEvaluator");
@@ -1565,14 +1577,14 @@ public class SpringBootAutoConfigurationMojo extends 
AbstractMojo {
         sb.append("            HashMap<String, Object> nestedParameters = new 
HashMap<>();\n");
         sb.append("            IntrospectionSupport.getProperties(value, 
nestedParameters, null, false);\n");
         sb.append("            Object nestedProperty = 
nestedClass.newInstance();\n");
-        sb.append("            
IntrospectionSupport.setProperties(camelContext, 
camelContext.getTypeConverter(), nestedProperty, nestedParameters);\n");
+        sb.append("            
CamelPropertiesHelper.setCamelProperties(camelContext, nestedProperty, 
nestedParameters, false);\n");
         sb.append("            entry.setValue(nestedProperty);\n");
         sb.append("        } catch (NoSuchFieldException e) {\n");
         sb.append("            // ignore, class must not be a nested 
configuration class after all\n");
         sb.append("        }\n");
         sb.append("    }\n");
         sb.append("}\n");
-        sb.append("IntrospectionSupport.setProperties(camelContext, 
camelContext.getTypeConverter(), component, parameters);\n");
+        sb.append("CamelPropertiesHelper.setCamelProperties(camelContext, 
component, parameters, false);\n");
         sb.append("\n");
         sb.append("if (ObjectHelper.isNotEmpty(customizers)) {\n");
         sb.append("    for 
(ComponentCustomizer<").append(shortJavaType).append("> customizer : 
customizers) {\n");
@@ -1616,7 +1628,7 @@ public class SpringBootAutoConfigurationMojo extends 
AbstractMojo {
         sb.append("        try {\n");
         sb.append("            Map<String, Object> parameters = new 
HashMap<>();\n");
         sb.append("            
IntrospectionSupport.getProperties(configuration, parameters, null, false);\n");
-        sb.append("            
IntrospectionSupport.setProperties(camelContext, 
camelContext.getTypeConverter(), dataformat, parameters);\n");
+        sb.append("            
CamelPropertiesHelper.setCamelProperties(camelContext, dataformat, parameters, 
false);\n");
         sb.append("        } catch (Exception e) {\n");
         sb.append("            throw new RuntimeCamelException(e);\n");
         sb.append("        }\n");
@@ -1661,7 +1673,7 @@ public class SpringBootAutoConfigurationMojo extends 
AbstractMojo {
         sb.append("\n");
         sb.append("Map<String, Object> parameters = new HashMap<>();\n");
         sb.append("IntrospectionSupport.getProperties(configuration, 
parameters, null, false);\n");
-        sb.append("IntrospectionSupport.setProperties(camelContext, 
camelContext.getTypeConverter(), language, parameters);\n");
+        sb.append("CamelPropertiesHelper.setCamelProperties(camelContext, 
language, parameters, false);\n");
         sb.append("\n");
         sb.append("\n");
         sb.append("if (ObjectHelper.isNotEmpty(customizers)) {\n");

Reply via email to