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 a1594cb7947ac957303b55a340c477652b56e645
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);

Reply via email to