Added appropriate synchronization around application event notification using the Spring ApplicationContext and ApplicationEventMulticaster. In addition, added support for late ContextRefreshedEvent notifications in the case where ApplicationListeners are registered after the ConfigurableApplicationContext has already been constructed, configured, initialized and refreshed. This latest feature is meant to support updates planned in the 7.5 GemFire release to the Gfsh 'deploy' command dynamically deploying and initializing Declarables in the JAR file. It is possible that Declarables are created and initialized in an unspecified order given there is not equivalent to Spring's Ordered interface in GemFire.
This commit is contained in:
@@ -47,10 +47,12 @@ import org.springframework.util.ObjectUtils;
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.springframework.context.ConfigurableApplicationContext
|
||||
* @see org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer
|
||||
* @see org.springframework.data.gemfire.support.SpringContextBootstrappingInitializerIntegrationTest
|
||||
* @since 1.3.4
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class SpringContextBootstrappingInitializerTest {
|
||||
|
||||
protected static Properties createParameters(final String parameter, final String value) {
|
||||
@@ -59,6 +61,11 @@ public class SpringContextBootstrappingInitializerTest {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
protected static Properties createParameters(final Properties parameters, final String parameter, final String value) {
|
||||
parameters.setProperty(parameter, value);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
SpringContextBootstrappingInitializer.applicationContext = null;
|
||||
@@ -82,17 +89,18 @@ public class SpringContextBootstrappingInitializerTest {
|
||||
new SpringContextBootstrappingInitializer().createApplicationContext(null);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertEquals("The configLocations must be specified to construct an instance of the ClassPathXmlApplicationContext.",
|
||||
assertEquals("'configLocations' must be specified to construct an instance of the ClassPathXmlApplicationContext.",
|
||||
expected.getMessage());
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testInitWithUnspecifiedContextConfigLocationsParameter() {
|
||||
public void testInitWithUnspecifiedBasePackagesAndContextConfigLocationsParameter() {
|
||||
try {
|
||||
new SpringContextBootstrappingInitializer().init(createParameters(
|
||||
SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER, ""));
|
||||
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.",
|
||||
@@ -122,6 +130,26 @@ public class SpringContextBootstrappingInitializerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@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,
|
||||
@@ -145,7 +173,7 @@ public class SpringContextBootstrappingInitializerTest {
|
||||
"/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])!",
|
||||
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;
|
||||
}
|
||||
@@ -199,13 +227,13 @@ public class SpringContextBootstrappingInitializerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullSafeGetApplicationContextWithNonNullReference() {
|
||||
public void testNullSafeGetApplicationContextIdWithNonNullReference() {
|
||||
ApplicationContext mockApplicationContext = mock(ApplicationContext.class,
|
||||
"testNullSafeGetApplicationContextWithNonNullReference");
|
||||
"testNullSafeGetApplicationContextIdWithNonNullReference");
|
||||
|
||||
when(mockApplicationContext.getId()).thenReturn("testNullSafeGetApplicationContextWithNonNullReference");
|
||||
when(mockApplicationContext.getId()).thenReturn("testNullSafeGetApplicationContextIdWithNonNullReference");
|
||||
|
||||
assertEquals("testNullSafeGetApplicationContextWithNonNullReference",
|
||||
assertEquals("testNullSafeGetApplicationContextIdWithNonNullReference",
|
||||
new SpringContextBootstrappingInitializer().nullSafeGetApplicationContextId(mockApplicationContext));
|
||||
}
|
||||
|
||||
@@ -235,7 +263,7 @@ public class SpringContextBootstrappingInitializerTest {
|
||||
|
||||
try {
|
||||
ContextRefreshedEvent testEvent = new ContextRefreshedEvent(mock(ApplicationContext.class,
|
||||
"testUnregisterAndOnApplicationEvent"));
|
||||
"testRegisterUnregisterAndOnApplicationEvent"));
|
||||
|
||||
new SpringContextBootstrappingInitializer().onApplicationEvent(testEvent);
|
||||
|
||||
@@ -246,6 +274,37 @@ public class SpringContextBootstrappingInitializerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyListenersOnContextRefreshedEventBeforeApplicationContextExists() {
|
||||
TestApplicationListener testApplicationListener = new TestApplicationListener();
|
||||
|
||||
SpringContextBootstrappingInitializer.applicationContext = null;
|
||||
SpringContextBootstrappingInitializer.register(testApplicationListener);
|
||||
|
||||
testApplicationListener.assertNotCalled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyListenersOnContextRefreshedEventAfterApplicationContextRefreshed() {
|
||||
ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
|
||||
"testNotifyListenersOnContextRefreshedEventAfterApplicationContextRefreshed");
|
||||
|
||||
ContextRefreshedEvent testEvent = new ContextRefreshedEvent(mockApplicationContext);
|
||||
|
||||
SpringContextBootstrappingInitializer.applicationContext = mockApplicationContext;
|
||||
new SpringContextBootstrappingInitializer().onApplicationEvent(testEvent);
|
||||
|
||||
TestApplicationListener testApplicationListener = new TestApplicationListener();
|
||||
|
||||
SpringContextBootstrappingInitializer.register(testApplicationListener);
|
||||
|
||||
testApplicationListener.assertCalled();
|
||||
testApplicationListener.assertSame(testEvent);
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
protected static class TestApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
private volatile boolean called = false;
|
||||
|
||||
Reference in New Issue
Block a user