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 2019/11/06 07:22:14 UTC

[camel] branch master updated: CAMEL-14136: Fix ConcurrentModificationException in Scanner (#3313)

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 3aa3cc4  CAMEL-14136: Fix ConcurrentModificationException in Scanner (#3313)
3aa3cc4 is described below

commit 3aa3cc47247dee0e7e8a526cd55981dc1dd36db6
Author: Zohhak <ch...@gmx.de>
AuthorDate: Wed Nov 6 08:21:57 2019 +0100

    CAMEL-14136: Fix ConcurrentModificationException in Scanner (#3313)
    
    Internal use of non threadsafe map wrapped with synchronized block,
    extracted some static patterns into class static block
---
 .../org/apache/camel/support/ObjectHelper.java     | 20 ++++++++---------
 .../main/java/org/apache/camel/util/Scanner.java   | 25 ++++++++++++++++------
 2 files changed, 28 insertions(+), 17 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 fd51dcb..bae4ac5 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
@@ -25,6 +25,7 @@ import java.util.Collections;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 import java.util.concurrent.Callable;
+import java.util.regex.Pattern;
 import java.util.stream.Stream;
 
 import org.w3c.dom.Node;
@@ -42,7 +43,12 @@ import org.apache.camel.util.StringHelper;
  * A number of useful helper methods for working with Objects
  */
 public final class ObjectHelper {
+  
+    static {
+        DEFAULT_PATTERN = Pattern.compile(",(?!(?:[^\\(,]|[^\\)],[^\\)])+\\))");
+    }
 
+    private static final Pattern DEFAULT_PATTERN;
     private static final String DEFAULT_DELIMITER = ",";
 
     /**
@@ -356,7 +362,6 @@ public final class ObjectHelper {
         if (value == null) {
             return Collections.emptyList();
         } else if (delimiter != null && (pattern || value.contains(delimiter))) {
-            String del;
             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.
@@ -368,11 +373,9 @@ public final class ObjectHelper {
                 // -> bean=foo?method=killer(a,b)
                 // -> bean=bar?method=great(a,b)
                 // http://stackoverflow.com/questions/1516090/splitting-a-title-into-separate-parts
-                del = ",(?!(?:[^\\(,]|[^\\)],[^\\)])+\\))";
-            } else {
-                del = delimiter;
+                return () -> new Scanner(value, DEFAULT_PATTERN);
             }
-            return () -> new Scanner(value, del);
+            return () -> new Scanner(value, delimiter);
         } else if (allowEmptyValues || org.apache.camel.util.ObjectHelper.isNotEmpty(value)) {
             return Collections.singletonList(value);
         } else {
@@ -574,7 +577,6 @@ 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))) {
-                String del;
                 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.
@@ -586,11 +588,9 @@ public final class ObjectHelper {
                     // -> bean=foo?method=killer(a,b)
                     // -> bean=bar?method=great(a,b)
                     // http://stackoverflow.com/questions/1516090/splitting-a-title-into-separate-parts
-                    del = ",(?!(?:[^\\(,]|[^\\)],[^\\)])+\\))";
-                } else {
-                    del = delimiter;
+                    return (Iterable<String>) () -> new Scanner(s, DEFAULT_PATTERN);
                 }
-                return (Iterable<String>) () -> new Scanner(s, del);
+                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/Scanner.java b/core/camel-util/src/main/java/org/apache/camel/util/Scanner.java
index 74b2daf..f305498 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/Scanner.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/Scanner.java
@@ -44,6 +44,11 @@ import java.util.regex.Pattern;
 import static org.apache.camel.util.BufferCaster.cast;
 
 public final class Scanner implements Iterator<String>, Closeable {
+  
+    static {
+        WHITESPACE_PATTERN = Pattern.compile("\\s+");
+        FIND_ANY_PATTERN = Pattern.compile("(?s).*");
+    }
 
     private static final Map<String, Pattern> CACHE = new LinkedHashMap<String, Pattern>() {
         @Override
@@ -51,10 +56,10 @@ public final class Scanner implements Iterator<String>, Closeable {
             return size() >= 7;
         }
     };
+    
+    private static final Pattern WHITESPACE_PATTERN;
 
-    private static final String WHITESPACE_PATTERN = "\\s+";
-
-    private static final String FIND_ANY_PATTERN = "(?s).*";
+    private static final Pattern FIND_ANY_PATTERN;
 
     private static final int BUFFER_SIZE = 1024;
 
@@ -81,6 +86,10 @@ public final class Scanner implements Iterator<String>, Closeable {
     public Scanner(String source, String pattern) {
         this(new StringReader(Objects.requireNonNull(source, "source")), cachePattern(pattern));
     }
+    
+    public Scanner(String source, Pattern pattern) {
+        this(new StringReader(Objects.requireNonNull(source, "source")), pattern);
+  }
 
     public Scanner(ReadableByteChannel source, String charsetName, String pattern) {
         this(Channels.newReader(Objects.requireNonNull(source, "source"), toDecoder(charsetName), -1), cachePattern(pattern));
@@ -92,7 +101,7 @@ public final class Scanner implements Iterator<String>, Closeable {
 
     private Scanner(Readable source, Pattern pattern) {
         this.source = source;
-        delimPattern = pattern != null ? pattern : cachePattern(WHITESPACE_PATTERN);
+        delimPattern = pattern != null ? pattern : WHITESPACE_PATTERN;
         buf = CharBuffer.allocate(BUFFER_SIZE);
         cast(buf).limit(0);
         matcher = delimPattern.matcher(buf);
@@ -251,7 +260,7 @@ public final class Scanner implements Iterator<String>, Closeable {
                 return null;
             }
             int tokenEnd = matcher.start();
-            matcher.usePattern(cachePattern(FIND_ANY_PATTERN));
+            matcher.usePattern(FIND_ANY_PATTERN);
             matcher.region(position, tokenEnd);
             if (matcher.matches()) {
                 String s = matcher.group();
@@ -262,7 +271,7 @@ public final class Scanner implements Iterator<String>, Closeable {
             }
         }
         if (inputExhausted) {
-            matcher.usePattern(cachePattern(FIND_ANY_PATTERN));
+            matcher.usePattern(FIND_ANY_PATTERN);
             matcher.region(position, buf.limit());
             if (matcher.matches()) {
                 String s = matcher.group();
@@ -302,7 +311,9 @@ public final class Scanner implements Iterator<String>, Closeable {
         if (pattern == null) {
             return null;
         }
-        return CACHE.computeIfAbsent(pattern, Pattern::compile);
+        synchronized (CACHE) {
+            return CACHE.computeIfAbsent(pattern, Pattern::compile);
+        }
     }
 
 }