[camel] branch master updated: CAMEL-16462: camel-core - Optimize RecipientList EIP to reduce object allocations.

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[camel] branch master updated: CAMEL-16462: camel-core - Optimize RecipientList EIP to reduce object allocations.

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

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


The following commit(s) were added to refs/heads/master by this push:
     new dd763f6  CAMEL-16462: camel-core - Optimize RecipientList EIP to reduce object allocations.
dd763f6 is described below

commit dd763f6b3bf48e0d71dea904961d16c089171ea2
Author: Claus Ibsen <[hidden email]>
AuthorDate: Thu Apr 8 09:17:44 2021 +0200

    CAMEL-16462: camel-core - Optimize RecipientList EIP to reduce object allocations.
---
 .../org/apache/camel/support/ObjectHelper.java     | 67 ++++++++++++++--------
 .../java/org/apache/camel/util/StringHelper.java   | 17 ++++++
 .../org/apache/camel/util/StringHelperTest.java    | 14 +++++
 3 files changed, 73 insertions(+), 25 deletions(-)

diff --git a/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java
index 64f613f..c1b9767 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java
@@ -38,6 +38,7 @@ import org.apache.camel.Ordered;
 import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.TypeConverter;
 import org.apache.camel.util.Scanner;
+import org.apache.camel.util.StringHelper;
 
 /**
  * A number of useful helper methods for working with Objects
@@ -45,11 +46,12 @@ import org.apache.camel.util.Scanner;
 public final class ObjectHelper {
 
     static {
-        DEFAULT_PATTERN = Pattern.compile(",(?!(?:[^\\(,]|[^\\)],[^\\)])+\\))");
+        PARENTHESIS_PATTERN = Pattern.compile(",(?!(?:[^\\(,]|[^\\)],[^\\)])+\\))");
     }
 
-    private static final Pattern DEFAULT_PATTERN;
+    private static final Pattern PARENTHESIS_PATTERN;
     private static final String DEFAULT_DELIMITER = ",";
+    private static final char DEFAULT_DELIMITER_CHAR = ',';
 
     /**
      * Utility classes should not have a public constructor.
@@ -522,18 +524,25 @@ public final class ObjectHelper {
         if (value == null) {
             return Collections.emptyList();
         } else if (delimiter != null && (pattern || value.contains(delimiter))) {
+            // if its the default delimiter and the value has parenthesis
             if (DEFAULT_DELIMITER.equals(delimiter)) {
-                // we use the default delimiter which is a comma, then cater for bean expressions with OGNL
-                // which may have balanced parentheses pairs as well.
-                // if the value contains parentheses we need to balance those, to avoid iterating
-                // in the middle of parentheses pair, so use this regular expression (a bit hard to read)
-                // the regexp will split by comma, but honor parentheses pair that may include commas
-                // as well, eg if value = "bean=foo?method=killer(a,b),bean=bar?method=great(a,b)"
-                // then the regexp will split that into two:
-                // -> bean=foo?method=killer(a,b)
-                // -> bean=bar?method=great(a,b)
-                // http://stackoverflow.com/questions/1516090/splitting-a-title-into-separate-parts
-                return () -> new Scanner(value, DEFAULT_PATTERN);
+                if (value.indexOf('(') != -1 && value.indexOf(')') != -1) {
+                    // we use the default delimiter which is a comma, then cater for bean expressions with OGNL
+                    // which may have balanced parentheses pairs as well.
+                    // if the value contains parentheses we need to balance those, to avoid iterating
+                    // in the middle of parentheses pair, so use this regular expression (a bit hard to read)
+                    // the regexp will split by comma, but honor parentheses pair that may include commas
+                    // as well, eg if value = "bean=foo?method=killer(a,b),bean=bar?method=great(a,b)"
+                    // then the regexp will split that into two:
+                    // -> bean=foo?method=killer(a,b)
+                    // -> bean=bar?method=great(a,b)
+                    // http://stackoverflow.com/questions/1516090/splitting-a-title-into-separate-parts
+                    return () -> new Scanner(value, PARENTHESIS_PATTERN);
+                } else {
+                    // optimized split string on default delimiter
+                    int count = StringHelper.countChar(value, DEFAULT_DELIMITER_CHAR) + 1;
+                    return StringHelper.splitOnCharacterAsList(value, DEFAULT_DELIMITER_CHAR, count);
+                }
             }
             return () -> new Scanner(value, delimiter);
         } else if (allowEmptyValues || org.apache.camel.util.ObjectHelper.isNotEmpty(value)) {
@@ -735,20 +744,28 @@ public final class ObjectHelper {
             // this code is optimized to only use a Scanner if needed, eg there is a delimiter
 
             if (delimiter != null && (pattern || s.contains(delimiter))) {
+                // if its the default delimiter and the value has parenthesis
                 if (DEFAULT_DELIMITER.equals(delimiter)) {
-                    // we use the default delimiter which is a comma, then cater for bean expressions with OGNL
-                    // which may have balanced parentheses pairs as well.
-                    // if the value contains parentheses we need to balance those, to avoid iterating
-                    // in the middle of parentheses pair, so use this regular expression (a bit hard to read)
-                    // the regexp will split by comma, but honor parentheses pair that may include commas
-                    // as well, eg if value = "bean=foo?method=killer(a,b),bean=bar?method=great(a,b)"
-                    // then the regexp will split that into two:
-                    // -> bean=foo?method=killer(a,b)
-                    // -> bean=bar?method=great(a,b)
-                    // http://stackoverflow.com/questions/1516090/splitting-a-title-into-separate-parts
-                    return (Iterable<String>) () -> new Scanner(s, DEFAULT_PATTERN);
+                    if (s.indexOf('(') != -1 && s.indexOf(')') != -1) {
+                        // we use the default delimiter which is a comma, then cater for bean expressions with OGNL
+                        // which may have balanced parentheses pairs as well.
+                        // if the value contains parentheses we need to balance those, to avoid iterating
+                        // in the middle of parentheses pair, so use this regular expression (a bit hard to read)
+                        // the regexp will split by comma, but honor parentheses pair that may include commas
+                        // as well, eg if value = "bean=foo?method=killer(a,b),bean=bar?method=great(a,b)"
+                        // then the regexp will split that into two:
+                        // -> bean=foo?method=killer(a,b)
+                        // -> bean=bar?method=great(a,b)
+                        // http://stackoverflow.com/questions/1516090/splitting-a-title-into-separate-parts
+                        return (Iterable<String>) () -> new Scanner(s, PARENTHESIS_PATTERN);
+                    } else {
+                        // optimized split string on default delimiter
+                        int count = StringHelper.countChar(s, DEFAULT_DELIMITER_CHAR) + 1;
+                        return StringHelper.splitOnCharacterAsList(s, DEFAULT_DELIMITER_CHAR, count);
+                    }
+                } else {
+                    return (Iterable<String>) () -> new Scanner(s, delimiter);
                 }
-                return (Iterable<String>) () -> new Scanner(s, delimiter);
             } else {
                 return (Iterable<Object>) () -> {
                     // use a plain iterator that returns the value as is as there are only a single value
diff --git a/core/camel-util/src/main/java/org/apache/camel/util/StringHelper.java b/core/camel-util/src/main/java/org/apache/camel/util/StringHelper.java
index ddde325..1f35195 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/StringHelper.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/StringHelper.java
@@ -396,6 +396,23 @@ public final class StringHelper {
         return rc;
     }
 
+    public static List<String> splitOnCharacterAsList(String value, char needle, int count) {
+        List<String> rc = new ArrayList<>(count);
+        int pos = 0;
+        for (int i = 0; i < count; i++) {
+            int end = value.indexOf(needle, pos);
+            if (end != -1) {
+                String part = value.substring(pos, end);
+                pos = end + 1;
+                rc.add(part);
+            } else {
+                rc.add(value.substring(pos));
+                break;
+            }
+        }
+        return rc;
+    }
+
     /**
      * Removes any starting characters on the given text which match the given character
      *
diff --git a/core/camel-util/src/test/java/org/apache/camel/util/StringHelperTest.java b/core/camel-util/src/test/java/org/apache/camel/util/StringHelperTest.java
index 3c89c24..c4e6dc0 100644
--- a/core/camel-util/src/test/java/org/apache/camel/util/StringHelperTest.java
+++ b/core/camel-util/src/test/java/org/apache/camel/util/StringHelperTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.util;
 
+import java.util.List;
+
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
@@ -120,4 +122,16 @@ public class StringHelperTest {
         assertEquals("jms:queue:bar?blah=123", replaceFirst("jms:queue:foo?blah=123", "foo", "bar"));
         assertEquals("jms:queue:bar?blah=foo", replaceFirst("jms:queue:foo?blah=foo", "foo", "bar"));
     }
+
+    @Test
+    public void testSplitOnCharacterAsList() throws Exception {
+        List<String> list = splitOnCharacterAsList("foo", ',', 1);
+        assertEquals(1, list.size());
+        assertEquals("foo", list.get(0));
+
+        list = splitOnCharacterAsList("foo,bar", ',', 2);
+        assertEquals(2, list.size());
+        assertEquals("foo", list.get(0));
+        assertEquals("bar", list.get(1));
+    }
 }