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 <claus.ib...@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.