Skip to content

Commit 5c5749e

Browse files
authored
Merge pull request #22 from Nordstrom/pr/add-listener-sync
Fix ServiceLoader threading issues
2 parents 573276e + f669c80 commit 5c5749e

File tree

4 files changed

+20
-51
lines changed

4 files changed

+20
-51
lines changed

src/main/java/com/nordstrom/automation/junit/CreateTestClass.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,10 @@
2323
*/
2424
@SuppressWarnings("squid:S1118")
2525
public class CreateTestClass {
26-
private static final ServiceLoader<TestClassWatcher> classWatcherLoader;
27-
private static final ServiceLoader<TestClassWatcher2> classWatcher2Loader;
2826
private static final Logger LOGGER = LoggerFactory.getLogger(CreateTestClass.class);
2927
private static final Map<TestClass, Object> TESTCLASS_TO_RUNNER = new ConcurrentHashMap<>();
3028
private static final Map<Object, TestClass> METHOD_TO_TESTCLASS = new ConcurrentHashMap<>();
3129

32-
static {
33-
classWatcherLoader = ServiceLoader.load(TestClassWatcher.class);
34-
classWatcher2Loader = ServiceLoader.load(TestClassWatcher2.class);
35-
}
36-
3730
/**
3831
* Interceptor for the {@link org.junit.runners.ParentRunner#createTestClass createTestClass} method.
3932
*
@@ -52,10 +45,10 @@ public static TestClass intercept(@This final Object runner, @SuperCall final Ca
5245
METHOD_TO_TESTCLASS.put(method, testClass);
5346
}
5447

55-
for (TestClassWatcher watcher : classWatcherLoader) {
48+
for (TestClassWatcher watcher : ServiceLoader.load(TestClassWatcher.class)) {
5649
watcher.testClassCreated(testClass, runner);
5750
}
58-
for (TestClassWatcher2 watcher : classWatcher2Loader) {
51+
for (TestClassWatcher2 watcher : ServiceLoader.load(TestClassWatcher2.class)) {
5952
watcher.testClassCreated(testClass, runner);
6053
}
6154

@@ -93,10 +86,10 @@ private static RunnerScheduler createRunnerScheduler(final TestClass testClass,
9386

9487
public void schedule(Runnable childStatement) {
9588
if (scheduled.compareAndSet(false, true)) {
96-
for (TestClassWatcher watcher : classWatcherLoader) {
89+
for (TestClassWatcher watcher : ServiceLoader.load(TestClassWatcher.class)) {
9790
watcher.testClassStarted(testClass);
9891
}
99-
for (TestClassWatcher2 watcher : classWatcher2Loader) {
92+
for (TestClassWatcher2 watcher : ServiceLoader.load(TestClassWatcher2.class)) {
10093
watcher.testClassStarted(testClass, runner);
10194
}
10295
}
@@ -113,10 +106,10 @@ public void schedule(Runnable childStatement) {
113106
}
114107

115108
public void finished() {
116-
for (TestClassWatcher watcher : classWatcherLoader) {
109+
for (TestClassWatcher watcher : ServiceLoader.load(TestClassWatcher.class)) {
117110
watcher.testClassFinished(testClass);
118111
}
119-
for (TestClassWatcher2 watcher : classWatcher2Loader) {
112+
for (TestClassWatcher2 watcher : ServiceLoader.load(TestClassWatcher2.class)) {
120113
watcher.testClassFinished(testClass, runner);
121114
}
122115
}

src/main/java/com/nordstrom/automation/junit/LifecycleHooks.java

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
import java.lang.reflect.Field;
88
import java.lang.reflect.InvocationTargetException;
99
import java.lang.reflect.Method;
10-
import java.util.HashSet;
1110
import java.util.List;
1211
import java.util.Map;
1312
import java.util.ServiceLoader;
1413
import java.util.Set;
1514
import java.util.concurrent.Callable;
1615
import java.util.concurrent.ConcurrentHashMap;
16+
import java.util.concurrent.CopyOnWriteArraySet;
17+
1718
import org.junit.Test;
1819
import org.junit.runner.Description;
1920
import org.junit.runner.notification.RunListener;
@@ -125,16 +126,9 @@ static synchronized JUnitConfig getConfig() {
125126
*/
126127
@SuppressWarnings("squid:S1118")
127128
public static class Run {
128-
private static final ServiceLoader<RunListener> runListenerLoader;
129-
private static final ServiceLoader<RunnerWatcher> runnerWatcherLoader;
130-
private static final Set<RunNotifier> NOTIFIERS = new HashSet<>();
129+
private static final Set<RunNotifier> NOTIFIERS = new CopyOnWriteArraySet<>();
131130
private static final Map<Object, Object> CHILD_TO_PARENT = new ConcurrentHashMap<>();
132131

133-
static {
134-
runListenerLoader = ServiceLoader.load(RunListener.class);
135-
runnerWatcherLoader = ServiceLoader.load(RunnerWatcher.class);
136-
}
137-
138132
/**
139133
* Interceptor for the {@link org.junit.runners.ParentRunner#run run} method.
140134
*
@@ -153,19 +147,19 @@ public static void intercept(@This final Object runner, @SuperCall final Callabl
153147

154148
if (NOTIFIERS.add(notifier)) {
155149
Description description = invoke(runner, "getDescription");
156-
for (RunListener listener : runListenerLoader) {
150+
for (RunListener listener : ServiceLoader.load(RunListener.class)) {
157151
notifier.addListener(listener);
158152
listener.testRunStarted(description);
159153
}
160154
}
161155

162-
for (RunnerWatcher watcher : runnerWatcherLoader) {
156+
for (RunnerWatcher watcher : ServiceLoader.load(RunnerWatcher.class)) {
163157
watcher.runStarted(runner);
164158
}
165159

166160
callProxy(proxy);
167161

168-
for (RunnerWatcher watcher : runnerWatcherLoader) {
162+
for (RunnerWatcher watcher : ServiceLoader.load(RunnerWatcher.class)) {
169163
watcher.runFinished(runner);
170164
}
171165
}
@@ -188,13 +182,8 @@ static Object getParentOf(Object child) {
188182
@SuppressWarnings("squid:S1118")
189183
public static class CreateTest {
190184

191-
private static final ServiceLoader<TestObjectWatcher> objectWatcherLoader;
192185
private static final Map<Object, TestClass> TARGET_TO_TESTCLASS = new ConcurrentHashMap<>();
193186

194-
static {
195-
objectWatcherLoader = ServiceLoader.load(TestObjectWatcher.class);
196-
}
197-
198187
/**
199188
* Interceptor for the {@link org.junit.runners.BlockJUnit4ClassRunner#createTest createTest} method.
200189
*
@@ -210,7 +199,7 @@ public static Object intercept(@This final Object runner,
210199
TARGET_TO_TESTCLASS.put(testObj, getTestClassOf(runner));
211200
applyTimeout(testObj);
212201

213-
for (TestObjectWatcher watcher : objectWatcherLoader) {
202+
for (TestObjectWatcher watcher : ServiceLoader.load(TestObjectWatcher.class)) {
214203
watcher.testObjectCreated(testObj, TARGET_TO_TESTCLASS.get(testObj));
215204
}
216205

src/main/java/com/nordstrom/automation/junit/RetryHandler.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,8 @@
2121
*/
2222
public class RetryHandler {
2323

24-
private static final ServiceLoader<JUnitRetryAnalyzer> retryAnalyzerLoader;
2524
private static final Logger LOGGER = LoggerFactory.getLogger(RetryHandler.class);
2625

27-
static {
28-
retryAnalyzerLoader = ServiceLoader.load(JUnitRetryAnalyzer.class);
29-
}
30-
3126
private RetryHandler() {
3227
throw new AssertionError("RetryHandler is a static utility class that cannot be instantiated");
3328
}
@@ -129,7 +124,7 @@ static int getMaxRetry(Object runner, final FrameworkMethod method) {
129124
* @return {@code true} if test should be retried; otherwise {@code false}
130125
*/
131126
static boolean isRetriable(final FrameworkMethod method, final Throwable thrown) {
132-
for (JUnitRetryAnalyzer analyzer : retryAnalyzerLoader) {
127+
for (JUnitRetryAnalyzer analyzer : ServiceLoader.load(JUnitRetryAnalyzer.class)) {
133128
if (analyzer.retry(method, thrown)) {
134129
return true;
135130
}

src/main/java/com/nordstrom/automation/junit/RunReflectiveCall.java

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,9 @@
2727
@SuppressWarnings("squid:S1118")
2828
public class RunReflectiveCall {
2929

30-
private static final ServiceLoader<MethodWatcher> methodWatcherLoader;
31-
private static final ServiceLoader<RunWatcher> runWatcherLoader;
32-
3330
private static final Map<FrameworkMethod, Object> METHOD_TO_TARGET = new ConcurrentHashMap<>();
3431
private static final Map<TestClass, AtomicTest> TESTCLASS_TO_ATOMICTEST = new ConcurrentHashMap<>();
3532

36-
static {
37-
methodWatcherLoader = ServiceLoader.load(MethodWatcher.class);
38-
runWatcherLoader = ServiceLoader.load(RunWatcher.class);
39-
}
40-
4133
/**
4234
* Interceptor for the {@link org.junit.internal.runners.model.ReflectiveCallable#runReflectiveCall
4335
* runReflectiveCall} method.
@@ -77,7 +69,7 @@ public static Object intercept(@This final Object callable, @SuperCall final Cal
7769

7870
Object result = null;
7971
Throwable thrown = null;
80-
for (MethodWatcher watcher : methodWatcherLoader) {
72+
for (MethodWatcher watcher : ServiceLoader.load(MethodWatcher.class)) {
8173
watcher.beforeInvocation(target, method, params);
8274
}
8375

@@ -86,7 +78,7 @@ public static Object intercept(@This final Object callable, @SuperCall final Cal
8678
} catch (Throwable t) {
8779
thrown = t;
8880
} finally {
89-
for (MethodWatcher watcher : methodWatcherLoader) {
81+
for (MethodWatcher watcher : ServiceLoader.load(MethodWatcher.class)) {
9082
watcher.afterInvocation(target, method, thrown);
9183
}
9284
}
@@ -108,7 +100,7 @@ public static Object intercept(@This final Object callable, @SuperCall final Cal
108100
static void fireTestStarted(TestClass testClass, Runnable runnable) {
109101
AtomicTest atomicTest = createAtomicTest(testClass, runnable);
110102
if (atomicTest != null) {
111-
for (RunWatcher watcher : runWatcherLoader) {
103+
for (RunWatcher watcher : ServiceLoader.load(RunWatcher.class)) {
112104
watcher.testStarted(atomicTest.getIdentity(), atomicTest.getTestClass());
113105
}
114106
}
@@ -122,7 +114,7 @@ static void fireTestStarted(TestClass testClass, Runnable runnable) {
122114
static void fireTestFinished(TestClass testClass) {
123115
AtomicTest atomicTest = TESTCLASS_TO_ATOMICTEST.get(testClass);
124116
if (atomicTest != null) {
125-
for (RunWatcher watcher : runWatcherLoader) {
117+
for (RunWatcher watcher : ServiceLoader.load(RunWatcher.class)) {
126118
notifyIfTestFailed(watcher, atomicTest);
127119
watcher.testFinished(atomicTest.getIdentity(), atomicTest.getTestClass());
128120
}
@@ -155,7 +147,7 @@ private static void notifyIfTestFailed(RunWatcher watcher, AtomicTest atomicTest
155147
*/
156148
static void fireTestIgnored(Object runner, FrameworkMethod method) {
157149
TestClass testClass = getTestClassOf(runner);
158-
for (RunWatcher watcher : runWatcherLoader) {
150+
for (RunWatcher watcher : ServiceLoader.load(RunWatcher.class)) {
159151
watcher.testIgnored(method, testClass);
160152
}
161153
}
@@ -183,7 +175,7 @@ public static Object getTargetFor(FrameworkMethod method) {
183175
public static Optional<MethodWatcher> getAttachedWatcher(
184176
Class<? extends MethodWatcher> watcherType) {
185177
Objects.requireNonNull(watcherType, "[watcherType] must be non-null");
186-
for (MethodWatcher watcher : methodWatcherLoader) {
178+
for (MethodWatcher watcher : ServiceLoader.load(MethodWatcher.class)) {
187179
if (watcher.getClass() == watcherType) {
188180
return Optional.of(watcher);
189181
}

0 commit comments

Comments
 (0)