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/06/27 18:14:33 UTC

[camel] branch master updated (5a0d6d7 -> 7f8412f)

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 5a0d6d7  CAMEL-13692 - More doc fixes
     new c3dac10  CAMEL-13683: Property binding support - Better error reporting for binding not possible.
     new 7f8412f  CAMEL-13695: camel-core - Injector allow to create beans via static factory methods

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/cdi/CdiCamelInjector.java     | 19 +++++++++
 .../camel/component/jcr/JcrConverterTest.java      |  5 +++
 .../apache/camel/spring/spi/SpringInjector.java    | 20 +++++++++
 .../main/java/org/apache/camel/spi/Injector.java   | 10 +++++
 .../apache/camel/impl/engine/DefaultInjector.java  | 21 +++++++++-
 .../xml/AbstractCamelContextFactoryBeanTest.java   |  5 +++
 .../org/apache/camel/impl/DefaultInjectorTest.java | 25 +++++++++++
 .../camel/support/PropertyBindingSupportTest.java  |  5 +++
 .../org/apache/camel/util/ReflectionInjector.java  | 19 +++++++++
 .../camel/support/PropertyBindingSupport.java      | 48 ++++++++++++++--------
 10 files changed, 159 insertions(+), 18 deletions(-)


[camel] 02/02: CAMEL-13695: camel-core - Injector allow to create beans via static factory methods

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 7f8412f88bf517ac21f3522027f1bcebc06c32ac
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Jun 27 19:01:34 2019 +0200

    CAMEL-13695: camel-core - Injector allow to create beans via static factory methods
---
 .../org/apache/camel/cdi/CdiCamelInjector.java     | 19 ++++++++++++++
 .../camel/component/jcr/JcrConverterTest.java      |  5 ++++
 .../apache/camel/spring/spi/SpringInjector.java    | 20 +++++++++++++++
 .../main/java/org/apache/camel/spi/Injector.java   | 10 ++++++++
 .../apache/camel/impl/engine/DefaultInjector.java  | 21 ++++++++++++++--
 .../xml/AbstractCamelContextFactoryBeanTest.java   |  5 ++++
 .../org/apache/camel/impl/DefaultInjectorTest.java | 25 +++++++++++++++++++
 .../camel/support/PropertyBindingSupportTest.java  |  5 ++++
 .../org/apache/camel/util/ReflectionInjector.java  | 19 ++++++++++++++
 .../camel/support/PropertyBindingSupport.java      | 29 +++++++++++++++-------
 10 files changed, 147 insertions(+), 11 deletions(-)

diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelInjector.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelInjector.java
index 582b3ee..3372793 100644
--- a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelInjector.java
+++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelInjector.java
@@ -16,8 +16,11 @@
  */
 package org.apache.camel.cdi;
 
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import javax.enterprise.inject.spi.BeanManager;
 
+import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.spi.Injector;
 
 import static org.apache.camel.cdi.BeanManagerHelper.getReferenceByType;
@@ -39,6 +42,22 @@ final class CdiCamelInjector implements Injector {
     }
 
     @Override
