Refactor the ConfigurableCacheAwareContextLoaderDelegate class to remember and reference the current ApplicationContext in use by the Spring TestContext framework and currently executing test class.
This is necessary in order to properly close the ApplicationContext and release all Spring bean resources after use (e.g. after the test class executes).
This commit is contained in:
@@ -17,28 +17,38 @@ package org.springframework.data.gemfire.tests.extensions.spring.test.context.ca
|
||||
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.SpringProperties;
|
||||
import org.springframework.data.gemfire.tests.util.SpringUtils;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.cache.ContextCache;
|
||||
import org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link ConfigurableCacheAwareContextLoaderDelegate} is a Spring {@link TestContext} framework
|
||||
* {@link DefaultCacheAwareContextLoaderDelegate} class implementation and extension that enables the configuration of
|
||||
* {@link TestContext} caching via {@link SpringProperties} and Java {@link System#getProperties() System properties}
|
||||
* configuration.
|
||||
* {@link DefaultCacheAwareContextLoaderDelegate} class extension and implementation used to enable
|
||||
* the configuration of {@link TestContext} caching via {@link SpringProperties}
|
||||
* or Java {@link System#getProperties() System properties}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.lang.System
|
||||
* @see java.lang.System#getProperties()
|
||||
* @see java.util.concurrent.atomic.AtomicReference
|
||||
* @see java.util.function.Function
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
* @see org.springframework.core.SpringProperties
|
||||
* @see org.springframework.test.annotation.DirtiesContext
|
||||
* @see org.springframework.test.context.MergedContextConfiguration
|
||||
* @see org.springframework.test.context.TestContext
|
||||
* @see org.springframework.test.context.cache.ContextCache
|
||||
@@ -54,6 +64,9 @@ public class ConfigurableCacheAwareContextLoaderDelegate extends DefaultCacheAwa
|
||||
|
||||
private final AtomicReference<Boolean> springTestContextCacheEnabled = new AtomicReference<>(null);
|
||||
|
||||
private final AtomicReference<MergedContextConfigurationAndApplicationContextPair> applicationContextReference =
|
||||
new AtomicReference<>();
|
||||
|
||||
public ConfigurableCacheAwareContextLoaderDelegate() { }
|
||||
|
||||
public ConfigurableCacheAwareContextLoaderDelegate(@NonNull ContextCache contextCache) {
|
||||
@@ -62,11 +75,11 @@ public class ConfigurableCacheAwareContextLoaderDelegate extends DefaultCacheAwa
|
||||
|
||||
protected boolean isSpringTestContextCacheEnabled() {
|
||||
return springTestContextCacheEnabled.updateAndGet(cacheEnabled -> cacheEnabled != null ? cacheEnabled
|
||||
: getSpringTestContextCacheEnabledResolver().apply(DEFAULT_SPRING_TEST_CONTEXT_CACHE_ENABLED));
|
||||
: getSpringTestContextCacheEnabledResolvingFunction().apply(DEFAULT_SPRING_TEST_CONTEXT_CACHE_ENABLED));
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
protected Function<Boolean, Boolean> getSpringTestContextCacheEnabledResolver() {
|
||||
protected Function<Boolean, Boolean> getSpringTestContextCacheEnabledResolvingFunction() {
|
||||
|
||||
return defaultCacheEnabled -> {
|
||||
|
||||
@@ -80,16 +93,42 @@ public class ConfigurableCacheAwareContextLoaderDelegate extends DefaultCacheAwa
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean isContextLoaded(@NonNull MergedContextConfiguration mergedContextConfiguration) {
|
||||
return isSpringTestContextCacheEnabled() && super.isContextLoaded(mergedContextConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public @NonNull ApplicationContext loadContext(@NonNull MergedContextConfiguration mergedContextConfiguration) {
|
||||
|
||||
return isSpringTestContextCacheEnabled()
|
||||
? super.loadContext(mergedContextConfiguration)
|
||||
: loadContext(mergedContextConfiguration, this::loadContextInternalWithExceptionHandling);
|
||||
}
|
||||
|
||||
private @NonNull ApplicationContext loadContext(@NonNull MergedContextConfiguration mergedContextConfiguration,
|
||||
@NonNull Function<MergedContextConfiguration, ApplicationContext> contextLoaderFunction) {
|
||||
|
||||
MergedContextConfigurationAndApplicationContextPair pair = this.applicationContextReference
|
||||
.updateAndGet(ref -> ref != null
|
||||
? ref.update(mergedContextConfiguration, contextLoaderFunction)
|
||||
: MergedContextConfigurationAndApplicationContextPair
|
||||
.from(mergedContextConfiguration, contextLoaderFunction.apply(mergedContextConfiguration)));
|
||||
|
||||
return pair.getApplicationContext();
|
||||
}
|
||||
|
||||
private @NonNull ApplicationContext loadContextInternalWithExceptionHandling(
|
||||
@NonNull MergedContextConfiguration mergedContextConfiguration) {
|
||||
|
||||
try {
|
||||
return isSpringTestContextCacheEnabled()
|
||||
? super.loadContext(mergedContextConfiguration)
|
||||
: loadContextInternal(mergedContextConfiguration);
|
||||
return loadContextInternal(mergedContextConfiguration);
|
||||
}
|
||||
catch (Exception cause) {
|
||||
throw newIllegalStateException(cause, "Failed to load ApplicationContext for context configuration [%s]",
|
||||
@@ -101,7 +140,127 @@ public class ConfigurableCacheAwareContextLoaderDelegate extends DefaultCacheAwa
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean isContextLoaded(@NonNull MergedContextConfiguration mergedContextConfiguration) {
|
||||
return isSpringTestContextCacheEnabled() && super.isContextLoaded(mergedContextConfiguration);
|
||||
public void closeContext(@NonNull MergedContextConfiguration mergedContextConfiguration,
|
||||
DirtiesContext.HierarchyMode hierarchyMode) {
|
||||
|
||||
if (isSpringTestContextCacheEnabled()) {
|
||||
super.closeContext(mergedContextConfiguration, hierarchyMode);
|
||||
}
|
||||
else {
|
||||
closeApplicationContext(mergedContextConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean closeApplicationContext(@NonNull MergedContextConfiguration contextConfiguration) {
|
||||
|
||||
return Optional.ofNullable(applicationContextReference.get())
|
||||
.map(pair -> pair.closeApplicationContextIfMatch(contextConfiguration))
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
protected static class MergedContextConfigurationAndApplicationContextPair {
|
||||
|
||||
protected static @NonNull MergedContextConfigurationAndApplicationContextPair from(
|
||||
@NonNull MergedContextConfiguration contextConfiguration, @NonNull ApplicationContext applicationContext) {
|
||||
|
||||
return new MergedContextConfigurationAndApplicationContextPair(contextConfiguration, applicationContext);
|
||||
}
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final MergedContextConfiguration mergedContextConfiguration;
|
||||
|
||||
protected MergedContextConfigurationAndApplicationContextPair(
|
||||
@NonNull MergedContextConfiguration mergedContextConfiguration,
|
||||
@NonNull ApplicationContext applicationContext) {
|
||||
|
||||
Assert.notNull(mergedContextConfiguration, "MergedContextConfiguration must not be null");
|
||||
Assert.notNull(applicationContext, "ApplicationContext must not be null");
|
||||
|
||||
this.mergedContextConfiguration = mergedContextConfiguration;
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
protected @NonNull ApplicationContext getApplicationContext() {
|
||||
return this.applicationContext;
|
||||
}
|
||||
|
||||
protected @NonNull MergedContextConfiguration getMergedContextConfiguration() {
|
||||
return this.mergedContextConfiguration;
|
||||
}
|
||||
|
||||
protected boolean isMatch(@Nullable MergedContextConfiguration mergedContextConfiguration) {
|
||||
return getMergedContextConfiguration().equals(mergedContextConfiguration);
|
||||
}
|
||||
|
||||
protected boolean isUpdatable(@Nullable MergedContextConfiguration mergedContextConfiguration) {
|
||||
return Objects.nonNull(mergedContextConfiguration)
|
||||
&& closeApplicationContextIfNotMatch(mergedContextConfiguration);
|
||||
}
|
||||
|
||||
protected boolean closeApplicationContextIfMatch(
|
||||
@Nullable MergedContextConfiguration mergedContextConfiguration) {
|
||||
|
||||
return isMatch(mergedContextConfiguration) && SpringUtils.closeApplicationContext(getApplicationContext());
|
||||
}
|
||||
|
||||
protected boolean closeApplicationContextIfNotMatch(
|
||||
@Nullable MergedContextConfiguration mergedContextConfiguration) {
|
||||
|
||||
return !isMatch(mergedContextConfiguration) && SpringUtils.closeApplicationContext(getApplicationContext());
|
||||
}
|
||||
|
||||
protected @NonNull MergedContextConfigurationAndApplicationContextPair update(
|
||||
@Nullable MergedContextConfiguration mergedContextConfiguration,
|
||||
@NonNull Function<MergedContextConfiguration, ApplicationContext> contextLoaderFunction) {
|
||||
|
||||
return isUpdatable(mergedContextConfiguration)
|
||||
? from(mergedContextConfiguration, contextLoaderFunction.apply(mergedContextConfiguration))
|
||||
: this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof MergedContextConfigurationAndApplicationContextPair)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MergedContextConfigurationAndApplicationContextPair that =
|
||||
(MergedContextConfigurationAndApplicationContextPair) obj;
|
||||
|
||||
return ObjectUtils.nullSafeEquals(this.getMergedContextConfiguration(), that.getMergedContextConfiguration())
|
||||
&& ObjectUtils.nullSafeEquals(this.getApplicationContext(), that.getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int hashValue = 17;
|
||||
|
||||
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getMergedContextConfiguration());
|
||||
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getApplicationContext());
|
||||
|
||||
return hashValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("MergedContextConfiguration [%1$s] for ApplicationContext [%2$s]",
|
||||
getMergedContextConfiguration(), getApplicationContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user