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 <claus.ib...@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

Reply via email to