Completed all testing and interface/functional changes for SGF-248 - Bootstrapping a Spring ApplicationContext with GemFire.

This commit is contained in:
John Blum
2014-01-27 18:45:53 -08:00
parent 5476837119
commit ba84061b48
9 changed files with 705 additions and 28 deletions

View File

@@ -40,6 +40,8 @@ import com.gemstone.gemfire.cache.Declarable;
* @author John Blum
* @see org.springframework.context.ApplicationListener
* @see org.springframework.context.event.ContextRefreshedEvent
* @see org.springframework.data.gemfire.DeclarableSupport
* @see org.springframework.data.gemfire.WiringDeclarableSupport
* @see com.gemstone.gemfire.cache.Declarable
* @since 1.3.4
*/
@@ -47,19 +49,19 @@ import com.gemstone.gemfire.cache.Declarable;
public abstract class LazyWiringDeclarableSupport implements ApplicationListener<ContextRefreshedEvent>, Declarable {
// The name of the template bean defined in the Spring context for wiring this Declarable instance.
private static final String BEAN_NAME_PARAMETER = "bean-name";
protected static final String BEAN_NAME_PARAMETER = "bean-name";
// atomic reference to the parameter passed by GemFire when this Declared was constructed
// and the init method was called.
private final AtomicReference<Properties> parametersReference = new AtomicReference<Properties>();
private volatile boolean initialized = false;
protected volatile boolean initialized = false;
/**
* Constructs an instance of the LazyWiringDeclarableSupport class register with the
* Constructs an instance of the LazyWiringDeclarableSupport class registered with the
* SpringContextBootstrappingInitializer to receive notification when the Spring context is created and initialized
* by GemFire to that this Declarable component can be configured and properly initialized with any required Spring
* bean dependencies.
* (refreshed) by GemFire in order for this Declarable component to be configured and properly initialized with any
* required Spring bean dependencies.
* <p/>
* @see org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer
* #register(org.springframework.context.ApplicationListener)
@@ -70,12 +72,13 @@ public abstract class LazyWiringDeclarableSupport implements ApplicationListener
/**
* Asserts that this Declarable object has been properly configured and initialized by the Spring container
* after GemFire has constructed this Declarable during startup. It is recommended that this method be called
* in any GemFire CacheCallback/Declarable object operational method (e.g. CacheLoader.load(..)) before use
* in order to ensure that this Declarable was properly constructed, configured and initialized.
* after GemFire has constructed this Declarable object during startup. It is recommended that this method
* be called in any GemFire CacheCallback/Declarable object operational method (e.g. CacheLoader.load(..))
* before use in order to ensure that this Declarable was properly constructed, configured and initialized.
* <p/>
* @throws IllegalStateException if the Declarable object has not been properly configured or initialized by the
* Spring container.
* @throws IllegalStateException if the Declarable object has not been properly configured or initialized
* by the Spring container.
* @see #init(java.util.Properties)
* @see #isInitialized()
*/
protected void assertInitialized() {
@@ -86,19 +89,21 @@ public abstract class LazyWiringDeclarableSupport implements ApplicationListener
/**
* Performs the actual configuration and initialization of this Declarable object before use. This method
* is triggered by an ApplicationEvent indicating that the Spring context has been created and refreshed.
* is triggered by an ApplicationEvent (specifically, the ContextRefreshedEvent) indicating that the Spring context
* has been created and refreshed.
* <p/>
* @param beanFactory the ConfigurableListableBeanFactory used to configure and initialize this Declarable GemFire
* component.
* @param parameters Properties instance containing the parameters from GemFire's configuration file
* (e.g. cache.xml) to configure this Declarable object.
* (e.g. cache.xml) to configure and initialize this Declarable object.
* @throws IllegalArgumentException if the bean-name parameter was specified in GemFire configuration meta-data
* but no bean with the specified name could be found in the Spring context.
* @see #init(java.util.Properties)
* @see org.springframework.beans.factory.wiring.BeanConfigurerSupport
* @see org.springframework.beans.factory.wiring.BeanWiringInfo
* @see org.springframework.beans.factory.wiring.BeanWiringInfoResolver
*/
protected void doInit(final ConfigurableListableBeanFactory beanFactory, final Properties parameters) {
/* package-private */ void doInit(final ConfigurableListableBeanFactory beanFactory, final Properties parameters) {
BeanConfigurerSupport beanConfigurer = new BeanConfigurerSupport();
beanConfigurer.setBeanFactory(beanFactory);
@@ -124,11 +129,24 @@ public abstract class LazyWiringDeclarableSupport implements ApplicationListener
beanConfigurer.destroy();
initialized = true;
doPostInit(parameters);
}
/**
* Default no operation method performed post initialization of this Declarable GemFire component to be overridden
* by subclasses for application specific extension and behavior.
* <p/>
* @param parameters Properties instance containing the parameters from GemFire's configuration file
* (e.g. cache.xml) to configure and initialize this Declarable object.
* @see #doInit(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.Properties)
*/
protected void doPostInit(final Properties parameters) {
}
/**
* Initialization method called by GemFire with the configured parameters once this Declarable object has been
* constructed during GemFire startup using an &lt;initalizer&gt;.
* constructed during GemFire startup using an &lt;initalizer&gt; element in GemFire's configuration meta-data.
* <p/>
* @param parameters the configured parameters passed from the GemFire configuration (e.g. cache.xml) to this
* Declarable as a Properties instance.
@@ -148,6 +166,7 @@ public abstract class LazyWiringDeclarableSupport implements ApplicationListener
* <p/>
* @return a boolean value indicating whether this Declarable object has been configured and initialized by
* the Spring container.
* @see #assertInitialized()
* @see #doInit(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.Properties)
*/
protected boolean isInitialized() {
@@ -164,6 +183,9 @@ public abstract class LazyWiringDeclarableSupport implements ApplicationListener
* called this Declarable object's init method yet, which is probably a bug and violates the lifecycle contract
* of Declarable GemFire objects).
* @throws IllegalArgumentException if the ApplicationContext is not an instance of ConfigurableApplicationContext.
* @see #doInit(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.Properties)
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
* @see org.springframework.context.event.ContextRefreshedEvent
*/
@Override
public final void onApplicationEvent(final ContextRefreshedEvent event) {

View File

@@ -39,6 +39,11 @@ import com.gemstone.gemfire.cache.Declarable;
* and initializes the Cache for use.
* <p/>
* @author John Blum
* @see org.springframework.context.ApplicationContext
* @see org.springframework.context.ApplicationListener
* @see org.springframework.context.event.ApplicationEventMulticaster
* @see org.springframework.context.event.ContextRefreshedEvent
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
* @see org.springframework.context.ConfigurableApplicationContext
* @see org.springframework.context.support.ClassPathXmlApplicationContext
* @see com.gemstone.gemfire.cache.Declarable
@@ -50,7 +55,7 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
public static final String CONTEXT_CONFIG_LOCATIONS_PARAMETER = "contextConfigLocations";
private static ConfigurableApplicationContext applicationContext;
/* package-private */ static ConfigurableApplicationContext applicationContext;
// TODO consider whether I should register a TaskExecutor to perform the event notifications in a separate Thread???
private static ApplicationEventMulticaster eventNotifier = new SimpleApplicationEventMulticaster();
@@ -72,25 +77,51 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
* when instantiating and initializing declared Initializers from the GemFire native configuration file
* (e.g. cache.xml).
* <p/>
* @param listener the ApplicationListener to register for ContextStartedEvents by this
* @param <T> the Class type of the Spring ApplicationListener.
* @param listener the ApplicationListener to register for ContextRefreshedEvents by this
* SpringContextBootstrappingInitializer.
* @return the reference to the ApplicationListener for method call chaining purposes.
* @see #unregister(org.springframework.context.ApplicationListener)
* @see org.springframework.context.ApplicationListener
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
* #addApplicationListener(org.springframework.context.ApplicationListener)
*/
public static void register(ApplicationListener listener) {
public static <T extends ApplicationListener> T register(final T listener) {
eventNotifier.addApplicationListener(listener);
return listener;
}
/**
* Gets the the ID of the Spring ApplicationContext in a null-safe manner.
* Unregisters the Spring ApplicationListener from this SpringContextBootstrappingInitializer in order to stop
* receiving ApplicationEvents on Spring context refreshes.
* <p/>
* @param applicationContext the Spring ApplicationContext to retrieve the ID of.
* @return the ID of the given Spring ApplicationContext or null if the ApplicationContext reference is null.
* @see org.springframework.context.ApplicationContext#getId()
* @param <T> the Class type of the Spring ApplicationListener.
* @param listener the ApplicationListener to unregister from receiving ContextRefreshedEvents by this
* SpringContextBootstrappingInitializer.
* @return the reference to the ApplicationListener for method call chaining purposes.
* @see #register(org.springframework.context.ApplicationListener)
* @see org.springframework.context.ApplicationListener
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
* #removeApplicationListener(org.springframework.context.ApplicationListener)
*/
protected static String nullSafeGetApplicationContextId(final ApplicationContext applicationContext) {
return (applicationContext != null ? applicationContext.getId() : null);
public static <T extends ApplicationListener> T unregister(final T listener) {
eventNotifier.removeApplicationListener(listener);
return listener;
}
/**
* Creates (constructs and initializes) a ConfigurableApplicationContext instance based on the specified locations
* of the context configuration meta-data files and indicates whether ApplicationContext.refresh should happen
* automatically during creation. This method allows subclasses to override the type of
* ConfigurableApplicationContext created; by default, a ClassPathXmlApplicationContext is created.
* <p/>
* @param configLocations a String array indicating the locations of the context configuration meta-data files.
* @param refresh a boolean value indicating whether the ApplicationContext is refreshed on creation.
* @return a newly constructed and initialized instance of a ConfigurableApplicationContext.
* @see org.springframework.context.support.ClassPathXmlApplicationContext
*/
protected ConfigurableApplicationContext createApplicationContext(final String[] configLocations, final boolean refresh) {
return new ClassPathXmlApplicationContext(configLocations, refresh);
}
/**
@@ -99,6 +130,7 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
* <p/>
* @param parameters a Properties object containing the configuration parameters and settings defined in the
* GemFire cache.xml &gt;initializer/&lt; element.
* @see #createApplicationContext(String[], boolean)
* @see java.util.Properties
*/
@Override
@@ -116,17 +148,28 @@ public class SpringContextBootstrappingInitializer implements Declarable, Applic
"A Spring application context with ID (%1$s) has already been created.",
nullSafeGetApplicationContextId(applicationContext)));
applicationContext = new ClassPathXmlApplicationContext(configLocations, false);
applicationContext = createApplicationContext(configLocations, false);
applicationContext.addApplicationListener(this);
applicationContext.registerShutdownHook();
applicationContext.refresh();
Assert.state(applicationContext.isActive(), String.format(
"The Spring application context has failed to be properly initialized with the following config files (%1$s)!",
Arrays.toString(configLocations)));
"The Spring application context (%1$s) has failed to be properly initialized with the following config files (%2$s)!",
nullSafeGetApplicationContextId(applicationContext), Arrays.toString(configLocations)));
}
}
/**
* Gets the the ID of the Spring ApplicationContext in a null-safe manner.
* <p/>
* @param applicationContext the Spring ApplicationContext to retrieve the ID for.
* @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) {
return (applicationContext != null ? applicationContext.getId() : null);
}
/**
* 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

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.Properties;
import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.gemfire.repository.sample.User;
import org.springframework.data.gemfire.test.support.DataSourceAdapter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.Assert;
/**
* The LazyWiringDeclarableSupportIntegrationTest class is a test suite of integration test cases testing
* a LazyWiringDeclarableSupport object/component's wiring configuration and initialization in
* a Spring container context.
* <p/>
* @author John Blum
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @since 1.3.4
*/
@ContextConfiguration("lazy-wiring-declarable-support.xml")
@RunWith(SpringJUnit4ClassRunner.class)
@SuppressWarnings("unused")
public class LazyWiringDeclarableSupportIntegrationTest {
@Autowired
private ApplicationContext applicationContext;
protected static Properties createParameters(final String parameter, final String value) {
Properties parameters = new Properties();
parameters.setProperty(parameter, value);
return parameters;
}
@Test
public void testWiring() {
TestDeclarable declarable = new TestDeclarable();
declarable.init(createParameters("testParam1", "testValue1"));
declarable.onApplicationEvent(new ContextRefreshedEvent(applicationContext));
declarable.assertInitialized();
assertNull(declarable.getDataSource());
assertNotNull(declarable.getUser());
assertEquals("supertool", declarable.getUser().getUsername());
}
@Test
public void testWiringWithBeanTemplate() {
TestDeclarable declarable = new TestDeclarable();
declarable.init(createParameters(LazyWiringDeclarableSupport.BEAN_NAME_PARAMETER, "declarableTemplateBean"));
declarable.onApplicationEvent(new ContextRefreshedEvent(applicationContext));
declarable.assertInitialized();
assertNotNull(declarable.getDataSource());
assertNotNull(declarable.getUser());
assertEquals("supertool", declarable.getUser().getUsername());
}
@Test(expected = IllegalArgumentException.class)
public void testWiringWithNonExistingBeanTemplate() {
TestDeclarable declarable = new TestDeclarable();
try {
declarable.init(createParameters(LazyWiringDeclarableSupport.BEAN_NAME_PARAMETER,
"nonExistingBeanTemplate"));
declarable.onApplicationEvent(new ContextRefreshedEvent(applicationContext));
}
catch (IllegalArgumentException expected) {
assertTrue(expected.getMessage().startsWith(
"No bean with name 'nonExistingBeanTemplate' was found in the Spring context"));
throw expected;
}
}
protected static final class TestDataSource extends DataSourceAdapter {
}
protected static final class TestDeclarable extends LazyWiringDeclarableSupport {
private DataSource dataSource;
@Autowired
private User user;
public final void setDataSource(final DataSource dataSource) {
this.dataSource = dataSource;
}
protected DataSource getDataSource() {
return dataSource;
}
protected User getUser() {
Assert.state(user != null, "A reference to the User was not properly configured!");
return user;
}
}
}

