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 2023/12/30 15:14:58 UTC
(camel) 01/25: CAMEL-19749: Add variables as concept to Camel
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch var
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 8e81b17b175544e5f7d10ba4c0449ddca06764fc
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Dec 28 10:40:30 2023 +0100
CAMEL-19749: Add variables as concept to Camel
---
.../src/main/java/org/apache/camel/Exchange.java | 77 ++++++++++++
.../java/org/apache/camel/ExchangeTestSupport.java | 1 +
.../org/apache/camel/impl/DefaultExchangeTest.java | 136 +++++++++++++++++++++
.../org/apache/camel/support/AbstractExchange.java | 109 ++++++++++++++++-
4 files changed, 322 insertions(+), 1 deletion(-)
diff --git a/core/camel-api/src/main/java/org/apache/camel/Exchange.java b/core/camel-api/src/main/java/org/apache/camel/Exchange.java
index 9e7abd83ad0..a91f9865b6c 100644
--- a/core/camel-api/src/main/java/org/apache/camel/Exchange.java
+++ b/core/camel-api/src/main/java/org/apache/camel/Exchange.java
@@ -419,6 +419,83 @@ public interface Exchange {
*/
boolean hasProperties();
+ /**
+ * Returns a variable by name
+ *
+ * @param name the name of the variable
+ * @return the value of the given variable or <tt>null</tt> if there is no variable for the given name
+ */
+ Object getVariable(String name);
+
+ /**
+ * Returns a variable by name and specifying the type required
+ *
+ * @param name the name of the variable
+ * @param type the type of the variable
+ * @return the value of the given variable or <tt>null</tt> if there is no variable for the given name or
+ * <tt>null</tt> if it cannot be converted to the given type
+ */
+ <T> T getVariable(String name, Class<T> type);
+
+ /**
+ * Returns a variable by name and specifying the type required
+ *
+ * @param name the name of the variable
+ * @param defaultValue the default value to return if variable was absent
+ * @param type the type of the variable
+ * @return the value of the given variable or <tt>defaultValue</tt> if there is no variable for the
+ * given name or <tt>null</tt> if it cannot be converted to the given type
+ */
+ <T> T getVariable(String name, Object defaultValue, Class<T> type);
+
+ /**
+ * Sets a varialbe on the exchange
+ *
+ * @param name of the variable
+ * @param value the value of the variable
+ */
+ void setVariable(String name, Object value);
+
+ /**
+ * Removes the given variable
+ *
+ * @param name of the variable
+ * @return the old value of the variable
+ */
+ Object removeVariable(String name);
+
+ /**
+ * Remove all the variables matching a specific pattern
+ *
+ * @param pattern pattern of names
+ * @return boolean whether any variables matched
+ */
+ boolean removeVariables(String pattern);
+
+ /**
+ * Removes the variables that match the given <tt>pattern</tt>, except for the ones matching one or more
+ * <tt>excludePatterns</tt>
+ *
+ * @param pattern pattern of names that should be removed
+ * @param excludePatterns one or more pattern of variable names that should be excluded (= preserved)
+ * @return boolean whether any variables matched
+ */
+ boolean removeVariables(String pattern, String... excludePatterns);
+
+ /**
+ * Returns the variables
+ *
+ * @return the variables in a Map
+ */
+ Map<String, Object> getVariables();
+
+ /**
+ * Returns whether any variables have been set
+ *
+ * @return <tt>true</tt> if any variables has been set
+ */
+ boolean hasVariables();
+
/**
* Returns the inbound request message
*
diff --git a/core/camel-core/src/test/java/org/apache/camel/ExchangeTestSupport.java b/core/camel-core/src/test/java/org/apache/camel/ExchangeTestSupport.java
index 422d82d141f..872c588d6e3 100644
--- a/core/camel-core/src/test/java/org/apache/camel/ExchangeTestSupport.java
+++ b/core/camel-core/src/test/java/org/apache/camel/ExchangeTestSupport.java
@@ -44,6 +44,7 @@ public abstract class ExchangeTestSupport extends ContextTestSupport {
in.setBody("<hello id='m123'>world!</hello>");
exchange.setProperty("foobar", "cba");
+ exchange.setVariable("cheese", "gauda");
}
@Override
diff --git a/core/camel-core/src/test/java/org/apache/camel/impl/DefaultExchangeTest.java b/core/camel-core/src/test/java/org/apache/camel/impl/DefaultExchangeTest.java
index 6147d9877ec..b8aa1d8af06 100644
--- a/core/camel-core/src/test/java/org/apache/camel/impl/DefaultExchangeTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/impl/DefaultExchangeTest.java
@@ -142,6 +142,30 @@ public class DefaultExchangeTest extends ExchangeTestSupport {
assertEquals("banana", exchange.getProperty("beer", "banana", String.class));
}
+ @Test
+ public void testVariable() throws Exception {
+ exchange.removeVariable("cheese");
+ assertFalse(exchange.hasVariables());
+
+ exchange.setVariable("fruit", "apple");
+ assertTrue(exchange.hasVariables());
+
+ assertEquals("apple", exchange.getVariable("fruit"));
+ assertNull(exchange.getVariable("beer"));
+ assertNull(exchange.getVariable("beer", String.class));
+
+ // Current TypeConverter support to turn the null value to false of
+ // boolean,
+ // as assertEquals needs the Object as the parameter, we have to use
+ // Boolean.FALSE value in this case
+ assertEquals(Boolean.FALSE, exchange.getVariable("beer", boolean.class));
+ assertNull(exchange.getVariable("beer", Boolean.class));
+
+ assertEquals("apple", exchange.getVariable("fruit", String.class));
+ assertEquals("apple", exchange.getVariable("fruit", "banana", String.class));
+ assertEquals("banana", exchange.getVariable("beer", "banana", String.class));
+ }
+
@Test
public void testRemoveProperties() throws Exception {
exchange.removeProperty("foobar");
@@ -164,6 +188,28 @@ public class DefaultExchangeTest extends ExchangeTestSupport {
assertEquals("Africa", exchange.getProperty("zone", String.class));
}
+ @Test
+ public void testRemoveVariables() throws Exception {
+ exchange.removeVariable("cheese");
+ assertFalse(exchange.hasVariables());
+
+ exchange.setVariable("fruit", "apple");
+ exchange.setVariable("fruit1", "banana");
+ exchange.setVariable("zone", "Africa");
+ assertTrue(exchange.hasVariables());
+
+ assertEquals("apple", exchange.getVariable("fruit"));
+ assertEquals("banana", exchange.getVariable("fruit1"));
+ assertEquals("Africa", exchange.getVariable("zone"));
+
+ exchange.removeVariables("fr*");
+ assertTrue(exchange.hasVariables());
+ assertEquals(1, exchange.getVariables().size());
+ assertNull(exchange.getVariable("fruit", String.class));
+ assertNull(exchange.getVariable("fruit1", String.class));
+ assertEquals("Africa", exchange.getVariable("zone", String.class));
+ }
+
@Test
public void testRemoveAllProperties() throws Exception {
exchange.removeProperty("foobar");
@@ -179,6 +225,21 @@ public class DefaultExchangeTest extends ExchangeTestSupport {
assertEquals(0, exchange.getProperties().size());
}
+ @Test
+ public void testRemoveAllVariables() throws Exception {
+ exchange.removeVariable("cheese");
+ assertFalse(exchange.hasVariables());
+
+ exchange.setVariable("fruit", "apple");
+ exchange.setVariable("fruit1", "banana");
+ exchange.setVariable("zone", "Africa");
+ assertTrue(exchange.hasVariables());
+
+ exchange.removeVariables("*");
+ assertFalse(exchange.hasVariables());
+ assertEquals(0, exchange.getVariables().size());
+ }
+
@Test
public void testRemovePropertiesWithExclusion() throws Exception {
exchange.removeProperty("foobar");
@@ -204,6 +265,31 @@ public class DefaultExchangeTest extends ExchangeTestSupport {
assertEquals("Africa", exchange.getProperty("zone", String.class));
}
+ @Test
+ public void testRemoveVariablesWithExclusion() throws Exception {
+ exchange.removeVariable("cheese");
+ assertFalse(exchange.hasVariables());
+
+ exchange.setVariable("fruit", "apple");
+ exchange.setVariable("fruit1", "banana");
+ exchange.setVariable("fruit2", "peach");
+ exchange.setVariable("zone", "Africa");
+ assertTrue(exchange.hasVariables());
+
+ assertEquals("apple", exchange.getVariable("fruit"));
+ assertEquals("banana", exchange.getVariable("fruit1"));
+ assertEquals("peach", exchange.getVariable("fruit2"));
+ assertEquals("Africa", exchange.getVariable("zone"));
+
+ exchange.removeVariables("fr*", "fruit1", "fruit2");
+ assertTrue(exchange.hasVariables());
+ assertEquals(3, exchange.getVariables().size());
+ assertNull(exchange.getVariable("fruit", String.class));
+ assertEquals("banana", exchange.getVariable("fruit1", String.class));
+ assertEquals("peach", exchange.getVariable("fruit2", String.class));
+ assertEquals("Africa", exchange.getVariable("zone", String.class));
+ }
+
@Test
public void testRemovePropertiesPatternWithAllExcluded() throws Exception {
exchange.removeProperty("foobar");
@@ -229,6 +315,31 @@ public class DefaultExchangeTest extends ExchangeTestSupport {
assertEquals("Africa", exchange.getProperty("zone", String.class));
}
+ @Test
+ public void testRemoveVariablesPatternWithAllExcluded() throws Exception {
+ exchange.removeVariable("cheese");
+ assertFalse(exchange.hasVariables());
+
+ exchange.setVariable("fruit", "apple");
+ exchange.setVariable("fruit1", "banana");
+ exchange.setVariable("fruit2", "peach");
+ exchange.setVariable("zone", "Africa");
+ assertTrue(exchange.hasVariables());
+
+ assertEquals("apple", exchange.getVariable("fruit"));
+ assertEquals("banana", exchange.getVariable("fruit1"));
+ assertEquals("peach", exchange.getVariable("fruit2"));
+ assertEquals("Africa", exchange.getVariable("zone"));
+
+ exchange.removeVariables("fr*", "fruit", "fruit1", "fruit2", "zone");
+ assertTrue(exchange.hasVariables());
+ assertEquals(4, exchange.getVariables().size());
+ assertEquals("apple", exchange.getVariable("fruit", String.class));
+ assertEquals("banana", exchange.getVariable("fruit1", String.class));
+ assertEquals("peach", exchange.getVariable("fruit2", String.class));
+ assertEquals("Africa", exchange.getVariable("zone", String.class));
+ }
+
@Test
public void testRemoveInternalProperties() throws Exception {
exchange.setProperty(ExchangePropertyKey.CHARSET_NAME, "iso-8859-1");
@@ -265,6 +376,31 @@ public class DefaultExchangeTest extends ExchangeTestSupport {
assertEquals(3, exchange.getAllProperties().size());
}
+ @Test
+ public void testCopyExchangeWithVariables() throws Exception {
+ exchange.setVariable("beer", "Carlsberg");
+ assertEquals(2, exchange.getVariables().size());
+
+ Exchange copy = exchange.copy();
+ assertTrue(copy.hasVariables());
+ assertEquals(2, copy.getVariables().size());
+ assertEquals("gauda", copy.getVariable("cheese"));
+ assertEquals("Carlsberg", copy.getVariable("beer"));
+
+ exchange.setVariable("beer", "Heineken");
+ assertEquals("Carlsberg", copy.getVariable("beer"));
+
+ exchange.removeVariable("beer");
+ assertEquals(1, exchange.getVariables().size());
+ assertEquals("Carlsberg", copy.getVariable("beer"));
+ assertEquals(2, copy.getVariables().size());
+
+ exchange.removeVariables("*");
+ assertFalse(exchange.hasVariables());
+ assertTrue(copy.hasVariables());
+ assertEquals(2, copy.getVariables().size());
+ }
+
@Test
public void testInType() throws Exception {
exchange.setIn(new MyMessage(context));
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java b/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java
index ce3acd09507..b0e4ddbcffc 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java
@@ -53,6 +53,7 @@ abstract class AbstractExchange implements Exchange {
protected final CamelContext context;
protected Map<String, Object> properties; // create properties on-demand as we use internal properties mostly
+ protected Map<String, Object> variables; // create variables on-demand
protected Message in;
protected Message out;
protected Exception exception;
@@ -65,6 +66,7 @@ abstract class AbstractExchange implements Exchange {
private final ExtendedExchangeExtension privateExtension;
private RedeliveryTraitPayload externalRedelivered = RedeliveryTraitPayload.UNDEFINED_REDELIVERY;
+ // TODO: variables ?
protected AbstractExchange(CamelContext context, EnumMap<ExchangePropertyKey, Object> internalProperties,
Map<String, Object> properties) {
this.context = context;
@@ -124,10 +126,12 @@ abstract class AbstractExchange implements Exchange {
privateExtension.setErrorHandlerHandled(parent.getExchangeExtension().getErrorHandlerHandled());
privateExtension.setStreamCacheDisabled(parent.getExchangeExtension().isStreamCacheDisabled());
+ if (parent.hasVariables()) {
+ this.variables = safeCopyProperties(parent.variables);
+ }
if (parent.hasProperties()) {
this.properties = safeCopyProperties(parent.properties);
}
-
if (parent.hasSafeCopyProperties()) {
this.safeCopyProperties = parent.copySafeCopyProperties();
}
@@ -385,6 +389,109 @@ abstract class AbstractExchange implements Exchange {
return safeCopyProperties != null && !safeCopyProperties.isEmpty();
}
+ @Override
+ public Object getVariable(String name) {
+ if (variables != null) {
+ return variables.get(name);
+ }
+ return null;
+ }
+
+ @Override
+ public <T> T getVariable(String name, Class<T> type) {
+ Object value = getVariable(name);
+ return evalPropertyValue(type, value);
+ }
+
+ @Override
+ public <T> T getVariable(String name, Object defaultValue, Class<T> type) {
+ Object value = getVariable(name);
+ return evalPropertyValue(defaultValue, type, value);
+ }
+
+ @Override
+ public void setVariable(String name, Object value) {
+ if (value != null) {
+ // avoid the NullPointException
+ if (variables == null) {
+ this.variables = new ConcurrentHashMap<>(8);
+ }
+ variables.put(name, value);
+ } else if (variables != null) {
+ // if the value is null, we just remove the key from the map
+ variables.remove(name);
+ }
+ }
+
+ @Override
+ public Object removeVariable(String name) {
+ if (!hasVariables()) {
+ return null;
+ }
+ return variables.remove(name);
+ }
+
+ @Override
+ public boolean removeVariables(String pattern) {
+ return removeVariables(pattern, (String[]) null);
+ }
+
+ @Override
+ public boolean removeVariables(String pattern, String... excludePatterns) {
+ // special optimized
+ if (excludePatterns == null && "*".equals(pattern)) {
+ if (variables != null) {
+ variables.clear();
+ }
+ return true;
+ }
+
+ boolean matches = false;
+
+ // store keys to be removed as we cannot loop and remove at the same time in implementations such as HashMap
+ if (variables != null) {
+ Set<String> toBeRemoved = null;
+ for (String key : variables.keySet()) {
+ if (PatternHelper.matchPattern(key, pattern)) {
+ if (excludePatterns != null && PatternHelper.isExcludePatternMatch(key, excludePatterns)) {
+ continue;
+ }
+ matches = true;
+ if (toBeRemoved == null) {
+ toBeRemoved = new HashSet<>();
+ }
+ toBeRemoved.add(key);
+ }
+ }
+
+ if (matches) {
+ if (toBeRemoved.size() == variables.size()) {
+ // special optimization when all should be removed
+ variables.clear();
+ } else {
+ for (String key : toBeRemoved) {
+ variables.remove(key);
+ }
+ }
+ }
+ }
+
+ return matches;
+ }
+
+ @Override
+ public Map<String, Object> getVariables() {
+ if (variables == null) {
+ this.variables = new ConcurrentHashMap<>(8);
+ }
+ return variables;
+ }
+
+ @Override
+ public boolean hasVariables() {
+ return variables != null && !variables.isEmpty();
+ }
+
@Override
public Message getIn() {
if (in == null) {