This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch CAMEL-13850 in repository https://gitbox.apache.org/repos/asf/camel.git
commit b7071c82f4ff06dd19779ee295dcf00964fc97ae Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Mon Aug 12 07:18:47 2019 +0200 CAMEL-13850: Optimize model classes to provide changeable properties that support property placeholders to avoid reflection. Work in progress. --- .../org/apache/camel/model/FromDefinition.java | 26 ++++- .../java/org/apache/camel/model/LogDefinition.java | 31 +++++- .../camel/model/ProcessorDefinitionHelper.java | 111 ++++++++++----------- .../camel/model/PropertyPlaceholderAware.java | 38 +++++++ .../java/org/apache/camel/model/ToDefinition.java | 56 ++++++++++- ...ockTest.java => SimpleMockPlaceholderTest.java} | 23 ++++- .../org/apache/camel/processor/SimpleMockTest.java | 2 - .../camel/support/PropertyPlaceholdersHelper.java | 2 + 8 files changed, 221 insertions(+), 68 deletions(-) diff --git a/core/camel-core/src/main/java/org/apache/camel/model/FromDefinition.java b/core/camel-core/src/main/java/org/apache/camel/model/FromDefinition.java index e1faba2..6d61c6d 100644 --- a/core/camel-core/src/main/java/org/apache/camel/model/FromDefinition.java +++ b/core/camel-core/src/main/java/org/apache/camel/model/FromDefinition.java @@ -16,12 +16,17 @@ */ package org.apache.camel.model; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; +import org.apache.camel.CamelContext; import org.apache.camel.Endpoint; import org.apache.camel.builder.EndpointConsumerBuilder; import org.apache.camel.spi.Metadata; @@ -32,7 +37,7 @@ import org.apache.camel.spi.Metadata; @Metadata(label = "eip,endpoint,routing") @XmlRootElement(name = "from") @XmlAccessorType(XmlAccessType.FIELD) -public class FromDefinition extends OptionalIdentifiedDefinition<FromDefinition> implements EndpointRequiredDefinition { +public class FromDefinition extends OptionalIdentifiedDefinition<FromDefinition> implements PropertyPlaceholderAware, EndpointRequiredDefinition { @XmlAttribute @Metadata(required = true) private String uri; @XmlTransient @@ -40,18 +45,28 @@ public class FromDefinition extends OptionalIdentifiedDefinition<FromDefinition> @XmlTransient private EndpointConsumerBuilder endpointConsumerBuilder; + private final Map<String, Supplier<String>> readPlaceholders = new HashMap<>(); + private final Map<String, Consumer<String>> writePlaceholders = new HashMap<>(); + public FromDefinition() { + readPlaceholders.put("id", this::getId); + readPlaceholders.put("uri", this::getUri); + writePlaceholders.put("id", this::setId); + writePlaceholders.put("uri", this::setUri); } public FromDefinition(String uri) { + this(); setUri(uri); } public FromDefinition(Endpoint endpoint) { + this(); setEndpoint(endpoint); } public FromDefinition(EndpointConsumerBuilder endpointConsumerBuilder) { + this(); setEndpointConsumerBuilder(endpointConsumerBuilder); } @@ -135,4 +150,13 @@ public class FromDefinition extends OptionalIdentifiedDefinition<FromDefinition> this.uri = null; } + @Override + public Map<String, Supplier<String>> getReadPropertyPlaceholderOptions(CamelContext camelContext) { + return readPlaceholders; + } + + @Override + public Map<String, Consumer<String>> getWritePropertyPlaceholderOptions(CamelContext camelContext) { + return writePlaceholders; + } } diff --git a/core/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java b/core/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java index c8e1b0a..62281e5 100644 --- a/core/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java +++ b/core/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java @@ -16,12 +16,17 @@ */ package org.apache.camel.model; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; +import org.apache.camel.CamelContext; import org.apache.camel.LoggingLevel; import org.apache.camel.spi.Metadata; import org.slf4j.Logger; @@ -32,7 +37,7 @@ import org.slf4j.Logger; @Metadata(label = "eip,configuration") @XmlRootElement(name = "log") @XmlAccessorType(XmlAccessType.FIELD) -public class LogDefinition extends NoOutputDefinition<LogDefinition> { +public class LogDefinition extends NoOutputDefinition<LogDefinition> implements PropertyPlaceholderAware { @XmlAttribute(required = true) private String message; @@ -47,10 +52,24 @@ public class LogDefinition extends NoOutputDefinition<LogDefinition> { @XmlTransient private Logger logger; + private final Map<String, Supplier<String>> readPlaceholders = new HashMap<>(); + private final Map<String, Consumer<String>> writePlaceholders = new HashMap<>(); + public LogDefinition() { + readPlaceholders.put("id", this::getId); + readPlaceholders.put("message", this::getMessage); + readPlaceholders.put("logName", this::getLogName); + readPlaceholders.put("marker", this::getMarker); + readPlaceholders.put("loggerRef", this::getLoggerRef); + writePlaceholders.put("id", this::setId); + writePlaceholders.put("message", this::setMessage); + writePlaceholders.put("logName", this::setLogName); + writePlaceholders.put("marker", this::setMarker); + writePlaceholders.put("loggerRef", this::setLoggerRef); } public LogDefinition(String message) { + this(); this.message = message; } @@ -136,4 +155,14 @@ public class LogDefinition extends NoOutputDefinition<LogDefinition> { public void setLogger(Logger logger) { this.logger = logger; } + + @Override + public Map<String, Supplier<String>> getReadPropertyPlaceholderOptions(CamelContext camelContext) { + return readPlaceholders; + } + + @Override + public Map<String, Consumer<String>> getWritePropertyPlaceholderOptions(CamelContext camelContext) { + return writePlaceholders; + } } diff --git a/core/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java b/core/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java index 420a7af..280965b 100644 --- a/core/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java +++ b/core/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java @@ -22,9 +22,12 @@ import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Consumer; +import java.util.function.Supplier; import javax.xml.namespace.QName; import org.apache.camel.CamelContext; @@ -628,10 +631,31 @@ public final class ProcessorDefinitionHelper { return rc; } - private static void addRestoreAction(final Object target, final Map<String, Object> properties) { - addRestoreAction(null, target, properties); + private static void addRestoreAction(Map<String, Consumer<String>> writeProperties, final Map<String, String> properties) { + if (properties.isEmpty()) { + return; + } + + RestoreAction restoreAction = CURRENT_RESTORE_ACTION.get(); + if (restoreAction == null) { + return; + } + + restoreAction.actions.add(new Runnable() { + @Override + public void run() { + try { + properties.forEach((k, v) -> { + writeProperties.get(k).accept(v); + }); + } catch (Exception e) { + LOG.warn("Cannot restore definition properties. This exception is ignored.", e); + } + } + }); } - + + @Deprecated private static void addRestoreAction(final CamelContext context, final Object target, final Map<String, Object> properties) { if (properties.isEmpty()) { return; @@ -681,70 +705,37 @@ public final class ProcessorDefinitionHelper { public static void resolvePropertyPlaceholders(CamelContext camelContext, Object definition) throws Exception { LOG.trace("Resolving property placeholders for: {}", definition); + // only do this for models that supports property placeholders + if (!(definition instanceof PropertyPlaceholderAware)) { + return; + } + + PropertyPlaceholderAware ppa = (PropertyPlaceholderAware) definition; + // find all getter/setter which we can use for property placeholders - Map<String, Object> properties = new HashMap<>(); - IntrospectionSupport.getProperties(definition, properties, null); + Map<String, String> changedProperties = new HashMap<>(); + Map<String, Supplier<String>> readProperties = ppa.getReadPropertyPlaceholderOptions(camelContext); + Map<String, Consumer<String>> writeProperties = ppa.getWritePropertyPlaceholderOptions(camelContext); - OtherAttributesAware other = null; - if (definition instanceof OtherAttributesAware) { - other = (OtherAttributesAware) definition; - } - // include additional properties which have the Camel placeholder QName - // and when the definition parameter is this (otherAttributes belong to this) - if (other != null && other.getOtherAttributes() != null) { - for (QName key : other.getOtherAttributes().keySet()) { - if (Constants.PLACEHOLDER_QNAME.equals(key.getNamespaceURI())) { - String local = key.getLocalPart(); - Object value = other.getOtherAttributes().get(key); - if (value instanceof String) { - // enforce a properties component to be created if none existed - camelContext.getPropertiesComponent(true); - - // value must be enclosed with placeholder tokens - String s = (String) value; - String prefixToken = PropertiesComponent.PREFIX_TOKEN; - String suffixToken = PropertiesComponent.SUFFIX_TOKEN; - - if (!s.startsWith(prefixToken)) { - s = prefixToken + s; - } - if (!s.endsWith(suffixToken)) { - s = s + suffixToken; - } - value = s; - } - properties.put(local, value); - } + if (!readProperties.isEmpty()) { + if (LOG.isTraceEnabled()) { + LOG.trace("There are {} properties on: {}", readProperties.size(), definition); } - } - - Map<String, Object> changedProperties = new HashMap<>(); - if (!properties.isEmpty()) { - LOG.trace("There are {} properties on: {}", properties.size(), definition); // lookup and resolve properties for String based properties - for (Map.Entry<String, Object> entry : properties.entrySet()) { - // the name is always a String + for (Map.Entry<String, Supplier<String>> entry : readProperties.entrySet()) { String name = entry.getKey(); - Object value = entry.getValue(); - if (value instanceof String) { - // value must be a String, as a String is the key for a property placeholder - String text = (String) value; - text = camelContext.resolvePropertyPlaceholders(text); - if (text != value) { - // invoke setter as the text has changed - boolean changed = IntrospectionSupport.setProperty(camelContext.getTypeConverter(), definition, name, text); - if (!changed) { - throw new IllegalArgumentException("No setter to set property: " + name + " to: " + text + " on: " + definition); - } - changedProperties.put(name, value); - if (LOG.isDebugEnabled()) { - LOG.debug("Changed property [{}] from: {} to: {}", name, value, text); - } + String value = entry.getValue().get(); + String text = camelContext.resolvePropertyPlaceholders(value); + if (!Objects.equals(text, value)) { + writeProperties.get(name).accept(text); + changedProperties.put(name, value); + if (LOG.isDebugEnabled()) { + LOG.debug("Changed property [{}] from: {} to: {}", name, value, text); } } } } - addRestoreAction(camelContext, definition, changedProperties); + addRestoreAction(writeProperties, changedProperties); } /** @@ -758,7 +749,7 @@ public final class ProcessorDefinitionHelper { */ public static void resolveKnownConstantFields(CamelContext camelContext, Object definition) throws Exception { LOG.trace("Resolving known fields for: {}", definition); - +/* // find all String getter/setter Map<String, Object> properties = new HashMap<>(); IntrospectionSupport.getProperties(definition, properties, null); @@ -793,7 +784,7 @@ public final class ProcessorDefinitionHelper { } } } - addRestoreAction(definition, changedProperties); + addRestoreAction(camelContext, definition, changedProperties);*/ } } diff --git a/core/camel-core/src/main/java/org/apache/camel/model/PropertyPlaceholderAware.java b/core/camel-core/src/main/java/org/apache/camel/model/PropertyPlaceholderAware.java new file mode 100644 index 0000000..9bc6b4a --- /dev/null +++ b/core/camel-core/src/main/java/org/apache/camel/model/PropertyPlaceholderAware.java @@ -0,0 +1,38 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.model; + +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.apache.camel.CamelContext; + +public interface PropertyPlaceholderAware { + + /** + * Gets the options on the model definition which supports property placeholders and can be resolved. + * + * @return key/values of options + */ + Map<String, Supplier<String>> getReadPropertyPlaceholderOptions(CamelContext camelContext); + + /** + * To update an existing property using the function with the ket/value and returning the changed value + */ + Map<String, Consumer<String>> getWritePropertyPlaceholderOptions(CamelContext camelContext); +} diff --git a/core/camel-core/src/main/java/org/apache/camel/model/ToDefinition.java b/core/camel-core/src/main/java/org/apache/camel/model/ToDefinition.java index 78828ad..3a0ba35 100644 --- a/core/camel-core/src/main/java/org/apache/camel/model/ToDefinition.java +++ b/core/camel-core/src/main/java/org/apache/camel/model/ToDefinition.java @@ -16,15 +16,22 @@ */ package org.apache.camel.model; +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Supplier; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; +import org.apache.camel.CamelContext; import org.apache.camel.Endpoint; import org.apache.camel.ExchangePattern; import org.apache.camel.builder.EndpointProducerBuilder; import org.apache.camel.spi.Metadata; +import org.apache.camel.spi.PropertiesComponent; /** * Sends the message to a static endpoint @@ -32,22 +39,32 @@ import org.apache.camel.spi.Metadata; @Metadata(label = "eip,endpoint,routing") @XmlRootElement(name = "to") @XmlAccessorType(XmlAccessType.FIELD) -public class ToDefinition extends SendDefinition<ToDefinition> { +public class ToDefinition extends SendDefinition<ToDefinition> implements PropertyPlaceholderAware { @XmlAttribute private ExchangePattern pattern; + private final Map<String, Supplier<String>> readPlaceholders = new HashMap<>(); + private final Map<String, Consumer<String>> writePlaceholders = new HashMap<>(); + public ToDefinition() { + readPlaceholders.put("id", this::getId); + readPlaceholders.put("uri", this::getUri); + writePlaceholders.put("id", this::setId); + writePlaceholders.put("uri", this::setUri); } public ToDefinition(String uri) { + this(); setUri(uri); } public ToDefinition(Endpoint endpoint) { + this(); setEndpoint(endpoint); } public ToDefinition(EndpointProducerBuilder endpointDefinition) { + this(); setEndpointProducerBuilder(endpointDefinition); } @@ -88,4 +105,41 @@ public class ToDefinition extends SendDefinition<ToDefinition> { this.pattern = pattern; } + @Override + public Map<String, Supplier<String>> getReadPropertyPlaceholderOptions(final CamelContext camelContext) { + if (getOtherAttributes() != null && !getOtherAttributes().isEmpty()) { + final Map<String, Supplier<String>> answer = new HashMap<>(readPlaceholders); + getOtherAttributes().forEach((k, v) -> { + if (Constants.PLACEHOLDER_QNAME.equals(k.getNamespaceURI())) { + if (v instanceof String) { + // enforce a properties component to be created if none existed + camelContext.getPropertiesComponent(true); + + // value must be enclosed with placeholder tokens + String s = (String) v; + String prefixToken = PropertiesComponent.PREFIX_TOKEN; + String suffixToken = PropertiesComponent.SUFFIX_TOKEN; + + if (!s.startsWith(prefixToken)) { + s = prefixToken + s; + } + if (!s.endsWith(suffixToken)) { + s = s + suffixToken; + } + final String value = s; + answer.put(k.getLocalPart(), () -> value); + } + } + }); + return answer; + } else { + return readPlaceholders; + } + } + + @Override + public Map<String, Consumer<String>> getWritePropertyPlaceholderOptions(CamelContext camelContext) { + return writePlaceholders; + } } + diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/SimpleMockTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/SimpleMockPlaceholderTest.java similarity index 71% copy from core/camel-core/src/test/java/org/apache/camel/processor/SimpleMockTest.java copy to core/camel-core/src/test/java/org/apache/camel/processor/SimpleMockPlaceholderTest.java index b8bd72d..dd8b986 100644 --- a/core/camel-core/src/test/java/org/apache/camel/processor/SimpleMockTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/processor/SimpleMockPlaceholderTest.java @@ -16,14 +16,31 @@ */ package org.apache.camel.processor; -import java.util.stream.Stream; +import java.util.Properties; +import org.apache.camel.CamelContext; import org.apache.camel.ContextTestSupport; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.component.properties.PropertiesComponent; import org.junit.Test; -public class SimpleMockTest extends ContextTestSupport { +public class SimpleMockPlaceholderTest extends ContextTestSupport { + + @Override + protected CamelContext createCamelContext() throws Exception { + CamelContext context = super.createCamelContext(); + + Properties myProp = new Properties(); + myProp.put("foo", "log:foo"); + myProp.put("end", "result"); + + PropertiesComponent pc = new PropertiesComponent(); + pc.setInitialProperties(myProp); + context.addComponent("properties", pc); + + return context; + } @Test public void testSimple() throws Exception { @@ -51,7 +68,7 @@ public class SimpleMockTest extends ContextTestSupport { return new RouteBuilder() { @Override public void configure() throws Exception { - from("direct:start").to("log:foo").to("log:bar").to("mock:result"); + from("direct:start").to("{{foo}}").to("log:bar").to("mock:{{end}}"); } }; } diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/SimpleMockTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/SimpleMockTest.java index b8bd72d..015a692 100644 --- a/core/camel-core/src/test/java/org/apache/camel/processor/SimpleMockTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/processor/SimpleMockTest.java @@ -16,8 +16,6 @@ */ package org.apache.camel.processor; -import java.util.stream.Stream; - import org.apache.camel.ContextTestSupport; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; diff --git a/core/camel-support/src/main/java/org/apache/camel/support/PropertyPlaceholdersHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/PropertyPlaceholdersHelper.java index c895914..84e44e9 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/PropertyPlaceholdersHelper.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/PropertyPlaceholdersHelper.java @@ -45,6 +45,8 @@ public final class PropertyPlaceholdersHelper { public static void resolvePropertyPlaceholders(CamelContext camelContext, Object object) throws Exception { LOG.trace("Resolving property placeholders for: {}", object); + // TODO: Like ProcessorDefinitionHelper we want to avoid reflection + // find all getter/setter which we can use for property placeholders Map<String, Object> properties = new HashMap<>(); IntrospectionSupport.getProperties(object, properties, null);