SGF-395 - Allow Spring JavaConfig @Configuration classes to be registered and used to configure the (AnnotationConfig)ApplicationContext created by the SpringContextBootstrappingInitializer.

(cherry picked from commit c492a94a4c05b4697aa0a39920a48dd283e0c27e)

Signed-off-by: John Blum <jblum@pivotal.io>
This commit is contained in:
John Blum
2015-04-10 14:48:54 -07:00
parent c1aabdbb53
commit 79bec8522d
3 changed files with 318 additions and 129 deletions

View File

@@ -17,7 +17,10 @@
package org.springframework.data.gemfire.support;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -32,6 +35,7 @@ import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@@ -71,6 +75,8 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
/* package-private */ static volatile ContextRefreshedEvent contextRefreshedEvent;
private static final List<Class<?>> registeredAnnotatedClasses = new CopyOnWriteArrayList<Class<?>>();
protected final Log logger = initLogger();
/**
@@ -81,7 +87,7 @@ 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 properly configured and initialized!");
Assert.state(applicationContext != null, "The Spring ApplicationContext was not configured and initialized properly!");
return applicationContext;
}
@@ -97,7 +103,7 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
* @see org.springframework.context.event.ContextRefreshedEvent
*/
protected static void notifyOnExistingContextRefreshedEvent(final ApplicationListener<ContextRefreshedEvent> listener) {
protected static void notifyOnExistingContextRefreshedEvent(ApplicationListener<ContextRefreshedEvent> listener) {
synchronized (applicationEventNotifier) {
if (contextRefreshedEvent != null) {
listener.onApplicationEvent(contextRefreshedEvent);
@@ -121,7 +127,7 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
* #addApplicationListener(org.springframework.context.ApplicationListener)
*/
public static <T extends ApplicationListener<ContextRefreshedEvent>> T register(final T listener) {
public static <T extends ApplicationListener<ContextRefreshedEvent>> T register(T listener) {
synchronized (applicationEventNotifier) {
applicationEventNotifier.addApplicationListener(listener);
notifyOnExistingContextRefreshedEvent(listener);
@@ -130,6 +136,19 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
return listener;
}
/**
* Registers the specified Spring annotated POJO class, which will be used to configure and initialize
* the Spring ApplicationContext.
*
* @param annotatedClass the Spring annotated (@Configuration) POJO class to register.
* @return a boolean value indicating whether the Spring annotated POJO class was successfully registered.
* @see #unregister(Class)
*/
public static boolean register(Class<?> annotatedClass) {
Assert.notNull(annotatedClass, "the Spring annotated class to register must not be null");
return registeredAnnotatedClasses.add(annotatedClass);
}
/**
* Un-registers the Spring ApplicationListener from this SpringContextBootstrappingInitializer in order to stop
* receiving ApplicationEvents on Spring context refreshes.
@@ -144,7 +163,7 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
* #removeApplicationListener(org.springframework.context.ApplicationListener)
*/
public static <T extends ApplicationListener<ContextRefreshedEvent>> T unregister(final T listener) {
public static <T extends ApplicationListener<ContextRefreshedEvent>> T unregister(T listener) {
synchronized (applicationEventNotifier) {
applicationEventNotifier.removeApplicationListener(listener);
}
@@ -152,6 +171,18 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
return listener;
}
/**
* Un-registers the specified Spring annotated POJO class used to configure and initialize
* the Spring ApplicationContext.
*
* @param annotatedClass the Spring annotated (@Configuration) POJO class to unregister.
* @return a boolean value indicating whether the Spring annotated POJO class was successfully un-registered.
* @see #register(Class)
*/
public static boolean unregister(Class<?> annotatedClass) {
return registeredAnnotatedClasses.remove(annotatedClass);
}
/**
* Initialization method for the logger used to log important messages from this initializer.
*
@@ -163,25 +194,6 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
return LogFactory.getLog(getClass());
}
/**
* Creates (constructs and configures) a ConfigurableApplicationContext instance based on the specified locations
* of the context configuration meta-data files. The created ConfigurableApplicationContext is not automatically
* "refreshed" and therefore must be "refreshed" by the caller manually.
*
* @param configLocations a String array indicating the locations of the context configuration meta-data files
* used to configure the ConfigurableApplicationContext instance.
* @return a newly constructed and configured instance of the ConfigurableApplicationContext class. Note, the
* "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.ConfigurableApplicationContext
*/
protected ConfigurableApplicationContext createApplicationContext(final String[] configLocations) {
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
@@ -206,23 +218,29 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
* @see org.springframework.context.annotation.AnnotationConfigApplicationContext#scan(String...)
* @see org.springframework.context.support.ClassPathXmlApplicationContext
*/
protected ConfigurableApplicationContext createApplicationContext(final String[] basePackages,
final String[] configLocations) {
protected ConfigurableApplicationContext createApplicationContext(String[] basePackages, String[] configLocations) {
if (!ObjectUtils.isEmpty(configLocations)) {
return new ClassPathXmlApplicationContext(configLocations, false);
return createApplicationContext(configLocations);
}
else {
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;
Assert.isTrue(isConfigurable(basePackages, configLocations, registeredAnnotatedClasses),
"'basePackages', 'configLocations' or 'AnnotatedClasses' must be specified in order to"
+ " construct and configure an instance of the ConfigurableApplicationContext");
return scanBasePackages(registerAnnotatedClasses((AnnotationConfigApplicationContext) createApplicationContext(null),
registeredAnnotatedClasses.toArray(new Class<?>[registeredAnnotatedClasses.size()])), basePackages);
}
}
/* (non-Javadoc) - used for testing purposes only */
ConfigurableApplicationContext createApplicationContext(String[] configLocations) {
return (ObjectUtils.isEmpty(configLocations) ? new AnnotationConfigApplicationContext()
: new ClassPathXmlApplicationContext(configLocations, false));
}
/**
* Initializes the given ApplicationContext by registering this SpringContextBootstrappingInitializer as an
* ApplicationListener and registering a Runtime shutdown hook.
* ApplicationListener and registering a runtime shutdown hook.
*
* @param applicationContext the ConfigurableApplicationContext to initialize.
* @return the initialized ApplicationContext.
@@ -231,8 +249,7 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
* @see org.springframework.context.ConfigurableApplicationContext#registerShutdownHook()
* @throws java.lang.IllegalArgumentException if the ApplicationContext reference is null!
*/
protected ConfigurableApplicationContext initApplicationContext(
final ConfigurableApplicationContext applicationContext) {
protected ConfigurableApplicationContext initApplicationContext(ConfigurableApplicationContext applicationContext) {
Assert.notNull(applicationContext, "The ConfigurableApplicationContext reference must not be null!");
applicationContext.addApplicationListener(this);
applicationContext.registerShutdownHook();
@@ -248,24 +265,69 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
* @see org.springframework.context.ConfigurableApplicationContext#refresh()
* @throws java.lang.IllegalArgumentException if the ApplicationContext reference is null!
*/
protected ConfigurableApplicationContext refreshApplicationContext(
final ConfigurableApplicationContext applicationContext) {
protected ConfigurableApplicationContext refreshApplicationContext(ConfigurableApplicationContext applicationContext) {
Assert.notNull(applicationContext, "The ConfigurableApplicationContext reference must not be null!");
applicationContext.refresh();
return applicationContext;
}
/* (non-Javadoc) */
private boolean isConfigurable(String[] basePackages, String[] contextConfigLocations,
Collection<Class<?>> annotatedClasses) {
return !(ObjectUtils.isEmpty(basePackages) && ObjectUtils.isEmpty(contextConfigLocations)
&& CollectionUtils.isEmpty(annotatedClasses));
}
/**
* Gets the the ID of the Spring ApplicationContext in a null-safe manner.
* Null-safe operation used to get the ID of the Spring ApplicationContext.
*
* @param applicationContext the Spring ApplicationContext to retrieve the ID for.
* @param applicationContext the Spring ApplicationContext from which to get the ID.
* @return the ID of the given Spring ApplicationContext or null if the ApplicationContext reference is null.
* @see org.springframework.context.ApplicationContext#getId()
*/
protected String nullSafeGetApplicationContextId(final ApplicationContext applicationContext) {
String nullSafeGetApplicationContextId(ApplicationContext applicationContext) {
return (applicationContext != null ? applicationContext.getId() : null);
}
/**
* Registers the given Spring annotated (@Configuration) POJO classes with the specified
* AnnotationConfigApplicationContext.
*
* @param applicationContext the AnnotationConfigApplicationContext used to register the Spring annotated,
* POJO classes.
* @param annotatedClasses a Class array of Spring annotated (@Configuration) classes used to configure
* and initialize the Spring AnnotationConfigApplicationContext.
* @return the given AnnotationConfigApplicationContext.
* @see org.springframework.context.annotation.AnnotationConfigApplicationContext#register(Class[])
*/
AnnotationConfigApplicationContext registerAnnotatedClasses(AnnotationConfigApplicationContext applicationContext,
Class<?>[] annotatedClasses) {
if (!ObjectUtils.isEmpty(annotatedClasses)) {
applicationContext.register(annotatedClasses);
}
return applicationContext;
}
/**
* Configures classpath component scanning using the specified base packages on the specified
* AnnotationConfigApplicationContext.
*
* @param applicationContext the AnnotationConfigApplicationContext to setup with classpath component scanning
* using the specified base packages.
* @param basePackages an array of Strings indicating the base packages to use in the classpath component scan.
* @return the given AnnotationConfigApplicationContext.
* @see org.springframework.context.annotation.AnnotationConfigApplicationContext#scan(String...)
*/
AnnotationConfigApplicationContext scanBasePackages(AnnotationConfigApplicationContext applicationContext,
String[] basePackages) {
if (!ObjectUtils.isEmpty(basePackages)) {
applicationContext.scan(basePackages);
}
return applicationContext;
}
/**
* Initializes a Spring ApplicationContext with the given parameters specified with a GemFire &lt;initializer&gt;
* block in cache.xml.
@@ -288,14 +350,11 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
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(
StringUtils.trimWhitespace(basePackages), COMMA_DELIMITER, CHARS_TO_DELETE);
String[] basePackagesArray = StringUtils.delimitedListToStringArray(basePackages,
COMMA_DELIMITER, CHARS_TO_DELETE);
String[] contextConfigLocationsArray = StringUtils.delimitedListToStringArray(contextConfigLocations,
COMMA_DELIMITER, CHARS_TO_DELETE);
String[] contextConfigLocationsArray = StringUtils.delimitedListToStringArray(
StringUtils.trimWhitespace(contextConfigLocations), COMMA_DELIMITER, CHARS_TO_DELETE);
ConfigurableApplicationContext localApplicationContext = refreshApplicationContext(
initApplicationContext(createApplicationContext(basePackagesArray, contextConfigLocationsArray)));

View File

@@ -25,6 +25,7 @@ import static org.junit.Assert.fail;
import java.util.Calendar;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@@ -36,6 +37,8 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.gemfire.LazyWiringDeclarableSupport;
import org.springframework.data.gemfire.config.GemfireConstants;
import org.springframework.data.gemfire.repository.sample.User;
@@ -80,7 +83,7 @@ public class SpringContextBootstrappingInitializerIntegrationTest {
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 = "SpringContextBootstrappingInitializationIntegrationTest";
protected static final String GEMFIRE_NAME = SpringContextBootstrappingInitializerIntegrationTest.class.getSimpleName();
protected static final String GEMFIRE_START_LOCATORS = "localhost[11235]";
@Before
@@ -207,16 +210,45 @@ public class SpringContextBootstrappingInitializerIntegrationTest {
}
@Test
public void testSpringContextBootstrappingInitializationUsingBasePackages() {
public void testSpringContextBootstrappingInitializerUsingAnnotatedClasses() {
SpringContextBootstrappingInitializer.register(TestAppConfig.class);
new SpringContextBootstrappingInitializer().init(new Properties());
ConfigurableApplicationContext applicationContext = SpringContextBootstrappingInitializer.getApplicationContext();
UserDataStoreCacheLoader userDataStoreCacheLoader = applicationContext.getBean(UserDataStoreCacheLoader.class);
DataSource userDataSource = applicationContext.getBean(DataSource.class);
assertSame(UserDataStoreCacheLoader.getInstance(), userDataStoreCacheLoader);
assertSame(userDataStoreCacheLoader.getDataSource(), userDataSource);
}
@Test
public void testSpringContextBootstrappingInitializerUsingBasePackages() {
doSpringContextBootstrappingInitializationTest(
"cache-with-spring-context-bootstrap-initializer-using-base-packages.xml");
}
@Test
public void testSpringContextBootstrappingInitializationUsingContextConfigLocations() {
public void testSpringContextBootstrappingInitializerUsingContextConfigLocations() {
doSpringContextBootstrappingInitializationTest("cache-with-spring-context-bootstrap-initializer.xml");
}
@Configuration
public static class TestAppConfig {
@Bean
public DataSource userDataSource() {
return new TestDataSource();
}
@Bean
public UserDataStoreCacheLoader userDataStoreCacheLoader() {
return new UserDataStoreCacheLoader();
}
}
public static final class TestDataSource extends DataSourceAdapter {
}
@@ -235,24 +267,19 @@ public class SpringContextBootstrappingInitializerIntegrationTest {
@Autowired
private DataSource userDataSource;
public static UserDataStoreCacheLoader getInstance() {
return INSTANCE.get();
}
protected static User createUser(final String username) {
protected static User createUser(String username) {
return createUser(username, true, Calendar.getInstance(), String.format("%1$s@xcompay.com", username));
}
protected static User createUser(final String username, final Boolean active) {
protected static User createUser(String username, Boolean active) {
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) {
protected static User createUser(String username, Boolean active, Calendar since) {
return createUser(username, active, since, String.format("%1$s@xcompay.com", username));
}
protected static User createUser(final String username, final Boolean active, final Calendar since,
final String email) {
protected static User createUser(String username, Boolean active, Calendar since, String email) {
User user = new User(username);
user.setActive(active);
user.setEmail(email);
@@ -260,6 +287,10 @@ public class SpringContextBootstrappingInitializerIntegrationTest {
return user;
}
public static UserDataStoreCacheLoader getInstance() {
return INSTANCE.get();
}
public UserDataStoreCacheLoader() {
Assert.state(INSTANCE.compareAndSet(null, this), String.format("An instance of %1$s was already created!",
getClass().getName()));

View File

@@ -22,24 +22,31 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.doNothing;
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;
import java.util.Arrays;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.Matchers;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStartedEvent;
@@ -54,6 +61,7 @@ import org.springframework.context.event.ContextStoppedEvent;
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.context.ApplicationContext
* @see org.springframework.context.ConfigurableApplicationContext
* @see org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer
* @see org.springframework.data.gemfire.support.SpringContextBootstrappingInitializerIntegrationTest
@@ -77,6 +85,8 @@ public class SpringContextBootstrappingInitializerTest {
public void tearDown() {
SpringContextBootstrappingInitializer.applicationContext = null;
SpringContextBootstrappingInitializer.contextRefreshedEvent = null;
SpringContextBootstrappingInitializer.unregister(TestAppConfigOne.class);
SpringContextBootstrappingInitializer.unregister(TestAppConfigTwo.class);
}
@Test
@@ -95,32 +105,20 @@ public class SpringContextBootstrappingInitializerTest {
SpringContextBootstrappingInitializer.getApplicationContext();
}
catch (IllegalStateException expected) {
assertEquals("The Spring ApplicationContext has not been properly configured and initialized!",
assertEquals("The Spring ApplicationContext was not configured and initialized properly!",
expected.getMessage());
throw expected;
}
}
@Test(expected = IllegalArgumentException.class)
public void testCreateApplicationContextWhenBothBasePackagesAndConfigLocationsAreUnspecified() {
public void testCreateApplicationContextWhenBasePackagesAndConfigLocationsAreUnspecified() {
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());
assertEquals("'basePackages', 'configLocations' or 'AnnotatedClasses' must be specified in order to"
+ " construct and configure an instance of the ConfigurableApplicationContext", expected.getMessage());
throw expected;
}
}
@@ -134,8 +132,8 @@ public class SpringContextBootstrappingInitializerTest {
initializer.initApplicationContext(mockApplicationContext);
verify(mockApplicationContext).addApplicationListener(same(initializer));
verify(mockApplicationContext).registerShutdownHook();
verify(mockApplicationContext, times(1)).addApplicationListener(same(initializer));
verify(mockApplicationContext, times(1)).registerShutdownHook();
}
@Test(expected = IllegalArgumentException.class)
@@ -152,11 +150,11 @@ public class SpringContextBootstrappingInitializerTest {
@Test
public void testRefreshApplicationContext() {
ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
"testInitApplicationContext");
"testRefreshApplicationContext");
new SpringContextBootstrappingInitializer().refreshApplicationContext(mockApplicationContext);
verify(mockApplicationContext).refresh();
verify(mockApplicationContext, times(1)).refresh();
}
@Test(expected = IllegalArgumentException.class)
@@ -186,6 +184,96 @@ public class SpringContextBootstrappingInitializerTest {
new SpringContextBootstrappingInitializer().nullSafeGetApplicationContextId(mockApplicationContext));
}
@Test
public void testRegisterAnnotatedClasses() {
AnnotationConfigApplicationContext mockApplicationContext = mock(AnnotationConfigApplicationContext.class,
"testRegisterAnnotatedClasses");
Class<?>[] annotatedClasses = { TestAppConfigOne.class, TestAppConfigTwo.class };
new SpringContextBootstrappingInitializer().registerAnnotatedClasses(mockApplicationContext, annotatedClasses);
verify(mockApplicationContext, times(1)).register(annotatedClasses);
}
@Test
public void testRegisterAnnotatedClassesWithEmptyAnnotatedClassesArray() {
AnnotationConfigApplicationContext mockApplicationContext = mock(AnnotationConfigApplicationContext.class,
"testRegisterAnnotatedClassesWithEmptyAnnotatedClassesArray");
new SpringContextBootstrappingInitializer().registerAnnotatedClasses(mockApplicationContext, new Class<?>[0]);
verify(mockApplicationContext, never()).register(any(Class.class));
}
@Test
public void testScanBasePackages() {
AnnotationConfigApplicationContext mockApplicationContext = mock(AnnotationConfigApplicationContext.class,
"testScanBasePackages");
String[] basePackages = { "org.example.app", "org.example.plugins" };
new SpringContextBootstrappingInitializer().scanBasePackages(mockApplicationContext, basePackages);
verify(mockApplicationContext, times(1)).scan(basePackages);
}
@Test
public void testScanBasePackagesWithEmptyBasePackagesArray() {
AnnotationConfigApplicationContext mockApplicationContext = mock(AnnotationConfigApplicationContext.class,
"testScanBasePackages");
new SpringContextBootstrappingInitializer().scanBasePackages(mockApplicationContext, null);
verify(mockApplicationContext, never()).scan(any(String[].class));
}
private Class<?>[] annotatedClasses(final Class<?>... annotatedClasses) {
return argThat(new ArgumentMatcher<Class<?>[]>() {
@Override public boolean matches(final Object argument) {
assertTrue(argument instanceof Class<?>[]);
return Arrays.equals(annotatedClasses, (Class<?>[]) argument);
}
});
}
@Test
public void testInitWithAnnotatedClasses() {
final AnnotationConfigApplicationContext mockApplicationContext = mock(AnnotationConfigApplicationContext.class,
"testInitWithAnnotatedClasses");
doNothing().when(mockApplicationContext).addApplicationListener(any(ApplicationListener.class));
doNothing().when(mockApplicationContext).registerShutdownHook();
doNothing().when(mockApplicationContext).refresh();
doNothing().when(mockApplicationContext).register(Matchers.<Class<?>[]>anyVararg());
//doNothing().when(mockApplicationContext).register(annotatedClasses(TestAppConfigOne.class, TestAppConfigTwo.class));
when(mockApplicationContext.getId()).thenReturn("testInitWithAnnotatedClasses");
when(mockApplicationContext.isRunning()).thenReturn(true);
assertNull(SpringContextBootstrappingInitializer.applicationContext);
SpringContextBootstrappingInitializer.register(TestAppConfigOne.class);
SpringContextBootstrappingInitializer.register(TestAppConfigTwo.class);
SpringContextBootstrappingInitializer initializer = new SpringContextBootstrappingInitializer() {
@Override protected ConfigurableApplicationContext createApplicationContext(String[] configLocations) {
return mockApplicationContext;
}
};
initializer.init(createParameters("test", "test"));
verify(mockApplicationContext, times(1)).addApplicationListener(same(initializer));
verify(mockApplicationContext, times(1)).registerShutdownHook();
verify(mockApplicationContext, times(1)).register(TestAppConfigOne.class, TestAppConfigTwo.class);
//verify(mockApplicationContext, times(1)).register(annotatedClasses(TestAppConfigOne.class, TestAppConfigTwo.class));
//verify(mockApplicationContext, times(1)).register(Matchers.<Class<?>[]>anyVararg());
verify(mockApplicationContext, never()).scan(any(String[].class));
assertEquals(mockApplicationContext, SpringContextBootstrappingInitializer.getApplicationContext());
}
@Test
public void testInitWithExistingApplicationContext() {
ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
@@ -200,10 +288,9 @@ public class SpringContextBootstrappingInitializerTest {
SpringContextBootstrappingInitializer initializer = new SpringContextBootstrappingInitializer();
initializer.init(createParameters(SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER,
"/path/to/spring/application/context.xml"));
initializer.init(createParameters("test", "test"));
verify(mockApplicationContext, never()).addApplicationListener(same(initializer));
verify(mockApplicationContext, never()).addApplicationListener(any(SpringContextBootstrappingInitializer.class));
verify(mockApplicationContext, never()).registerShutdownHook();
verify(mockApplicationContext, never()).refresh();
@@ -263,8 +350,8 @@ public class SpringContextBootstrappingInitializerTest {
}
};
initializer.init(createParameters(SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER,
"/path/to/spring/application/context.xml"));
initializer.init(createParameters(SpringContextBootstrappingInitializer.BASE_PACKAGES_PARAMETER,
"org.example.app"));
verify(mockNewApplicationContext, times(1)).addApplicationListener(same(initializer));
verify(mockNewApplicationContext, times(1)).registerShutdownHook();
@@ -274,67 +361,71 @@ public class SpringContextBootstrappingInitializerTest {
}
@Test(expected = IllegalArgumentException.class)
public void testInitWhenBothBasePackagesAndContextConfigLocationsParametersAreUnspecified() {
public void testInitWhenBasePackagesAndContextConfigLocationsParametersAreUnspecified() throws Throwable {
assertNull(SpringContextBootstrappingInitializer.applicationContext);
try {
new SpringContextBootstrappingInitializer().init(createParameters(createParameters(
SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER, ""),
SpringContextBootstrappingInitializer.BASE_PACKAGES_PARAMETER, ""));
SpringContextBootstrappingInitializer.BASE_PACKAGES_PARAMETER, " "));
}
catch (ApplicationContextException expected) {
assertTrue(expected.getMessage().contains("Failed to bootstrap the Spring ApplicationContext!"));
assertTrue(expected.getCause() instanceof IllegalArgumentException);
assertEquals("Either 'basePackages' or the 'contextConfigLocations' parameter must be specified.",
expected.getCause().getMessage());
throw (IllegalArgumentException) expected.getCause();
assertEquals("'basePackages', 'configLocations' or 'AnnotatedClasses' must be specified"
+ " in order to construct and configure an instance of the ConfigurableApplicationContext",
expected.getCause().getMessage());
throw expected.getCause();
}
}
@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;
}
};
try {
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();
initializer.init(createParameters(SpringContextBootstrappingInitializer.BASE_PACKAGES_PARAMETER,
"org.example.app, org.example.plugins"));
SpringContextBootstrappingInitializer.getApplicationContext();
}
catch (ApplicationContextException expected) {
assertTrue(expected.getMessage().contains("Failed to bootstrap the Spring ApplicationContext!"));
assertTrue(expected.getCause() instanceof IllegalStateException);
assertEquals("The Spring ApplicationContext (testInitWhenApplicationContextIsNotRunning) failed to be properly initialized with the context config files ([/path/to/spring/application/context.xml]) or base packages ([])!",
assertEquals("The Spring ApplicationContext (testInitWhenApplicationContextIsNotRunning) failed to be properly initialized with the context config files ([]) or base packages ([org.example.app, org.example.plugins])!",
expected.getCause().getMessage());
throw (IllegalStateException) expected.getCause();
}
finally {
verify(mockApplicationContext, times(1)).addApplicationListener(same(initializer));
verify(mockApplicationContext, times(1)).registerShutdownHook();
verify(mockApplicationContext, times(1)).refresh();
assertNull(SpringContextBootstrappingInitializer.applicationContext);
}
}
@Test(expected = IllegalStateException.class)
public void testInitLogsErrors() throws Throwable {
final Log mockLogger = mock(Log.class, "testInitLogsErrors.MockLog");
final Log mockLog = mock(Log.class, "testInitLogsErrors.MockLog");
SpringContextBootstrappingInitializer initializer = new SpringContextBootstrappingInitializer() {
@Override protected Log initLogger() {
return mockLogger;
return mockLog;
}
@Override protected ConfigurableApplicationContext createApplicationContext(final String[] basePackages,
@@ -354,17 +445,17 @@ public class SpringContextBootstrappingInitializerTest {
throw expected.getCause();
}
finally {
verify(mockLogger, times(1)).error(eq("Failed to bootstrap the Spring ApplicationContext!"),
verify(mockLog, times(1)).error(eq("Failed to bootstrap the Spring ApplicationContext!"),
any(RuntimeException.class));
}
}
protected static void assertNotifiedWithEvent(final TestApplicationListener listener, final ContextRefreshedEvent expectedEvent) {
protected static void assertNotified(TestApplicationListener listener, ContextRefreshedEvent expectedEvent) {
assertTrue(listener.isNotified());
Assert.assertSame(expectedEvent, listener.getActualEvent());
}
protected static void assertUnnotified(final TestApplicationListener listener) {
protected static void assertUnnotified(TestApplicationListener listener) {
assertFalse(listener.isNotified());
assertNull(listener.getActualEvent());
}
@@ -383,7 +474,7 @@ public class SpringContextBootstrappingInitializerTest {
new SpringContextBootstrappingInitializer().onApplicationEvent(testContextRefreshedEvent);
assertNotifiedWithEvent(testApplicationListener, testContextRefreshedEvent);
assertNotified(testApplicationListener, testContextRefreshedEvent);
}
finally {
SpringContextBootstrappingInitializer.unregister(testApplicationListener);
@@ -433,13 +524,13 @@ public class SpringContextBootstrappingInitializerTest {
assertUnnotified(testApplicationListenerThree);
ContextRefreshedEvent testContextRefreshedEvent = new ContextRefreshedEvent(mock(ApplicationContext.class,
"testRegisterWithOnApplicationEvent"));
"testOnApplicationEventWithMultipleRegisteredApplicationListeners"));
new SpringContextBootstrappingInitializer().onApplicationEvent(testContextRefreshedEvent);
assertNotifiedWithEvent(testApplicationListenerOne, testContextRefreshedEvent);
assertNotifiedWithEvent(testApplicationListenerTwo, testContextRefreshedEvent);
assertNotifiedWithEvent(testApplicationListenerThree, testContextRefreshedEvent);
assertNotified(testApplicationListenerOne, testContextRefreshedEvent);
assertNotified(testApplicationListenerTwo, testContextRefreshedEvent);
assertNotified(testApplicationListenerThree, testContextRefreshedEvent);
}
finally {
SpringContextBootstrappingInitializer.unregister(testApplicationListenerOne);
@@ -494,11 +585,11 @@ public class SpringContextBootstrappingInitializerTest {
new SpringContextBootstrappingInitializer().onApplicationEvent(testContextRefreshedEvent);
TestApplicationListener testApplicationListener = new TestApplicationListener(
"testNotifyApplicationListenersOnContextRefreshedEventAfterApplicationContextRefreshed");
"testNotifyOnExistingContextRefreshedEventAfterContextRefreshed");
try {
testApplicationListener = SpringContextBootstrappingInitializer.register(testApplicationListener);
assertNotifiedWithEvent(testApplicationListener, testContextRefreshedEvent);
assertNotified(testApplicationListener, testContextRefreshedEvent);
}
finally {
SpringContextBootstrappingInitializer.unregister(testApplicationListener);
@@ -532,19 +623,19 @@ public class SpringContextBootstrappingInitializerTest {
initializer.onApplicationEvent(testContextRefreshedEvent);
assertNotifiedWithEvent(testApplicationListenerOne, testContextRefreshedEvent);
assertNotified(testApplicationListenerOne, testContextRefreshedEvent);
assertUnnotified(testApplicationListenerTwo);
assertUnnotified(testApplicationListenerThree);
testApplicationListenerTwo = SpringContextBootstrappingInitializer.register(testApplicationListenerTwo);
assertNotifiedWithEvent(testApplicationListenerTwo, testContextRefreshedEvent);
assertNotified(testApplicationListenerTwo, testContextRefreshedEvent);
assertUnnotified(testApplicationListenerOne);
assertUnnotified(testApplicationListenerThree);
ContextStoppedEvent contextStoppedEvent = new ContextStoppedEvent(mockApplicationContext);
ContextStoppedEvent testContextStoppedEvent = new ContextStoppedEvent(mockApplicationContext);
initializer.onApplicationEvent(contextStoppedEvent);
initializer.onApplicationEvent(testContextStoppedEvent);
assertUnnotified(testApplicationListenerOne);
assertUnnotified(testApplicationListenerTwo);
@@ -552,8 +643,8 @@ public class SpringContextBootstrappingInitializerTest {
initializer.onApplicationEvent(testContextRefreshedEvent);
assertNotifiedWithEvent(testApplicationListenerOne, testContextRefreshedEvent);
assertNotifiedWithEvent(testApplicationListenerTwo, testContextRefreshedEvent);
assertNotified(testApplicationListenerOne, testContextRefreshedEvent);
assertNotified(testApplicationListenerTwo, testContextRefreshedEvent);
assertUnnotified(testApplicationListenerThree);
ContextClosedEvent testContextClosedEvent = new ContextClosedEvent(mockApplicationContext);
@@ -577,6 +668,14 @@ public class SpringContextBootstrappingInitializerTest {
}
}
@Configuration
protected static class TestAppConfigOne {
}
@Configuration
protected static class TestAppConfigTwo {
}
// 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.