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 2020/02/08 11:42:01 UTC
[camel] branch master updated: CAMEL-14525: camel-main now supports
binding beans to registry via the camel.beans. prefix
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
The following commit(s) were added to refs/heads/master by this push:
new cedd55d CAMEL-14525: camel-main now supports binding beans to registry via the camel.beans. prefix
cedd55d is described below
commit cedd55dbe01befb79aa68b2a7c96bf382506387e
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Feb 8 12:36:32 2020 +0100
CAMEL-14525: camel-main now supports binding beans to registry via the camel.beans. prefix
---
.../org/apache/camel/main/BaseMainSupport.java | 48 ++++++++++
.../main/{MainSedaTest.java => MainBeansTest.java} | 30 ++++--
.../java/org/apache/camel/main/MainSedaTest.java | 24 +++++
.../camel/support/PropertyBindingSupport.java | 101 ++++++++++++---------
4 files changed, 152 insertions(+), 51 deletions(-)
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index 5432b77..cd5e5b6 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -22,6 +22,7 @@ import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -58,6 +59,7 @@ import org.apache.camel.util.FileUtil;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.OrderedProperties;
+import org.apache.camel.util.PropertiesHelper;
import org.apache.camel.util.StringHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -694,6 +696,7 @@ public abstract class BaseMainSupport extends ServiceSupport {
Map<String, Object> hystrixProperties = new LinkedHashMap<>();
Map<String, Object> resilience4jProperties = new LinkedHashMap<>();
Map<String, Object> restProperties = new LinkedHashMap<>();
+ Map<String, Object> beansProperties = new LinkedHashMap<>();
for (String key : prop.stringPropertyNames()) {
if (key.startsWith("camel.context.")) {
// grab the value
@@ -719,8 +722,22 @@ public abstract class BaseMainSupport extends ServiceSupport {
String option = key.substring(11);
validateOptionAndValue(key, option, value);
restProperties.put(optionKey(option), value);
+ } else if (key.startsWith("camel.beans.")) {
+ // grab the value
+ String value = prop.getProperty(key);
+ String option = key.substring(12);
+ validateOptionAndValue(key, option, value);
+ beansProperties.put(optionKey(option), value);
}
}
+
+ // create beans first as they may be used later
+ if (!beansProperties.isEmpty()) {
+ LOG.debug("Creating and binding beans to registry from loaded properties: {}", beansProperties.size());
+ bindBeansToRegistry(camelContext, beansProperties, "camel.beans.",
+ mainConfigurationProperties.isAutoConfigurationFailFast(), true, autoConfiguredProperties);
+ }
+
if (!contextProperties.isEmpty()) {
LOG.debug("Auto-configuring CamelContext from loaded properties: {}", contextProperties.size());
setPropertiesOnTarget(camelContext, camelContext, contextProperties, "camel.context.",
@@ -761,6 +778,11 @@ public abstract class BaseMainSupport extends ServiceSupport {
}
// log which options was not set
+ if (!beansProperties.isEmpty()) {
+ beansProperties.forEach((k, v) -> {
+ LOG.warn("Property not auto-configured: camel.beans.{}={}", k, v);
+ });
+ }
if (!contextProperties.isEmpty()) {
contextProperties.forEach((k, v) -> {
LOG.warn("Property not auto-configured: camel.context.{}={} on bean: {}", k, v, camelContext);
@@ -789,6 +811,32 @@ public abstract class BaseMainSupport extends ServiceSupport {
}
}
+ private void bindBeansToRegistry(CamelContext camelContext, Map<String, Object> properties,
+ String optionPrefix, boolean failIfNotSet, boolean ignoreCase,
+ Map<String, String> autoConfiguredProperties) throws Exception {
+
+ // make defensive copy as we mutate the map
+ Set<String> keys = new LinkedHashSet<>(properties.keySet());
+ for (String key : keys) {
+ if (key.indexOf('.') == -1) {
+ // create beans first and then set properties
+ String name = key;
+ Object value = properties.remove(key);
+ Object bean = PropertyBindingSupport.resolveBean(camelContext, name, value);
+ if (bean == null) {
+ throw new IllegalArgumentException("Cannot create/resolve bean with name " + name + " from value: " + value);
+ }
+ // register bean
+ camelContext.getRegistry().bind(name, bean);
+ autoConfiguredProperties.put(optionPrefix + key, value.toString());
+ // and then configure properties on the beans afterwards
+ Map<String, Object> config = PropertiesHelper.extractProperties(properties, key + ".");
+ setPropertiesOnTarget(camelContext, bean, config, optionPrefix + key + ".", failIfNotSet, ignoreCase, autoConfiguredProperties);
+ LOG.info("Binding bean: {} (type: {}) to the registry", key, ObjectHelper.classCanonicalName(bean));
+ }
+ }
+ }
+
protected void autoConfigurationPropertiesComponent(CamelContext camelContext, Map<String, String> autoConfiguredProperties) throws Exception {
// load properties
Properties prop = camelContext.getPropertiesComponent().loadProperties(name -> name.startsWith("camel."));
diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MainSedaTest.java b/core/camel-main/src/test/java/org/apache/camel/main/MainBeansTest.java
similarity index 62%
copy from core/camel-main/src/test/java/org/apache/camel/main/MainSedaTest.java
copy to core/camel-main/src/test/java/org/apache/camel/main/MainBeansTest.java
index 09ad4a1..a0aa9a1 100644
--- a/core/camel-main/src/test/java/org/apache/camel/main/MainSedaTest.java
+++ b/core/camel-main/src/test/java/org/apache/camel/main/MainBeansTest.java
@@ -22,24 +22,36 @@ import org.apache.camel.component.seda.SedaComponent;
import org.junit.Assert;
import org.junit.Test;
-public class MainSedaTest extends Assert {
+public class MainBeansTest extends Assert {
@Test
- public void testSedaMain() throws Exception {
+ public void testBindBeans() throws Exception {
+ MyFoo myFoo = new MyFoo();
+
Main main = new Main();
main.addRoutesBuilder(new MyRouteBuilder());
- main.addProperty("camel.component.seda.defaultQueueFactory", "#class:org.apache.camel.main.MySedaBlockingQueueFactory");
- main.addProperty("camel.component.seda.defaultQueueFactory.counter", "123");
+ main.bind("myFoolish", myFoo);
+
+ // create by class
+ main.addProperty("camel.beans.foo", "#class:org.apache.camel.main.MySedaBlockingQueueFactory");
+ main.addProperty("camel.beans.foo.counter", "123");
+
+ // lookup by type
+ main.addProperty("camel.beans.myfoo", "#type:org.apache.camel.main.MyFoo");
+ main.addProperty("camel.beans.myfoo.name", "Donkey");
main.start();
CamelContext camelContext = main.getCamelContext();
assertNotNull(camelContext);
- SedaComponent seda = camelContext.getComponent("seda", SedaComponent.class);
- assertNotNull(seda);
- assertTrue(seda.getDefaultQueueFactory() instanceof MySedaBlockingQueueFactory);
- MySedaBlockingQueueFactory myBQF = (MySedaBlockingQueueFactory) seda.getDefaultQueueFactory();
+ Object foo = camelContext.getRegistry().lookupByName("foo");
+ assertNotNull(foo);
+
+ MySedaBlockingQueueFactory myBQF = camelContext.getRegistry().findByType(MySedaBlockingQueueFactory.class).iterator().next();
+ assertSame(foo, myBQF);
+
assertEquals(123, myBQF.getCounter());
+ assertEquals("Donkey", myFoo.getName());
main.stop();
}
@@ -47,7 +59,7 @@ public class MainSedaTest extends Assert {
public static class MyRouteBuilder extends RouteBuilder {
@Override
public void configure() throws Exception {
- from("direct:start").to("seda:foo");
+ from("direct:start").to("mock:foo");
}
}
diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MainSedaTest.java b/core/camel-main/src/test/java/org/apache/camel/main/MainSedaTest.java
index 09ad4a1..8172671 100644
--- a/core/camel-main/src/test/java/org/apache/camel/main/MainSedaTest.java
+++ b/core/camel-main/src/test/java/org/apache/camel/main/MainSedaTest.java
@@ -44,6 +44,30 @@ public class MainSedaTest extends Assert {
main.stop();
}
+ @Test
+ public void testSedaAutowireFromRegistryMain() throws Exception {
+ Main main = new Main();
+ main.addRoutesBuilder(new MyRouteBuilder());
+ main.addProperty("camel.beans.myqf", "#class:org.apache.camel.main.MySedaBlockingQueueFactory");
+ main.addProperty("camel.beans.myqf.counter", "123");
+ main.start();
+
+ CamelContext camelContext = main.getCamelContext();
+ assertNotNull(camelContext);
+
+ // the keys will be lower-cased
+ assertNotNull(camelContext.getRegistry().lookupByName("myqf"));
+
+ // seda will autowire from registry and discover the custom qf and use it
+ SedaComponent seda = camelContext.getComponent("seda", SedaComponent.class);
+ assertNotNull(seda);
+ assertTrue(seda.getDefaultQueueFactory() instanceof MySedaBlockingQueueFactory);
+ MySedaBlockingQueueFactory myBQF = (MySedaBlockingQueueFactory) seda.getDefaultQueueFactory();
+ assertEquals(123, myBQF.getCounter());
+
+ main.stop();
+ }
+
public static class MyRouteBuilder extends RouteBuilder {
@Override
public void configure() throws Exception {
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 e01aa7d..0849675 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
@@ -555,45 +555,7 @@ public final class PropertyBindingSupport {
private static Object resolveValue(CamelContext context, Object target, String name, Object value,
boolean ignoreCase, boolean fluentBuilder, boolean allowPrivateSetter) throws Exception {
if (value instanceof String) {
- if (value.toString().startsWith("#class:")) {
- // its a new class to be created
- String className = value.toString().substring(7);
- String factoryMethod = null;
- String parameters = null;
- if (className.endsWith(")") && className.indexOf('(') != -1) {
- parameters = StringHelper.after(className, "(");
- parameters = parameters.substring(0, parameters.length() - 1); // clip last )
- className = StringHelper.before(className, "(");
- }
- if (className != null && className.indexOf('#') != -1) {
- factoryMethod = StringHelper.after(className, "#");
- className = StringHelper.before(className, "#");
- }
- Class<?> type = context.getClassResolver().resolveMandatoryClass(className);
- if (factoryMethod != null) {
- value = context.getInjector().newInstance(type, factoryMethod);
- } else if (parameters != null) {
- // special to support constructor parameters
- value = newInstanceConstructorParameters(context, type, parameters);
- } else {
- value = context.getInjector().newInstance(type);
- }
- if (value == null) {
- 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
- String typeName = value.toString().substring(6);
- Class<?> type = context.getClassResolver().resolveMandatoryClass(typeName);
- Set<?> types = context.getRegistry().findByType(type);
- if (types.size() == 1) {
- value = types.iterator().next();
- } else if (types.size() > 1) {
- throw new IllegalStateException("Cannot select single type: " + typeName + " as there are " + types.size() + " beans in the registry with this type");
- } else {
- 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")) {
+ if (value.toString().equals("#autowired")) {
// we should get the type from the setter
Method method = findBestSetterMethod(context, target.getClass(), name, fluentBuilder, allowPrivateSetter, ignoreCase);
if (method != null) {
@@ -609,9 +571,8 @@ public final class PropertyBindingSupport {
} else {
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:")) {
- String key = value.toString().substring(6);
- value = context.getRegistry().lookupByName(key);
+ } else {
+ value = resolveBean(context, name, value);
}
}
return value;
@@ -957,4 +918,60 @@ public final class PropertyBindingSupport {
return parameterType.isAssignableFrom(expectedType);
}
+ /**
+ * Resolves the value as either a class, type or bean.
+ *
+ * @param camelContext the camel context
+ * @param name the name of the bean
+ * @param value how to resolve the bean with a prefix of either class#:, type#: or bean#:
+ * @return the resolve bean
+ * @throws Exception is thrown if error resolving the bean, or if the value is invalid.
+ */
+ public static Object resolveBean(CamelContext camelContext, String name, Object value) throws Exception {
+ if (value.toString().startsWith("#class:")) {
+ // its a new class to be created
+ String className = value.toString().substring(7);
+ String factoryMethod = null;
+ String parameters = null;
+ if (className.endsWith(")") && className.indexOf('(') != -1) {
+ parameters = StringHelper.after(className, "(");
+ parameters = parameters.substring(0, parameters.length() - 1); // clip last )
+ className = StringHelper.before(className, "(");
+ }
+ if (className != null && className.indexOf('#') != -1) {
+ factoryMethod = StringHelper.after(className, "#");
+ className = StringHelper.before(className, "#");
+ }
+ Class<?> type = camelContext.getClassResolver().resolveMandatoryClass(className);
+ if (factoryMethod != null) {
+ value = camelContext.getInjector().newInstance(type, factoryMethod);
+ } else if (parameters != null) {
+ // special to support constructor parameters
+ value = newInstanceConstructorParameters(camelContext, type, parameters);
+ } else {
+ value = camelContext.getInjector().newInstance(type);
+ }
+ if (value == null) {
+ 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
+ String typeName = value.toString().substring(6);
+ Class<?> type = camelContext.getClassResolver().resolveMandatoryClass(typeName);
+ Set<?> types = camelContext.getRegistry().findByType(type);
+ if (types.size() == 1) {
+ value = types.iterator().next();
+ } else if (types.size() > 1) {
+ throw new IllegalStateException("Cannot select single type: " + typeName + " as there are " + types.size() + " beans in the registry with this type");
+ } else {
+ throw new IllegalStateException("Cannot select single type: " + typeName + " as there are no beans in the registry with this type");
+ }
+ } else if (value.toString().startsWith("#bean:")) {
+ String key = value.toString().substring(6);
+ value = camelContext.getRegistry().lookupByName(key);
+ }
+
+ return value;
+ }
+
}