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 7a8ecca4236 CAMEL-20378: Languages should be thread-safe and be 
configured only via properties array, all in the same way. (#13004)
7a8ecca4236 is described below

commit 7a8ecca42363837330ede7ad1d9b94c971ea80c6
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Feb 5 11:43:02 2024 +0100

    CAMEL-20378: Languages should be thread-safe and be configured only via 
properties array, all in the same way. (#13004)
---
 .../language/xtokenizer/XMLTokenizeLanguage.java   | 41 +++++++++-
 .../camel/language/tokenizer/TokenizeLanguage.java | 32 ++++++++
 .../camel/language/TokenizeLanguageTest.java       |  7 +-
 .../apache/camel/support/IteratorConvertTo.java    | 93 ++++++++++++++++++++++
 4 files changed, 167 insertions(+), 6 deletions(-)

diff --git 
a/components/camel-stax/src/main/java/org/apache/camel/language/xtokenizer/XMLTokenizeLanguage.java
 
b/components/camel-stax/src/main/java/org/apache/camel/language/xtokenizer/XMLTokenizeLanguage.java
index d87499136be..5d41314788d 100644
--- 
a/components/camel-stax/src/main/java/org/apache/camel/language/xtokenizer/XMLTokenizeLanguage.java
+++ 
b/components/camel-stax/src/main/java/org/apache/camel/language/xtokenizer/XMLTokenizeLanguage.java
@@ -16,10 +16,15 @@
  */
 package org.apache.camel.language.xtokenizer;
 
+import java.util.Iterator;
 import java.util.Map;
 
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
 import org.apache.camel.Expression;
 import org.apache.camel.spi.annotations.Language;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.support.IteratorConvertTo;
 import org.apache.camel.support.SingleInputTypedLanguageSupport;
 import org.apache.camel.support.builder.Namespaces;
 
@@ -45,9 +50,11 @@ public class XMLTokenizeLanguage extends 
SingleInputTypedLanguageSupport {
 
     @Override
     public Expression createExpression(Expression source, String expression, 
Object[] properties) {
+        Class<?> type = property(Class.class, properties, 0, null);
         Character mode = property(Character.class, properties, 4, "i");
-        XMLTokenExpressionIterator answer = new 
XMLTokenExpressionIterator(source, expression, mode);
-        answer.setGroup(property(int.class, properties, 5, 1));
+
+        XMLTokenExpressionIterator xml = new 
XMLTokenExpressionIterator(source, expression, mode);
+        xml.setGroup(property(int.class, properties, 5, 1));
         Object obj = properties[6];
         if (obj != null) {
             Namespaces ns;
@@ -60,8 +67,36 @@ public class XMLTokenizeLanguage extends 
SingleInputTypedLanguageSupport {
                 throw new IllegalArgumentException(
                         "Namespaces is not instance of java.util.Map or " + 
Namespaces.class.getName());
             }
-            answer.setNamespaces(ns.getNamespaces());
+            xml.setNamespaces(ns.getNamespaces());
+        }
+        Expression answer = xml;
+
+        if (type != null && type != Object.class) {
+            // wrap iterator in a converter
+            final Expression delegate = xml;
+            answer = new ExpressionAdapter() {
+                @Override
+                public Object evaluate(Exchange exchange) {
+                    Object value = delegate.evaluate(exchange, Object.class);
+                    if (value instanceof Iterator<?> it) {
+                        value = new IteratorConvertTo(exchange, it, type);
+                    }
+                    return value;
+                }
+
+                @Override
+                public void init(CamelContext context) {
+                    super.init(context);
+                    delegate.init(context);
+                }
+
+                @Override
+                public String toString() {
+                    return delegate.toString();
+                }
+            };
         }
+
         if (getCamelContext() != null) {
             answer.init(getCamelContext());
         }
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 ca9c7c5286b..ad2986db362 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,7 +16,13 @@
  */
 package org.apache.camel.language.tokenizer;
 
+import java.util.Iterator;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
 import org.apache.camel.Expression;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.support.IteratorConvertTo;
 import org.apache.camel.support.SingleInputTypedLanguageSupport;
 import org.apache.camel.support.builder.ExpressionBuilder;
 
@@ -94,6 +100,32 @@ public class TokenizeLanguage extends 
SingleInputTypedLanguageSupport {
             }
         }
 
+        if (type != null && type != Object.class) {
+            // wrap iterator in a converter
+            final Expression delegate = answer;
+            answer = new ExpressionAdapter() {
+                @Override
+                public Object evaluate(Exchange exchange) {
+                    Object value = delegate.evaluate(exchange, Object.class);
+                    if (value instanceof Iterator<?> it) {
+                        value = new IteratorConvertTo(exchange, it, type);
+                    }
+                    return value;
+                }
+
+                @Override
+                public void init(CamelContext context) {
+                    super.init(context);
+                    delegate.init(context);
+                }
+
+                @Override
+                public String toString() {
+                    return delegate.toString();
+                }
+            };
+        }
+
         if (getCamelContext() != null) {
             answer.init(getCamelContext());
         }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/language/TokenizeLanguageTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/language/TokenizeLanguageTest.java
index 7818da28363..e8709be396e 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/language/TokenizeLanguageTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/language/TokenizeLanguageTest.java
@@ -16,8 +16,9 @@
  */
 package org.apache.camel.language;
 
+import java.util.Iterator;
+
 import org.apache.camel.model.language.TokenizerExpression;
-import org.apache.camel.util.Scanner;
 
 /**
  * Ensures that the "tokenize" language is compliant with the single input 
expectations.
@@ -30,7 +31,7 @@ class TokenizeLanguageTest extends 
AbstractSingleInputTypedLanguageTest<Tokenize
 
     @Override
     protected TestContext testWithTypeContext() {
-        return new TestContext("1\n", "1", String.class);
+        return new TestContext("1\n", 1, Integer.class);
     }
 
     @Override
@@ -46,7 +47,7 @@ class TokenizeLanguageTest extends 
AbstractSingleInputTypedLanguageTest<Tokenize
     @Override
     protected void assertBodyReceived(Object expected, Object body) {
         // uses an scanner, so we need to walk it to get the body
-        if (body instanceof Scanner it) {
+        if (body instanceof Iterator it) {
             body = it.next();
         }
         super.assertBodyReceived(expected, body);
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/IteratorConvertTo.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/IteratorConvertTo.java
new file mode 100644
index 00000000000..269a4973571
--- /dev/null
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/IteratorConvertTo.java
@@ -0,0 +1,93 @@
+/*
+ * 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.support;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Iterator;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.TypeConverter;
+import org.apache.camel.util.IOHelper;
+
+/**
+ * Wraps an {@link Iterator} so its returned values are automatically 
converted to a given type.
+ */
+public final class IteratorConvertTo implements Iterator<Object>, Closeable {
+
+    private final Exchange exchange;
+    private final TypeConverter converter;
+    private final Iterator<?> it;
+    private final Class<?> type;
+    private boolean closed;
+
+    /**
+     * Creates the convert iterator.
+     *
+     * @param exchange the exchange
+     * @param it       the iterator to wrap
+     * @param type     the type to convert to
+     */
+    public IteratorConvertTo(Exchange exchange, Iterator<?> it, Class<?> type) 
{
+        this.exchange = exchange;
+        this.converter = exchange.getContext().getTypeConverter();
+        this.it = it;
+        this.type = type;
+    }
+
+    @Override
+    public void close() throws IOException {
+        try {
+            IOHelper.closeIterator(it);
+        } finally {
+            // we are now closed
+            closed = true;
+        }
+    }
+
+    @Override
+    public boolean hasNext() {
+        if (closed) {
+            return false;
+        }
+
+        boolean answer = it.hasNext();
+        if (!answer) {
+            // auto close
+            try {
+                close();
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+        return answer;
+    }
+
+    @Override
+    public Object next() {
+        Object next = it.next();
+        if (next != null) {
+            next = converter.convertTo(type, exchange, next);
+        }
+        return next;
+    }
+
+    @Override
+    public void remove() {
+        it.remove();
+    }
+}

Reply via email to