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:35 UTC
[camel] 02/02: CAMEL-13695: camel-core - Injector allow to create
beans via static factory methods
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