From a458115c7f145bce9f02af71572920894ba66465 Mon Sep 17 00:00:00 2001 From: John Blum Date: Tue, 13 Oct 2015 23:52:28 -0700 Subject: [PATCH] SGF-434 - Add a durable GemFire client cache test to assert proper behavior by SDG. Removed the ClientCacheFactoryBean.readyForEvents() method and moved the logic to onApplicationEvent(..). Updated the spring-gemfire 1.6 and 1.7 XSD with the new keep-alive attribue on the client-cache element. Changed the generic signature of the CacheFactoryBean class back to com.gemstone.gemfire.cache.Cache due to the Spring container bean resolution Exception when using JavaConfig in SDG 1.7 with GemFire 8.1 and core Spring Framework 4.1.7. --- .../data/gemfire/CacheFactoryBean.java | 43 +++++--- .../client/ClientCacheFactoryBean.java | 31 +++--- .../gemfire/config/spring-gemfire-1.6.xsd | 7 ++ .../gemfire/config/spring-gemfire-1.7.xsd | 7 ++ .../gemfire/config/spring-gemfire-1.8.xsd | 1 + .../client/ClientCacheFactoryBeanTest.java | 99 ++++++++++--------- .../DurableClientCacheIntegrationTest.java | 17 ++-- ...fireTestApplicationContextInitializer.java | 28 +++--- 8 files changed, 141 insertions(+), 92 deletions(-) diff --git a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java index 2669345d..3f90119a 100644 --- a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java @@ -76,9 +76,15 @@ import com.gemstone.gemfire.pdx.PdxSerializer; * @see org.springframework.beans.factory.InitializingBean * @see org.springframework.beans.factory.DisposableBean * @see org.springframework.dao.support.PersistenceExceptionTranslator + * @see com.gemstone.gemfire.cache.Cache + * @see com.gemstone.gemfire.cache.CacheFactory + * @see com.gemstone.gemfire.cache.DynamicRegionFactory + * @see com.gemstone.gemfire.cache.GemFireCache + * @see com.gemstone.gemfire.distributed.DistributedMember + * @see com.gemstone.gemfire.distributed.DistributedSystem */ @SuppressWarnings("unused") -public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, BeanNameAware, FactoryBean, +public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, BeanNameAware, FactoryBean, InitializingBean, DisposableBean, PersistenceExceptionTranslator { protected boolean close = true; @@ -96,7 +102,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, protected Boolean pdxReadSerialized; protected Boolean useClusterConfiguration; - protected GemFireCache cache; + protected Cache cache; protected ClassLoader beanClassLoader; @@ -226,7 +232,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, } /* (non-Javadoc) */ - private GemFireCache init() throws Exception { + private Cache init() throws Exception { initBeanFactoryLocator(); final ClassLoader currentThreadContextClassLoader = Thread.currentThread().getContextClassLoader(); @@ -396,6 +402,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, * Post processes the GemFire Cache instance by loading any cache.xml, applying settings specified in SDG XML * configuration meta-data, and registering the appropriate Transaction Listeners, Writer and JNDI settings. * + * @param parameterized Class type extension of GemFireCache. * @param cache the GemFire Cache instance to process. * @return the GemFire Cache instance after processing. * @throws IOException if the cache.xml Resource could not be loaded and applied to the Cache instance. @@ -785,35 +792,44 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, } /** - * @return the beanClassLoader + * Gets a reference to the JRE ClassLoader used to load and create bean classes in the Spring container. + * + * @return the JRE ClassLoader used to load and created beans in the Spring container. + * @see java.lang.ClassLoader */ public ClassLoader getBeanClassLoader() { return beanClassLoader; } /** - * @return the beanFactory + * Gets a reference to the Spring BeanFactory that created this GemFire Cache FactoryBean. + * + * @return a reference to the Spring BeanFactory. + * @see org.springframework.beans.factory.BeanFactory */ public BeanFactory getBeanFactory() { return beanFactory; } - /** - * @return the beanFactoryLocator - */ + /* (non-Javadoc) */ public GemfireBeanFactoryLocator getBeanFactoryLocator() { return beanFactoryLocator; } /** - * @return the beanName + * Gets the Spring bean name for the GemFire Cache. + * + * @return a String value indicating the Spring container bean name for the GemFire Cache object/component. */ public String getBeanName() { return beanName; } /** - * @return the cacheXml + * Gets a reference to the GemFire native cache.xml file as a Spring Resource. + * + * @return the a reference to the GemFire native cache.xml as a Spring Resource. + * @see org.springframework.core.io.Resource */ public Resource getCacheXml() { return cacheXml; @@ -841,14 +857,17 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, } /** - * @return the properties + * Gets a reference to the GemFire System Properties. + * + * @return a reference to the GemFire System Properties. + * @see java.util.Properties */ public Properties getProperties() { return (properties != null ? properties : (properties = new Properties())); } @Override - public GemFireCache getObject() throws Exception { + public Cache getObject() throws Exception { return init(); } diff --git a/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java index 637e8115..1c7e4461 100644 --- a/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java @@ -27,6 +27,7 @@ import org.springframework.data.gemfire.config.GemfireConstants; import org.springframework.util.Assert; import org.springframework.util.StringUtils; +import com.gemstone.gemfire.cache.CacheClosedException; import com.gemstone.gemfire.cache.GemFireCache; import com.gemstone.gemfire.cache.client.ClientCache; import com.gemstone.gemfire.cache.client.ClientCacheFactory; @@ -219,32 +220,25 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat } /** - * Register for events after Pool and Regions have been created and iff non-durable client... + * Inform the GemFire cluster that this client cache is ready to receive events iff the client is non-durable. * * @param event the ApplicationContextEvent fired when the ApplicationContext is refreshed. - * @see org.springframework.context.Lifecycle#start() - * @see com.gemstone.gemfire.cache.client.ClientCache - * @see #readyForEvents(com.gemstone.gemfire.cache.GemFireCache) + * @see com.gemstone.gemfire.cache.client.ClientCache#readyForEvents() + * @see #getReadyForEvents() + * @see #getObject() */ @Override public void onApplicationEvent(final ContextRefreshedEvent event) { - readyForEvents(this.cache); - } - - /** - * Inform the GemFire cluster that this client cache is ready to receive events. - */ - private T readyForEvents(T clientCache) { - if (Boolean.TRUE.equals(getReadyForEvents()) && !clientCache.isClosed()) { + if (isReadyForEvents()) { try { - ((ClientCache) clientCache).readyForEvents(); + ((ClientCache) fetchCache()).readyForEvents(); } catch (IllegalStateException ignore) { - // cannot be called for a non-durable client so exception is thrown + // thrown if clientCache.readyForEvents() is called on a non-durable client + } + catch (CacheClosedException ignore) { } } - - return clientCache; } @Override @@ -336,6 +330,11 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat return readyForEvents; } + /* (non-Javadoc) */ + public boolean isReadyForEvents() { + return Boolean.TRUE.equals(getReadyForEvents()); + } + @Override public final void setUseClusterConfiguration(Boolean useClusterConfiguration) { throw new UnsupportedOperationException("Shared, cluster configuration is not applicable to clients."); diff --git a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.6.xsd b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.6.xsd index a0eb7491..4047f523 100644 --- a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.6.xsd +++ b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.6.xsd @@ -349,6 +349,13 @@ Defines a GemFire Client Cache instance used for creating or retrieving 'regions + + + + + + + + + + diff --git a/src/test/java/org/springframework/data/gemfire/client/ClientCacheFactoryBeanTest.java b/src/test/java/org/springframework/data/gemfire/client/ClientCacheFactoryBeanTest.java index 507b6e39..44d19f13 100644 --- a/src/test/java/org/springframework/data/gemfire/client/ClientCacheFactoryBeanTest.java +++ b/src/test/java/org/springframework/data/gemfire/client/ClientCacheFactoryBeanTest.java @@ -27,13 +27,13 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doThrow; 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.lang.reflect.Field; import java.util.Properties; import org.junit.Test; @@ -42,8 +42,8 @@ import org.springframework.beans.factory.BeanInitializationException; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.data.gemfire.TestUtils; import org.springframework.data.gemfire.config.GemfireConstants; -import org.springframework.data.util.ReflectionUtils; +import com.gemstone.gemfire.cache.CacheClosedException; import com.gemstone.gemfire.cache.GemFireCache; import com.gemstone.gemfire.cache.client.ClientCache; import com.gemstone.gemfire.cache.client.ClientCacheFactory; @@ -60,7 +60,7 @@ import com.gemstone.gemfire.pdx.PdxSerializer; * @see org.mockito.Mockito * @see org.junit.Test * @see org.springframework.data.gemfire.client.ClientCacheFactoryBean - * @see org.springframework.data.gemfire.TestUtils + * @see com.gemstone.gemfire.cache.client.ClientCache * @since 1.7.0 */ public class ClientCacheFactoryBeanTest { @@ -415,78 +415,83 @@ public class ClientCacheFactoryBeanTest { } @Test - public void onApplicationEventSignalsReadyForEvents() throws Exception { - ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache"); + @SuppressWarnings("unchecked") + public void onApplicationEventCallsClientCacheReadyForEvents() { + final ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache"); - when(mockClientCache.isClosed()).thenReturn(false); - - ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean() { + @Override protected T fetchCache() { + return (T) mockClientCache; + } + }; clientCacheFactoryBean.setReadyForEvents(true); - ReflectionUtils.setField(ReflectionUtils.findField(ClientCacheFactoryBean.class, - new org.springframework.util.ReflectionUtils.FieldFilter() { - @Override public boolean matches(final Field field) { - return field.getName().equals("cache"); - } - }), clientCacheFactoryBean, mockClientCache); - - assertThat(clientCacheFactoryBean.getReadyForEvents(), is(true)); + assertThat(clientCacheFactoryBean.isReadyForEvents(), is(true)); clientCacheFactoryBean.onApplicationEvent(mock(ContextRefreshedEvent.class, "MockContextRefreshedEvent")); - verify(mockClientCache, times(1)).isClosed(); verify(mockClientCache, times(1)).readyForEvents(); } @Test - public void onApplicationEventDoesNotSignalReadyForEventsWhenClientCacheIsClosed() { - ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache"); + @SuppressWarnings("unchecked") + public void onApplicationEventDoesNotCallClientCacheReadyForEventsWhenClientCacheFactoryBeanReadyForEventsIsFalse() { + final ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache"); - when(mockClientCache.isClosed()).thenReturn(true); + doThrow(new RuntimeException("test")).when(mockClientCache).readyForEvents(); - ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean() { + @Override protected T fetchCache() { + return (T) mockClientCache; + } + }; - clientCacheFactoryBean.setReadyForEvents(true); + clientCacheFactoryBean.setReadyForEvents(false); - ReflectionUtils.setField(ReflectionUtils.findField(ClientCacheFactoryBean.class, - new org.springframework.util.ReflectionUtils.FieldFilter() { - @Override public boolean matches(final Field field) { - return field.getName().equals("cache"); - } - }), clientCacheFactoryBean, mockClientCache); - - assertThat(clientCacheFactoryBean.getReadyForEvents(), is(true)); + assertThat(clientCacheFactoryBean.isReadyForEvents(), is(false)); clientCacheFactoryBean.onApplicationEvent(mock(ContextRefreshedEvent.class, "MockContextRefreshedEvent")); - verify(mockClientCache, times(1)).isClosed(); verify(mockClientCache, never()).readyForEvents(); } @Test - public void onApplicationEventDoesNotSignalReadyForEventsWhenClientCacheFactoryBeanReadyForEventsIsFalse() { - ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache"); + @SuppressWarnings("unchecked") + public void onApplicationEventHandlesIllegalStateException() { + final ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache"); - when(mockClientCache.isClosed()).thenReturn(false); + doThrow(new IllegalStateException("non-durable client")).when(mockClientCache).readyForEvents(); - ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean() { + @Override protected T fetchCache() { + return (T) mockClientCache; + } + }; - clientCacheFactoryBean.setReadyForEvents(false); + clientCacheFactoryBean.setReadyForEvents(true); - ReflectionUtils.setField(ReflectionUtils.findField(ClientCacheFactoryBean.class, - new org.springframework.util.ReflectionUtils.FieldFilter() { - @Override public boolean matches(final Field field) { - return field.getName().equals("cache"); - } - }), clientCacheFactoryBean, mockClientCache); - - assertThat(clientCacheFactoryBean.getReadyForEvents(), is(false)); + assertThat(clientCacheFactoryBean.isReadyForEvents(), is(true)); clientCacheFactoryBean.onApplicationEvent(mock(ContextRefreshedEvent.class, "MockContextRefreshedEvent")); - verify(mockClientCache, never()).isClosed(); - verify(mockClientCache, never()).readyForEvents(); + verify(mockClientCache, times(1)).readyForEvents(); + } + + @Test + @SuppressWarnings("unchecked") + public void onApplicationEventHandlesCacheClosedException() { + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean() { + @Override protected T fetchCache() { + throw new CacheClosedException("test"); + } + }; + + clientCacheFactoryBean.setReadyForEvents(true); + + assertThat(clientCacheFactoryBean.isReadyForEvents(), is(true)); + + clientCacheFactoryBean.onApplicationEvent(mock(ContextRefreshedEvent.class, "MockContextRefreshedEvent")); } @Test @@ -541,7 +546,7 @@ public class ClientCacheFactoryBeanTest { } @Test(expected = IllegalArgumentException.class) - public void setPoolNameToInvalidValue() { + public void setPoolNameWithAnIllegalArgument() { try { new ClientCacheFactoryBean().setPoolName(" "); } diff --git a/src/test/java/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest.java index 24fd6dc2..ec45b2ad 100644 --- a/src/test/java/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest.java +++ b/src/test/java/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest.java @@ -19,6 +19,7 @@ package org.springframework.data.gemfire.client; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo; import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeThat; @@ -221,12 +222,16 @@ public class DurableClientCacheIntegrationTest extends AbstractGemFireClientServ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (RUN_COUNT.get() == 2 && bean instanceof ClientCache) { - // NOTE pending event count is possibly 3 because it includes the 2 puts from the client cache producer - // as well as the "marker" - assertThat(((ClientCache) bean).getDefaultPool().getPendingEventCount(), is(equalTo( - RUN_COUNT.get() == 1 ? -2 : 3))); - pause(TimeUnit.SECONDS.toMillis(3)); + if (bean instanceof ClientCache) { + if (RUN_COUNT.get() == 1) { + assertThat(((ClientCache) bean).getDefaultPool().getPendingEventCount(), is(equalTo(-2))); + } + else { + // NOTE pending event count is possibly 3 because it includes the 2 puts from the client cache producer + // as well as the "marker" + assertThat(((ClientCache) bean).getDefaultPool().getPendingEventCount(), is(greaterThanOrEqualTo(2))); + pause(TimeUnit.SECONDS.toMillis(3)); + } } return bean; diff --git a/src/test/java/org/springframework/data/gemfire/test/GemfireTestApplicationContextInitializer.java b/src/test/java/org/springframework/data/gemfire/test/GemfireTestApplicationContextInitializer.java index e6606181..1e148fa7 100644 --- a/src/test/java/org/springframework/data/gemfire/test/GemfireTestApplicationContextInitializer.java +++ b/src/test/java/org/springframework/data/gemfire/test/GemfireTestApplicationContextInitializer.java @@ -10,7 +10,9 @@ * 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.test; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContextInitializer; @@ -19,30 +21,34 @@ import org.springframework.util.StringUtils; /** * @author David Turanski - * + * @author John Blum */ public class GemfireTestApplicationContextInitializer implements ApplicationContextInitializer { - private static Log logger = LogFactory.getLog(GemfireTestApplicationContextInitializer.class); + private static final Log LOG = LogFactory.getLog(GemfireTestApplicationContextInitializer.class); public static final String GEMFIRE_TEST_RUNNER_DISABLED = "org.springframework.data.gemfire.test.GemfireTestRunner.nomock"; - /* (non-Javadoc) + /* + * (non-Javadoc) * @see org.springframework.context.ApplicationContextInitializer#initialize(org.springframework.context.ConfigurableApplicationContext) */ @Override public void initialize(ConfigurableApplicationContext applicationContext) { - if (StringUtils.hasText(System.getProperty(GEMFIRE_TEST_RUNNER_DISABLED))) { - String value = System.getProperty(GEMFIRE_TEST_RUNNER_DISABLED); + String gemfireTestRunnerDisabled = System.getProperty(GEMFIRE_TEST_RUNNER_DISABLED, Boolean.FALSE.toString()); - if (!("NO".equalsIgnoreCase(value) || "FALSE".equalsIgnoreCase(value))) { - logger.warn(String.format("Mocks disabled. Using real GemFire components: %1$s = %2$s", - GEMFIRE_TEST_RUNNER_DISABLED, value)); - return; - } + if (isGemFireTestRunnerDisable(gemfireTestRunnerDisabled)) { + LOG.warn(String.format("WARNING - Mocks disabled; Using real GemFire components (%1$s = %2$s)", + GEMFIRE_TEST_RUNNER_DISABLED, gemfireTestRunnerDisabled)); } + else { + applicationContext.getBeanFactory().addBeanPostProcessor(new GemfireTestBeanPostProcessor()); + } + } - applicationContext.getBeanFactory().addBeanPostProcessor(new GemfireTestBeanPostProcessor()); + private boolean isGemFireTestRunnerDisable(final String systemPropertyValue) { + return (Boolean.valueOf(StringUtils.trimAllWhitespace(systemPropertyValue)) + || "yes".equalsIgnoreCase(systemPropertyValue)); } }