+    public <T> T newInstance(Class<T> type, String factoryMethod) {
+        T answer = null;
+        try {
+            // lookup factory method
+            Method fm = type.getMethod(factoryMethod);
+            if (Modifier.isStatic(fm.getModifiers()) && Modifier.isPublic(fm.getModifiers()) && fm.getReturnType() == type) {
+                Object obj = fm.invoke(null);
+                answer = type.cast(obj);
+            }
+        } catch (Exception e) {
+            throw new RuntimeCamelException("Error invoking factory method: " + factoryMethod + " on class: " + type, e);
+        }
+        return answer;
+    }
+
+    @Override
     public <T> T newInstance(Class<T> type, boolean postProcessBean) {
         return getReferenceByType(manager, type)
                 .orElseGet(() -> injector.newInstance(type, postProcessBean));
diff --git a/components/camel-jcr/src/test/java/org/apache/camel/component/jcr/JcrConverterTest.java b/components/camel-jcr/src/test/java/org/apache/camel/component/jcr/JcrConverterTest.java
index ac1f35f..addd428 100644
--- a/components/camel-jcr/src/test/java/org/apache/camel/component/jcr/JcrConverterTest.java
+++ b/components/camel-jcr/src/test/java/org/apache/camel/component/jcr/JcrConverterTest.java
@@ -54,6 +54,11 @@ public class JcrConverterTest extends Assert {
                     }
 
                     @Override
+                    public <T> T newInstance(Class<T> type, String factoryMethod) {
+                        return null;
+                    }
+
+                    @Override
                     public <T> T newInstance(Class<T> type, boolean postProcessBean) {
                         return ObjectHelper.newInstance(type);
                     }
diff --git a/components/camel-spring/src/main/java/org/apache/camel/spring/spi/SpringInjector.java b/components/camel-spring/src/main/java/org/apache/camel/spring/spi/SpringInjector.java
index 7d50435..56efd9e 100644
--- a/components/camel-spring/src/main/java/org/apache/camel/spring/spi/SpringInjector.java
+++ b/components/camel-spring/src/main/java/org/apache/camel/spring/spi/SpringInjector.java
@@ -16,6 +16,10 @@
  */
 package org.apache.camel.spring.spi;
 
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.spi.Injector;
 import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
 import org.springframework.context.ConfigurableApplicationContext;
@@ -37,6 +41,22 @@ public class SpringInjector implements Injector {
     }
 
     @Override
+    public <T> T newInstance(Class<T> type, String factoryMethod) {
+        T answer = null;
+        try {
+            // lookup factory method
+            Method fm = type.getMethod(factoryMethod);
+            if (Modifier.isStatic(fm.getModifiers()) && Modifier.isPublic(fm.getModifiers()) && fm.getReturnType() == type) {
+                Object obj = fm.invoke(null);
+                answer = type.cast(obj);
+            }
+        } catch (Exception e) {
+            throw new RuntimeCamelException("Error invoking factory method: " + factoryMethod + " on class: " + type, e);
+        }
+        return answer;
+    }
+
+    @Override
     public <T> T newInstance(Class<T> type, boolean postProcessBean) {
         Object value;
         if (postProcessBean) {
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/Injector.java b/core/camel-api/src/main/java/org/apache/camel/spi/Injector.java
index 81c7fa1..d37d8b1 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/Injector.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/Injector.java
@@ -33,6 +33,16 @@ public interface Injector {
     <T> T newInstance(Class<T> type);
 
     /**
+     * Instantiates a new instance of the given type by using the factory method
+     * (this will not perform bean post processing)
+     *
+     * @param type the type of object to create
+     * @param factoryMethod to create the new instance via factory method which must be public static and return the type
+     * @return a newly created instance
+     */
+    <T> T newInstance(Class<T> type, String factoryMethod);
+
+    /**
      * Instantiates a new instance of the given type; possibly injecting values
      * into the object in the process (bean post processing if enabled)
      *
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultInjector.java b/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultInjector.java
index eee18ed..2b8f8de 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultInjector.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultInjector.java
@@ -16,6 +16,9 @@
  */
 package org.apache.camel.impl.engine;
 
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
 import org.apache.camel.CamelContext;
 import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.RuntimeCamelException;
@@ -27,8 +30,6 @@ import org.apache.camel.support.ObjectHelper;
  * A default implementation of {@link Injector} which just uses reflection to
  * instantiate new objects using their zero argument constructor,
  * and then performing bean post processing using {@link CamelBeanPostProcessor}.
- * <p/>
- * For more complex implementations try the Spring or Guice implementations.
  */
 public class DefaultInjector implements Injector  {
 
@@ -45,6 +46,22 @@ public class DefaultInjector implements Injector  {
     }
 
     @Override
+    public <T> T newInstance(Class<T> type, String factoryMethod) {
+        T answer = null;
+        try {
+            // lookup factory method
+            Method fm = type.getMethod(factoryMethod);
+            if (Modifier.isStatic(fm.getModifiers()) && Modifier.isPublic(fm.getModifiers()) && fm.getReturnType() == type) {
+                Object obj = fm.invoke(null);
+                answer = type.cast(obj);
+            }
+        } catch (Exception e) {
+            throw new RuntimeCamelException("Error invoking factory method: " + factoryMethod + " on class: " + type, e);
+        }
+        return answer;
+    }
+
+    @Override
     public <T> T newInstance(Class<T> type, boolean postProcessBean) {
         T answer = ObjectHelper.newInstance(type);
         if (answer != null && postProcessBean) {
diff --git a/core/camel-core-xml/src/test/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBeanTest.java b/core/camel-core-xml/src/test/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBeanTest.java
index 8bfcdf1..375dd89 100644
--- a/core/camel-core-xml/src/test/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBeanTest.java
+++ b/core/camel-core-xml/src/test/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBeanTest.java
@@ -69,6 +69,11 @@ public class AbstractCamelContextFactoryBeanTest {
             }
 
             @Override
+            public <T> T newInstance(Class<T> type, String factoryMethod) {
+                return null;
+            }
+
+            @Override
             public <T> T newInstance(Class<T> type, boolean postProcessBean) {
                 return ObjectHelper.newInstance(type);
             }
diff --git a/core/camel-core/src/test/java/org/apache/camel/impl/DefaultInjectorTest.java b/core/camel-core/src/test/java/org/apache/camel/impl/DefaultInjectorTest.java
index 6d128d7..9eec2e6 100644
--- a/core/camel-core/src/test/java/org/apache/camel/impl/DefaultInjectorTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/impl/DefaultInjectorTest.java
@@ -40,6 +40,18 @@ public class DefaultInjectorTest extends Assert {
         assertEquals("WorldWorld", reply);
     }
 
+    @Test
+    public void testDefaultInjectorFactory() throws Exception {
+        CamelContext context = new DefaultCamelContext();
+        context.start();
+
+        // use the injector (will use the default)
+        MyOtherBean bean = context.getInjector().newInstance(MyOtherBean.class, "getInstance");
+
+        Object reply = bean.doSomething("World");
+        assertEquals("WorldWorld", reply);
+    }
+
     public static class MyBean {
 
         @Produce("language:simple:${body}${body}")
@@ -50,4 +62,17 @@ public class DefaultInjectorTest extends Assert {
         }
     }
 
+    public static class MyOtherBean {
+
+        private static MyOtherBean me = new MyOtherBean();
+
+        public static MyOtherBean getInstance() {
+            return me;
+        }
+
+        public Object doSomething(String body) {
+            return body + body;
+        }
+    }
+
 }
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 588f3ec..5c751df 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
@@ -298,6 +298,11 @@ public class PropertyBindingSupportTest extends ContextTestSupport {
             }
 
             @Override
+            public <T> T newInstance(Class<T> type, String factoryMethod) {
+                return null;
+            }
+
+            @Override
             public <T> T newInstance(Class<T> type, boolean postProcessBean) {
                 return null;
             }
diff --git a/core/camel-core/src/test/java/org/apache/camel/util/ReflectionInjector.java b/core/camel-core/src/test/java/org/apache/camel/util/ReflectionInjector.java
index ed081a9..b7fc377 100644
--- a/core/camel-core/src/test/java/org/apache/camel/util/ReflectionInjector.java
+++ b/core/camel-core/src/test/java/org/apache/camel/util/ReflectionInjector.java
@@ -16,6 +16,10 @@
  */
 package org.apache.camel.util;
 
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.spi.Injector;
 import org.apache.camel.support.ObjectHelper;
 
@@ -32,6 +36,21 @@ public class ReflectionInjector implements Injector {
     }
 
     @Override
+    public <T> T newInstance(Class<T> type, String factoryMethod) {
+        T answer = null;
+        try {
+            // lookup factory method
+            Method fm = type.getMethod(factoryMethod);
+            if (Modifier.isStatic(fm.getModifiers()) && Modifier.isPublic(fm.getModifiers()) && fm.getReturnType() == type) {
+                answer = (T) fm.invoke(null);
+            }
+        } catch (Exception e) {
+            throw new RuntimeCamelException("Error invoking factory method: " + factoryMethod + " on class: " + type, e);
+        }
+        return answer;
+    }
+
+    @Override
     public <T> T newInstance(Class<T> type, boolean postProcessBean) {
         return ObjectHelper.newInstance(type);
         // does not support post processing
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 1eaecb7..975fea8 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
@@ -26,8 +26,8 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.camel.CamelContext;
-import org.apache.camel.NoSuchBeanException;
 import org.apache.camel.PropertyBindingException;
+import org.apache.camel.util.StringHelper;
 
 import static org.apache.camel.support.IntrospectionSupport.findSetterMethods;
 import static org.apache.camel.util.ObjectHelper.isNotEmpty;
@@ -44,7 +44,9 @@ import static org.apache.camel.util.ObjectHelper.isNotEmpty;
  *     <li>reference by bean id - Values can refer to other beans in the registry by prefixing with with # or #bean: eg #myBean or #bean:myBean</li>
  *     <li>reference by type - Values can refer to singleton beans by their type in the registry by prefixing with #type: syntax, eg #type:com.foo.MyClassType</li>
  *     <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</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>.
  *     <li>ignore case - Whether to ignore case for property keys<li>
  * </ul>
  * <p/>
@@ -549,10 +551,19 @@ public final class PropertyBindingSupport {
             if (value.toString().startsWith("#class:")) {
                 // its a new class to be created
                 String className = value.toString().substring(7);
+                String factoryMethod = null;
+                if (className.indexOf('#') != -1) {
+                    factoryMethod = StringHelper.after(className, "#");
+                    className = StringHelper.before(className, "#");
+                }
                 Class<?> type = context.getClassResolver().resolveMandatoryClass(className);
-                value = context.getInjector().newInstance(type);
+                if (factoryMethod != null) {
+                    value = context.getInjector().newInstance(type, factoryMethod);
+                } else {
+                    value = context.getInjector().newInstance(type);
+                }
                 if (value == null) {
-                    throw new IllegalArgumentException("Cannot create instance of class: " + className);
+                    throw new IllegalStateException("Cannot create instance of class: " + className);
                 }
             } else if (value.toString().startsWith("#type:")) {
                 // its reference by type, so lookup the actual value and use it if there is only one instance in the registry
@@ -562,9 +573,9 @@ public final class PropertyBindingSupport {
                 if (types.size() == 1) {
                     value = types.iterator().next();
                 } else if (types.size() > 1) {
-                    throw new IllegalArgumentException("Cannot select single type: " + typeName + " as there are " + types.size() + " beans in the registry with this type");
+                    throw new IllegalStateException("Cannot select single type: " + typeName + " as there are " + types.size() + " beans in the registry with this type");
                 } else {
-                    throw new IllegalArgumentException("Cannot select single type: " + typeName + " as there are no beans in the registry with this type");
+                    throw new IllegalStateException("Cannot select single type: " + typeName + " as there are no beans in the registry with this type");
                 }
             } else if (value.toString().equals("#autowired")) {
                 // we should get the type from the setter
@@ -575,12 +586,12 @@ public final class PropertyBindingSupport {
                     if (types.size() == 1) {
                         value = types.iterator().next();
                     } else if (types.size() > 1) {
-                        throw new IllegalArgumentException("Cannot select single type: " + parameterType + " as there are " + types.size() + " beans in the registry with this type");
+                        throw new IllegalStateException("Cannot select single type: " + parameterType + " as there are " + types.size() + " beans in the registry with this type");
                     } else {
-                        throw new IllegalArgumentException("Cannot select single type: " + parameterType + " as there are no beans in the registry with this type");
+                        throw new IllegalStateException("Cannot select single type: " + parameterType + " as there are no beans in the registry with this type");
                     }
                 } else {
-                    throw new IllegalArgumentException("Cannot find setter method with name: " + name + " on class: " + target.getClass().getName() + " to use for autowiring");
+                    throw new IllegalStateException("Cannot find setter method with name: " + name + " on class: " + target.getClass().getName() + " to use for autowiring");
                 }
             } else if (value.toString().startsWith("#bean:")) {
                 // okay its a reference so swap to lookup this which is already supported in IntrospectionSupport


[camel] 01/02: CAMEL-13683: Property binding support - Better error reporting for binding not possible.

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 c3dac109baa74aa32f105557474856b4c54aa963
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Jun 27 17:06:03 2019 +0200

    CAMEL-13683: Property binding support - Better error reporting for binding not possible.
---
 .../camel/support/PropertyBindingSupport.java      | 37 ++++++++++++----------
 1 file changed, 21 insertions(+), 16 deletions(-)

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 d74932c..1eaecb7 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
@@ -26,6 +26,7 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.NoSuchBeanException;
 import org.apache.camel.PropertyBindingException;
 
 import static org.apache.camel.support.IntrospectionSupport.findSetterMethods;
@@ -549,33 +550,37 @@ public final class PropertyBindingSupport {
                 // its a new class to be created
                 String className = value.toString().substring(7);
                 Class<?> type = context.getClassResolver().resolveMandatoryClass(className);
-                if (type != null) {
-                    value = context.getInjector().newInstance(type);
-                    if (value == null) {
-                        throw new IllegalArgumentException("Cannot create instance of class: " + className);
-                    }
+                value = context.getInjector().newInstance(type);
+                if (value == null) {
+                    throw new IllegalArgumentException("Cannot create instance of class: " + className);
                 }
             } else if (value.toString().startsWith("#type:")) {
                 // its reference by type, so lookup the actual value and use it if there is only one instance in the registry
                 String typeName = value.toString().substring(6);
                 Class<?> type = context.getClassResolver().resolveMandatoryClass(typeName);
-                if (type != null) {
-                    Set<?> types = context.getRegistry().findByType(type);
-                    if (types.size() == 1) {
-                        value = types.iterator().next();
-                    }
+                Set<?> types = context.getRegistry().findByType(type);
+                if (types.size() == 1) {
+                    value = types.iterator().next();
+                } else if (types.size() > 1) {
+                    throw new IllegalArgumentException("Cannot select single type: " + typeName + " as there are " + types.size() + " beans in the registry with this type");
+                } else {
+                    throw new IllegalArgumentException("Cannot select single type: " + typeName + " as there are no beans in the registry with this type");
                 }
             } else if (value.toString().equals("#autowired")) {
                 // we should get the type from the setter
                 Method method = findBestSetterMethod(target.getClass(), name, fluentBuilder, allowPrivateSetter, ignoreCase);
                 if (method != null) {
                     Class<?> parameterType = method.getParameterTypes()[0];
-                    if (parameterType != null) {
-                        Set<?> types = context.getRegistry().findByType(parameterType);
-                        if (types.size() == 1) {
-                            value = types.iterator().next();
-                        }
+                    Set<?> types = context.getRegistry().findByType(parameterType);
+                    if (types.size() == 1) {
+                        value = types.iterator().next();
+                    } else if (types.size() > 1) {
+                        throw new IllegalArgumentException("Cannot select single type: " + parameterType + " as there are " + types.size() + " beans in the registry with this type");
+                    } else {
+                        throw new IllegalArgumentException("Cannot select single type: " + parameterType + " as there are no beans in the registry with this type");
                     }
+                } else {
+                    throw new IllegalArgumentException("Cannot find setter method with name: " + name + " on class: " + target.getClass().getName() + " to use for autowiring");
                 }
             } else if (value.toString().startsWith("#bean:")) {
                 // okay its a reference so swap to lookup this which is already supported in IntrospectionSupport
@@ -587,7 +592,7 @@ public final class PropertyBindingSupport {
         boolean hit = IntrospectionSupport.setProperty(context, context.getTypeConverter(), target, name, value, refName, fluentBuilder, allowPrivateSetter, ignoreCase);
         if (!hit && mandatory) {
             // there is no setter with this given name, so lets report this as a problem
-            throw new IllegalArgumentException("Cannot find setter method: " + name + " on bean: " + target + " when binding property: " + ognlPath);
+            throw new IllegalArgumentException("Cannot find setter method: " + name + " on bean: " + target + " of type: " + target.getClass().getName() + " when binding property: " + ognlPath);
         }
         return hit;
     }