From 7633bfe8f131e6701b1aab82e0aa2487e4eb50c0 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. (cherry picked from commit a458115) Signed-off-by: John Blum --- .../data/gemfire/CacheFactoryBean.java | 37 +++++-- .../client/ClientCacheFactoryBean.java | 40 ++++---- .../gemfire/config/spring-gemfire-1.6.xsd | 7 ++ .../client/ClientCacheFactoryBeanTest.java | 99 ++++++++++--------- .../DurableClientCacheIntegrationTest.java | 6 +- ...fireTestApplicationContextInitializer.java | 28 +++--- 6 files changed, 127 insertions(+), 90 deletions(-) diff --git a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java index f1bf8ef6..20ecb3fd 100644 --- a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java @@ -77,6 +77,12 @@ 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, @@ -400,6 +406,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. @@ -789,35 +796,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; @@ -845,7 +861,10 @@ 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())); @@ -857,7 +876,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, } @Override - public Class getObjectType() { + public Class getObjectType() { return (cache != null ? cache.getClass() : Cache.class); } 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 5d02f1cf..cdce8994 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 @@ -262,6 +256,11 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat return Boolean.FALSE; } + @Override + public Class getObjectType() { + return (cache != null ? cache.getClass() : ClientCache.class); + } + /** * Sets whether the server(s) should keep the durable client's queue alive for the duration of the timeout * when the client voluntarily disconnects. @@ -288,7 +287,7 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat * @param pool the GemFire pool used by the Client Cache to obtain connections to the GemFire cluster. */ public void setPool(Pool pool) { - Assert.notNull(pool, "The GemFire Pool must not be null!"); + Assert.notNull(pool, "GemFire Pool must not be null"); this.pool = pool; } @@ -298,7 +297,7 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat * @param poolName set the name of the GemFire Pool used by the GemFire Client Cache. */ public void setPoolName(String poolName) { - Assert.hasText(poolName, "The Pool 'name' is required!"); + Assert.hasText(poolName, "Pool 'name' is required"); this.poolName = poolName; } @@ -331,6 +330,11 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat return this.readyForEvents; } + /* (non-Javadoc) */ + public boolean isReadyForEvents() { + return Boolean.TRUE.equals(getReadyForEvents()); + } + @Override public final Boolean getUseClusterConfiguration() { return Boolean.FALSE; 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 + + + + + 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..ae39de7e 100644 --- a/src/test/java/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest.java +++ b/src/test/java/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest.java @@ -221,11 +221,7 @@ 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))); + if (bean instanceof ClientCache) { pause(TimeUnit.SECONDS.toMillis(3)); } 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)); } }