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/08/24 16:58:09 UTC
[camel] 01/03: CAMEL-13870: Generating fast component options
configurer
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 b9e55dd08891dc50141d1d94953af445db214e4b
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Aug 24 16:30:10 2019 +0200
CAMEL-13870: Generating fast component options configurer
---
.../component/properties/PropertiesComponent.java | 14 +-
.../src/main/java/org/apache/camel/Component.java | 9 ++
.../core/xml/AbstractCamelContextFactoryBean.java | 2 +-
.../component/log/LogComponentOptionsTest.java | 146 +++++++++++++++++++++
.../org/apache/camel/support/DefaultComponent.java | 19 ++-
.../org/apache/camel/support/DefaultEndpoint.java | 4 +-
.../camel/support/PropertyBindingSupport.java | 9 +-
.../tools/apt/EndpointAnnotationProcessor.java | 75 +++++++----
8 files changed, 248 insertions(+), 30 deletions(-)
diff --git a/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java b/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
index 13f57de..1613bab 100644
--- a/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
+++ b/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
@@ -106,10 +106,11 @@ public class PropertiesComponent extends DefaultComponent implements org.apache.
private PropertiesParser propertiesParser = new DefaultPropertiesParser(this);
private final PropertiesLookup propertiesLookup = new DefaultPropertiesLookup(this);
private final List<PropertiesSource> sources = new ArrayList<>();
-
private List<PropertiesLocation> locations = Collections.emptyList();
@Metadata
+ private String location;
+ @Metadata
private boolean ignoreMissingLocation;
@Metadata
private String encoding;
@@ -282,7 +283,7 @@ public class PropertiesComponent extends DefaultComponent implements org.apache.
* A list of locations to load properties.
* This option will override any default locations and only use the locations from this option.
*/
- public void setLocations(List<PropertiesLocation> locations) {
+ private void setLocations(List<PropertiesLocation> locations) {
// reset locations
locations = parseLocations(locations);
this.locations = Collections.unmodifiableList(locations);
@@ -324,6 +325,10 @@ public class PropertiesComponent extends DefaultComponent implements org.apache.
setLocations(locations);
}
+ public void addLocation(PropertiesLocation location) {
+ this.locations.add(location);
+ }
+
@Override
public void addLocation(String location) {
if (location != null) {
@@ -345,11 +350,16 @@ public class PropertiesComponent extends DefaultComponent implements org.apache.
*/
@Override
public void setLocation(String location) {
+ this.location = location;
if (location != null) {
setLocations(location.split(","));
}
}
+ public String getLocation() {
+ return location;
+ }
+
@ManagedAttribute(description = "Encoding to use when loading properties file from the file system or classpath")
public String getEncoding() {
return encoding;
diff --git a/core/camel-api/src/main/java/org/apache/camel/Component.java b/core/camel-api/src/main/java/org/apache/camel/Component.java
index 32683c8..809c90e 100644
--- a/core/camel-api/src/main/java/org/apache/camel/Component.java
+++ b/core/camel-api/src/main/java/org/apache/camel/Component.java
@@ -73,6 +73,15 @@ public interface Component extends CamelContextAware, Service {
boolean useRawUri();
/**
+ * Gets the component {@link PropertyConfigurer}.
+ *
+ * @return the configurer, or <tt>null</tt> if the component does not support using property configurer.
+ */
+ default PropertyConfigurer getComponentPropertyConfigurer() {
+ return null;
+ }
+
+ /**
* Gets the endpoint {@link PropertyConfigurer}.
*
* @return the configurer, or <tt>null</tt> if the endpoint does not support using property configurer.
diff --git a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
index 8fbc61d..abdbbd2 100644
--- a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
+++ b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
@@ -627,7 +627,7 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex
}
PropertiesComponent pc = new PropertiesComponent();
- pc.setLocations(locations);
+ locations.forEach(pc::addLocation);
pc.setEncoding(def.getEncoding());
if (def.isIgnoreMissingLocation() != null) {
diff --git a/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentOptionsTest.java b/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentOptionsTest.java
new file mode 100644
index 0000000..df492e1
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentOptionsTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.component.log;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.support.PropertyBindingSupport;
+import org.apache.camel.support.processor.DefaultExchangeFormatter;
+import org.junit.Test;
+
+public class LogComponentOptionsTest extends ContextTestSupport {
+
+ @Override
+ public boolean isUseRouteBuilder() {
+ return false;
+ }
+
+ @Test
+ public void testFastLogComponentOptions() throws Exception {
+ context.start();
+
+ long before = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+ DefaultExchangeFormatter myFormatter = new DefaultExchangeFormatter();
+
+ LogComponent log = context.getComponent("log", LogComponent.class);
+ assertNull(log.getExchangeFormatter());
+
+ new PropertyBindingSupport.Builder().withCamelContext(context).withTarget(log)
+ .withConfigurer(log.getComponentPropertyConfigurer())
+ .withProperty("exchangeFormatter", myFormatter).bind();
+
+ assertSame(myFormatter, log.getExchangeFormatter());
+
+ long after = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+ assertEquals("Should not use Java reflection", before, after);
+ }
+
+ @Test
+ public void testFastLogComponentNestedOptions() throws Exception {
+ context.start();
+
+ long before = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+ DefaultExchangeFormatter myFormatter = new DefaultExchangeFormatter();
+
+ LogComponent log = context.getComponent("log", LogComponent.class);
+ assertNull(log.getExchangeFormatter());
+
+ new PropertyBindingSupport.Builder().withCamelContext(context).withTarget(log)
+ .withConfigurer(log.getComponentPropertyConfigurer())
+ .withProperty("exchangeFormatter", myFormatter)
+ .withProperty("exchangeFormatter.showExchangeId", "true").bind();
+
+ assertSame(myFormatter, log.getExchangeFormatter());
+
+ long after = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+ assertTrue("Should use Java reflection", after > before);
+ }
+
+ @Test
+ public void testFastLogComponentOptionsLookupRegistry() throws Exception {
+ context.start();
+
+ long before = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+ DefaultExchangeFormatter myFormatter = new DefaultExchangeFormatter();
+ context.getRegistry().bind("myGreatFormatter", myFormatter);
+
+ LogComponent log = context.getComponent("log", LogComponent.class);
+ assertNull(log.getExchangeFormatter());
+
+ new PropertyBindingSupport.Builder().withCamelContext(context).withTarget(log)
+ .withConfigurer(log.getComponentPropertyConfigurer())
+ .withProperty("exchangeFormatter", "#bean:myGreatFormatter").bind();
+
+ assertSame(myFormatter, log.getExchangeFormatter());
+
+ long after = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+ assertEquals("Should not use Java reflection", before, after);
+ }
+
+ @Test
+ public void testSlowLogComponentOptions() throws Exception {
+ context.start();
+
+ long before = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+ DefaultExchangeFormatter myFormatter = new DefaultExchangeFormatter();
+
+ LogComponent log = context.getComponent("log", LogComponent.class);
+ assertNull(log.getExchangeFormatter());
+
+ new PropertyBindingSupport.Builder().withCamelContext(context).withTarget(log)
+ .withProperty("exchangeFormatter", myFormatter)
+ .withProperty("exchangeFormatter.showExchangeId", "true").bind();
+
+ assertSame(myFormatter, log.getExchangeFormatter());
+ assertTrue(myFormatter.isShowExchangeId());
+
+ long after = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+ assertTrue("Should use reflection", after > before);
+ }
+
+ @Test
+ public void testSlowLogComponentOptionsLookupRegistry() throws Exception {
+ context.start();
+
+ long before = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+ DefaultExchangeFormatter myFormatter = new DefaultExchangeFormatter();
+ context.getRegistry().bind("myGreatFormatter", myFormatter);
+
+ LogComponent log = context.getComponent("log", LogComponent.class);
+ assertNull(log.getExchangeFormatter());
+
+ new PropertyBindingSupport.Builder().withCamelContext(context).withTarget(log)
+ .withProperty("exchangeFormatter", "#bean:myGreatFormatter")
+ .withProperty("exchangeFormatter.showExchangeId", "true").bind();
+
+ assertSame(myFormatter, log.getExchangeFormatter());
+ assertTrue(myFormatter.isShowExchangeId());
+
+ long after = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+ assertTrue("Should use reflection", after > before);
+ }
+}
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/DefaultComponent.java b/core/camel-support/src/main/java/org/apache/camel/support/DefaultComponent.java
index d7fc017..4c6aa71 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/DefaultComponent.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/DefaultComponent.java
@@ -59,6 +59,7 @@ public abstract class DefaultComponent extends ServiceSupport implements Compone
private static final String RESOURCE_PATH = "META-INF/services/org/apache/camel/configurer/";
+ private volatile TriPropertyConfigurer componentPropertyConfigurer;
private volatile TriPropertyConfigurer endpointPropertyConfigurer;
private final List<Supplier<ComponentExtension>> extensions = new ArrayList<>();
private CamelContext camelContext;
@@ -339,8 +340,15 @@ public abstract class DefaultComponent extends ServiceSupport implements Compone
name = StringHelper.before(name, ",");
}
try {
- log.trace("Discovering optional endpoint property configurer class for component: {}", name);
+ log.trace("Discovering optional component property configurer class for component: {}", name);
Optional<Class<?>> clazz = getCamelContext().adapt(ExtendedCamelContext.class).getFactoryFinder(RESOURCE_PATH)
+ .findOptionalClass(name + "-component", null);
+ clazz.ifPresent(c -> componentPropertyConfigurer = org.apache.camel.support.ObjectHelper.newInstance(c, TriPropertyConfigurer.class));
+ if (log.isDebugEnabled() && componentPropertyConfigurer != null) {
+ log.debug("Discovered component property configurer: {} -> {}", name, componentPropertyConfigurer);
+ }
+ log.trace("Discovering optional endpoint property configurer class for component: {}", name);
+ clazz = getCamelContext().adapt(ExtendedCamelContext.class).getFactoryFinder(RESOURCE_PATH)
.findOptionalClass(name + "-endpoint", null);
clazz.ifPresent(c -> endpointPropertyConfigurer = org.apache.camel.support.ObjectHelper.newInstance(c, TriPropertyConfigurer.class));
if (log.isDebugEnabled() && endpointPropertyConfigurer != null) {
@@ -407,7 +415,9 @@ public abstract class DefaultComponent extends ServiceSupport implements Compone
.bind(camelContext, bean, parameters);
} else {
PropertyConfigurer configurer = null;
- if (bean instanceof Endpoint) {
+ if (bean instanceof Component) {
+ configurer = getComponentPropertyConfigurer();
+ } else if (bean instanceof Endpoint) {
configurer = getEndpointPropertyConfigurer();
} else if (bean instanceof PropertyConfigurerAware) {
configurer = ((PropertyConfigurerAware) bean).getPropertyConfigurer(bean);
@@ -418,6 +428,11 @@ public abstract class DefaultComponent extends ServiceSupport implements Compone
}
@Override
+ public PropertyConfigurer getComponentPropertyConfigurer() {
+ return componentPropertyConfigurer;
+ }
+
+ @Override
public PropertyConfigurer getEndpointPropertyConfigurer() {
return endpointPropertyConfigurer;
}
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/DefaultEndpoint.java b/core/camel-support/src/main/java/org/apache/camel/support/DefaultEndpoint.java
index 2041a5f..ff7b490 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/DefaultEndpoint.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/DefaultEndpoint.java
@@ -423,7 +423,9 @@ public abstract class DefaultEndpoint extends ServiceSupport implements Endpoint
.bind(camelContext, bean, parameters);
} else {
PropertyConfigurer configurer = null;
- if (bean instanceof Endpoint) {
+ if (bean instanceof Component) {
+ configurer = getComponent().getComponentPropertyConfigurer();
+ } else if (bean instanceof Endpoint) {
configurer = getComponent().getEndpointPropertyConfigurer();
} else if (bean instanceof PropertyConfigurerAware) {
configurer = ((PropertyConfigurerAware) bean).getPropertyConfigurer(bean);
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 bdce66f..f45f557 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
@@ -465,7 +465,12 @@ public final class PropertyBindingSupport {
Map.Entry<String, Object> entry = iter.next();
String key = entry.getKey();
Object value = entry.getValue();
- if (writeProperties.containsKey(key)) {
+ boolean valid = true;
+ if (nesting) {
+ // property configurer does not support nested names so skip if the name has a dot
+ valid = key.indexOf('.') == -1;
+ }
+ if (valid && writeProperties.containsKey(key)) {
writeProperties.get(key).accept(camelContext, target, value);
if (removeParameter) {
iter.remove();
@@ -641,6 +646,8 @@ public final class PropertyBindingSupport {
}
}
+ // TODO: Move configurer here so the value can have done all its magic
+
boolean hit = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().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
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java
index 8d7d7d6..bd396e1 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java
@@ -163,39 +163,70 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
}
}
+ // component options
TypeElement componentClassElement = findTypeElement(processingEnv, roundEnv, componentModel.getJavaType());
if (componentClassElement != null) {
findComponentClassProperties(writer, roundEnv, componentModel, componentOptions, componentClassElement, "", parentData, null, null);
}
+ generateComponentConfigurer(roundEnv, uriEndpoint, scheme, schemes, componentModel, componentOptions);
+ // endpoint options
findClassProperties(writer, roundEnv, componentModel, endpointPaths, endpointOptions, classElement, "", uriEndpoint.excludeProperties(), parentData, null, null);
-
String json = createParameterJsonSchema(componentModel, componentOptions, endpointPaths, endpointOptions, schemes, parentData);
writer.println(json);
+ generateEndpointConfigurer(roundEnv, classElement, uriEndpoint, scheme, schemes, componentModel, endpointOptions);
+ }
- // special for activemq and amqp scheme which should reuse jms
+ private void generateComponentConfigurer(RoundEnvironment roundEnv, UriEndpoint uriEndpoint, String scheme, String[] schemes,
+ ComponentModel componentModel, Set<ComponentOption> componentOptions) {
+ TypeElement parent;
if ("activemq".equals(scheme) || "amqp".equals(scheme)) {
- TypeElement parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.component.jms.JmsEndpointConfigurer");
- String fqen = classElement.getQualifiedName().toString();
- String pn = fqen.substring(0, fqen.lastIndexOf('.'));
- String en = classElement.getSimpleName().toString();
- String cn = en + "Configurer";
- String fqn = pn + "." + cn;
-
- EndpointPropertyConfigurerGenerator.generateExtendConfigurer(processingEnv, parent, pn, cn, fqn);
- EndpointPropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-endpoint", fqn);
- } else if (uriEndpoint.generateConfigurer() && !endpointOptions.isEmpty()) {
- TypeElement parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.spi.TriPropertyConfigurer");
- String fqen = classElement.getQualifiedName().toString();
- String pn = fqen.substring(0, fqen.lastIndexOf('.'));
- String en = classElement.getSimpleName().toString();
- String cn = en + "Configurer";
- String fqn = pn + "." + cn;
+ // special for activemq and amqp scheme which should reuse jms
+ parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.component.jms.JmsComponentConfigurer");
+ } else {
+ parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.spi.TriPropertyConfigurer");
+ }
+ String fqComponentClassName = componentModel.getJavaType();
+ String componentClassName = fqComponentClassName.substring(fqComponentClassName.lastIndexOf('.') + 1);
+ String className = componentClassName + "Configurer";
+ String packageName = fqComponentClassName.substring(0, fqComponentClassName.lastIndexOf('.'));
+ String fqClassName = packageName + "." + className;
+ if ("activemq".equals(scheme) || "amqp".equals(scheme)) {
+ ComponentPropertyConfigurerGenerator.generateExtendConfigurer(processingEnv, parent, packageName, className, fqClassName);
+ ComponentPropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-component", fqClassName);
+ } else if (uriEndpoint.generateConfigurer() && !componentOptions.isEmpty()) {
// only generate this once for the first scheme
if (schemes == null || schemes[0].equals(scheme)) {
- EndpointPropertyConfigurerGenerator.generatePropertyConfigurer(processingEnv, parent, pn, cn, fqn, en, endpointOptions);
- EndpointPropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-endpoint", fqn);
+ ComponentPropertyConfigurerGenerator.generatePropertyConfigurer(processingEnv, parent, packageName, className, fqClassName, componentClassName, componentOptions);
+ ComponentPropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-component", fqClassName);
+ }
+ }
+ }
+
+ private void generateEndpointConfigurer(RoundEnvironment roundEnv, TypeElement classElement, UriEndpoint uriEndpoint, String scheme, String[] schemes,
+ ComponentModel componentModel, Set<EndpointOption> endpointOptions) {
+ TypeElement parent;
+ if ("activemq".equals(scheme) || "amqp".equals(scheme)) {
+ // special for activemq and amqp scheme which should reuse jms
+ parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.component.jms.JmsEndpointConfigurer");
+ } else {
+ parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.spi.TriPropertyConfigurer");
+ }
+ String fqEndpointClassName = classElement.getQualifiedName().toString();
+ String packageName = fqEndpointClassName.substring(0, fqEndpointClassName.lastIndexOf('.'));
+ String endpointClassName = classElement.getSimpleName().toString();
+ String className = endpointClassName + "Configurer";
+ String fqClassName = packageName + "." + className;
+
+ if ("activemq".equals(scheme) || "amqp".equals(scheme)) {
+ EndpointPropertyConfigurerGenerator.generateExtendConfigurer(processingEnv, parent, packageName, className, fqClassName);
+ EndpointPropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-endpoint", fqClassName);
+ } else if (uriEndpoint.generateConfigurer() && !endpointOptions.isEmpty()) {
+ // only generate this once for the first scheme
+ if (schemes == null || schemes[0].equals(scheme)) {
+ EndpointPropertyConfigurerGenerator.generatePropertyConfigurer(processingEnv, parent, packageName, className, fqClassName, endpointClassName, endpointOptions);
+ EndpointPropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-endpoint", fqClassName);
}
}
}
@@ -542,11 +573,9 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
continue;
}
- // must be a getter/setter pair
+ // we usually favor putting the @Metadata annotation on the field instead of the setter, so try to use it if its there
String fieldName = methodName.substring(3);
fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1);
-
- // we usually favor putting the @Metadata annotation on the field instead of the setter, so try to use it if its there
VariableElement field = findFieldElement(classElement, fieldName);
if (field != null && metadata == null) {
metadata = field.getAnnotation(Metadata.class);