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 2022/10/11 08:58:38 UTC

[camel] branch main updated: CAMEL-18600: properties component - Allow to turn off nested placeholders

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 15c67534785 CAMEL-18600: properties component - Allow to turn off nested placeholders
15c67534785 is described below

commit 15c675347854f3eb1546e0fcd74845af8cca0e4d
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue Oct 11 10:57:13 2022 +0200

    CAMEL-18600: properties component - Allow to turn off nested placeholders
---
 .../org/apache/camel/spi/PropertiesComponent.java  |  6 +++
 .../properties/PropertiesComponentConfigurer.java  |  6 +++
 .../properties/DefaultPropertiesParser.java        | 48 +++++++++++++++---
 .../component/properties/PropertiesComponent.java  | 18 ++++++-
 .../component/properties/PropertiesParser.java     |  7 ++-
 .../PropertiesComponentNestedFalseTest.java        | 57 ++++++++++++++++++++++
 .../properties/PropertiesComponentNestedTest.java  | 56 +++++++++++++++++++++
 7 files changed, 190 insertions(+), 8 deletions(-)

diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesComponent.java b/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesComponent.java
index 4c284a7618f..a49fbc81bd6 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesComponent.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesComponent.java
@@ -204,6 +204,12 @@ public interface PropertiesComponent extends StaticService {
      */
     void setIgnoreMissingLocation(boolean ignoreMissingLocation);
 
+    /**
+     * Whether to support nested property placeholders. A nested placeholder, means that a placeholder, has also a
+     * placeholder, that should be resolved (recursively).
+     */
+    void setNestedPlaceholder(boolean nestedPlaceholder);
+
     /**
      * Sets initial properties which will be added before any property locations are loaded.
      */
diff --git a/core/camel-base/src/generated/java/org/apache/camel/component/properties/PropertiesComponentConfigurer.java b/core/camel-base/src/generated/java/org/apache/camel/component/properties/PropertiesComponentConfigurer.java
index 9da5505251a..a41836deb51 100644
--- a/core/camel-base/src/generated/java/org/apache/camel/component/properties/PropertiesComponentConfigurer.java
+++ b/core/camel-base/src/generated/java/org/apache/camel/component/properties/PropertiesComponentConfigurer.java
@@ -41,6 +41,8 @@ public class PropertiesComponentConfigurer extends org.apache.camel.support.comp
         case "Location": target.setLocation(property(camelContext, java.lang.String.class, value)); return true;
         case "locations":
         case "Locations": target.setLocations(property(camelContext, java.util.List.class, value)); return true;
+        case "nestedplaceholder":
+        case "NestedPlaceholder": target.setNestedPlaceholder(property(camelContext, boolean.class, value)); return true;
         case "overrideproperties":
         case "OverrideProperties": target.setOverrideProperties(property(camelContext, java.util.Properties.class, value)); return true;
         case "propertiesfunctionresolver":
@@ -76,6 +78,8 @@ public class PropertiesComponentConfigurer extends org.apache.camel.support.comp
         case "Location": return java.lang.String.class;
         case "locations":
         case "Locations": return java.util.List.class;
+        case "nestedplaceholder":
+        case "NestedPlaceholder": return boolean.class;
         case "overrideproperties":
         case "OverrideProperties": return java.util.Properties.class;
         case "propertiesfunctionresolver":
@@ -112,6 +116,8 @@ public class PropertiesComponentConfigurer extends org.apache.camel.support.comp
         case "Location": return target.getLocation();
         case "locations":
         case "Locations": return target.getLocations();
+        case "nestedplaceholder":
+        case "NestedPlaceholder": return target.isNestedPlaceholder();
         case "overrideproperties":
         case "OverrideProperties": return target.getOverrideProperties();
         case "propertiesfunctionresolver":
diff --git a/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesParser.java b/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesParser.java
index aed9f3c6ebc..5f74bf043a3 100644
--- a/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesParser.java
+++ b/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesParser.java
@@ -62,9 +62,11 @@ public class DefaultPropertiesParser implements PropertiesParser {
 
     @Override
     public String parseUri(
-            String text, PropertiesLookup properties, boolean defaultFallbackEnabled, boolean keepUnresolvedOptional)
+            String text, PropertiesLookup properties, boolean defaultFallbackEnabled, boolean keepUnresolvedOptional,
+            boolean nestedPlaceholder)
             throws IllegalArgumentException {
-        ParsingContext context = new ParsingContext(properties, defaultFallbackEnabled, keepUnresolvedOptional);
+        ParsingContext context
+                = new ParsingContext(properties, defaultFallbackEnabled, keepUnresolvedOptional, nestedPlaceholder);
         String answer = context.parse(text);
         if (keepUnresolvedOptional && answer != null && answer.contains(UNRESOLVED_PREFIX_TOKEN)) {
             // replace temporary unresolved keys back to with placeholders so they are kept as-is
@@ -86,11 +88,14 @@ public class DefaultPropertiesParser implements PropertiesParser {
         private final PropertiesLookup properties;
         private final boolean defaultFallbackEnabled;
         private final boolean keepUnresolvedOptional;
+        private final boolean nestedPlaceholder;
 
-        ParsingContext(PropertiesLookup properties, boolean defaultFallbackEnabled, boolean keepUnresolvedOptional) {
+        ParsingContext(PropertiesLookup properties, boolean defaultFallbackEnabled, boolean keepUnresolvedOptional,
+                       boolean nestedPlaceholder) {
             this.properties = properties;
             this.defaultFallbackEnabled = defaultFallbackEnabled;
             this.keepUnresolvedOptional = keepUnresolvedOptional;
+            this.nestedPlaceholder = nestedPlaceholder;
         }
 
         /**
@@ -100,7 +105,38 @@ public class DefaultPropertiesParser implements PropertiesParser {
          * @return       Evaluated string
          */
         public String parse(String input) {
-            return doParse(input, new HashSet<String>());
+            if (nestedPlaceholder) {
+                return doParseNested(input, new HashSet<String>());
+            } else {
+                return doParse(input);
+            }
+        }
+
+        /**
+         * Parses the given input string and replaces all properties (not nested)
+         *
+         * @param  input Input string
+         * @return       Evaluated string
+         */
+        private String doParse(String input) {
+            if (input == null) {
+                return null;
+            }
+
+            StringBuilder answer = new StringBuilder();
+            Property property;
+            while ((property = readProperty(input)) != null) {
+                String before = input.substring(0, property.getBeginIndex());
+                String after = input.substring(property.getEndIndex());
+                String parsed = property.getValue();
+                if (parsed != null) {
+                    answer.append(before);
+                    answer.append(parsed);
+                }
+                input = after;
+            }
+            answer.append(input);
+            return answer.toString();
         }
 
         /**
@@ -110,7 +146,7 @@ public class DefaultPropertiesParser implements PropertiesParser {
          * @param  replacedPropertyKeys Already replaced property keys used for tracking circular references
          * @return                      Evaluated string
          */
-        private String doParse(String input, Set<String> replacedPropertyKeys) {
+        private String doParseNested(String input, Set<String> replacedPropertyKeys) {
             if (input == null) {
                 return null;
             }
@@ -133,7 +169,7 @@ public class DefaultPropertiesParser implements PropertiesParser {
 
                 String before = answer.substring(0, property.getBeginIndex());
                 String after = answer.substring(property.getEndIndex());
-                String parsed = doParse(property.getValue(), newReplaced);
+                String parsed = doParseNested(property.getValue(), newReplaced);
                 if (parsed != null) {
                     answer = before + parsed + after;
                 } else {
diff --git a/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java b/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
index d7e53a0fed2..da06caa6b2e 100644
--- a/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
@@ -114,6 +114,7 @@ public class PropertiesComponent extends ServiceSupport
     private List<PropertiesLocation> locations = new ArrayList<>();
     private String location;
     private boolean ignoreMissingLocation;
+    private boolean nestedPlaceholder = true;
     private String encoding;
     private boolean defaultFallbackEnabled = true;
     private Properties initialProperties;
@@ -310,7 +311,8 @@ public class PropertiesComponent extends ServiceSupport
             key = PREFIX_TOKEN + key.substring(NEGATE_PREFIX.length());
         }
 
-        String answer = propertiesParser.parseUri(key, properties, defaultFallbackEnabled, keepUnresolvedOptional);
+        String answer
+                = propertiesParser.parseUri(key, properties, defaultFallbackEnabled, keepUnresolvedOptional, nestedPlaceholder);
         if (negate) {
             if ("true".equalsIgnoreCase(answer)) {
                 answer = "false";
@@ -461,6 +463,20 @@ public class PropertiesComponent extends ServiceSupport
         this.ignoreMissingLocation = ignoreMissingLocation;
     }
 
+    @ManagedAttribute(description = "Nested placeholder")
+    public boolean isNestedPlaceholder() {
+        return nestedPlaceholder;
+    }
+
+    /**
+     * Whether to support nested property placeholders. A nested placeholder, means that a placeholder, has also a
+     * placeholder, that should be resolved (recursively).
+     */
+    @Override
+    public void setNestedPlaceholder(boolean nestedPlaceholder) {
+        this.nestedPlaceholder = nestedPlaceholder;
+    }
+
     /**
      * @return a list of properties which will be used before any locations are resolved (can't be null).
      */
diff --git a/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesParser.java b/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesParser.java
index d2516403275..cb9f1cd67f0 100644
--- a/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesParser.java
+++ b/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesParser.java
@@ -28,10 +28,15 @@ public interface PropertiesParser {
      * @param  properties               the properties resolved which values should be looked up
      * @param  fallback                 whether to support using fallback values if a property cannot be found
      * @param  keepUnresolvedOptional   whether to keep placeholders that are optional and was unresolved
+     * @param  nestedPlaceholder        whether to support nested property placeholders. A nested placeholder, means
+     *                                  that a placeholder, has also a placeholder, that should be resolved
+     *                                  (recursively).
      * @return                          the parsed text with replaced placeholders
      * @throws IllegalArgumentException if uri syntax is not valid or a property is not found
      */
-    String parseUri(String text, PropertiesLookup properties, boolean fallback, boolean keepUnresolvedOptional)
+    String parseUri(
+            String text, PropertiesLookup properties, boolean fallback, boolean keepUnresolvedOptional,
+            boolean nestedPlaceholder)
             throws IllegalArgumentException;
 
     /**
diff --git a/core/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesComponentNestedFalseTest.java b/core/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesComponentNestedFalseTest.java
new file mode 100644
index 00000000000..4e5ca7b194a
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesComponentNestedFalseTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.properties;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+
+public class PropertiesComponentNestedFalseTest extends ContextTestSupport {
+
+    @Test
+    public void testNestedFalse() throws Exception {
+        getMockEndpoint("mock:result").expectedBodiesReceived("mock:{{cool.result}}");
+        getMockEndpoint("mock:result").expectedHeaderReceived("foo", "Hello mock:{{cool.result}} and Cheese how are you?");
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                        .setBody().constant("{{cool.concat}}")
+                        .setHeader("foo").constant("Hello {{cool.concat}} and {{cool.other.name}} how are you?")
+                        .to("mock:result");
+            }
+        };
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+        context.getPropertiesComponent().setLocation("classpath:org/apache/camel/component/properties/myproperties.properties");
+        context.getPropertiesComponent().setNestedPlaceholder(false);
+        return context;
+    }
+
+}
diff --git a/core/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesComponentNestedTest.java b/core/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesComponentNestedTest.java
new file mode 100644
index 00000000000..a1e6a5613fe
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesComponentNestedTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.properties;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+
+public class PropertiesComponentNestedTest extends ContextTestSupport {
+
+    @Test
+    public void testNested() throws Exception {
+        getMockEndpoint("mock:result").expectedBodiesReceived("mock:result");
+        getMockEndpoint("mock:result").expectedHeaderReceived("foo", "Hello mock:result and Cheese how are you?");
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                        .setBody().constant("{{cool.concat}}")
+                        .setHeader("foo").constant("Hello {{cool.concat}} and {{cool.other.name}} how are you?")
+                        .to("mock:result");
+            }
+        };
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+        context.getPropertiesComponent().setLocation("classpath:org/apache/camel/component/properties/myproperties.properties");
+        return context;
+    }
+
+}