SGF-333 - The SpringContextBootstrappingInitializer needs to handle the case when it's init(:Properties) method maybe called more than once on initialization.
This commit is contained in:
@@ -23,7 +23,9 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.event.ApplicationContextEvent;
|
||||
import org.springframework.context.event.ApplicationEventMulticaster;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.SimpleApplicationEventMulticaster;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
@@ -41,22 +43,18 @@ import com.gemstone.gemfire.cache.Declarable;
|
||||
* and initializes the Cache for use.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.util.Properties
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
* @see org.springframework.context.ApplicationListener
|
||||
* @see org.springframework.context.ConfigurableApplicationContext
|
||||
* @see org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
* @see org.springframework.context.event.ApplicationEventMulticaster
|
||||
* @see org.springframework.context.event.ContextRefreshedEvent
|
||||
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
|
||||
* @see org.springframework.context.support.ClassPathXmlApplicationContext
|
||||
* @see com.gemstone.gemfire.cache.Declarable
|
||||
* @since 1.4.0
|
||||
* @see <a href="http://pubs.vmware.com/vfabric53/topic/com.vmware.vfabric.gemfire.7.0/basic_config/the_cache/setting_cache_initializer.html">Setting Cache Initializer</a>
|
||||
* @see <a href="http://gemfire.docs.pivotal.io/latest/userguide/index.html#basic_config/the_cache/setting_cache_initializer.html">Setting Cache Initializer</a>
|
||||
* @see <a href="https://jira.springsource.org/browse/SGF-248">SGF-248</a>
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class SpringContextBootstrappingInitializer implements Declarable, ApplicationListener<ContextRefreshedEvent> {
|
||||
public class SpringContextBootstrappingInitializer implements Declarable, ApplicationListener<ApplicationContextEvent> {
|
||||
|
||||
public static final String BASE_PACKAGES_PARAMETER = "basePackages";
|
||||
public static final String CONTEXT_CONFIG_LOCATIONS_PARAMETER = "contextConfigLocations";
|
||||
@@ -64,12 +62,11 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
|
||||
protected static final String CHARS_TO_DELETE = " \n\t";
|
||||
protected static final String COMMA_DELIMITER = ",";
|
||||
|
||||
private static final ApplicationEventMulticaster applicationEventNotifier = new SimpleApplicationEventMulticaster();
|
||||
|
||||
/* package-private */ static volatile ConfigurableApplicationContext applicationContext;
|
||||
|
||||
// TODO consider whether I should register a TaskExecutor to perform the event notifications in a separate Thread???
|
||||
private static final ApplicationEventMulticaster eventNotifier = new SimpleApplicationEventMulticaster();
|
||||
|
||||
private static ContextRefreshedEvent contextRefreshedEvent;
|
||||
/* package-private */ static volatile ContextRefreshedEvent contextRefreshedEvent;
|
||||
|
||||
/**
|
||||
* Gets a reference to the Spring ApplicationContext constructed, configured and initialized inside the GemFire
|
||||
@@ -79,19 +76,40 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
|
||||
* @see org.springframework.context.ConfigurableApplicationContext
|
||||
*/
|
||||
public static synchronized ConfigurableApplicationContext getApplicationContext() {
|
||||
Assert.state(applicationContext != null, "The Spring ApplicationContext has not been created!");
|
||||
Assert.state(applicationContext != null, "The Spring ApplicationContext has not been properly configured and initialized!");
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies any Spring ApplicationListeners of a current and existing ContextRefreshedEvent if the
|
||||
* ApplicationContext had been previously created, initialized and refreshed before any ApplicationListeners
|
||||
* interested in ContextRefreshedEvents were registered so that application components (such as the
|
||||
* GemFire CacheLoaders extending LazyWiringDeclarableSupport objects) registered late, requiring configuration
|
||||
* (auto-wiring), also get notified and wired accordingly.
|
||||
*
|
||||
* @param listener a Spring ApplicationListener requiring notification of any ContextRefreshedEvents after the
|
||||
* ApplicationContext has already been created, initialized and/or refreshed.
|
||||
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
|
||||
* @see org.springframework.context.event.ContextRefreshedEvent
|
||||
*/
|
||||
protected static void notifyOnExistingContextRefreshedEvent(final ApplicationListener<ContextRefreshedEvent> listener) {
|
||||
synchronized (applicationEventNotifier) {
|
||||
if (contextRefreshedEvent != null) {
|
||||
listener.onApplicationEvent(contextRefreshedEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a Spring ApplicationListener to be notified when the Spring ApplicationContext is created by GemFire
|
||||
* when instantiating and initializing declared Initializers from the GemFire native configuration file
|
||||
* (e.g. cache.xml).
|
||||
* when instantiating and initializing Declarables declared inside the <initializer> block inside GemFire's
|
||||
* cache.xml file.
|
||||
*
|
||||
* @param <T> the Class type of the Spring ApplicationListener.
|
||||
* @param listener the ApplicationListener to register for ContextRefreshedEvents by this
|
||||
* @param listener the ApplicationListener to register for ContextRefreshedEvents multi-casted by this
|
||||
* SpringContextBootstrappingInitializer.
|
||||
* @return the reference to the ApplicationListener for method call chaining purposes.
|
||||
* @see #notifyOnExistingContextRefreshedEvent(org.springframework.context.ApplicationListener)
|
||||
* @see #unregister(org.springframework.context.ApplicationListener)
|
||||
* @see org.springframework.context.ApplicationListener
|
||||
* @see org.springframework.context.event.ContextRefreshedEvent
|
||||
@@ -99,38 +117,16 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
|
||||
* #addApplicationListener(org.springframework.context.ApplicationListener)
|
||||
*/
|
||||
public static <T extends ApplicationListener<ContextRefreshedEvent>> T register(final T listener) {
|
||||
synchronized (eventNotifier) {
|
||||
eventNotifier.addApplicationListener(listener);
|
||||
notifyListenerOfExistingContextRefreshedEvent(listener);
|
||||
synchronized (applicationEventNotifier) {
|
||||
applicationEventNotifier.addApplicationListener(listener);
|
||||
notifyOnExistingContextRefreshedEvent(listener);
|
||||
}
|
||||
|
||||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies any Spring ApplicationListeners of a current and existing ContextRefreshedEvent if the
|
||||
* ApplicationContext was previously created, initialized and refreshed before any ApplicationListeners interested
|
||||
* in ContextRefreshedEvents get registered so that application components (such as LazyWiringDeclarableSupport
|
||||
* objects) arriving late to the game that also require configuration (auto-wiring) get wired accordingly too.
|
||||
*
|
||||
* @param listener a Spring ApplicationListener requiring notification of any ContextRefreshedEvents after the
|
||||
* ApplicationContext has already been created, initialized and/or refreshed.
|
||||
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
|
||||
* @see org.springframework.context.event.ContextRefreshedEvent
|
||||
*/
|
||||
protected static void notifyListenerOfExistingContextRefreshedEvent(
|
||||
final ApplicationListener<ContextRefreshedEvent> listener) {
|
||||
synchronized (eventNotifier) {
|
||||
// NOTE the null check on the ApplicationContext is not absolutely necessary, but is an extra safety
|
||||
// precaution none-the-less.
|
||||
if (applicationContext != null && contextRefreshedEvent != null) {
|
||||
listener.onApplicationEvent(contextRefreshedEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the Spring ApplicationListener from this SpringContextBootstrappingInitializer in order to stop
|
||||
* Un-registers the Spring ApplicationListener from this SpringContextBootstrappingInitializer in order to stop
|
||||
* receiving ApplicationEvents on Spring context refreshes.
|
||||
*
|
||||
* @param <T> the Class type of the Spring ApplicationListener.
|
||||
@@ -144,8 +140,8 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
|
||||
* #removeApplicationListener(org.springframework.context.ApplicationListener)
|
||||
*/
|
||||
public static <T extends ApplicationListener<ContextRefreshedEvent>> T unregister(final T listener) {
|
||||
synchronized (eventNotifier) {
|
||||
eventNotifier.removeApplicationListener(listener);
|
||||
synchronized (applicationEventNotifier) {
|
||||
applicationEventNotifier.removeApplicationListener(listener);
|
||||
}
|
||||
|
||||
return listener;
|
||||
@@ -162,29 +158,28 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
|
||||
* "refresh" method must be called manually before using the context.
|
||||
* @throws IllegalArgumentException if the configLocations parameter argument is null or empty.
|
||||
* @see #createApplicationContext(String[], String[])
|
||||
* @see org.springframework.context.support.ClassPathXmlApplicationContext
|
||||
* @see org.springframework.context.ConfigurableApplicationContext
|
||||
*/
|
||||
protected ConfigurableApplicationContext createApplicationContext(final String[] configLocations) {
|
||||
Assert.notEmpty(configLocations, "'configLocations' must be specified to construct an instance"
|
||||
+ " of the ClassPathXmlApplicationContext.");
|
||||
Assert.notEmpty(configLocations, "'configLocations' must be specified to construct and configure"
|
||||
+ " an instance of the ClassPathXmlApplicationContext.");
|
||||
return createApplicationContext(null, configLocations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates (constructs and configures) an instance of the ConfigurableApplicationContext based on either the
|
||||
* specified base packages containing @Configuration, @Component or JSR 330 annotated classes to scan, or the
|
||||
* specified locations of context configuration meta-data files used to configure the context. The created
|
||||
* ConfigurableApplicationContext is not automatically "refreshed" and therefore must be "refreshed"
|
||||
* by the caller manually.
|
||||
* specified locations of context configuration meta-data files. The created ConfigurableApplicationContext
|
||||
* is not automatically "refreshed" and therefore must be "refreshed" by the caller manually.
|
||||
*
|
||||
* When basePackages are specified, an instance of AnnotationConfigApplicationContext is returned; otherwise
|
||||
* an instance of the ClassPathXmlApplicationContext is initialized with the configLocations and returned.
|
||||
* This method prefers the ClassPathXmlApplicationContext to the AnnotationConfigApplicationContext when both
|
||||
* basePackages and configLocations are specified.
|
||||
* When basePackages are specified, an instance of AnnotationConfigApplicationContext is constructed and a scan
|
||||
* is performed; otherwise an instance of the ClassPathXmlApplicationContext is initialized with the
|
||||
* configLocations. This method prefers the ClassPathXmlApplicationContext to the
|
||||
* AnnotationConfigApplicationContext when both basePackages and configLocations are specified.
|
||||
*
|
||||
* @param basePackages the base application packages to scan for application @Components and @Configuration classes. *
|
||||
* @param basePackages the base packages to scan for application @Components and @Configuration classes.
|
||||
* @param configLocations a String array indicating the locations of the context configuration meta-data files
|
||||
* used to configure the ConfigurableApplicationContext instance.
|
||||
* used to configure the ClassPathXmlApplicationContext instance.
|
||||
* @return an instance of ConfigurableApplicationContext configured and initialized with either configLocations
|
||||
* or the basePackages when configLocations is unspecified. Note, the "refresh" method must be called manually
|
||||
* before using the context.
|
||||
@@ -201,8 +196,8 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
|
||||
return new ClassPathXmlApplicationContext(configLocations, false);
|
||||
}
|
||||
else {
|
||||
Assert.notEmpty(basePackages, "Either 'basePackages' or 'configLocations' must be specified"
|
||||
+ " to construct an instance of the ConfigurableApplicationContext.");
|
||||
Assert.notEmpty(basePackages, "'basePackages' or 'configLocations' must be specified"
|
||||
+ " to construct and configure an instance of the ConfigurableApplicationContext.");
|
||||
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.scan(basePackages);
|
||||
return applicationContext;
|
||||
@@ -210,46 +205,38 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a Spring ApplicationContext with the given parameters from a GemFire Initializer in GemFire native
|
||||
* configuration meta-data (e.g. cache.xml).
|
||||
* Initializes the given ApplicationContext by registering this SpringContextBootstrappingInitializer as an
|
||||
* ApplicationListener and registering a Runtime shutdown hook.
|
||||
*
|
||||
* @param parameters a Properties object containing the configuration parameters and settings defined in the
|
||||
* GemFire cache.xml >initializer/< element.
|
||||
* @see #createApplicationContext
|
||||
* @see java.util.Properties
|
||||
* @param applicationContext the ConfigurableApplicationContext to initialize.
|
||||
* @return the initialized ApplicationContext.
|
||||
* @see org.springframework.context.ConfigurableApplicationContext
|
||||
* @see org.springframework.context.ConfigurableApplicationContext#addApplicationListener(org.springframework.context.ApplicationListener)
|
||||
* @see org.springframework.context.ConfigurableApplicationContext#registerShutdownHook()
|
||||
* @throws java.lang.IllegalArgumentException if the ApplicationContext reference is null!
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("null")
|
||||
public void init(final Properties parameters) {
|
||||
String basePackages = parameters.getProperty(BASE_PACKAGES_PARAMETER);
|
||||
String contextConfigLocations = parameters.getProperty(CONTEXT_CONFIG_LOCATIONS_PARAMETER);
|
||||
protected ConfigurableApplicationContext initApplicationContext(
|
||||
final ConfigurableApplicationContext applicationContext) {
|
||||
Assert.notNull(applicationContext, "The ConfigurableApplicationContext reference must not be null!");
|
||||
applicationContext.addApplicationListener(this);
|
||||
applicationContext.registerShutdownHook();
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
Assert.isTrue(StringUtils.hasText(basePackages) || StringUtils.hasText(contextConfigLocations),
|
||||
"Either 'basePackages' or the 'contextConfigLocations' parameter must be specified.");
|
||||
|
||||
String[] basePackagesArray = StringUtils.delimitedListToStringArray(basePackages,
|
||||
COMMA_DELIMITER, CHARS_TO_DELETE);
|
||||
|
||||
String[] configLocations = StringUtils.delimitedListToStringArray(contextConfigLocations,
|
||||
COMMA_DELIMITER, CHARS_TO_DELETE);
|
||||
|
||||
synchronized (SpringContextBootstrappingInitializer.class) {
|
||||
Assert.state(applicationContext == null, String.format(
|
||||
"A Spring application context with ID (%1$s) has already been created.",
|
||||
nullSafeGetApplicationContextId(applicationContext)));
|
||||
|
||||
applicationContext = createApplicationContext(basePackagesArray, configLocations);
|
||||
Assert.notNull(applicationContext, "The 'created' ConfigurableApplicationContext cannot be null!");
|
||||
assert applicationContext != null;
|
||||
applicationContext.addApplicationListener(this);
|
||||
applicationContext.registerShutdownHook();
|
||||
applicationContext.refresh();
|
||||
|
||||
Assert.state(applicationContext.isActive(), String.format(
|
||||
"The Spring application context (%1$s) has failed to be properly initialized with the following config files (%2$s) or base packages (%3$s)!",
|
||||
nullSafeGetApplicationContextId(applicationContext), Arrays.toString(configLocations),
|
||||
Arrays.toString(basePackagesArray)));
|
||||
}
|
||||
/**
|
||||
* Refreshes the given ApplicationContext making the context active.
|
||||
*
|
||||
* @param applicationContext the ConfigurableApplicationContext to refresh.
|
||||
* @return the refreshed ApplicationContext.
|
||||
* @see org.springframework.context.ConfigurableApplicationContext
|
||||
* @see org.springframework.context.ConfigurableApplicationContext#refresh()
|
||||
* @throws java.lang.IllegalArgumentException if the ApplicationContext reference is null!
|
||||
*/
|
||||
protected ConfigurableApplicationContext refreshApplicationContext(
|
||||
final ConfigurableApplicationContext applicationContext) {
|
||||
Assert.notNull(applicationContext, "The ConfigurableApplicationContext reference must not be null!");
|
||||
applicationContext.refresh();
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,21 +251,74 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets notified when the Spring ApplicationContext gets created and refreshed by GemFire. The handler method
|
||||
* proceeds in notifying any other GemFire components that need to be aware that the Spring ApplicationContext
|
||||
* now exists and is ready for use, such as other Declarable GemFire objects requiring auto-wiring support, etc.
|
||||
* Initializes a Spring ApplicationContext with the given parameters specified with a GemFire <initializer>
|
||||
* block in cache.xml.
|
||||
*
|
||||
* @param event the ContextRefreshedEvent signaling that the Spring ApplicationContext has been created
|
||||
* and refreshed by GemFire.
|
||||
* @see org.springframework.context.event.ContextRefreshedEvent
|
||||
* @see org.springframework.context.event.ApplicationEventMulticaster
|
||||
* #multicastEvent(org.springframework.context.ApplicationEvent)
|
||||
* @param parameters a Properties object containing the configuration parameters and settings defined in the
|
||||
* GemFire cache.xml <initializer> block for the declared SpringContextBootstrappingInitializer
|
||||
* GemFire Declarable object.
|
||||
* @see #createApplicationContext(String[], String[])
|
||||
* @see #initApplicationContext(org.springframework.context.ConfigurableApplicationContext)
|
||||
* @see #refreshApplicationContext(org.springframework.context.ConfigurableApplicationContext)
|
||||
* @see java.util.Properties
|
||||
*/
|
||||
@Override
|
||||
public void onApplicationEvent(final ContextRefreshedEvent event) {
|
||||
synchronized (eventNotifier) {
|
||||
contextRefreshedEvent = event;
|
||||
eventNotifier.multicastEvent(event);
|
||||
public void init(final Properties parameters) {
|
||||
synchronized (SpringContextBootstrappingInitializer.class) {
|
||||
if (applicationContext == null || !applicationContext.isActive()) {
|
||||
String basePackages = parameters.getProperty(BASE_PACKAGES_PARAMETER);
|
||||
String contextConfigLocations = parameters.getProperty(CONTEXT_CONFIG_LOCATIONS_PARAMETER);
|
||||
|
||||
Assert.isTrue(StringUtils.hasText(basePackages) || StringUtils.hasText(contextConfigLocations),
|
||||
"Either 'basePackages' or the 'contextConfigLocations' parameter must be specified.");
|
||||
|
||||
String[] basePackagesArray = StringUtils.delimitedListToStringArray(basePackages,
|
||||
COMMA_DELIMITER, CHARS_TO_DELETE);
|
||||
|
||||
String[] contextConfigLocationsArray = StringUtils.delimitedListToStringArray(contextConfigLocations,
|
||||
COMMA_DELIMITER, CHARS_TO_DELETE);
|
||||
|
||||
ConfigurableApplicationContext localApplicationContext = refreshApplicationContext(
|
||||
initApplicationContext(createApplicationContext( basePackagesArray, contextConfigLocationsArray)));
|
||||
|
||||
Assert.state(localApplicationContext.isRunning(), String.format(
|
||||
"The Spring ApplicationContext (%1$s) failed to be properly initialized with the context config files (%2$s) or base packages (%3$s)!",
|
||||
nullSafeGetApplicationContextId(applicationContext), Arrays.toString(contextConfigLocationsArray),
|
||||
Arrays.toString(basePackagesArray)));
|
||||
|
||||
applicationContext = localApplicationContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets notified when the Spring ApplicationContext gets created and refreshed by GemFire, once the
|
||||
* <initializer> block is processed and the SpringContextBootstrappingInitializer Declarable component
|
||||
* is initialized. This handler method proceeds in notifying any other GemFire components that need to be aware
|
||||
* that the Spring ApplicationContext now exists and is ready for use, such as other Declarable GemFire objects
|
||||
* requiring auto-wiring support, etc.
|
||||
*
|
||||
* In addition, this method handles the ContextClosedEvent by removing the ApplicationContext reference.
|
||||
*
|
||||
* @param event the ApplicationContextEvent signaling that the Spring ApplicationContext has been created
|
||||
* and refreshed by GemFire, or closed when the JVM process exits.
|
||||
* @see org.springframework.context.event.ContextClosedEvent
|
||||
* @see org.springframework.context.event.ContextRefreshedEvent
|
||||
* @see org.springframework.context.event.ApplicationEventMulticaster
|
||||
* #multicastEvent(org.springframework.context.ApplicationEvent)
|
||||
*/
|
||||
@Override
|
||||
public void onApplicationEvent(final ApplicationContextEvent event) {
|
||||
if (event instanceof ContextRefreshedEvent) {
|
||||
synchronized (applicationEventNotifier) {
|
||||
contextRefreshedEvent = (ContextRefreshedEvent) event;
|
||||
applicationEventNotifier.multicastEvent(event);
|
||||
}
|
||||
}
|
||||
else if (event instanceof ContextClosedEvent) {
|
||||
synchronized (applicationEventNotifier) {
|
||||
contextRefreshedEvent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,9 +24,10 @@ import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -69,14 +70,17 @@ import com.gemstone.gemfire.cache.Region;
|
||||
@SuppressWarnings("unused")
|
||||
public class SpringContextBootstrappingInitializerIntegrationTest {
|
||||
|
||||
private static final long CACHE_CLOSE_TIMEOUT = 15000l; // 15 seconds
|
||||
private static final long CACHE_CLOSE_TIMEOUT = TimeUnit.SECONDS.toMillis(15);
|
||||
|
||||
private static final Object MUTEX_LOCK = new Object();
|
||||
|
||||
protected static final String GEMFIRE_LOCATORS = "localhost[11235]";
|
||||
protected static final String GEMFIRE_LOG_LEVEL = "config";
|
||||
protected static final String GEMFIRE_LOG_LEVEL = "warning";
|
||||
protected static final String GEMFIRE_JMX_MANAGER = "true";
|
||||
protected static final String GEMFIRE_JMX_MANAGER_PORT = "1199";
|
||||
protected static final String GEMFIRE_JMX_MANAGER_START = "true";
|
||||
protected static final String GEMFIRE_MCAST_PORT = "0";
|
||||
protected static final String GEMFIRE_NAME = "SpringContextBootstrappingInitializationTest";
|
||||
protected static final String GEMFIRE_NAME = "SpringContextBootstrappingInitializationIntegrationTest";
|
||||
protected static final String GEMFIRE_START_LOCATORS = "localhost[11235]";
|
||||
|
||||
@Before
|
||||
@@ -99,7 +103,7 @@ public class SpringContextBootstrappingInitializerIntegrationTest {
|
||||
}
|
||||
}
|
||||
|
||||
fail(String.format("The Cache instance was not properly closed and shutdown by the timeout of %1$d seconds!%n",
|
||||
fail(String.format("The Cache instance was not properly closed in the allotted timeout of %1$d seconds!%n",
|
||||
(CACHE_CLOSE_TIMEOUT / 1000)));
|
||||
}
|
||||
catch (CacheClosedException ignore) {
|
||||
@@ -108,7 +112,7 @@ public class SpringContextBootstrappingInitializerIntegrationTest {
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
SpringContextBootstrappingInitializer.applicationContext = null;
|
||||
SpringContextBootstrappingInitializer.getApplicationContext().close();
|
||||
UserDataStoreCacheLoader.INSTANCE.set(null);
|
||||
tearDownCache();
|
||||
}
|
||||
@@ -145,16 +149,19 @@ public class SpringContextBootstrappingInitializerIntegrationTest {
|
||||
|
||||
protected void doSpringContextBootstrappingInitializationTest(final String cacheXmlFile) {
|
||||
Cache gemfireCache = new CacheFactory()
|
||||
.set("name", GEMFIRE_NAME)
|
||||
.set("mcast-port", GEMFIRE_MCAST_PORT)
|
||||
.set("log-level", GEMFIRE_LOG_LEVEL)
|
||||
.set("cache-xml-file", cacheXmlFile)
|
||||
//.set("locators", GEMFIRE_LOCATORS)
|
||||
.set("log-level", GEMFIRE_LOG_LEVEL)
|
||||
.set("mcast-port", GEMFIRE_MCAST_PORT)
|
||||
.set("name", GEMFIRE_NAME)
|
||||
//.set("start-locator", GEMFIRE_LOCATORS)
|
||||
//.set("jmx-manager", GEMFIRE_JMX_MANAGER)
|
||||
//.set("jmx-manager-port", GEMFIRE_JMX_MANAGER_PORT)
|
||||
//.set("jmx-manager-start", GEMFIRE_JMX_MANAGER_START)
|
||||
.create();
|
||||
|
||||
assertNotNull("The GemFire Cache was not properly created or initialized!", gemfireCache);
|
||||
assertFalse("The GemFire Cache is close!", gemfireCache.isClosed());
|
||||
assertNotNull("The GemFire Cache was not properly created and initialized!", gemfireCache);
|
||||
assertFalse("The GemFire Cache is closed!", gemfireCache.isClosed());
|
||||
|
||||
Set<Region<?, ?>> rootRegions = gemfireCache.rootRegions();
|
||||
|
||||
@@ -184,7 +191,6 @@ public class SpringContextBootstrappingInitializerIntegrationTest {
|
||||
// NOTE a GemFire declared component initialized by Spring!
|
||||
UserDataStoreCacheLoader usersCacheLoader = UserDataStoreCacheLoader.getInstance();
|
||||
|
||||
assertNotNull(usersCacheLoader);
|
||||
assertSame(userDataSource, usersCacheLoader.getDataSource());
|
||||
|
||||
Region<String, User> users = gemfireCache.getRegion("/Users");
|
||||
@@ -200,17 +206,17 @@ public class SpringContextBootstrappingInitializerIntegrationTest {
|
||||
assertEquals(3, users.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpringContextBootstrappingInitialization() {
|
||||
doSpringContextBootstrappingInitializationTest("cache-with-spring-context-bootstrap-initializer.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpringContextBootstrappingInitializationUsingBasePackages() {
|
||||
doSpringContextBootstrappingInitializationTest(
|
||||
"cache-with-spring-context-bootstrap-initializer-using-base-packages.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpringContextBootstrappingInitializationUsingContextConfigLocations() {
|
||||
doSpringContextBootstrappingInitializationTest("cache-with-spring-context-bootstrap-initializer.xml");
|
||||
}
|
||||
|
||||
public static final class TestDataSource extends DataSourceAdapter {
|
||||
}
|
||||
|
||||
@@ -218,7 +224,7 @@ public class SpringContextBootstrappingInitializerIntegrationTest {
|
||||
|
||||
private static final AtomicReference<UserDataStoreCacheLoader> INSTANCE = new AtomicReference<UserDataStoreCacheLoader>();
|
||||
|
||||
private static final Map<String, User> USER_DATA = new HashMap<String, User>(3);
|
||||
private static final Map<String, User> USER_DATA = new ConcurrentHashMap<String, User>(3);
|
||||
|
||||
static {
|
||||
USER_DATA.put("jblum", new User("jblum"));
|
||||
@@ -234,18 +240,19 @@ public class SpringContextBootstrappingInitializerIntegrationTest {
|
||||
}
|
||||
|
||||
protected static User createUser(final String username) {
|
||||
return createUser(username, String.format("%1$s@xcompay.com", username), true, Calendar.getInstance());
|
||||
return createUser(username, true, Calendar.getInstance(), String.format("%1$s@xcompay.com", username));
|
||||
}
|
||||
|
||||
protected static User createUser(final String username, final Boolean active) {
|
||||
return createUser(username, String.format("%1$s@xcompay.com", username), active, Calendar.getInstance());
|
||||
return createUser(username, active, Calendar.getInstance(), String.format("%1$s@xcompay.com", username));
|
||||
}
|
||||
|
||||
protected static User createUser(final String username, final Boolean active, final Calendar since) {
|
||||
return createUser(username, String.format("%1$s@xcompay.com", username), active, since);
|
||||
return createUser(username, active, since, String.format("%1$s@xcompay.com", username));
|
||||
}
|
||||
|
||||
protected static User createUser(final String username, final String email, final Boolean active, final Calendar since) {
|
||||
protected static User createUser(final String username, final Boolean active, final Calendar since,
|
||||
final String email) {
|
||||
User user = new User(username);
|
||||
user.setActive(active);
|
||||
user.setEmail(email);
|
||||
|
||||
@@ -20,9 +20,9 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Matchers.same;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -35,8 +35,10 @@ import org.junit.Test;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.context.event.ContextStartedEvent;
|
||||
import org.springframework.context.event.ContextStoppedEvent;
|
||||
|
||||
/**
|
||||
* The SpringContextBootstrappingInitializerTest class is a test suite of test cases testing the contract
|
||||
@@ -69,143 +71,15 @@ public class SpringContextBootstrappingInitializerTest {
|
||||
@After
|
||||
public void tearDown() {
|
||||
SpringContextBootstrappingInitializer.applicationContext = null;
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateApplicationContextWhenBasePackagesAndConfigLocationsAreBothUnspecified() {
|
||||
try {
|
||||
new SpringContextBootstrappingInitializer().createApplicationContext(null, null);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertEquals("Either 'basePackages' or 'configLocations' must be specified to construct an instance of the ConfigurableApplicationContext.",
|
||||
expected.getMessage());
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateClassPathXmlApplicationContextWhenConfigLocationsAreUnspecified() {
|
||||
try {
|
||||
new SpringContextBootstrappingInitializer().createApplicationContext(null);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertEquals("'configLocations' must be specified to construct an instance of the ClassPathXmlApplicationContext.",
|
||||
expected.getMessage());
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testInitWithUnspecifiedBasePackagesAndContextConfigLocationsParameter() {
|
||||
try {
|
||||
new SpringContextBootstrappingInitializer().init(createParameters(createParameters(
|
||||
SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER, ""),
|
||||
SpringContextBootstrappingInitializer.BASE_PACKAGES_PARAMETER, ""));
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertEquals("Either 'basePackages' or the 'contextConfigLocations' parameter must be specified.",
|
||||
expected.getMessage());
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testInitWithExistingApplicationContext() {
|
||||
try {
|
||||
ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
|
||||
"testInitWithExistingApplicationContext");
|
||||
|
||||
when(mockApplicationContext.getId()).thenReturn("testInitWithExistingApplicationContext");
|
||||
|
||||
SpringContextBootstrappingInitializer.applicationContext = mockApplicationContext;
|
||||
|
||||
new SpringContextBootstrappingInitializer().init(createParameters(
|
||||
SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER,
|
||||
"/path/to/spring/context/configuration/file.xml"));
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
assertEquals("A Spring application context with ID (testInitWithExistingApplicationContext) has already been created.",
|
||||
expected.getMessage());
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testInitWhenCreateApplicationContextReturnsNull() {
|
||||
try {
|
||||
SpringContextBootstrappingInitializer initializer = new SpringContextBootstrappingInitializer() {
|
||||
@Override
|
||||
protected ConfigurableApplicationContext createApplicationContext(final String[] basePackages,
|
||||
final String[] configLocations) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
initializer.init(createParameters(SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER,
|
||||
"/path/to/spring/context/configuration/file.xml"));
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertEquals("The 'created' ConfigurableApplicationContext cannot be null!", expected.getMessage());
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testInitWithNonActiveApplicationContext() {
|
||||
final ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
|
||||
"testInitWithNonActiveApplicationConstruct");
|
||||
|
||||
when(mockApplicationContext.getId()).thenReturn("testInitWithNonActiveApplicationContext");
|
||||
when(mockApplicationContext.isActive()).thenReturn(false);
|
||||
|
||||
SpringContextBootstrappingInitializer initializer = null;
|
||||
|
||||
try {
|
||||
initializer = new SpringContextBootstrappingInitializer() {
|
||||
@Override
|
||||
protected ConfigurableApplicationContext createApplicationContext(final String[] basePackages,
|
||||
final String[] configLocations) {
|
||||
return mockApplicationContext;
|
||||
}
|
||||
};
|
||||
|
||||
initializer.init(createParameters(SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER,
|
||||
"/path/to/spring/context/configuration/file.xml"));
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
assertEquals("The Spring application context (testInitWithNonActiveApplicationContext) has failed to be properly initialized with the following config files ([/path/to/spring/context/configuration/file.xml]) or base packages ([])!",
|
||||
expected.getMessage());
|
||||
throw expected;
|
||||
}
|
||||
finally {
|
||||
verify(mockApplicationContext, times(1)).addApplicationListener(eq(initializer));
|
||||
verify(mockApplicationContext, times(1)).registerShutdownHook();
|
||||
verify(mockApplicationContext, times(1)).refresh();
|
||||
}
|
||||
SpringContextBootstrappingInitializer.contextRefreshedEvent = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitFollowedByGetApplicationContext() {
|
||||
final ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
|
||||
"testInitFollowedByGetApplicationContext");
|
||||
public void testGetApplicationContext() {
|
||||
ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
|
||||
"testGetApplicationContext");
|
||||
|
||||
when(mockApplicationContext.getId()).thenReturn("testInitFollowedByGetApplicationContext");
|
||||
when(mockApplicationContext.isActive()).thenReturn(true);
|
||||
|
||||
SpringContextBootstrappingInitializer initializer = new SpringContextBootstrappingInitializer() {
|
||||
@Override
|
||||
protected ConfigurableApplicationContext createApplicationContext(final String[] basePackages,
|
||||
final String[] configLocations) {
|
||||
return mockApplicationContext;
|
||||
}
|
||||
};
|
||||
|
||||
initializer.init(createParameters(SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER,
|
||||
"/path/to/spring/context/configuration/file.xml"));
|
||||
|
||||
verify(mockApplicationContext, times(1)).addApplicationListener(eq(initializer));
|
||||
verify(mockApplicationContext, times(1)).registerShutdownHook();
|
||||
verify(mockApplicationContext, times(1)).refresh();
|
||||
SpringContextBootstrappingInitializer.applicationContext = mockApplicationContext;
|
||||
|
||||
assertSame(mockApplicationContext, SpringContextBootstrappingInitializer.getApplicationContext());
|
||||
}
|
||||
@@ -216,7 +90,77 @@ public class SpringContextBootstrappingInitializerTest {
|
||||
SpringContextBootstrappingInitializer.getApplicationContext();
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
assertEquals("The Spring ApplicationContext has not been created!", expected.getMessage());
|
||||
assertEquals("The Spring ApplicationContext has not been properly configured and initialized!",
|
||||
expected.getMessage());
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateApplicationContextWhenBothBasePackagesAndConfigLocationsAreUnspecified() {
|
||||
try {
|
||||
new SpringContextBootstrappingInitializer().createApplicationContext(null, null);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertEquals("'basePackages' or 'configLocations' must be specified to construct and configure"
|
||||
+" an instance of the ConfigurableApplicationContext.", expected.getMessage());
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateClassPathXmlApplicationContextWhenConfigLocationsAreUnspecified() {
|
||||
try {
|
||||
new SpringContextBootstrappingInitializer().createApplicationContext(null);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertEquals("'configLocations' must be specified to construct and configure an instance of the ClassPathXmlApplicationContext.",
|
||||
expected.getMessage());
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitApplicationContext() {
|
||||
ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
|
||||
"testInitApplicationContext");
|
||||
|
||||
SpringContextBootstrappingInitializer initializer = new SpringContextBootstrappingInitializer();
|
||||
|
||||
initializer.initApplicationContext(mockApplicationContext);
|
||||
|
||||
verify(mockApplicationContext).addApplicationListener(same(initializer));
|
||||
verify(mockApplicationContext).registerShutdownHook();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testInitApplicationContextWithNullContext() {
|
||||
try {
|
||||
new SpringContextBootstrappingInitializer().initApplicationContext(null);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertEquals("The ConfigurableApplicationContext reference must not be null!", expected.getMessage());
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshApplicationContext() {
|
||||
ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
|
||||
"testInitApplicationContext");
|
||||
|
||||
new SpringContextBootstrappingInitializer().refreshApplicationContext(mockApplicationContext);
|
||||
|
||||
verify(mockApplicationContext).refresh();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testRefreshApplicationContextWithNullContext() {
|
||||
try {
|
||||
new SpringContextBootstrappingInitializer().refreshApplicationContext(null);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertEquals("The ConfigurableApplicationContext reference must not be null!", expected.getMessage());
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
@@ -238,97 +182,390 @@ public class SpringContextBootstrappingInitializerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterAndOnApplicationEvent() {
|
||||
TestApplicationListener testListener = SpringContextBootstrappingInitializer.register(
|
||||
new TestApplicationListener());
|
||||
|
||||
try {
|
||||
ContextRefreshedEvent testEvent = new ContextRefreshedEvent(mock(ApplicationContext.class,
|
||||
"testRegisterAndOnApplicationEvent"));
|
||||
|
||||
new SpringContextBootstrappingInitializer().onApplicationEvent(testEvent);
|
||||
|
||||
testListener.assertCalled();
|
||||
testListener.assertSame(testEvent);
|
||||
}
|
||||
finally {
|
||||
SpringContextBootstrappingInitializer.unregister(testListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterUnregisterAndOnApplicationEvent() {
|
||||
TestApplicationListener testListener = SpringContextBootstrappingInitializer.unregister(
|
||||
SpringContextBootstrappingInitializer.register(new TestApplicationListener()));
|
||||
|
||||
try {
|
||||
ContextRefreshedEvent testEvent = new ContextRefreshedEvent(mock(ApplicationContext.class,
|
||||
"testRegisterUnregisterAndOnApplicationEvent"));
|
||||
|
||||
new SpringContextBootstrappingInitializer().onApplicationEvent(testEvent);
|
||||
|
||||
testListener.assertNotCalled();
|
||||
}
|
||||
finally {
|
||||
SpringContextBootstrappingInitializer.unregister(testListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyListenersOnContextRefreshedEventBeforeApplicationContextExists() {
|
||||
TestApplicationListener testApplicationListener = new TestApplicationListener();
|
||||
|
||||
SpringContextBootstrappingInitializer.applicationContext = null;
|
||||
SpringContextBootstrappingInitializer.register(testApplicationListener);
|
||||
|
||||
testApplicationListener.assertNotCalled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyListenersOnContextRefreshedEventAfterApplicationContextRefreshed() {
|
||||
public void testInitWithExistingApplicationContext() {
|
||||
ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
|
||||
"testNotifyListenersOnContextRefreshedEventAfterApplicationContextRefreshed");
|
||||
"testInitWithExistingApplicationContext");
|
||||
|
||||
ContextRefreshedEvent testEvent = new ContextRefreshedEvent(mockApplicationContext);
|
||||
when(mockApplicationContext.isActive()).thenReturn(true);
|
||||
when(mockApplicationContext.getId()).thenReturn("testInitWithExistingApplicationContext");
|
||||
|
||||
SpringContextBootstrappingInitializer.applicationContext = mockApplicationContext;
|
||||
new SpringContextBootstrappingInitializer().onApplicationEvent(testEvent);
|
||||
|
||||
TestApplicationListener testApplicationListener = new TestApplicationListener();
|
||||
assertSame(mockApplicationContext, SpringContextBootstrappingInitializer.getApplicationContext());
|
||||
|
||||
SpringContextBootstrappingInitializer.register(testApplicationListener);
|
||||
SpringContextBootstrappingInitializer initializer = new SpringContextBootstrappingInitializer();
|
||||
|
||||
testApplicationListener.assertCalled();
|
||||
testApplicationListener.assertSame(testEvent);
|
||||
initializer.init(createParameters(SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER,
|
||||
"/path/to/spring/application/context.xml"));
|
||||
|
||||
verify(mockApplicationContext, never()).addApplicationListener(same(initializer));
|
||||
verify(mockApplicationContext, never()).registerShutdownHook();
|
||||
verify(mockApplicationContext, never()).refresh();
|
||||
|
||||
assertSame(mockApplicationContext, SpringContextBootstrappingInitializer.getApplicationContext());
|
||||
}
|
||||
|
||||
// TODO add additional multi-thread test cases once MultithreadedTC test framework is added to the SDP project
|
||||
// to properly test concurrency of the notification and registration during Spring ApplicationContext creation.
|
||||
@Test
|
||||
public void testInitWhenApplicationContextIsNull() {
|
||||
assertNull(SpringContextBootstrappingInitializer.applicationContext);
|
||||
|
||||
final ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
|
||||
"testInitWhenApplicationContextIsNull");
|
||||
|
||||
when(mockApplicationContext.getId()).thenReturn("testInitWhenApplicationContextIsNull");
|
||||
when(mockApplicationContext.isRunning()).thenReturn(true);
|
||||
|
||||
SpringContextBootstrappingInitializer initializer = new SpringContextBootstrappingInitializer() {
|
||||
@Override
|
||||
protected ConfigurableApplicationContext createApplicationContext(final String[] basePackages,
|
||||
final String[] configLocations) {
|
||||
return mockApplicationContext;
|
||||
}
|
||||
};
|
||||
|
||||
initializer.init(createParameters(SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER,
|
||||
"/path/to/spring/application/context.xml"));
|
||||
|
||||
verify(mockApplicationContext, times(1)).addApplicationListener(same(initializer));
|
||||
verify(mockApplicationContext, times(1)).registerShutdownHook();
|
||||
verify(mockApplicationContext, times(1)).refresh();
|
||||
|
||||
assertSame(mockApplicationContext, SpringContextBootstrappingInitializer.getApplicationContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitWhenApplicationContextIsInactive() {
|
||||
ConfigurableApplicationContext mockInactiveApplicationContext = mock(ConfigurableApplicationContext.class,
|
||||
"testInitWhenApplicationContextIsInactive.Inactive");
|
||||
|
||||
when(mockInactiveApplicationContext.isActive()).thenReturn(false);
|
||||
|
||||
SpringContextBootstrappingInitializer.applicationContext = mockInactiveApplicationContext;
|
||||
|
||||
assertSame(mockInactiveApplicationContext, SpringContextBootstrappingInitializer.getApplicationContext());
|
||||
|
||||
final ConfigurableApplicationContext mockNewApplicationContext = mock(ConfigurableApplicationContext.class,
|
||||
"testInitWhenApplicationContextIsInactive.New");
|
||||
|
||||
when(mockNewApplicationContext.getId()).thenReturn("testInitWhenApplicationContextIsInactive.New");
|
||||
when(mockNewApplicationContext.isRunning()).thenReturn(true);
|
||||
|
||||
SpringContextBootstrappingInitializer initializer = new SpringContextBootstrappingInitializer() {
|
||||
@Override
|
||||
protected ConfigurableApplicationContext createApplicationContext(final String[] basePackages,
|
||||
final String[] configLocations) {
|
||||
return mockNewApplicationContext;
|
||||
}
|
||||
};
|
||||
|
||||
initializer.init(createParameters(SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER,
|
||||
"/path/to/spring/application/context.xml"));
|
||||
|
||||
verify(mockNewApplicationContext, times(1)).addApplicationListener(same(initializer));
|
||||
verify(mockNewApplicationContext, times(1)).registerShutdownHook();
|
||||
verify(mockNewApplicationContext, times(1)).refresh();
|
||||
|
||||
assertSame(mockNewApplicationContext, SpringContextBootstrappingInitializer.getApplicationContext());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testInitWhenBothBasePackagesAndContextConfigLocationsParametersAreUnspecified() {
|
||||
assertNull(SpringContextBootstrappingInitializer.applicationContext);
|
||||
|
||||
try {
|
||||
new SpringContextBootstrappingInitializer().init(createParameters(createParameters(
|
||||
SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER, ""),
|
||||
SpringContextBootstrappingInitializer.BASE_PACKAGES_PARAMETER, ""));
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertEquals("Either 'basePackages' or the 'contextConfigLocations' parameter must be specified.",
|
||||
expected.getMessage());
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testInitWhenApplicationContextIsNotRunning() {
|
||||
assertNull(SpringContextBootstrappingInitializer.applicationContext);
|
||||
|
||||
final ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
|
||||
"testInitWhenApplicationContextIsNotRunning");
|
||||
|
||||
when(mockApplicationContext.getId()).thenReturn("testInitWhenApplicationContextIsNotRunning");
|
||||
when(mockApplicationContext.isRunning()).thenReturn(false);
|
||||
|
||||
SpringContextBootstrappingInitializer initializer = new SpringContextBootstrappingInitializer() {
|
||||
@Override
|
||||
protected ConfigurableApplicationContext createApplicationContext(final String[] basePackages,
|
||||
final String[] configLocations) {
|
||||
return mockApplicationContext;
|
||||
}
|
||||
};
|
||||
|
||||
initializer.init(createParameters(SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER,
|
||||
"/path/to/spring/application/context.xml"));
|
||||
|
||||
verify(mockApplicationContext, times(1)).addApplicationListener(same(initializer));
|
||||
verify(mockApplicationContext, times(1)).registerShutdownHook();
|
||||
verify(mockApplicationContext, times(1)).refresh();
|
||||
|
||||
SpringContextBootstrappingInitializer.getApplicationContext();
|
||||
}
|
||||
|
||||
protected static void assertNotifiedWithEvent(final TestApplicationListener listener, final ContextRefreshedEvent expectedEvent) {
|
||||
Assert.assertTrue(listener.isNotified());
|
||||
Assert.assertSame(expectedEvent, listener.getActualEvent());
|
||||
}
|
||||
|
||||
protected static void assertUnnotified(final TestApplicationListener listener) {
|
||||
assertFalse(listener.isNotified());
|
||||
assertNull(listener.getActualEvent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnApplicationEvent() {
|
||||
TestApplicationListener testApplicationListener = new TestApplicationListener("testOnApplicationEvent");
|
||||
|
||||
try {
|
||||
testApplicationListener = SpringContextBootstrappingInitializer.register(testApplicationListener);
|
||||
|
||||
assertUnnotified(testApplicationListener);
|
||||
|
||||
ContextRefreshedEvent testContextRefreshedEvent = new ContextRefreshedEvent(mock(ApplicationContext.class,
|
||||
"testOnApplicationEvent"));
|
||||
|
||||
new SpringContextBootstrappingInitializer().onApplicationEvent(testContextRefreshedEvent);
|
||||
|
||||
assertNotifiedWithEvent(testApplicationListener, testContextRefreshedEvent);
|
||||
}
|
||||
finally {
|
||||
SpringContextBootstrappingInitializer.unregister(testApplicationListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnApplicationEventWithContextStartedEvent() {
|
||||
TestApplicationListener testApplicationListener = new TestApplicationListener(
|
||||
"testOnApplicationEventWithContextStartedEvent");
|
||||
|
||||
try {
|
||||
testApplicationListener = SpringContextBootstrappingInitializer.register(testApplicationListener);
|
||||
|
||||
assertUnnotified(testApplicationListener);
|
||||
|
||||
ContextStartedEvent testContextStartedEvent = mock(ContextStartedEvent.class,
|
||||
"testOnApplicationEventWithContextStartedEvent");
|
||||
|
||||
new SpringContextBootstrappingInitializer().onApplicationEvent(testContextStartedEvent);
|
||||
|
||||
assertUnnotified(testApplicationListener);
|
||||
}
|
||||
finally {
|
||||
SpringContextBootstrappingInitializer.unregister(testApplicationListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnApplicationEventWithMultipleRegisteredApplicationListeners() {
|
||||
TestApplicationListener testApplicationListenerOne = new TestApplicationListener(
|
||||
"testOnApplicationEventWithMultipleRegisteredApplicationListeners.1");
|
||||
|
||||
TestApplicationListener testApplicationListenerTwo = new TestApplicationListener(
|
||||
"testOnApplicationEventWithMultipleRegisteredApplicationListeners.2");
|
||||
|
||||
TestApplicationListener testApplicationListenerThree = new TestApplicationListener(
|
||||
"testOnApplicationEventWithMultipleRegisteredApplicationListeners.3");
|
||||
|
||||
try {
|
||||
testApplicationListenerOne = SpringContextBootstrappingInitializer.register(testApplicationListenerOne);
|
||||
testApplicationListenerTwo = SpringContextBootstrappingInitializer.register(testApplicationListenerTwo);
|
||||
testApplicationListenerThree = SpringContextBootstrappingInitializer.register(testApplicationListenerThree);
|
||||
|
||||
assertUnnotified(testApplicationListenerOne);
|
||||
assertUnnotified(testApplicationListenerTwo);
|
||||
assertUnnotified(testApplicationListenerThree);
|
||||
|
||||
ContextRefreshedEvent testContextRefreshedEvent = new ContextRefreshedEvent(mock(ApplicationContext.class,
|
||||
"testRegisterWithOnApplicationEvent"));
|
||||
|
||||
new SpringContextBootstrappingInitializer().onApplicationEvent(testContextRefreshedEvent);
|
||||
|
||||
assertNotifiedWithEvent(testApplicationListenerOne, testContextRefreshedEvent);
|
||||
assertNotifiedWithEvent(testApplicationListenerTwo, testContextRefreshedEvent);
|
||||
assertNotifiedWithEvent(testApplicationListenerThree, testContextRefreshedEvent);
|
||||
}
|
||||
finally {
|
||||
SpringContextBootstrappingInitializer.unregister(testApplicationListenerOne);
|
||||
SpringContextBootstrappingInitializer.unregister(testApplicationListenerTwo);
|
||||
SpringContextBootstrappingInitializer.unregister(testApplicationListenerThree);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnApplicationEventWithUnregisteredApplicationListener() {
|
||||
TestApplicationListener testApplicationListener = new TestApplicationListener(
|
||||
"testOnApplicationEventWithUnregisteredApplicationListener");
|
||||
|
||||
try {
|
||||
testApplicationListener = SpringContextBootstrappingInitializer.unregister(
|
||||
SpringContextBootstrappingInitializer.register(testApplicationListener));
|
||||
|
||||
assertUnnotified(testApplicationListener);
|
||||
|
||||
ContextRefreshedEvent testContextRefreshedEvent = new ContextRefreshedEvent(mock(ApplicationContext.class,
|
||||
"testRegisterThenUnregisterWithOnApplicationEvent"));
|
||||
|
||||
new SpringContextBootstrappingInitializer().onApplicationEvent(testContextRefreshedEvent);
|
||||
|
||||
assertUnnotified(testApplicationListener);
|
||||
}
|
||||
finally {
|
||||
SpringContextBootstrappingInitializer.unregister(testApplicationListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyOnExistingContextRefreshedEventBeforeApplicationContextExists() {
|
||||
assertNull(SpringContextBootstrappingInitializer.contextRefreshedEvent);
|
||||
|
||||
TestApplicationListener testApplicationListener = new TestApplicationListener(
|
||||
"testNotifyOnExistingContextRefreshedEventBeforeApplicationContextExists");
|
||||
|
||||
try {
|
||||
testApplicationListener = SpringContextBootstrappingInitializer.register(testApplicationListener);
|
||||
assertUnnotified(testApplicationListener);
|
||||
}
|
||||
finally {
|
||||
SpringContextBootstrappingInitializer.unregister(testApplicationListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyOnExistingContextRefreshedEventAfterContextRefreshed() {
|
||||
ContextRefreshedEvent testContextRefreshedEvent = new ContextRefreshedEvent(mock(ApplicationContext.class));
|
||||
|
||||
new SpringContextBootstrappingInitializer().onApplicationEvent(testContextRefreshedEvent);
|
||||
|
||||
TestApplicationListener testApplicationListener = new TestApplicationListener(
|
||||
"testNotifyApplicationListenersOnContextRefreshedEventAfterApplicationContextRefreshed");
|
||||
|
||||
try {
|
||||
testApplicationListener = SpringContextBootstrappingInitializer.register(testApplicationListener);
|
||||
assertNotifiedWithEvent(testApplicationListener, testContextRefreshedEvent);
|
||||
}
|
||||
finally {
|
||||
SpringContextBootstrappingInitializer.unregister(testApplicationListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnApplicationEventAndNotifyOnExistingContextRefreshedEvent() {
|
||||
ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
|
||||
"testOnApplicationEventAndNotifyOnExistingContextRefreshedEvent");
|
||||
|
||||
SpringContextBootstrappingInitializer initializer = new SpringContextBootstrappingInitializer();
|
||||
|
||||
TestApplicationListener testApplicationListenerOne = new TestApplicationListener(
|
||||
"testOnApplicationEventAndNotifyOnExistingContextRefreshedEvent.1");
|
||||
|
||||
TestApplicationListener testApplicationListenerTwo = new TestApplicationListener(
|
||||
"testOnApplicationEventAndNotifyOnExistingContextRefreshedEvent.2");
|
||||
|
||||
TestApplicationListener testApplicationListenerThree = new TestApplicationListener(
|
||||
"testOnApplicationEventAndNotifyOnExistingContextRefreshedEvent.3");
|
||||
|
||||
try {
|
||||
testApplicationListenerOne = SpringContextBootstrappingInitializer.register(testApplicationListenerOne);
|
||||
|
||||
assertUnnotified(testApplicationListenerOne);
|
||||
assertUnnotified(testApplicationListenerTwo);
|
||||
assertUnnotified(testApplicationListenerThree);
|
||||
|
||||
ContextRefreshedEvent testContextRefreshedEvent = new ContextRefreshedEvent(mockApplicationContext);
|
||||
|
||||
initializer.onApplicationEvent(testContextRefreshedEvent);
|
||||
|
||||
assertNotifiedWithEvent(testApplicationListenerOne, testContextRefreshedEvent);
|
||||
assertUnnotified(testApplicationListenerTwo);
|
||||
assertUnnotified(testApplicationListenerThree);
|
||||
|
||||
testApplicationListenerTwo = SpringContextBootstrappingInitializer.register(testApplicationListenerTwo);
|
||||
|
||||
assertNotifiedWithEvent(testApplicationListenerTwo, testContextRefreshedEvent);
|
||||
assertUnnotified(testApplicationListenerOne);
|
||||
assertUnnotified(testApplicationListenerThree);
|
||||
|
||||
ContextStoppedEvent contextStoppedEvent = new ContextStoppedEvent(mockApplicationContext);
|
||||
|
||||
initializer.onApplicationEvent(contextStoppedEvent);
|
||||
|
||||
assertUnnotified(testApplicationListenerOne);
|
||||
assertUnnotified(testApplicationListenerTwo);
|
||||
assertUnnotified(testApplicationListenerThree);
|
||||
|
||||
initializer.onApplicationEvent(testContextRefreshedEvent);
|
||||
|
||||
assertNotifiedWithEvent(testApplicationListenerOne, testContextRefreshedEvent);
|
||||
assertNotifiedWithEvent(testApplicationListenerTwo, testContextRefreshedEvent);
|
||||
assertUnnotified(testApplicationListenerThree);
|
||||
|
||||
ContextClosedEvent testContextClosedEvent = new ContextClosedEvent(mockApplicationContext);
|
||||
|
||||
initializer.onApplicationEvent(testContextClosedEvent);
|
||||
|
||||
assertUnnotified(testApplicationListenerOne);
|
||||
assertUnnotified(testApplicationListenerTwo);
|
||||
assertUnnotified(testApplicationListenerThree);
|
||||
|
||||
SpringContextBootstrappingInitializer.register(testApplicationListenerThree);
|
||||
|
||||
assertUnnotified(testApplicationListenerOne);
|
||||
assertUnnotified(testApplicationListenerTwo);
|
||||
assertUnnotified(testApplicationListenerThree);
|
||||
}
|
||||
finally {
|
||||
SpringContextBootstrappingInitializer.unregister(testApplicationListenerOne);
|
||||
SpringContextBootstrappingInitializer.unregister(testApplicationListenerTwo);
|
||||
SpringContextBootstrappingInitializer.unregister(testApplicationListenerThree);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO add additional multi-threaded test cases once MultithreadedTC test framework is added to the SDP project
|
||||
// in order to properly test concurrency of notification and registration during Spring ApplicationContext creation.
|
||||
|
||||
protected static class TestApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
private volatile boolean called = false;
|
||||
private volatile boolean notified = false;
|
||||
|
||||
private volatile ContextRefreshedEvent actualEvent;
|
||||
|
||||
public void assertCalled() {
|
||||
assertTrue(String.format("Expected the (%1$s).onApplicationEvent(:ContextRefreshedEvent) method to be called!",
|
||||
getClass().getName()), called);
|
||||
private final String name;
|
||||
|
||||
public TestApplicationListener(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void assertNotCalled() {
|
||||
assertFalse(String.format("Expected the (%1$s).onApplicationEvent(:ContextRefreshedEvent) method to not be called for ApplicationEvent (%2$s)!",
|
||||
getClass().getName(), ObjectUtils.nullSafeClassName(actualEvent)), called);
|
||||
public ContextRefreshedEvent getActualEvent() {
|
||||
ContextRefreshedEvent localActualEvent = this.actualEvent;
|
||||
this.actualEvent = null;
|
||||
return localActualEvent;
|
||||
}
|
||||
|
||||
public void assertSame(final ContextRefreshedEvent expectedEvent) {
|
||||
Assert.assertSame(expectedEvent, actualEvent);
|
||||
public boolean isNotified() {
|
||||
boolean localNotified = this.notified;
|
||||
this.notified = false;
|
||||
return localNotified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(final ContextRefreshedEvent event) {
|
||||
this.actualEvent = event;
|
||||
called = true;
|
||||
this.notified = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user