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 2024/02/02 11:33:33 UTC

(camel) 01/13: CAMEL-20378: Languages should be thread-safe and be configured only via properties array, all in the same way.

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

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

commit c3205f090cd8c1d8e09713cc6e0287b135b2c036
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Fri Feb 2 09:50:14 2024 +0100

    CAMEL-20378: Languages should be thread-safe and be configured only via properties array, all in the same way.
---
 .../camel/language/tokenizer/TokenizeLanguage.java | 210 +++------------------
 .../org/apache/camel/builder/ExpressionClause.java |   4 +-
 .../org/apache/camel/language/TokenizerTest.java   |  37 ++--
 .../org/apache/camel/support/CustomizersTest.java  | 114 -----------
 .../camel/support/SingleInputLanguageSupport.java  |   1 +
 .../ROOT/pages/camel-4x-upgrade-guide-4_4.adoc     |   6 +
 6 files changed, 48 insertions(+), 324 deletions(-)

diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/tokenizer/TokenizeLanguage.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/tokenizer/TokenizeLanguage.java
index 9456066bc54..6c246662ab6 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/tokenizer/TokenizeLanguage.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/tokenizer/TokenizeLanguage.java
@@ -16,15 +16,11 @@
  */
 package org.apache.camel.language.tokenizer;
 
-import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
 import org.apache.camel.Predicate;
-import org.apache.camel.spi.PropertyConfigurer;
 import org.apache.camel.support.ExpressionToPredicateAdapter;
-import org.apache.camel.support.SingleInputLanguageSupport;
+import org.apache.camel.support.LanguageSupport;
 import org.apache.camel.support.builder.ExpressionBuilder;
-import org.apache.camel.support.component.PropertyConfigurerSupport;
-import org.apache.camel.util.ObjectHelper;
 
 /**
  * A language for tokenizer expressions.
@@ -39,89 +35,42 @@ import org.apache.camel.util.ObjectHelper;
  * <tt>token</tt> and <tt>endToken</tt>. And the <tt>xml</tt> mode supports the <tt>inheritNamespaceTagName</tt> option.
  */
 @org.apache.camel.spi.annotations.Language("tokenize")