View File

@@ -0,0 +1,199 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer;
/**
* The LazyWiringDeclarableSupportTest class is a test suite of test cases testing the contract and functionality
* of the LazyWiringDeclarableSupport class. This test class focuses on testing isolated units of functionality
* in the Declarable class directly, mocking any dependencies as appropriate, in order for the class to uphold
* it's contract.
* <p/>
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.LazyWiringDeclarableSupport
* @since 1.3.4
*/
public class LazyWiringDeclarableSupportTest {
protected static Properties createParameters(final String parameter, final String value) {
Properties parameters = new Properties();
parameters.setProperty(parameter, value);
return parameters;
}
@Test
public void testAssertInitialized() {
LazyWiringDeclarableSupport declarable = new TestLazyWiringDeclarableSupport() {
@Override protected boolean isInitialized() {
return true;
}
};
try {
declarable.assertInitialized();
}
finally {
SpringContextBootstrappingInitializer.unregister(declarable);
}
}
@Test(expected = IllegalStateException.class)
public void testAssertInitializedWhenUninitialized() {
LazyWiringDeclarableSupport declarable = new TestLazyWiringDeclarableSupport();
try {
declarable.assertInitialized();
}
catch (IllegalStateException expected) {
assertEquals(String.format("This Declarable object (%1$s) has not ben properly configured and initialized!",
TestLazyWiringDeclarableSupport.class.getName()), expected.getMessage());
throw expected;
}
finally {
SpringContextBootstrappingInitializer.unregister(declarable);
}
}
@Test
public void testInit() {
new TestLazyWiringDeclarableSupport().init(createParameters("testParam1", "testValue1"));
}
@Test(expected = IllegalStateException.class)
public void testInitWhenInitialized() {
LazyWiringDeclarableSupport declarable = new TestLazyWiringDeclarableSupport();
try {
declarable.init(createParameters("testParam1", "testValue1"));
}
catch (IllegalStateException unexpected) {
fail("Calling init the first time should not throw an IllegalStateException!");
}
try {
declarable.init(createParameters("testParam2", "testValue1"));
}
catch (IllegalStateException expected) {
assertEquals(String.format("This Declarable (%1$s) has already been initialized.",
declarable.getClass().getName()), expected.getMessage());
throw expected;
}
}
@Test(expected = IllegalStateException.class)
public void testOnApplicationEventWithNoParameters() {
LazyWiringDeclarableSupport declarable = new TestLazyWiringDeclarableSupport();
try {
declarable.onApplicationEvent(new ContextRefreshedEvent(mock(ConfigurableApplicationContext.class,
"testOnApplicationEventWithNoParameters")));
}
catch (IllegalStateException expected) {
assertEquals(String.format("This Declarable object's (%1$s) init method has not been invoked!",
declarable.getClass().getName()), expected.getMessage());
throw expected;
}
}
@Test(expected = IllegalArgumentException.class)
public void testOnApplicationEventWithNonConfigurableApplicationContext() {
ApplicationContext mockApplicationContext = mock(ApplicationContext.class,
"testOnApplicationEventWithNonConfigurableApplicationContext");
LazyWiringDeclarableSupport declarable = new TestLazyWiringDeclarableSupport();
try {
declarable.init(createParameters("testParam1", "testValue1"));
declarable.onApplicationEvent(new ContextRefreshedEvent(mockApplicationContext));
}
catch (IllegalArgumentException expected) {
assertEquals(String.format("The Spring ApplicationContext (%1$s) must be an instance of ConfigurableApplicationContext.",
mockApplicationContext.getClass().getName()), expected.getMessage());
throw expected;
}
}
@Test
public void testOnApplicationEventAndDoPostInit() {
ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
"testOnApplicationEventAndDoPostInit.ApplicationContext");
ConfigurableListableBeanFactory mockBeanFactory = mock(ConfigurableListableBeanFactory.class,
"testOnApplicationEventAndDoPostInit.BeanFactory");
when(mockApplicationContext.getBeanFactory()).thenReturn(mockBeanFactory);
Properties testParameters = createParameters("testParam1", "testValue1");
final AtomicBoolean doPostInitCalled = new AtomicBoolean(false);
TestLazyWiringDeclarableSupport declarable = new TestLazyWiringDeclarableSupport() {
@Override protected void doPostInit(final Properties parameters) {
assertInitialized();
doPostInitCalled.set(true);
}
};
declarable.init(testParameters);
declarable.onApplicationEvent(new ContextRefreshedEvent(mockApplicationContext));
declarable.assertEquals(testParameters);
declarable.assertSame(mockBeanFactory);
assertTrue(doPostInitCalled.get());
}
protected static class TestLazyWiringDeclarableSupport extends LazyWiringDeclarableSupport {
private ConfigurableListableBeanFactory actualBeanFactory;
private Properties actualParameters;
protected void assertEquals(final Properties expectedParameters) {
Assert.assertEquals(expectedParameters, actualParameters);
}
protected void assertSame(final ConfigurableListableBeanFactory expectedBeanFactory) {
Assert.assertSame(expectedBeanFactory, actualBeanFactory);
}
@Override
void doInit(final ConfigurableListableBeanFactory beanFactory, final Properties parameters) {
this.actualBeanFactory = beanFactory;
this.actualParameters = parameters;
initialized = true;
doPostInit(parameters);
}
}
}

