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/12/08 09:00:41 UTC
[camel] branch main updated: CAMEL-18802: properties function to support looking up key first before apply function. base64 function needs this.
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 7c8e2ef8d72 CAMEL-18802: properties function to support looking up key first before apply function. base64 function needs this.
7c8e2ef8d72 is described below
commit 7c8e2ef8d72def4f82bfe04fd6c3d962ce7d02ae
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Dec 8 10:00:18 2022 +0100
CAMEL-18802: properties function to support looking up key first before apply function. base64 function needs this.
---
.../base64/Base64PropertiesFunction.java | 8 +
...ava => Base64PropertiesFunctionDecodeTest.java} | 4 +-
...a => Base64PropertiesFunctionOptionalTest.java} | 10 +-
.../base64/Base64PropertiesFunctionTest.java | 8 +-
.../org/apache/camel/spi/PropertiesFunction.java | 16 +-
.../properties/DefaultPropertiesParser.java | 13 ++
...rtyFunctionOptionalPropertyPlaceholderTest.java | 229 +++++++++++++++++++++
.../ROOT/pages/camel-3x-upgrade-guide-3_20.adoc | 18 ++
8 files changed, 294 insertions(+), 12 deletions(-)
diff --git a/components/camel-base64/src/main/java/org/apache/camel/dataformat/base64/Base64PropertiesFunction.java b/components/camel-base64/src/main/java/org/apache/camel/dataformat/base64/Base64PropertiesFunction.java
index 4eedd37c8dd..b3109cc4144 100644
--- a/components/camel-base64/src/main/java/org/apache/camel/dataformat/base64/Base64PropertiesFunction.java
+++ b/components/camel-base64/src/main/java/org/apache/camel/dataformat/base64/Base64PropertiesFunction.java
@@ -35,8 +35,16 @@ public class Base64PropertiesFunction implements PropertiesFunction {
return "base64";
}
+ @Override
+ public boolean lookupFirst(String remainder) {
+ return !remainder.startsWith("decode:");
+ }
+
@Override
public String apply(String remainder) {
+ if (remainder.startsWith("decode:")) {
+ remainder = remainder.substring(7);
+ }
byte[] arr = codec.decode(remainder);
return new String(arr);
}
diff --git a/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionTest.java b/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionDecodeTest.java
similarity index 91%
copy from components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionTest.java
copy to components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionDecodeTest.java
index 2121b2e36ab..865b194e8ac 100644
--- a/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionTest.java
+++ b/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionDecodeTest.java
@@ -22,7 +22,7 @@ import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit5.CamelTestSupport;
import org.junit.jupiter.api.Test;
-public class Base64PropertiesFunctionTest extends CamelTestSupport {
+public class Base64PropertiesFunctionDecodeTest extends CamelTestSupport {
@Test
public void testBase64() throws Exception {
@@ -40,7 +40,7 @@ public class Base64PropertiesFunctionTest extends CamelTestSupport {
public void configure() {
from("direct:start")
// Q2FtZWw== is the word Camel
- .setBody(simple("${body} {{base64:Q2FtZWw==}}"))
+ .setBody(simple("${body} {{base64:decode:Q2FtZWw==}}"))
.to("mock:result");
}
};
diff --git a/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionTest.java b/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionOptionalTest.java
similarity index 85%
copy from components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionTest.java
copy to components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionOptionalTest.java
index 2121b2e36ab..3aff51ebbdd 100644
--- a/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionTest.java
+++ b/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionOptionalTest.java
@@ -22,11 +22,11 @@ import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit5.CamelTestSupport;
import org.junit.jupiter.api.Test;
-public class Base64PropertiesFunctionTest extends CamelTestSupport {
+public class Base64PropertiesFunctionOptionalTest extends CamelTestSupport {
@Test
- public void testBase64() throws Exception {
- getMockEndpoint("mock:result").expectedBodiesReceived("Hello Camel");
+ public void testBase64Key() throws Exception {
+ getMockEndpoint("mock:result").expectedBodiesReceived("Hello");
template.sendBody("direct:start", "Hello");
@@ -38,9 +38,9 @@ public class Base64PropertiesFunctionTest extends CamelTestSupport {
return new RouteBuilder() {
@Override
public void configure() {
+ // there is no fooKey
from("direct:start")
- // Q2FtZWw== is the word Camel
- .setBody(simple("${body} {{base64:Q2FtZWw==}}"))
+ .setBody(simple("${body} {{base64:?fooKey}}"))
.to("mock:result");
}
};
diff --git a/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionTest.java b/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionTest.java
index 2121b2e36ab..5c392fb9f0c 100644
--- a/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionTest.java
+++ b/components/camel-base64/src/test/java/org/apache/camel/dataformat/base64/Base64PropertiesFunctionTest.java
@@ -25,7 +25,7 @@ import org.junit.jupiter.api.Test;
public class Base64PropertiesFunctionTest extends CamelTestSupport {
@Test
- public void testBase64() throws Exception {
+ public void testBase64Key() throws Exception {
getMockEndpoint("mock:result").expectedBodiesReceived("Hello Camel");
template.sendBody("direct:start", "Hello");
@@ -38,9 +38,11 @@ public class Base64PropertiesFunctionTest extends CamelTestSupport {
return new RouteBuilder() {
@Override
public void configure() {
+ // Q2FtZWw== is the word Camel
+ context.getPropertiesComponent().addInitialProperty("fooKey", "Q2FtZWw==");
+
from("direct:start")
- // Q2FtZWw== is the word Camel
- .setBody(simple("${body} {{base64:Q2FtZWw==}}"))
+ .setBody(simple("${body} {{base64:fooKey}}"))
.to("mock:result");
}
};
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesFunction.java b/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesFunction.java
index 665218ba309..85dc86eeaa5 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesFunction.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesFunction.java
@@ -22,16 +22,28 @@ package org.apache.camel.spi;
public interface PropertiesFunction {
/**
- * Name of the function which is used as <tt>name:</tt> to let the properties component know its a function.
+ * Name of the function which is used as <tt>name:</tt> to let the properties component know it is a function.
*/
String getName();
/**
- * Applies the function
+ * Applies the function.
*
* @param remainder the remainder value
* @return a value as the result of the function
+ * @see #lookupFirst(String)
*/
String apply(String remainder);
+ /**
+ * Whether the value should be looked up as a regular properties first, before applying this function.
+ *
+ * @param remainder the remainder value
+ * @return true to resolve the remainder value as a property value, and then afterwards apply this function,
+ * false to apply this function without lookup (default).
+ */
+ default boolean lookupFirst(String remainder) {
+ return false;
+ }
+
}
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 80b52b0518c..8c7f00b17e5 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
@@ -313,6 +313,19 @@ public class DefaultPropertiesParser implements PropertiesParser {
PropertiesFunction function = propertiesComponent.getPropertiesFunction(prefix);
if (function != null) {
String remainder = StringHelper.after(key, ":");
+ if (function.lookupFirst(remainder)) {
+ boolean optional = remainder != null && remainder.startsWith(OPTIONAL_TOKEN);
+ String value = getPropertyValue(remainder, input);
+ if (optional && value == null) {
+ return null;
+ }
+ // it was not possible to resolve
+ if (value != null && value.startsWith(UNRESOLVED_PREFIX_TOKEN)) {
+ return value;
+ } else {
+ remainder = value;
+ }
+ }
log.debug("Property with key [{}] is applied by function [{}]", key, function.getName());
String value = function.apply(remainder);
if (value == null) {
diff --git a/core/camel-core/src/test/java/org/apache/camel/component/properties/PropertyFunctionOptionalPropertyPlaceholderTest.java b/core/camel-core/src/test/java/org/apache/camel/component/properties/PropertyFunctionOptionalPropertyPlaceholderTest.java
new file mode 100644
index 00000000000..abb0396bd18
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/component/properties/PropertyFunctionOptionalPropertyPlaceholderTest.java
@@ -0,0 +1,229 @@
+/*
+ * 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 java.util.Properties;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.PropertiesFunction;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class PropertyFunctionOptionalPropertyPlaceholderTest extends ContextTestSupport {
+
+ @Override
+ public boolean isUseRouteBuilder() {
+ return false;
+ }
+
+ @Test
+ public void testNoFunctionNotPresent() throws Exception {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .setBody().constant("{{?myKey}}")
+ .to("mock:result");
+ }
+ });
+ context.start();
+
+ getMockEndpoint("mock:result").expectedMessageCount(2);
+ getMockEndpoint("mock:result").allMessages().body().isNull();
+
+ template.sendBody("direct:start", "Hello World");
+ template.sendBody("direct:start", "Bye World");
+
+ assertMockEndpointsSatisfied();
+
+ assertEquals(2, getMockEndpoint("mock:result").getReceivedExchanges().size());
+ }
+
+ @Test
+ public void testNoFunctionPresent() throws Exception {
+ Properties prop = new Properties();
+ prop.put("myKey", "123");
+ context.getPropertiesComponent().setInitialProperties(prop);
+
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .setBody().constant("{{?myKey}}")
+ .to("mock:result");
+ }
+ });
+ context.start();
+
+ getMockEndpoint("mock:result").expectedBodiesReceived("123", "123");
+
+ template.sendBody("direct:start", "Hello World");
+ template.sendBody("direct:start", "Bye World");
+
+ assertMockEndpointsSatisfied();
+
+ assertEquals(2, getMockEndpoint("mock:result").getReceivedExchanges().size());
+ }
+
+ @Test
+ public void testFunctionNotPresent() throws Exception {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .setBody().constant("{{reverse:?myKey}}")
+ .to("mock:result");
+ }
+ });
+ context.start();
+
+ getMockEndpoint("mock:result").expectedMessageCount(2);
+ getMockEndpoint("mock:result").allMessages().body().isNull();
+
+ template.sendBody("direct:start", "Hello World");
+ template.sendBody("direct:start", "Bye World");
+
+ assertMockEndpointsSatisfied();
+
+ assertEquals(2, getMockEndpoint("mock:result").getReceivedExchanges().size());
+ }
+
+ @Test
+ public void testFunctionPresent() throws Exception {
+ Properties prop = new Properties();
+ prop.put("myKey", "123");
+ context.getPropertiesComponent().setInitialProperties(prop);
+
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .setBody().constant("{{reverse:?myKey}}")
+ .to("mock:result");
+ }
+ });
+ context.start();
+
+ getMockEndpoint("mock:result").expectedBodiesReceived("321", "321");
+
+ template.sendBody("direct:start", "Hello World");
+ template.sendBody("direct:start", "Bye World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Test
+ public void testKeepUnresolved() throws Exception {
+ String out = context.adapt(ExtendedCamelContext.class)
+ .resolvePropertyPlaceholders("{{reverse:?myKey}}", true);
+ Assertions.assertEquals("{{?myKey}}", out);
+ }
+
+ @Test
+ public void testQueryOptionalNotPresent() throws Exception {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .to("mock:result?retainFirst={{reverse:?maxKeep}}");
+ }
+ });
+ context.start();
+
+ getMockEndpoint("mock:result").expectedMessageCount(2);
+
+ template.sendBody("direct:start", "Hello World");
+ template.sendBody("direct:start", "Bye World");
+
+ assertMockEndpointsSatisfied();
+
+ assertEquals(2, getMockEndpoint("mock:result").getReceivedExchanges().size());
+ }
+
+ @Test
+ public void testQueryOptionalPresent() throws Exception {
+ context.getPropertiesComponent().addInitialProperty("maxKeep", "321");
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .to("mock:result?retainFirst={{reverse:?maxKeep}}");
+ }
+ });
+ context.start();
+
+ getMockEndpoint("mock:result?retainFirst=123").expectedMessageCount(2);
+
+ template.sendBody("direct:start", "Hello World");
+ template.sendBody("direct:start", "Bye World");
+
+ assertMockEndpointsSatisfied();
+
+ assertEquals(2, getMockEndpoint("mock:result?retainFirst=123").getReceivedExchanges().size());
+ }
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ CamelContext context = super.createCamelContext();
+ context.getPropertiesComponent().setLocation("classpath:org/apache/camel/component/properties/myproperties.properties");
+ ReverseFunction func = new ReverseFunction();
+ func.setCamelContext(context);
+ context.getPropertiesComponent().addPropertiesFunction(func);
+ return context;
+ }
+
+ private static class ReverseFunction implements PropertiesFunction, CamelContextAware {
+
+ private CamelContext camelContext;
+
+ @Override
+ public CamelContext getCamelContext() {
+ return camelContext;
+ }
+
+ @Override
+ public void setCamelContext(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
+ @Override
+ public String getName() {
+ return "reverse";
+ }
+
+ @Override
+ public boolean lookupFirst(String remainder) {
+ return true;
+ }
+
+ @Override
+ public String apply(String remainder) {
+ if (remainder == null || remainder.isEmpty()) {
+ return remainder;
+ }
+ StringBuilder sb = new StringBuilder(remainder);
+ return sb.reverse().toString();
+ }
+ }
+
+}
diff --git a/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_20.adoc b/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_20.adoc
index a5bbdbf619c..97b71823006 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_20.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_20.adoc
@@ -27,6 +27,24 @@ from `<groupdId>org.apache.camel</groupdId>` to `<groupdId>org.apache.camel.mave
Dependencies not intended for end users has been removed, such as all `-parent` JARs.
+=== camel-base64
+
+The `base64` property placeholder function will now lookup the value as a property key.
+For example
+
+[source,text]
+----
+{{base64:myKey}}
+----
+
+Will now look up myKey as a property placeholder value, which then is decoded.
+If you want to decode the value as-is, then use `base64:decode:` as shown below:
+
+[source,text]
+----
+{{base64:decode:Q2FtZWw==}}
+----
+
=== camel-log
The log component now shows cached streams (`org.apache.camel.StreamCache`) message bodies by default.