-public class TokenizeLanguage extends SingleInputLanguageSupport implements PropertyConfigurer {
+public class TokenizeLanguage extends LanguageSupport {
 
-    private String token;
-    private String endToken;
-    private String inheritNamespaceTagName;
-    private boolean regex;
-    private boolean xml;
-    private boolean includeTokens;
-    private String group;
-    private String groupDelimiter;
-    private boolean skipFirst;
+    @Override
+    public Predicate createPredicate(String expression) {
+        return ExpressionToPredicateAdapter.toPredicate(createExpression(expression));
+    }
 
     @Override
-    public boolean configure(CamelContext camelContext, Object target, String name, Object value, boolean ignoreCase) {
-        if (target != this) {
-            throw new IllegalStateException("Can only configure our own instance !");
-        }
-        switch (ignoreCase ? name.toLowerCase() : name) {
-            case "token":
-                setToken(PropertyConfigurerSupport.property(camelContext, String.class, value));
-                return true;
-            case "endtoken":
-            case "endToken":
-                setEndToken(PropertyConfigurerSupport.property(camelContext, String.class, value));
-                return true;
-            case "inheritnamespacetagname":
-            case "inheritNamespaceTagName":
-                setInheritNamespaceTagName(PropertyConfigurerSupport.property(camelContext, String.class, value));
-                return true;
-            case "headername":
-            case "headerName":
-                setHeaderName(PropertyConfigurerSupport.property(camelContext, String.class, value));
-                return true;
-            case "propertyname":
-            case "propertyName":
-                setPropertyName(PropertyConfigurerSupport.property(camelContext, String.class, value));
-                return true;
-            case "variablename":
-            case "variableName":
-                setVariableName(PropertyConfigurerSupport.property(camelContext, String.class, value));
-                return true;
-            case "regex":
-                setRegex(PropertyConfigurerSupport.property(camelContext, Boolean.class, value));
-                return true;
-            case "xml":
-                setXml(PropertyConfigurerSupport.property(camelContext, Boolean.class, value));
-                return true;
-            case "includetokens":
-            case "includeTokens":
-                setIncludeTokens(PropertyConfigurerSupport.property(camelContext, Boolean.class, value));
-                return true;
-            case "group":
-                setGroup(PropertyConfigurerSupport.property(camelContext, String.class, value));
-                return true;
-            case "groupdelimiter":
-            case "groupDelimiter":
-                setGroupDelimiter(PropertyConfigurerSupport.property(camelContext, String.class, value));
-                return true;
-            case "skipfirst":
-            case "skipFirst":
-                setSkipFirst(PropertyConfigurerSupport.property(camelContext, Boolean.class, value));
-                return true;
-            default:
-                return false;
-        }
+    public Expression createExpression(String expression) {
+        return createExpression(expression, null);
     }
 
     @Override
-    public Predicate createPredicate(String expression) {
-        return ExpressionToPredicateAdapter.toPredicate(createExpression(expression));
+    public Predicate createPredicate(String expression, Object[] properties) {
+        return ExpressionToPredicateAdapter.toPredicate(createExpression(expression, properties));
     }
 
-    /**
-     * Creates a tokenize expression.
-     */
-    public Expression createExpression() {
-        ObjectHelper.notNull(token, "token");
+    @Override
+    public Expression createExpression(String expression, Object[] properties) {
+        String token = property(String.class, properties, 0, expression);
+        String endToken = property(String.class, properties, 1, null);
+        String inheritNamespaceTagName = property(String.class, properties, 2, null);
+        String headerName = property(String.class, properties, 3, null);
+        String groupDelimiter = property(String.class, properties, 4, null);
+        boolean regex = property(boolean.class, properties, 5, false);
+        boolean xml = property(boolean.class, properties, 6, false);
+        boolean includeTokens = property(boolean.class, properties, 7, false);
+        String group = property(String.class, properties, 8, null);
+        boolean skipFirst = property(boolean.class, properties, 9, false);
+        String propertyName = property(String.class, properties, 10, null);
+        String variableName = property(String.class, properties, 11, null);
 
-        // validate some invalid combinations
         if (endToken != null && inheritNamespaceTagName != null) {
             throw new IllegalArgumentException("Cannot have both xml and pair tokenizer enabled.");
         }
-        if (isXml() && (endToken != null || includeTokens)) {
+        if (xml && (endToken != null || includeTokens)) {
             throw new IllegalArgumentException("Cannot have both xml and pair tokenizer enabled.");
         }
         if (endToken == null && includeTokens) {
@@ -129,7 +78,7 @@ public class TokenizeLanguage extends SingleInputLanguageSupport implements Prop
         }
 
         Expression answer = null;
-        if (isXml()) {
+        if (xml) {
             answer = ExpressionBuilder.tokenizeXMLExpression(token, inheritNamespaceTagName);
         } else if (endToken != null) {
             answer = ExpressionBuilder.tokenizePairExpression(token, endToken, includeTokens);
@@ -138,7 +87,7 @@ public class TokenizeLanguage extends SingleInputLanguageSupport implements Prop
         if (answer == null) {
             // use the regular tokenizer
             final Expression exp
-                    = ExpressionBuilder.singleInputExpression(getVariableName(), getHeaderName(), getPropertyName());
+                    = ExpressionBuilder.singleInputExpression(variableName, headerName, propertyName);
             if (regex) {
                 answer = ExpressionBuilder.regexTokenizeExpression(exp, token);
             } else {
@@ -152,7 +101,7 @@ public class TokenizeLanguage extends SingleInputLanguageSupport implements Prop
 
         // if group then wrap answer in group expression
         if (group != null) {
-            if (isXml()) {
+            if (xml) {
                 answer = ExpressionBuilder.groupXmlIteratorExpression(answer, group);
             } else {
                 String delim = groupDelimiter != null ? groupDelimiter : token;
@@ -164,110 +113,7 @@ public class TokenizeLanguage extends SingleInputLanguageSupport implements Prop
             answer.init(getCamelContext());
         }
         return answer;
-    }
-
-    @Override
-    public Expression createExpression(String expression) {
-        if (ObjectHelper.isNotEmpty(expression)) {
-            this.token = expression;
-        }
-        return createExpression();
-    }
-
-    @Override
-    public Predicate createPredicate(String expression, Object[] properties) {
-        return ExpressionToPredicateAdapter.toPredicate(createExpression(expression, properties));
-    }
-
-    @Override
-    public Expression createExpression(String expression, Object[] properties) {
-        TokenizeLanguage answer = new TokenizeLanguage();
-        answer.setToken(property(String.class, properties, 0, token));
-        answer.setEndToken(property(String.class, properties, 1, endToken));
-        answer.setInheritNamespaceTagName(
-                property(String.class, properties, 2, inheritNamespaceTagName));
-        answer.setHeaderName(property(String.class, properties, 3, getHeaderName()));
-        answer.setGroupDelimiter(property(String.class, properties, 4, groupDelimiter));
-        answer.setRegex(property(boolean.class, properties, 5, regex));
-        answer.setXml(property(boolean.class, properties, 6, xml));
-        answer.setIncludeTokens(property(boolean.class, properties, 7, includeTokens));
-        answer.setGroup(property(String.class, properties, 8, group));
-        answer.setSkipFirst(property(boolean.class, properties, 9, skipFirst));
-        answer.setPropertyName(property(String.class, properties, 10, getPropertyName()));
-        answer.setVariableName(property(String.class, properties, 11, getVariableName()));
-        return answer.createExpression(expression);
-    }
-
-    public String getToken() {
-        return token;
-    }
-
-    public void setToken(String token) {
-        this.token = token;
-    }
-
-    public String getEndToken() {
-        return endToken;
-    }
-
-    public void setEndToken(String endToken) {
-        this.endToken = endToken;
-    }
-
-    public boolean isRegex() {
-        return regex;
-    }
-
-    public void setRegex(boolean regex) {
-        this.regex = regex;
-    }
-
-    public String getInheritNamespaceTagName() {
-        return inheritNamespaceTagName;
-    }
-
-    public void setInheritNamespaceTagName(String inheritNamespaceTagName) {
-        this.inheritNamespaceTagName = inheritNamespaceTagName;
-    }
-
-    public boolean isXml() {
-        return xml;
-    }
-
-    public void setXml(boolean xml) {
-        this.xml = xml;
-    }
-
-    public boolean isIncludeTokens() {
-        return includeTokens;
-    }
-
-    public void setIncludeTokens(boolean includeTokens) {
-        this.includeTokens = includeTokens;
-    }
-
-    public String getGroup() {
-        return group;
-    }
-
-    public void setGroup(String group) {
-        this.group = "0".equals(group) ? null : group;
-    }
-
-    public String getGroupDelimiter() {
-        return groupDelimiter;
-    }
-
-    public void setGroupDelimiter(String groupDelimiter) {
-        this.groupDelimiter = groupDelimiter;
-    }
-
-    public boolean isSkipFirst() {
-        return skipFirst;
-    }
 
-    public void setSkipFirst(boolean skipFirst) {
-        this.skipFirst = skipFirst;
     }
 
 }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClause.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClause.java
index 68f944bf094..9a4e468b23e 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClause.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClause.java
@@ -882,7 +882,7 @@ public class ExpressionClause<T> implements Expression, Predicate {
      * @return         the builder to continue processing the DSL
      */
     public T tokenizeXML(String tagName) {
-        return tokenizeXML(tagName, null);
+        return delegate.tokenizeXMLPair(tagName, null, null);
     }
 
     /**
@@ -904,7 +904,7 @@ public class ExpressionClause<T> implements Expression, Predicate {
      * @return                         the builder to continue processing the DSL
      */
     public T tokenizeXML(String tagName, String inheritNamespaceTagName) {
-        return tokenizeXML(tagName, inheritNamespaceTagName, 0);
+        return delegate.tokenizeXMLPair(tagName, inheritNamespaceTagName, null);
     }
 
     /**
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/TokenizerTest.java b/core/camel-core/src/test/java/org/apache/camel/language/TokenizerTest.java
index 2e4f055e929..b0b99a9474f 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/TokenizerTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/TokenizerTest.java
@@ -25,9 +25,7 @@ import org.apache.camel.language.tokenizer.TokenizeLanguage;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class TokenizerTest extends ExchangeTestSupport {
 
@@ -37,9 +35,8 @@ public class TokenizerTest extends ExchangeTestSupport {
 
     Expression tokenize(String token, boolean regex) {
         TokenizeLanguage language = new TokenizeLanguage();
-        language.setToken(token);
-        language.setRegex(regex);
-        return language.createExpression(null);
+        language.setCamelContext(context);
+        return language.createExpression(null, new Object[] { token, null, null, null, null, regex });
     }
 
     Expression tokenize(String headerName, String token) {
@@ -48,26 +45,21 @@ public class TokenizerTest extends ExchangeTestSupport {
 
     Expression tokenize(String headerName, String token, boolean regex) {
         TokenizeLanguage language = new TokenizeLanguage();
-        language.setHeaderName(headerName);
-        language.setToken(token);
-        language.setRegex(regex);
-        return language.createExpression(null);
+        language.setCamelContext(context);
+        return language.createExpression(null, new Object[] { token, null, null, headerName });
     }
 
     Expression tokenizePair(String startToken, String endToken, boolean includeTokens) {
         TokenizeLanguage language = new TokenizeLanguage();
-        language.setToken(startToken);
-        language.setEndToken(endToken);
-        language.setIncludeTokens(includeTokens);
-        return language.createExpression(null);
+        language.setCamelContext(context);
+        return language.createExpression(null,
+                new Object[] { startToken, endToken, null, null, null, null, null, includeTokens });
     }
 
     Expression tokenizeXML(String tagName, String inheritNamespaceTagName) {
         TokenizeLanguage language = new TokenizeLanguage();
-        language.setToken(tagName);
-        language.setInheritNamespaceTagName(inheritNamespaceTagName);
-        language.setXml(true);
-        return language.createExpression(null);
+        language.setCamelContext(context);
+        return language.createExpression(null, new Object[] { tagName, null, inheritNamespaceTagName, null, null, null, true });
     }
 
     @Override
@@ -145,10 +137,8 @@ public class TokenizerTest extends ExchangeTestSupport {
     @Test
     public void testTokenizeManualConfiguration() throws Exception {
         TokenizeLanguage lan = new TokenizeLanguage();
-        lan.setHeaderName("names");
-        lan.setRegex(false);
-        lan.setToken(",");
-        Expression exp = lan.createExpression();
+        lan.setCamelContext(context);
+        Expression exp = lan.createExpression(null, new Object[] { ",", null, null, "names" });
         exp.init(context);
 
         List<?> names = exp.evaluate(exchange, List.class);
@@ -157,11 +147,6 @@ public class TokenizerTest extends ExchangeTestSupport {
         assertEquals("Claus", names.get(0));
         assertEquals("James", names.get(1));
         assertEquals("Willem", names.get(2));
-
-        assertEquals("names", lan.getHeaderName());
-        assertEquals(",", lan.getToken());
-        assertFalse(lan.isRegex());
-        assertTrue(lan.isSingleton());
     }
 
     @Test
diff --git a/core/camel-core/src/test/java/org/apache/camel/support/CustomizersTest.java b/core/camel-core/src/test/java/org/apache/camel/support/CustomizersTest.java
index ec590239e92..88d7a37d9de 100644
--- a/core/camel-core/src/test/java/org/apache/camel/support/CustomizersTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/support/CustomizersTest.java
@@ -25,13 +25,10 @@ import java.util.stream.Stream;
 import org.apache.camel.Exchange;
 import org.apache.camel.component.log.LogComponent;
 import org.apache.camel.impl.DefaultCamelContext;
-import org.apache.camel.language.tokenizer.TokenizeLanguage;
 import org.apache.camel.spi.ComponentCustomizer;
 import org.apache.camel.spi.DataFormat;
 import org.apache.camel.spi.DataFormatCustomizer;
 import org.apache.camel.spi.DataFormatFactory;
-import org.apache.camel.spi.Language;
-import org.apache.camel.spi.LanguageCustomizer;
 import org.apache.camel.support.processor.DefaultExchangeFormatter;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -305,117 +302,6 @@ public class CustomizersTest {
         assertEquals(1, ((MyDataFormat) df1).getId());
     }
 
-    // *****************************
-    //
-    // Language
-    //
-    // *****************************
-
-    @Test
-    public void testLanguageCustomizationFromRegistry() {
-        AtomicInteger counter = new AtomicInteger();
-
-        DefaultCamelContext context = new DefaultCamelContext();
-        context.getCamelContextExtension().getRegistry().bind(
-                "tokenize",
-                new TokenizeLanguage());
-        context.getCamelContextExtension().getRegistry().bind(
-                "tokenize-customizer",
-                LanguageCustomizer.forType(TokenizeLanguage.class,
-                        target -> target.setGroup(Integer.toString(counter.incrementAndGet()))));
-
-        Language l1 = context.resolveLanguage("tokenize");
-        assertTrue(l1 instanceof TokenizeLanguage);
-        assertEquals("1", ((TokenizeLanguage) l1).getGroup());
-
-        Language l2 = context.resolveLanguage("tokenize");
-        assertTrue(l2 instanceof TokenizeLanguage);
-        assertEquals("1", ((TokenizeLanguage) l2).getGroup());
-
-        // as the language is resolved via the registry, then the instance is the same
-        // even if it is not a singleton
-        assertSame(l1, l2);
-    }
-
-    @Test
-    public void testLanguageCustomizationFromResource() {
-        AtomicInteger counter = new AtomicInteger();
-
-        DefaultCamelContext context = new DefaultCamelContext();
-        context.getCamelContextExtension().getRegistry().bind(
-                "tokenize-customizer",
-                LanguageCustomizer.forType(TokenizeLanguage.class,
-                        target -> target.setGroup(Integer.toString(counter.incrementAndGet()))));
-
-        // singleton language so its customized once
-        Language l1 = context.resolveLanguage("tokenize");
-        assertTrue(l1 instanceof TokenizeLanguage);
-        assertEquals("1", ((TokenizeLanguage) l1).getGroup());
-
-        Language l2 = context.resolveLanguage("tokenize");
-        assertTrue(l2 instanceof TokenizeLanguage);
-        assertEquals("1", ((TokenizeLanguage) l2).getGroup());
-
-        assertSame(l1, l2);
-    }
-
-    @Test
-    public void testLanguageCustomizationFromResourceWithFilter() {
-        AtomicInteger counter = new AtomicInteger();
-
-        DefaultCamelContext context = new DefaultCamelContext();
-        context.getCamelContextExtension().getRegistry().bind(
-                "customizer-filter",
-                LanguageCustomizer.Policy.none());
-        context.getCamelContextExtension().getRegistry().bind(
-                "tokenize-customizer",
-                LanguageCustomizer.forType(TokenizeLanguage.class,
-                        target -> target.setGroup(Integer.toString(counter.incrementAndGet()))));
-
-        // singleton language so its customized once
-        Language l1 = context.resolveLanguage("tokenize");
-        assertTrue(l1 instanceof TokenizeLanguage);
-        assertNull(((TokenizeLanguage) l1).getGroup());
-
-        Language l2 = context.resolveLanguage("tokenize");
-        assertTrue(l2 instanceof TokenizeLanguage);
-        assertNull(((TokenizeLanguage) l2).getGroup());
-
-        assertSame(l1, l2);
-    }
-
-    @ParameterizedTest
-    @MethodSource("disableLanguageCustomizationProperties")
-    public void testLanguageCustomizationDisabledByProperty(Properties initialProperties) {
-        DefaultCamelContext context = new DefaultCamelContext();
-        context.getPropertiesComponent().setInitialProperties(initialProperties);
-
-        context.getCamelContextExtension().getRegistry().bind(
-                "customizer-filter",
-                new CustomizersSupport.LanguageCustomizationEnabledPolicy());
-        context.getCamelContextExtension().getRegistry().bind(
-                "tokenize-customizer",
-                LanguageCustomizer.forType(TokenizeLanguage.class, target -> target.setGroup("something")));
-
-        assertNotEquals("something", ((TokenizeLanguage) context.resolveLanguage("tokenize")).getGroup());
-    }
-
-    @ParameterizedTest
-    @MethodSource("enableLanguageCustomizationProperties")
-    public void testLanguageCustomizationEnabledByProperty(Properties initialProperties) {
-        DefaultCamelContext context = new DefaultCamelContext();
-        context.getPropertiesComponent().setInitialProperties(initialProperties);
-
-        context.getCamelContextExtension().getRegistry().bind(
-                "customizer-filter",
-                new CustomizersSupport.LanguageCustomizationEnabledPolicy());
-        context.getCamelContextExtension().getRegistry().bind(
-                "tokenize-customizer",
-                LanguageCustomizer.forType(TokenizeLanguage.class, target -> target.setGroup("something")));
-
-        assertEquals("something", ((TokenizeLanguage) context.resolveLanguage("tokenize")).getGroup());
-    }
-
     // *****************************
     //
     // Model
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/SingleInputLanguageSupport.java b/core/camel-support/src/main/java/org/apache/camel/support/SingleInputLanguageSupport.java
index eabffb5a5a7..4b84833105d 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/SingleInputLanguageSupport.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/SingleInputLanguageSupport.java
@@ -21,6 +21,7 @@ import org.apache.camel.spi.Language;
 /**
  * Base class for {@link Language} implementations that support different sources of input data.
  */
+@Deprecated
 public abstract class SingleInputLanguageSupport extends LanguageSupport {
 
     private String variableName;
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_4.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_4.adoc
index f2e2e1b353c..127c4768f2d 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_4.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_4.adoc
@@ -21,6 +21,12 @@ Durations and some time-related information were consolidated in a new internal
 
 The `lookup` method in `org.apache.camel.component.properties.PropertiesLookup` now has a 2nd parameter for the default value.
 
+==== Languages
+
+The way languages are created and configured by Camel has been refactored to be aligned and avoid a thread-safety issues
+when using Java DSL. All the setter/getter on the `Language` classes has been removed (such as `TokenizeLanguage`) as the options are configured in the DSL
+in a consistent and thread-safe manner.
+
 ==== WireTap EIP
 
 The copied exchange is no longer having exchange property CORRELATION_ID set that links to the original exchange.