View File

@@ -87,6 +87,10 @@ public class User implements Comparable<User> {
return getUsername().compareTo(user.getUsername());
}
protected static boolean equalsIgnoreNull(final Object obj1, final Object obj2) {
return (obj1 == null ? obj2 == null : obj1.equals(obj2));
}
@Override
public boolean equals(final Object obj) {
if (obj == this) {
@@ -103,6 +107,10 @@ public class User implements Comparable<User> {
&& ObjectUtils.nullSafeEquals(this.getEmail(), that.getEmail());
}
protected static int hashCodeIgnoreNull(final Object obj) {
return (obj != null ? obj.hashCode() : 0);
}
@Override
public int hashCode() {
int hashValue = 17;

View File

@@ -48,8 +48,8 @@ import com.gemstone.gemfire.cache.LoaderHelper;
import com.gemstone.gemfire.cache.Region;
/**
* The SpringContextBootstrappingInitializerTest class is a test suite of test cases testing the functionality of the
* SpringContextBootstrappingInitializer class
* The SpringContextBootstrappingInitializerTest class is a test suite of test cases testing the integrated
* functionality of the SpringContextBootstrappingInitializer class.
* <p/>
* @author John Blum
* @see org.junit.Test
@@ -137,6 +137,7 @@ public class SpringContextBootstrappingInitializerIntegrationTest {
private DataSource userDataSource;
protected DataSource getDataSource() {
Assert.state(userDataSource != null, "A reference to the Users DataSource as not properly configured!");
return userDataSource;
}
}
@@ -151,6 +152,7 @@ public class SpringContextBootstrappingInitializerIntegrationTest {
private UserDao userDao;
protected UserDao getUserDao() {
Assert.state(userDao != null, "A reference to the UserDao was not properly configured!");
return userDao;
}
}
@@ -182,6 +184,10 @@ public class SpringContextBootstrappingInitializerIntegrationTest {
return createUser(username, String.format("%1$s@xcompay.com", username), active, Calendar.getInstance());
}
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);
}
protected static User createUser(final String username, final String email, final Boolean active, final Calendar since) {
User user = new User(username);
user.setActive(active);

View File

@@ -0,0 +1,249 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.support;
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.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Properties;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.util.ObjectUtils;
/**
* The SpringContextBootstrappingInitializerTest class is a test suite of test cases testing the contract
* and functionality of the SpringContextBootstrappingInitializer class. This test class focuses on testing isolated
* units of functionality in the Initializer class directly, mocking any dependencies as appropriate, in order for the
* class to uphold it's contract.
* <p/>
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer
* @see org.springframework.data.gemfire.support.SpringContextBootstrappingInitializerIntegrationTest
* @since 1.3.4
*/
public class SpringContextBootstrappingInitializerTest {
protected static Properties createParameters(final String parameter, final String value) {
Properties parameters = new Properties();
parameters.setProperty(parameter, value);
return parameters;
}
@After
public void tearDown() {
SpringContextBootstrappingInitializer.applicationContext = null;
}
@Test(expected = IllegalArgumentException.class)
public void testInitWithUnspecifiedContextConfigLocationsParameter() {
try {
new SpringContextBootstrappingInitializer().init(createParameters(
SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER, ""));
}
catch (IllegalArgumentException expected) {
assertEquals("The contextConfigLocations parameter is required.", 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 = IllegalStateException.class)
public void testInitWithNonActiveApplicationConstruct() {
final ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
"testInitWithNonActiveApplicationConstruct");
when(mockApplicationContext.getId()).thenReturn("testInitWithNonActiveApplicationConstruct");
when(mockApplicationContext.isActive()).thenReturn(false);
SpringContextBootstrappingInitializer initializer = null;
try {
initializer = new SpringContextBootstrappingInitializer() {
@Override
protected ConfigurableApplicationContext createApplicationContext(final String[] configLocations, final boolean refresh) {
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 (testInitWithNonActiveApplicationConstruct) has failed to be properly initialized with the following config files ([/path/to/spring/context/configuration/file.xml])!",
expected.getMessage());
throw expected;
}
finally {
verify(mockApplicationContext, times(1)).addApplicationListener(eq(initializer));
verify(mockApplicationContext, times(1)).registerShutdownHook();
verify(mockApplicationContext, times(1)).refresh();
}
}
@Test
public void testInitFollowedByGetApplicationContext() {
final ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class,
"testInitFollowedByGetApplicationContext");
when(mockApplicationContext.getId()).thenReturn("testInitFollowedByGetApplicationContext");
when(mockApplicationContext.isActive()).thenReturn(true);
SpringContextBootstrappingInitializer initializer = new SpringContextBootstrappingInitializer() {
@Override
protected ConfigurableApplicationContext createApplicationContext(final String[] configLocations, final boolean refresh) {
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();
assertSame(mockApplicationContext, SpringContextBootstrappingInitializer.getApplicationContext());
}
@Test(expected = IllegalStateException.class)
public void testGetApplicationContextUninitialized() {
try {
SpringContextBootstrappingInitializer.getApplicationContext();
}
catch (IllegalStateException expected) {
assertEquals("The Spring ApplicationContext has not been created!", expected.getMessage());
throw expected;
}
}
@Test
public void testNullSafeGetApplicationContextIdWithNullReference() {
assertNull(new SpringContextBootstrappingInitializer().nullSafeGetApplicationContextId(null));
}
@Test
public void testNullSafeGetApplicationContextWithNonNullReference() {
ApplicationContext mockApplicationContext = mock(ApplicationContext.class,
"testNullSafeGetApplicationContextWithNonNullReference");
when(mockApplicationContext.getId()).thenReturn("testNullSafeGetApplicationContextWithNonNullReference");
assertEquals("testNullSafeGetApplicationContextWithNonNullReference",
new SpringContextBootstrappingInitializer().nullSafeGetApplicationContextId(mockApplicationContext));
}
@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,
"testUnregisterAndOnApplicationEvent"));
new SpringContextBootstrappingInitializer().onApplicationEvent(testEvent);
testListener.assertNotCalled();
}
finally {
SpringContextBootstrappingInitializer.unregister(testListener);
}
}
protected static class TestApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
private volatile boolean called = false;
private volatile ContextRefreshedEvent actualEvent;
public void assertCalled() {
assertTrue(String.format("Expected the (%1$s).onApplicationEvent(:ContextRefreshedEvent) method to be called!",
getClass().getName()), called);
}
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 void assertSame(final ContextRefreshedEvent expectedEvent) {
Assert.assertSame(expectedEvent, actualEvent);
}
@Override
public void onApplicationEvent(final ContextRefreshedEvent event) {
this.actualEvent = event;
called = true;
}
}
}

View File

@@ -4,6 +4,8 @@
<cache>
<region name="Users" refid="REPLICATE">
<region-attributes initial-capacity="101" load-factor="0.85">
<key-constraint>java.lang.String</key-constraint>
<value-constraint>org.springframework.data.gemfire.repository.sample.User</value-constraint>
<cache-loader>
<class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializerIntegrationTest$UserDataStoreCacheLoader</class-name>
</cache-loader>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
">
<bean id="userDataSource" class="org.springframework.data.gemfire.LazyWiringDeclarableSupportIntegrationTest$TestDataSource"/>
<bean id="supertoolUser" class="org.springframework.data.gemfire.repository.sample.User">
<constructor-arg type="java.lang.String" value="supertool"/>
</bean>
<bean id="declarableTemplateBean" abstract="true">
<property name="dataSource" ref="userDataSource"/>
</bean>
</beans>