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