GH-191 - Prevent premature access of ApplicationContext from ParameterResolver implementations.

We now avoid accessing the ApplicationContext from a BeforeAllCallback as this might cause the context initialization before other extensions had time to kick in.
This commit is contained in:
Oliver Drotbohm
2023-04-30 17:20:04 +02:00
parent c111e41f2b
commit 6dabfb425b
3 changed files with 42 additions and 43 deletions

View File

@@ -18,7 +18,6 @@ package org.springframework.modulith.test;
import java.util.function.Function;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolver;
@@ -35,7 +34,7 @@ import org.springframework.util.ClassUtils;
*
* @author Oliver Drotbohm
*/
class PublishedEventsParameterResolver implements ParameterResolver, BeforeAllCallback, AfterEachCallback {
class PublishedEventsParameterResolver implements ParameterResolver, AfterEachCallback {
private static final boolean ASSERT_J_PRESENT = ClassUtils.isPresent("org.assertj.core.api.Assert",
PublishedEventsParameterResolver.class.getClassLoader());
@@ -51,32 +50,6 @@ class PublishedEventsParameterResolver implements ParameterResolver, BeforeAllCa
this.lookup = supplier;
}
/*
* (non-Javadoc)
* @see org.junit.jupiter.api.extension.BeforeAllCallback#beforeAll(org.junit.jupiter.api.extension.ExtensionContext)
*/
@Override
public void beforeAll(ExtensionContext extensionContext) {
ApplicationContext context = lookup.apply(extensionContext);
if (!(context instanceof AbstractApplicationContext aac)) {
throw new IllegalStateException();
}
listener = aac.getApplicationListeners().stream()
.filter(ThreadBoundApplicationListenerAdapter.class::isInstance)
.map(ThreadBoundApplicationListenerAdapter.class::cast)
.findFirst()
.orElseGet(() -> {
var adapter = new ThreadBoundApplicationListenerAdapter();
aac.addApplicationListener(adapter);
return adapter;
});
}
/*
* (non-Javadoc)
* @see org.junit.jupiter.api.extension.ParameterResolver#supportsParameter(org.junit.jupiter.api.extension.ParameterContext, org.junit.jupiter.api.extension.ExtensionContext)
@@ -105,18 +78,47 @@ class PublishedEventsParameterResolver implements ParameterResolver, BeforeAllCa
? new DefaultAssertablePublishedEvents()
: new DefaultPublishedEvents();
initializeListener(extensionContext);
listener.registerDelegate(publishedEvents);
return publishedEvents;
}
private void initializeListener(ExtensionContext extensionContext) {
if (listener != null) {
return;
}
ApplicationContext context = lookup.apply(extensionContext);
if (!(context instanceof AbstractApplicationContext aac)) {
throw new IllegalStateException();
}
listener = aac.getApplicationListeners().stream()
.filter(ThreadBoundApplicationListenerAdapter.class::isInstance)
.map(ThreadBoundApplicationListenerAdapter.class::cast)
.findFirst()
.orElseGet(() -> {
var adapter = new ThreadBoundApplicationListenerAdapter();
aac.addApplicationListener(adapter);
return adapter;
});
}
/*
* (non-Javadoc)
* @see org.junit.jupiter.api.extension.AfterEachCallback#afterEach(org.junit.jupiter.api.extension.ExtensionContext)
*/
@Override
public void afterEach(ExtensionContext context) {
listener.unregisterDelegate();
if (listener != null) {
listener.unregisterDelegate();
}
}
/**

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.modulith.test;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
@@ -30,7 +30,7 @@ import org.springframework.transaction.support.TransactionTemplate;
*
* @author Oliver Drotbohm
*/
class ScenarioParameterResolver implements ParameterResolver, BeforeAllCallback {
class ScenarioParameterResolver implements ParameterResolver, AfterEachCallback {
private static final String MISSING_TRANSACTION_TEMPLATE = "To use a Scenario in an integration test you need to define a bean of type TransactionTemplate! Please check your ApplicationContext setup.";
@@ -43,6 +43,15 @@ class ScenarioParameterResolver implements ParameterResolver, BeforeAllCallback
this.delegate = new PublishedEventsParameterResolver();
}
/*
* (non-Javadoc)
* @see org.junit.jupiter.api.extension.AfterEachCallback#afterEach(org.junit.jupiter.api.extension.ExtensionContext)
*/
@Override
public void afterEach(ExtensionContext context) throws Exception {
delegate.afterEach(context);
}
/*
* (non-Javadoc)
* @see org.junit.jupiter.api.extension.ParameterResolver#supportsParameter(org.junit.jupiter.api.extension.ParameterContext, org.junit.jupiter.api.extension.ExtensionContext)
@@ -56,15 +65,6 @@ class ScenarioParameterResolver implements ParameterResolver, BeforeAllCallback
return Scenario.class.isAssignableFrom(type);
}
/*
* (non-Javadoc)
* @see org.junit.jupiter.api.extension.BeforeAllCallback#beforeAll(org.junit.jupiter.api.extension.ExtensionContext)
*/
@Override
public void beforeAll(ExtensionContext context) throws Exception {
delegate.beforeAll(context);
}
/*
* (non-Javadoc)
* @see org.junit.jupiter.api.extension.ParameterResolver#resolveParameter(org.junit.jupiter.api.extension.ParameterContext, org.junit.jupiter.api.extension.ExtensionContext)

View File

@@ -56,8 +56,6 @@ class PublishedEventsParameterResolverUnitTests {
PublishedEventsParameterResolver resolver = new PublishedEventsParameterResolver(__ -> context);
context.refresh();
resolver.beforeAll(null);
Map<String, PublishedEvents> allEvents = new ConcurrentHashMap<>();
List<String> keys = Arrays.asList("first", "second", "third");
CountDownLatch latch = new CountDownLatch(3);
@@ -75,7 +73,6 @@ class PublishedEventsParameterResolverUnitTests {
latch.countDown();
}).start();
}
latch.await(50, TimeUnit.MILLISECONDS);