You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ol...@apache.org on 2020/05/26 10:46:15 UTC

[maven-enforcer] branch master updated: MENFORCER-336: Changed beanshell interpreter from singleton / static field to ThreadLocal.

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

olamy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-enforcer.git


The following commit(s) were added to refs/heads/master by this push:
     new c273451  MENFORCER-336: Changed beanshell interpreter from singleton / static field to ThreadLocal.
c273451 is described below

commit c273451308dec5642760e3bcf56bd292f3ef587c
Author: Vidar Breivik <vb...@gmail.com>
AuthorDate: Tue Dec 24 00:10:14 2019 +0100

    MENFORCER-336: Changed beanshell interpreter from singleton / static field to ThreadLocal.
---
 .../maven/plugins/enforcer/EvaluateBeanshell.java  |  11 +-
 .../plugins/enforcer/TestEvaluateBeanshell.java    | 116 +++++++++++++++++++++
 2 files changed, 125 insertions(+), 2 deletions(-)

diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/EvaluateBeanshell.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/EvaluateBeanshell.java
index c41982f..91a6635 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/EvaluateBeanshell.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/EvaluateBeanshell.java
@@ -38,7 +38,14 @@ public class EvaluateBeanshell
 {
 
     /** Beanshell interpreter. */
-    private static final Interpreter BSH = new Interpreter();
+    private static final ThreadLocal<Interpreter> INTERPRETER = new ThreadLocal<Interpreter>()
+    {
+        @Override
+        protected Interpreter initialValue()
+        {
+            return new Interpreter();
+        }
+    };
 
     /** The condition to be evaluated.
      *  
@@ -99,7 +106,7 @@ public class EvaluateBeanshell
         Boolean evaluation = Boolean.FALSE;
         try
         {
-            evaluation = (Boolean) BSH.eval( script );
+            evaluation = (Boolean) INTERPRETER.get().eval( script );
             log.debug( "Echo evaluating : " + evaluation );
         }
         catch ( EvalError ex )
diff --git a/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestEvaluateBeanshell.java b/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestEvaluateBeanshell.java
index 7f8faa7..f78729f 100644
--- a/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestEvaluateBeanshell.java
+++ b/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestEvaluateBeanshell.java
@@ -24,6 +24,15 @@ import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
 import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
 import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
 import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
 import static org.mockito.Mockito.*;
 
 /**
@@ -136,4 +145,111 @@ public class TestEvaluateBeanshell
             assertFalse( e.getLocalizedMessage().equals( rule.getMessage() ) );
         }
     }
+
+
+    public void testRuleCanExecuteMultipleThreads() throws InterruptedException {
+        final String condition = "String property1 = \"${property1}\";\n" +
+                "(property1.equals(\"prop0\") && \"${property2}\".equals(\"prop0\"))\n" +
+                "|| (property1.equals(\"prop1\") && \"${property2}\".equals(\"prop1\"))\n" +
+                "|| (property1.equals(\"prop2\") && \"${property2}\".equals(\"prop2\"))\n";
+
+        final List<Runnable> runnables = new ArrayList<>();
+
+        runnables.add(new Runnable() {
+            @Override
+            public void run() {
+                final int threadNumber = 0;
+                MockProject multiProject = new MockProject();
+                multiProject.setProperty("property1", "prop" + threadNumber);
+                multiProject.setProperty("property2", "prop" + threadNumber);
+
+                EvaluateBeanshell rule = new EvaluateBeanshell();
+                rule.setCondition(condition);
+                rule.setMessage("Race condition in thread " + threadNumber);
+                EnforcerRuleHelper helper = EnforcerTestUtils.getHelper(multiProject);
+                try {
+                    rule.execute(helper);
+
+                } catch (EnforcerRuleException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        runnables.add(new Runnable() {
+            @Override
+            public void run() {
+                final int threadNumber = 1;
+                MockProject multiProject = new MockProject();
+                multiProject.setProperty("property1", "prop" + threadNumber);
+                multiProject.setProperty("property2", "prop" + threadNumber);
+
+                EvaluateBeanshell rule = new EvaluateBeanshell();
+                rule.setCondition(condition);
+                rule.setMessage("Race condition in thread " + threadNumber);
+                EnforcerRuleHelper helper = EnforcerTestUtils.getHelper(multiProject);
+                try {
+                    rule.execute(helper);
+
+                } catch (EnforcerRuleException e) {
+                    throw new RuntimeException(e);
+                }
+
+            }
+        });
+        runnables.add(new Runnable() {
+            @Override
+            public void run() {
+                final int threadNumber = 2;
+                MockProject multiProject = new MockProject();
+                multiProject.setProperty("property1", "prop" + threadNumber);
+                multiProject.setProperty("property2", "prop" + threadNumber);
+
+                EvaluateBeanshell rule = new EvaluateBeanshell();
+                rule.setCondition(condition);
+                rule.setMessage("Race condition in thread " + threadNumber);
+                EnforcerRuleHelper helper = EnforcerTestUtils.getHelper(multiProject);
+                try {
+                    rule.execute(helper);
+                } catch (EnforcerRuleException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+
+        assertConcurrent( runnables, 4);
+    }
+
+    private static void assertConcurrent(final List<? extends Runnable> runnables, final int maxTimeoutSeconds) throws InterruptedException {
+        final int numThreads = runnables.size();
+        final List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<Throwable>());
+        final ExecutorService threadPool = Executors.newFixedThreadPool(numThreads);
+        try {
+            final CountDownLatch allExecutorThreadsReady = new CountDownLatch(numThreads);
+            final CountDownLatch afterInitBlocker = new CountDownLatch(1);
+            final CountDownLatch allDone = new CountDownLatch(numThreads);
+            for (final Runnable submittedTestRunnable : runnables) {
+                threadPool.submit(new Runnable() {
+                    public void run() {
+                        allExecutorThreadsReady.countDown();
+                        try {
+                            afterInitBlocker.await();
+                            submittedTestRunnable.run();
+                        } catch (final Throwable e) {
+                            exceptions.add(e);
+                        } finally {
+                            allDone.countDown();
+                        }
+                    }
+                });
+            }
+            // wait until all threads are ready
+            assertTrue("Timeout initializing threads! Perform long lasting initializations before passing runnables to assertConcurrent", allExecutorThreadsReady.await(runnables.size() * 10, TimeUnit.MILLISECONDS));
+            // start all test runners
+            afterInitBlocker.countDown();
+            assertTrue("Timeout! More than" + maxTimeoutSeconds + "seconds", allDone.await(maxTimeoutSeconds, TimeUnit.SECONDS));
+        } finally {
+            threadPool.shutdownNow();
+        }
+        assertTrue("Failed with exception(s)" + exceptions, exceptions.isEmpty());
+    }
 }