diff --git a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java index e7af7758..ebd8c7b8 100644 --- a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java @@ -78,7 +78,7 @@ import com.gemstone.gemfire.pdx.PdxSerializer; * @see org.springframework.dao.support.PersistenceExceptionTranslator */ @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 +96,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, protected Boolean pdxReadSerialized; protected Boolean useClusterConfiguration; - protected Cache cache; + protected GemFireCache cache; protected ClassLoader beanClassLoader; @@ -224,7 +224,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, } /* (non-Javadoc) */ - private Cache init() throws Exception { + private GemFireCache init() throws Exception { initBeanFactoryLocator(); final ClassLoader currentThreadContextClassLoader = Thread.currentThread().getContextClassLoader(); @@ -495,7 +495,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, Cache localCache = fetchCache(); if (localCache != null && !localCache.isClosed()) { - localCache.close(); + close(localCache); } this.cache = null; @@ -507,6 +507,10 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, } } + protected void close(GemFireCache cache) { + cache.close(); + } + @Override public DataAccessException translateExceptionIfPossible(RuntimeException e) { if (e instanceof GemFireException) { @@ -842,7 +846,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, } @Override - public Cache getObject() throws Exception { + public GemFireCache getObject() throws Exception { return init(); } diff --git a/src/main/java/org/springframework/data/gemfire/GemfireUtils.java b/src/main/java/org/springframework/data/gemfire/GemfireUtils.java index b015f568..661fdfa2 100644 --- a/src/main/java/org/springframework/data/gemfire/GemfireUtils.java +++ b/src/main/java/org/springframework/data/gemfire/GemfireUtils.java @@ -25,6 +25,7 @@ import org.w3c.dom.Element; import com.gemstone.gemfire.cache.CacheFactory; import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.client.ClientCacheFactory; import com.gemstone.gemfire.internal.GemFireVersion; /** @@ -35,6 +36,7 @@ import com.gemstone.gemfire.internal.GemFireVersion; * @see org.springframework.data.gemfire.util.DistributedSystemUtils * @since 1.3.3 */ +@SuppressWarnings("unused") public abstract class GemfireUtils extends DistributedSystemUtils { public final static String GEMFIRE_NAME = GemFireVersion.getProductName(); @@ -96,6 +98,26 @@ public abstract class GemfireUtils extends DistributedSystemUtils { return isClassAvailable(GATEWAY_TYPE_NAME); } + public static boolean closeCache() { + try { + CacheFactory.getAnyInstance().close(); + return true; + } + catch (Exception ignore) { + return false; + } + } + + public static boolean closeClientCache() { + try { + ClientCacheFactory.getAnyInstance().close(); + return true; + } + catch (Exception ignore) { + return false; + } + } + public static boolean isGemfireVersionGreaterThanEqual(double expectedVersion) { double actualVersion = Double.parseDouble(GEMFIRE_VERSION.substring(0, 3)); return (actualVersion >= expectedVersion); 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 a2b58fb2..637e8115 100644 --- a/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java @@ -16,10 +16,11 @@ package org.springframework.data.gemfire.client; -import java.io.IOException; import java.util.Properties; import org.springframework.beans.factory.BeanInitializationException; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.data.gemfire.CacheFactoryBean; import org.springframework.data.gemfire.GemfireUtils; import org.springframework.data.gemfire.config.GemfireConstants; @@ -40,19 +41,26 @@ import com.gemstone.gemfire.pdx.PdxSerializer; * @author Costin Leau * @author Lyndon Adams * @author John Blum + * @see org.springframework.context.ApplicationListener + * @see org.springframework.context.event.ContextRefreshedEvent * @see org.springframework.data.gemfire.CacheFactoryBean * @see com.gemstone.gemfire.cache.GemFireCache * @see com.gemstone.gemfire.cache.client.ClientCache * @see com.gemstone.gemfire.cache.client.ClientCacheFactory + * @see com.gemstone.gemfire.cache.client.Pool + * @see com.gemstone.gemfire.cache.client.PoolManager + * @see com.gemstone.gemfire.distributed.DistributedSystem */ @SuppressWarnings("unused") -public class ClientCacheFactoryBean extends CacheFactoryBean { +public class ClientCacheFactoryBean extends CacheFactoryBean implements ApplicationListener { + + protected Boolean keepAlive = false; protected Boolean readyForEvents = false; private Pool pool; - private String poolName; + protected String poolName; @Override protected void postProcessPropertiesBeforeInitialization(Properties gemfireProperties) { @@ -213,18 +221,14 @@ public class ClientCacheFactoryBean extends CacheFactoryBean { /** * Register for events after Pool and Regions have been created and iff non-durable client... * - * @param parameterized Class type extension of GemFireCache. - * @param cache the GemFire cache instance to process. - * @return the processed cache instance after ready for events. - * @throws java.io.IOException if an error occurs during post processing. - * @see org.springframework.data.gemfire.CacheFactoryBean#postProcess(com.gemstone.gemfire.cache.GemFireCache) - * @see #readyForEvents(com.gemstone.gemfire.cache.GemFireCache) - * @see com.gemstone.gemfire.cache.GemFireCache + * @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) */ @Override - protected T postProcess(T cache) throws IOException { - return readyForEvents(super.postProcess(cache)); + public void onApplicationEvent(final ContextRefreshedEvent event) { + readyForEvents(this.cache); } /** @@ -243,6 +247,11 @@ public class ClientCacheFactoryBean extends CacheFactoryBean { return clientCache; } + @Override + protected void close(final GemFireCache cache) { + ((ClientCache) cache).close(isKeepAlive()); + } + @Override public final void setEnableAutoReconnect(final Boolean enableAutoReconnect) { throw new UnsupportedOperationException("Auto-reconnect is not supported on ClientCache."); @@ -258,6 +267,26 @@ public class ClientCacheFactoryBean extends CacheFactoryBean { 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. + * + * @param keepAlive a boolean value indicating to the server to keep the durable client's queues alive. + */ + public void setKeepAlive(Boolean keepAlive) { + this.keepAlive = keepAlive; + } + + /** + * Determines whether the server(s) should keep the durable client's queue alive for the duration of the timeout + * when the client voluntarily disconnects. + * + * @return a boolean value indicating whether the server should keep the durable client's queues alive. + */ + public boolean isKeepAlive() { + return Boolean.TRUE.equals(this.keepAlive); + } + /** * Sets the pool used by this client. * @@ -278,6 +307,15 @@ public class ClientCacheFactoryBean extends CacheFactoryBean { this.poolName = poolName; } + /** + * Gets the pool name used by this client. + * + * @return the name of the GemFire Pool used by the GemFire Client Cache. + */ + public String getPoolName() { + return poolName; + } + /** * Set the readyForEvents flag. * diff --git a/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java index 6f97656a..dac37d56 100644 --- a/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java @@ -49,6 +49,14 @@ import com.gemstone.gemfire.internal.cache.GemFireCacheImpl; * @author Costin Leau * @author David Turanski * @author John Blum + * @see org.springframework.beans.factory.BeanFactory + * @see org.springframework.beans.factory.BeanFactoryAware + * @see org.springframework.beans.factory.DisposableBean + * @see org.springframework.data.gemfire.RegionLookupFactoryBean + * @see com.gemstone.gemfire.cache.GemFireCache + * @see com.gemstone.gemfire.cache.client.ClientCache + * @see com.gemstone.gemfire.cache.client.ClientRegionFactory + * @see com.gemstone.gemfire.cache.client.ClientRegionShortcut */ @SuppressWarnings("unused") public class ClientRegionFactoryBean extends RegionLookupFactoryBean implements BeanFactoryAware, @@ -89,7 +97,7 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean } @Override - @SuppressWarnings("deprecation") + @SuppressWarnings("all") protected Region lookupFallback(GemFireCache cache, String regionName) throws Exception { Assert.isTrue(cache instanceof ClientCache, String.format("Unable to create Regions from %1$s!", cache)); @@ -298,24 +306,6 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean public void destroy() throws Exception { Region region = getObject(); - try { - if (region != null && !ObjectUtils.isEmpty(interests)) { - for (Interest interest : interests) { - if (interest instanceof RegexInterest) { - region.unregisterInterestRegex((String) interest.getKey()); - } - else { - region.unregisterInterest(interest.getKey()); - } - } - } - } - // NOTE AdminRegion, LocalDataSet, Proxy Region and RegionCreation all throw UnsupportedOperationException; - // however, should not really happen since Interests are validated at start/registration - catch (UnsupportedOperationException ex) { - log.warn("Cannot unregister cache interests", ex); - } - if (region != null) { if (close) { if (!region.getRegionService().isClosed()) { @@ -326,8 +316,8 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean } } } - // TODO I think Region.close and Region.destroyRegion are mutually exclusive; thus, 1 operation (e.g. close) - // does not cancel the other (i.e. destroy). This should just be if, not else if. + // TODO perhaps 'destroy' should take precedence over 'close' since 'destroy' is a functional superset + // of 'close' else if (destroy) { region.destroyRegion(); } diff --git a/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java index 0871c9e0..b9ee3ed3 100644 --- a/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java @@ -243,18 +243,10 @@ public class PoolFactoryBean implements FactoryBean, InitializingBean, Dis this.beanName = name; } - /** - * @param name - * the name to set - */ public void setName(String name) { this.name = name; } - /** - * @param pool - * the pool to set - */ public void setPool(Pool pool) { this.pool = pool; } diff --git a/src/main/java/org/springframework/data/gemfire/config/ClientCacheParser.java b/src/main/java/org/springframework/data/gemfire/config/ClientCacheParser.java index 7d9b2fe6..103a70be 100644 --- a/src/main/java/org/springframework/data/gemfire/config/ClientCacheParser.java +++ b/src/main/java/org/springframework/data/gemfire/config/ClientCacheParser.java @@ -38,6 +38,7 @@ class ClientCacheParser extends CacheParser { @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { super.doParse(element, parserContext, builder); + ParsingUtils.setPropertyValue(element, builder, "keep-alive", "keepAlive"); ParsingUtils.setPropertyValue(element, builder, "pool-name", "poolName"); ParsingUtils.setPropertyValue(element, builder, "ready-for-events"); } diff --git a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.8.xsd b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.8.xsd index b6db74e0..db61b5fa 100644 --- a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.8.xsd +++ b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.8.xsd @@ -349,6 +349,12 @@ Defines a GemFire Client Cache instance used for creating or retrieving 'regions + + + + + T fetchCache() { - return (T) mockCache; + @Override protected GemFireCache fetchCache() { + fetchCacheCalled.set(true); + return mockCache; } }; @@ -487,12 +494,15 @@ public class CacheFactoryBeanTest { cacheFactoryBean.setUseBeanFactoryLocator(true); cacheFactoryBean.destroy(); + assertThat(fetchCacheCalled.get(), is(true)); + verify(mockCache, times(1)).isClosed(); verify(mockCache, times(1)).close(); verify(mockGemfireBeanFactoryLocator, times(1)).destroy(); } @Test + @SuppressWarnings("unchecked") public void destroyWhenCacheIsNull() throws Exception { final AtomicBoolean fetchCacheCalled = new AtomicBoolean(false); @@ -511,7 +521,8 @@ public class CacheFactoryBeanTest { } @Test - public void destroyWhenClosedIsFalse() throws Exception { + @SuppressWarnings("unchecked") + public void destroyWhenCacheClosedIsTrue() throws Exception { final AtomicBoolean fetchCacheCalled = new AtomicBoolean(false); final Cache mockCache = mock(Cache.class, "GemFireCache"); @@ -532,6 +543,15 @@ public class CacheFactoryBeanTest { assertFalse(fetchCacheCalled.get()); } + @Test + public void closeCache() { + GemFireCache mockCache = mock(GemFireCache.class, "testCloseCache.MockCache"); + + new CacheFactoryBean().close(mockCache); + + verify(mockCache, times(1)).close(); + } + @Test public void setAndGetCacheFactoryBeanProperties() throws Exception { BeanFactory mockBeanFactory = mock(BeanFactory.class, "SpringBeanFactory"); diff --git a/src/test/java/org/springframework/data/gemfire/CacheIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/CacheIntegrationTest.java index c3fb3836..677ea5fa 100644 --- a/src/test/java/org/springframework/data/gemfire/CacheIntegrationTest.java +++ b/src/test/java/org/springframework/data/gemfire/CacheIntegrationTest.java @@ -18,8 +18,6 @@ package org.springframework.data.gemfire; import static org.junit.Assert.assertEquals; -import junit.framework.Assert; - import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/src/test/java/org/springframework/data/gemfire/SubRegionTest.java b/src/test/java/org/springframework/data/gemfire/SubRegionTest.java index 58e4be7d..1c6fa3c4 100644 --- a/src/test/java/org/springframework/data/gemfire/SubRegionTest.java +++ b/src/test/java/org/springframework/data/gemfire/SubRegionTest.java @@ -21,7 +21,7 @@ import static org.junit.Assert.assertSame; import org.junit.Test; -import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.GemFireCache; import com.gemstone.gemfire.cache.Region; /** @@ -49,7 +49,7 @@ public class SubRegionTest extends RecreatingContextTest { cacheFactoryBean.setBeanName("gemfireCache"); cacheFactoryBean.setUseBeanFactoryLocator(false); - Cache cache = cacheFactoryBean.getObject(); + GemFireCache cache = cacheFactoryBean.getObject(); assertNotNull(cache); 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 3c29ce39..507b6e39 100644 --- a/src/test/java/org/springframework/data/gemfire/client/ClientCacheFactoryBeanTest.java +++ b/src/test/java/org/springframework/data/gemfire/client/ClientCacheFactoryBeanTest.java @@ -16,12 +16,14 @@ package org.springframework.data.gemfire.client; +import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; @@ -31,13 +33,16 @@ 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; import org.springframework.beans.factory.BeanFactory; 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.GemFireCache; import com.gemstone.gemfire.cache.client.ClientCache; @@ -81,7 +86,7 @@ public class ClientCacheFactoryBeanTest { Properties gemfireProperties = createProperties("gf", "test"); Properties distributedSystemProperties = createProperties("ds", "mock"); - final DistributedSystem mockDistributedSystem = mock(DistributedSystem.class, "MockGemFireDistributedSystem"); + final DistributedSystem mockDistributedSystem = mock(DistributedSystem.class, "MockDistributedSystem"); when(mockDistributedSystem.isConnected()).thenReturn(true); when(mockDistributedSystem.getProperties()).thenReturn(distributedSystemProperties); @@ -112,7 +117,7 @@ public class ClientCacheFactoryBeanTest { Properties gemfireProperties = createProperties("gf", "test"); Properties distributedSystemProperties = createProperties("ds", "mock"); - final DistributedSystem mockDistributedSystem = mock(DistributedSystem.class, "MockGemFireDistributedSystem"); + final DistributedSystem mockDistributedSystem = mock(DistributedSystem.class, "MockDistributedSystem"); when(mockDistributedSystem.isConnected()).thenReturn(false); when(mockDistributedSystem.getProperties()).thenReturn(distributedSystemProperties); @@ -410,19 +415,110 @@ public class ClientCacheFactoryBeanTest { } @Test - public void postProcessClientCacheAndSignalReadyForEvents() throws Exception { - ClientCache mockClientCache = mock(ClientCache.class, "MockGemFireClientCache"); + public void onApplicationEventSignalsReadyForEvents() throws Exception { + ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache"); + + when(mockClientCache.isClosed()).thenReturn(false); ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); clientCacheFactoryBean.setReadyForEvents(true); - assertTrue(clientCacheFactoryBean.getReadyForEvents()); - assertSame(mockClientCache, clientCacheFactoryBean.postProcess(mockClientCache)); + 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)); + + 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"); + + when(mockClientCache.isClosed()).thenReturn(true); + + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); + + 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)); + + 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"); + + when(mockClientCache.isClosed()).thenReturn(false); + + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); + + 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(false)); + + clientCacheFactoryBean.onApplicationEvent(mock(ContextRefreshedEvent.class, "MockContextRefreshedEvent")); + + verify(mockClientCache, never()).isClosed(); + verify(mockClientCache, never()).readyForEvents(); + } + + @Test + public void closeClientCacheWithKeepAlive() { + ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache"); + + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); + + clientCacheFactoryBean.setKeepAlive(true); + + assertThat(clientCacheFactoryBean.isKeepAlive(), is(true)); + + clientCacheFactoryBean.close(mockClientCache); + + verify(mockClientCache, times(1)).close(eq(true)); + } + + @Test + public void closeClientCacheWithoutKeepAlive() { + ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache"); + + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); + + clientCacheFactoryBean.setKeepAlive(false); + + assertThat(clientCacheFactoryBean.isKeepAlive(), is(false)); + + clientCacheFactoryBean.close(mockClientCache); + + verify(mockClientCache, times(1)).close(eq(false)); + } + @Test public void autoReconnectDisabled() { assertFalse(new ClientCacheFactoryBean().getEnableAutoReconnect()); diff --git a/src/test/java/org/springframework/data/gemfire/client/ClientRegionFactoryBeanTest.java b/src/test/java/org/springframework/data/gemfire/client/ClientRegionFactoryBeanTest.java index 4e1e2fb6..39b61990 100644 --- a/src/test/java/org/springframework/data/gemfire/client/ClientRegionFactoryBeanTest.java +++ b/src/test/java/org/springframework/data/gemfire/client/ClientRegionFactoryBeanTest.java @@ -15,13 +15,18 @@ */ 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.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -43,6 +48,7 @@ import com.gemstone.gemfire.cache.EvictionAttributes; import com.gemstone.gemfire.cache.ExpirationAttributes; import com.gemstone.gemfire.cache.Region; import com.gemstone.gemfire.cache.RegionAttributes; +import com.gemstone.gemfire.cache.RegionService; import com.gemstone.gemfire.cache.client.ClientCache; import com.gemstone.gemfire.cache.client.ClientRegionFactory; import com.gemstone.gemfire.cache.client.ClientRegionShortcut; @@ -187,7 +193,8 @@ public class ClientRegionFactoryBeanTest { ClientRegionFactory mockClientRegionFactory = mock(ClientRegionFactory.class); Region mockRegion = mock(Region.class); - when(mockClientCache.createClientRegionFactory(eq(ClientRegionShortcut.CACHING_PROXY))).thenReturn(mockClientRegionFactory); + when(mockClientCache.createClientRegionFactory(eq(ClientRegionShortcut.CACHING_PROXY))).thenReturn( + mockClientRegionFactory); when(mockClientRegionFactory.create(eq("TestRegion"))).thenReturn(mockRegion); factoryBean.setAttributes(null); @@ -523,4 +530,146 @@ public class ClientRegionFactoryBeanTest { assertEquals(ClientRegionShortcut.LOCAL_PERSISTENT, factoryBean.resolveClientRegionShortcut()); } + protected Interest newInterest(K key) { + return new Interest(key); + } + + protected Interest[] toArray(Interest... interests) { + return interests; + } + + @Test + @SuppressWarnings("unchecked") + public void destroyCallsRegionClose() throws Exception { + final Region mockRegion = mock(Region.class, "MockRegion"); + + RegionService mockRegionService = mock(RegionService.class, "MockRegionService"); + + when(mockRegion.getRegionService()).thenReturn(mockRegionService); + when(mockRegionService.isClosed()).thenReturn(false); + + ClientRegionFactoryBean clientRegionFactoryBean = new ClientRegionFactoryBean() { + @Override public Region getObject() throws Exception { + return mockRegion; + } + }; + + clientRegionFactoryBean.setClose(true); + clientRegionFactoryBean.setInterests(toArray(newInterest("test"))); + + assertThat(clientRegionFactoryBean.isClose(), is(true)); + assertThat(clientRegionFactoryBean.isDestroy(), is(false)); + assertThat(clientRegionFactoryBean.getInterests(), is(notNullValue())); + assertThat(clientRegionFactoryBean.getInterests().length, is(equalTo(1))); + + clientRegionFactoryBean.destroy(); + + verify(mockRegion, times(1)).getRegionService(); + verify(mockRegionService, times(1)).isClosed(); + verify(mockRegion, times(1)).close(); + verify(mockRegion, never()).destroyRegion(); + verify(mockRegion, never()).unregisterInterest(any()); + verify(mockRegion, never()).unregisterInterestRegex(anyString()); + } + + @Test + @SuppressWarnings("unchecked") + public void destroyCallsRegionDestroy() throws Exception { + final Region mockRegion = mock(Region.class, "MockRegion"); + + RegionService mockRegionService = mock(RegionService.class, "MockRegionService"); + + when(mockRegion.getRegionService()).thenReturn(mockRegionService); + when(mockRegionService.isClosed()).thenReturn(false); + + ClientRegionFactoryBean clientRegionFactoryBean = new ClientRegionFactoryBean() { + @Override public Region getObject() throws Exception { + return mockRegion; + } + }; + + clientRegionFactoryBean.setClose(false); + clientRegionFactoryBean.setDestroy(true); + clientRegionFactoryBean.setInterests(toArray(newInterest("test"))); + + assertThat(clientRegionFactoryBean.isClose(), is(false)); + assertThat(clientRegionFactoryBean.isDestroy(), is(true)); + assertThat(clientRegionFactoryBean.getInterests(), is(notNullValue())); + assertThat(clientRegionFactoryBean.getInterests().length, is(equalTo(1))); + + clientRegionFactoryBean.destroy(); + + verify(mockRegion, never()).getRegionService(); + verify(mockRegionService, never()).isClosed(); + verify(mockRegion, never()).close(); + verify(mockRegion, times(1)).destroyRegion(); + verify(mockRegion, never()).unregisterInterest(any()); + verify(mockRegion, never()).unregisterInterestRegex(anyString()); + } + + @Test + @SuppressWarnings("unchecked") + public void destroyDoesNothingWhenClientRegionFactoryBeanCloseIsTrueButRegionServiceIsClosed() throws Exception { + final Region mockRegion = mock(Region.class, "MockRegion"); + + RegionService mockRegionService = mock(RegionService.class, "MockRegionService"); + + when(mockRegion.getRegionService()).thenReturn(mockRegionService); + when(mockRegionService.isClosed()).thenReturn(true); + + ClientRegionFactoryBean clientRegionFactoryBean = new ClientRegionFactoryBean() { + @Override public Region getObject() throws Exception { + return mockRegion; + } + }; + + clientRegionFactoryBean.setClose(true); + clientRegionFactoryBean.setInterests(toArray(newInterest("test"))); + + assertThat(clientRegionFactoryBean.isClose(), is(true)); + assertThat(clientRegionFactoryBean.isDestroy(), is(false)); + assertThat(clientRegionFactoryBean.getInterests(), is(notNullValue())); + assertThat(clientRegionFactoryBean.getInterests().length, is(equalTo(1))); + + clientRegionFactoryBean.destroy(); + + verify(mockRegion, times(1)).getRegionService(); + verify(mockRegionService, times(1)).isClosed(); + verify(mockRegion, never()).close(); + verify(mockRegion, never()).destroyRegion(); + verify(mockRegion, never()).unregisterInterest(any()); + verify(mockRegion, never()).unregisterInterestRegex(anyString()); + } + + @Test + @SuppressWarnings("unchecked") + public void destroyDoesNothingWhenClientRegionFactoryBeanCloseAndDestroyAreFalse() throws Exception { + final Region mockRegion = mock(Region.class, "MockRegion"); + + ClientRegionFactoryBean clientRegionFactoryBean = new ClientRegionFactoryBean() { + @Override public Region getObject() throws Exception { + return mockRegion; + } + }; + + clientRegionFactoryBean.destroy(); + + verify(mockRegion, never()).getRegionService(); + verify(mockRegion, never()).close(); + verify(mockRegion, never()).destroyRegion(); + verify(mockRegion, never()).unregisterInterest(any()); + verify(mockRegion, never()).unregisterInterestRegex(anyString()); + } + + @Test + public void destroyDoesNothingWhenRegionIsNull() throws Exception { + ClientRegionFactoryBean clientRegionFactoryBean = new ClientRegionFactoryBean() { + @Override public Region getObject() throws Exception { + return null; + } + }; + + clientRegionFactoryBean.destroy(); + } + } diff --git a/src/test/java/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest.java new file mode 100644 index 00000000..24fd6dc2 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest.java @@ -0,0 +1,291 @@ +/* + * 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.client; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeThat; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.Resource; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.data.gemfire.GemfireUtils; +import org.springframework.data.gemfire.process.ProcessWrapper; +import org.springframework.data.gemfire.test.AbstractGemFireClientServerIntegrationTest; +import org.springframework.data.gemfire.test.support.ThreadUtils; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.Assert; + +import com.gemstone.gemfire.cache.DataPolicy; +import com.gemstone.gemfire.cache.EntryEvent; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.client.ClientCache; +import com.gemstone.gemfire.cache.client.ClientCacheFactory; +import com.gemstone.gemfire.cache.client.ClientRegionShortcut; +import com.gemstone.gemfire.cache.util.CacheListenerAdapter; + +/** + * The DurableClientCacheIntegrationTest class is a test suite of test cases testing GemFire's Durable Client + * functionality in the context of Spring Data GemFire. + * + * @author John Blum + * @see org.junit.Test + * @see org.junit.runner.RunWith + * @see org.springframework.beans.factory.config.BeanPostProcessor + * @see org.springframework.context.ConfigurableApplicationContext + * @see org.springframework.data.gemfire.process.ProcessWrapper + * @see org.springframework.data.gemfire.test.AbstractGemFireClientServerIntegrationTest + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner + * @see com.gemstone.gemfire.cache.client.ClientCache + * @see com.gemstone.gemfire.cache.Region + * @see com.gemstone.gemfire.cache.util.CacheListenerAdapter + * @since 1.6.3 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@SuppressWarnings("all") +public class DurableClientCacheIntegrationTest extends AbstractGemFireClientServerIntegrationTest { + + private static final int SERVER_PORT = 24842; + + private static final AtomicInteger RUN_COUNT = new AtomicInteger(1); + + private static List regionCacheListenerEventValues = + Collections.synchronizedList(new ArrayList(5)); + + private static ProcessWrapper serverProcess; + + private static final String CLIENT_CACHE_INTERESTS_RESULT_POLICY_SYSTEM_PROPERTY = + "gemfire.cache.client.interests.result-policy"; + + private static final String SERVER_HOST = "localhost"; + + @Autowired + private ConfigurableApplicationContext applicationContext; + + @Autowired + private ClientCache clientCache; + + @Resource(name = "Example") + private Region example; + + @BeforeClass + public static void setupGemFireServer() throws IOException { + serverProcess = setupGemFireServer(DurableClientCacheIntegrationTest.class); + } + + @AfterClass + public static void tearDownGemFireServer() { + tearDownGemFireServer(serverProcess); + serverProcess = null; + } + + @Before + public void setup() { + assertRegion(example, "Example", DataPolicy.NORMAL); + } + + @After + public void tearDown() { + if (RUN_COUNT.get() == 1) { + closeApplicationContext(); + runClientCacheProducer(); + System.setProperty(CLIENT_CACHE_INTERESTS_RESULT_POLICY_SYSTEM_PROPERTY, + InterestResultPolicyType.NONE.name()); + RUN_COUNT.incrementAndGet(); + } + + regionCacheListenerEventValues.clear(); + } + + protected void closeApplicationContext() { + applicationContext.close(); + + assertThat(applicationContext.isRunning(), is(false)); + assertThat(applicationContext.isActive(), is(false)); + } + + protected void runClientCacheProducer() { + try { + ClientCache gemfireClientCache = new ClientCacheFactory() + .addPoolServer(SERVER_HOST, SERVER_PORT) + .set("name", "ClientCacheProducer") + .set("mcast-port", "0") + .set("log-level", "warning") + .create(); + + Region exampleRegion = gemfireClientCache.createClientRegionFactory( + ClientRegionShortcut.PROXY).create("Example"); + + exampleRegion.put("four", 4); + exampleRegion.put("five", 5); + } + finally { + GemfireUtils.closeClientCache(); + } + } + + protected void waitForRegionEntryEvents() { + ThreadUtils.timedWait(TimeUnit.SECONDS.toMillis(5), TimeUnit.MILLISECONDS.toMillis(500), + new ThreadUtils.WaitCondition() { + @Override public boolean waiting() { + return (regionCacheListenerEventValues.size() < 2); + } + } + ); + } + + protected void assertRegion(Region region, String expectedName, DataPolicy expectedDataPolicy) { + assertRegion(region, expectedName, String.format("%1$s%2$s", Region.SEPARATOR, expectedName), + expectedDataPolicy); + } + + protected void assertRegion(Region region, String expectedName, String expectedPath, DataPolicy expectedDataPolicy) { + assertThat(region, is(notNullValue())); + assertThat(region.getName(), is(equalTo(expectedName))); + assertThat(region.getFullPath(), is(equalTo(expectedPath))); + assertThat(region.getAttributes(), is(notNullValue())); + assertThat(region.getAttributes().getDataPolicy(), is(equalTo(expectedDataPolicy))); + } + + protected void assertRegionContents(Region region, Object... values) { + assertThat(region.size(), is(equalTo(values.length))); + + for (Object value : values) { + assertThat(region.containsValue(value), is(true)); + } + } + + @Test + @DirtiesContext + public void durableClientGetsInitializedWithDataOnServer() { + assumeThat(RUN_COUNT.get(), is(equalTo(1))); + assertRegionContents(example, 1, 2, 3); + assertThat(regionCacheListenerEventValues.isEmpty(), is(true)); + } + + @Test + public void durableClientGetsUpdatesFromServerWhileClientWasOffline() { + assumeThat(RUN_COUNT.get(), is(equalTo(2))); + assertThat(example.isEmpty(), is(true)); + + waitForRegionEntryEvents(); + + assertThat(regionCacheListenerEventValues.size(), is(equalTo(2))); + assertThat(regionCacheListenerEventValues, is(equalTo(Arrays.asList(4, 5)))); + assertThat(example.isEmpty(), is(true)); + } + + public static class ClientCacheBeanPostProcessor implements BeanPostProcessor { + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @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)); + } + + return bean; + } + } + + public static class RegionDataLoadingBeanPostProcessor implements BeanPostProcessor { + + private Map regionData; + + private final String regionName; + + public RegionDataLoadingBeanPostProcessor(final String regionName) { + Assert.hasText(regionName, "Region name must be specified"); + this.regionName = regionName; + } + + public void setRegionData(Map regionData) { + this.regionData = regionData; + } + + protected Map getRegionData() { + Assert.state(regionData != null, "Region data was not properly initialized"); + return regionData; + } + + protected String getRegionName() { + return regionName; + } + + protected void loadData(Region region) { + region.putAll(getRegionData()); + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof Region) { + Region region = (Region) bean; + + if (getRegionName().equals(region.getName())) { + loadData(region); + } + } + + return bean; + } + } + + public static class RegionEntryEventRecordingCacheListener extends CacheListenerAdapter { + + @Override + public void afterCreate(final EntryEvent event) { + regionCacheListenerEventValues.add(event.getNewValue()); + } + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/config/ClientCacheNamespaceTest.java b/src/test/java/org/springframework/data/gemfire/config/ClientCacheNamespaceTest.java new file mode 100644 index 00000000..d7faa2bd --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/config/ClientCacheNamespaceTest.java @@ -0,0 +1,80 @@ +/* + * 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.config; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.Properties; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.gemfire.TestUtils; +import org.springframework.data.gemfire.client.ClientCacheFactoryBean; +import org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.gemstone.gemfire.pdx.PdxSerializer; + +/** + * The ClientCacheNamespaceTest class is a test suite of test cases testing the contract and functionality + * of the Spring Data GemFire ClientCacheParser. + * + * @author John Blum + * @see org.junit.Test + * @see org.junit.runner.RunWith + * @see org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner + * @since 1.6.3 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(initializers = GemfireTestApplicationContextInitializer.class) +@SuppressWarnings("unused") +public class ClientCacheNamespaceTest { + + @Autowired + private ClientCacheFactoryBean clientCacheFactoryBean; + + @Autowired + private Properties gemfireProperties; + + @Autowired + private PdxSerializer reflectionPdxSerializer; + + @Test + public void clientCacheFactoryBeanConfiguration() throws Exception { + assertThat(clientCacheFactoryBean.getCacheXml().toString(), containsString("path/to/bogus/cache.xml")); + assertThat(clientCacheFactoryBean.getProperties(), is(equalTo(gemfireProperties))); + assertThat(clientCacheFactoryBean.isLazyInitialize(), is(true)); + assertThat(clientCacheFactoryBean.getCopyOnRead(), is(true)); + assertThat(clientCacheFactoryBean.getCriticalHeapPercentage(), is(equalTo(0.85f))); + assertThat(clientCacheFactoryBean.getEvictionHeapPercentage(), is(equalTo(0.65f))); + assertThat((PdxSerializer) clientCacheFactoryBean.getPdxSerializer(), is(equalTo(reflectionPdxSerializer))); + assertThat(clientCacheFactoryBean.getPdxIgnoreUnreadFields(), is(true)); + assertThat(clientCacheFactoryBean.getPdxPersistent(), is(false)); + assertThat(clientCacheFactoryBean.getPdxReadSerialized(), is(true)); + assertThat(clientCacheFactoryBean.isKeepAlive(), is(true)); + assertThat(TestUtils.readField("poolName", clientCacheFactoryBean), is(equalTo("serverPool"))); + assertThat(clientCacheFactoryBean.getReadyForEvents(), is(false)); + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/fork/ServerProcess.java b/src/test/java/org/springframework/data/gemfire/fork/ServerProcess.java index 47881c88..d8595021 100644 --- a/src/test/java/org/springframework/data/gemfire/fork/ServerProcess.java +++ b/src/test/java/org/springframework/data/gemfire/fork/ServerProcess.java @@ -55,9 +55,7 @@ public class ServerProcess { throw e; } finally { - if (applicationContext != null) { - applicationContext.close(); - } + close(applicationContext); } } @@ -65,4 +63,13 @@ public class ServerProcess { return ServerProcess.class.getSimpleName().toLowerCase().concat(".pid"); } + protected static boolean close(final ConfigurableApplicationContext applicationContext) { + if (applicationContext != null) { + applicationContext.close(); + return !(applicationContext.isRunning() || applicationContext.isActive()); + } + + return true; + } + } diff --git a/src/test/java/org/springframework/data/gemfire/test/AbstractGemFireClientServerIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/test/AbstractGemFireClientServerIntegrationTest.java new file mode 100644 index 00000000..1bf44cd1 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/test/AbstractGemFireClientServerIntegrationTest.java @@ -0,0 +1,109 @@ +/* + * 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.test; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.springframework.data.gemfire.fork.ServerProcess; +import org.springframework.data.gemfire.process.ProcessExecutor; +import org.springframework.data.gemfire.process.ProcessWrapper; +import org.springframework.data.gemfire.test.support.FileSystemUtils; +import org.springframework.data.gemfire.test.support.ThreadUtils; +import org.springframework.util.Assert; + +/** + * The AbstractGemFireClientServerIntegrationTest class is an abstract test suite base class encapsulating functionality + * common to all test classes implementing GemFire client/server test cases. + * + * @author John Blum + * @see org.springframework.data.gemfire.fork.ServerProcess + * @see org.springframework.data.gemfire.process.ProcessExecutor + * @see org.springframework.data.gemfire.process.ProcessWrapper + * @since 1.8.0 + */ +@SuppressWarnings("unused") +public abstract class AbstractGemFireClientServerIntegrationTest { + + protected static long DEFAULT_WAIT_TIME_FOR_SERVER_TO_START = TimeUnit.SECONDS.toMillis(20); + protected static long FIVE_HUNDRED_MILLISECONDS = TimeUnit.MILLISECONDS.toMillis(500); + protected static long ONE_SECOND_IN_MILLISECONDS = TimeUnit.SECONDS.toMillis(1); + + protected static String PROCESS_WORKING_DIRECTORY_CLEAN_SYSTEM_PROPERTY = "spring.gemfire.force.clean"; + + protected static void pause(final long duration) { + ThreadUtils.timedWait(Math.max(duration, ONE_SECOND_IN_MILLISECONDS), ONE_SECOND_IN_MILLISECONDS, + new ThreadUtils.WaitCondition() { + @Override public boolean waiting() { + return true; + } + } + ); + } + + protected static ProcessWrapper setupGemFireServer(final Class testClass) throws IOException { + return setupGemFireServer(testClass, DEFAULT_WAIT_TIME_FOR_SERVER_TO_START); + } + + protected static ProcessWrapper setupGemFireServer(final Class testClass, final long waitTimeInMilliseconds) throws IOException { + String serverName = testClass.getSimpleName() + "Server"; + + File serverWorkingDirectory = new File(FileSystemUtils.WORKING_DIRECTORY, serverName.toLowerCase()); + + Assert.isTrue(serverWorkingDirectory.isDirectory() || serverWorkingDirectory.mkdirs()); + + List arguments = new ArrayList(); + + arguments.add(String.format("-Dgemfire.name=%1$s", serverName)); + arguments.add("/".concat(testClass.getName().replace(".", "/").concat("-server-context.xml"))); + + ProcessWrapper serverProcess = ProcessExecutor.launch(serverWorkingDirectory, ServerProcess.class, + arguments.toArray(new String[arguments.size()])); + + waitForServerToStart(serverProcess, waitTimeInMilliseconds); + + System.out.printf("The Spring-based, GemFire Cache Server process for %1$s should be running...%n", + testClass.getSimpleName()); + + return serverProcess; + } + + static void waitForServerToStart(final ProcessWrapper process, final long duration) { + ThreadUtils.timedWait(Math.max(duration, FIVE_HUNDRED_MILLISECONDS), FIVE_HUNDRED_MILLISECONDS, + new ThreadUtils.WaitCondition() { + private File processPidControlFile = new File(process.getWorkingDirectory(), + ServerProcess.getServerProcessControlFilename()); + + @Override public boolean waiting() { + return !processPidControlFile.isFile(); + } + } + ); + } + + protected static void tearDownGemFireServer(final ProcessWrapper process) { + process.shutdown(); + + if (Boolean.valueOf(System.getProperty(PROCESS_WORKING_DIRECTORY_CLEAN_SYSTEM_PROPERTY, Boolean.TRUE.toString()))) { + org.springframework.util.FileSystemUtils.deleteRecursively(process.getWorkingDirectory()); + } + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/test/MockClientCacheFactoryBean.java b/src/test/java/org/springframework/data/gemfire/test/MockClientCacheFactoryBean.java index e856a84b..d356070c 100644 --- a/src/test/java/org/springframework/data/gemfire/test/MockClientCacheFactoryBean.java +++ b/src/test/java/org/springframework/data/gemfire/test/MockClientCacheFactoryBean.java @@ -29,32 +29,36 @@ public class MockClientCacheFactoryBean extends ClientCacheFactoryBean { this.cache = new StubCache(); } - public MockClientCacheFactoryBean(ClientCacheFactoryBean cacheFactoryBean) { + public MockClientCacheFactoryBean(ClientCacheFactoryBean clientCacheFactoryBean) { this(); - if (cacheFactoryBean != null) { - this.beanFactoryLocator = cacheFactoryBean.getBeanFactoryLocator(); - this.beanClassLoader = cacheFactoryBean.getBeanClassLoader(); - this.beanFactory = cacheFactoryBean.getBeanFactory(); - this.beanName = cacheFactoryBean.getBeanName(); - this.cacheXml = cacheFactoryBean.getCacheXml(); - this.copyOnRead = cacheFactoryBean.getCopyOnRead(); - this.criticalHeapPercentage = cacheFactoryBean.getCriticalHeapPercentage(); - this.dynamicRegionSupport = cacheFactoryBean.getDynamicRegionSupport(); - this.evictionHeapPercentage = cacheFactoryBean.getEvictionHeapPercentage(); - this.gatewayConflictResolver = cacheFactoryBean.getGatewayConflictResolver(); - this.jndiDataSources = cacheFactoryBean.getJndiDataSources(); - this.lockLease = cacheFactoryBean.getLockLease(); - this.lockTimeout = cacheFactoryBean.getLockTimeout(); - this.messageSyncInterval = cacheFactoryBean.getMessageSyncInterval(); - this.pdxDiskStoreName = cacheFactoryBean.getPdxDiskStoreName(); - this.pdxIgnoreUnreadFields = cacheFactoryBean.getPdxIgnoreUnreadFields(); - this.pdxPersistent = cacheFactoryBean.getPdxPersistent(); - this.pdxSerializer = cacheFactoryBean.getPdxSerializer(); - this.properties = cacheFactoryBean.getProperties(); - this.readyForEvents = cacheFactoryBean.getReadyForEvents(); - this.searchTimeout = cacheFactoryBean.getSearchTimeout(); - this.transactionListeners = cacheFactoryBean.getTransactionListeners(); - this.transactionWriter = cacheFactoryBean.getTransactionWriter(); + + if (clientCacheFactoryBean != null) { + this.beanFactoryLocator = clientCacheFactoryBean.getBeanFactoryLocator(); + this.beanClassLoader = clientCacheFactoryBean.getBeanClassLoader(); + this.beanFactory = clientCacheFactoryBean.getBeanFactory(); + this.beanName = clientCacheFactoryBean.getBeanName(); + this.cacheXml = clientCacheFactoryBean.getCacheXml(); + this.copyOnRead = clientCacheFactoryBean.getCopyOnRead(); + this.criticalHeapPercentage = clientCacheFactoryBean.getCriticalHeapPercentage(); + this.dynamicRegionSupport = clientCacheFactoryBean.getDynamicRegionSupport(); + this.evictionHeapPercentage = clientCacheFactoryBean.getEvictionHeapPercentage(); + this.gatewayConflictResolver = clientCacheFactoryBean.getGatewayConflictResolver(); + this.jndiDataSources = clientCacheFactoryBean.getJndiDataSources(); + this.keepAlive = clientCacheFactoryBean.isKeepAlive(); + this.lockLease = clientCacheFactoryBean.getLockLease(); + this.lockTimeout = clientCacheFactoryBean.getLockTimeout(); + this.messageSyncInterval = clientCacheFactoryBean.getMessageSyncInterval(); + this.pdxDiskStoreName = clientCacheFactoryBean.getPdxDiskStoreName(); + this.pdxIgnoreUnreadFields = clientCacheFactoryBean.getPdxIgnoreUnreadFields(); + this.pdxPersistent = clientCacheFactoryBean.getPdxPersistent(); + this.pdxReadSerialized = clientCacheFactoryBean.getPdxReadSerialized(); + this.pdxSerializer = clientCacheFactoryBean.getPdxSerializer(); + this.poolName = clientCacheFactoryBean.getPoolName(); + this.properties = clientCacheFactoryBean.getProperties(); + this.readyForEvents = clientCacheFactoryBean.getReadyForEvents(); + this.searchTimeout = clientCacheFactoryBean.getSearchTimeout(); + this.transactionListeners = clientCacheFactoryBean.getTransactionListeners(); + this.transactionWriter = clientCacheFactoryBean.getTransactionWriter(); } } diff --git a/src/test/java/org/springframework/data/gemfire/test/support/ThreadUtils.java b/src/test/java/org/springframework/data/gemfire/test/support/ThreadUtils.java index d2318079..3e9d97dc 100644 --- a/src/test/java/org/springframework/data/gemfire/test/support/ThreadUtils.java +++ b/src/test/java/org/springframework/data/gemfire/test/support/ThreadUtils.java @@ -38,22 +38,22 @@ public abstract class ThreadUtils { } } - public static void timedWait(final long milliseconds) { - timedWait(milliseconds, milliseconds); + public static void timedWait(final long duration) { + timedWait(duration, duration); } - public static void timedWait(final long milliseconds, final long interval) { - timedWait(milliseconds, interval, new WaitCondition() { + public static void timedWait(final long duration, final long interval) { + timedWait(duration, interval, new WaitCondition() { @Override public boolean waiting() { return true; } }); } - public static void timedWait(final long milliseconds, long interval, final WaitCondition waitCondition) { - final long timeout = (System.currentTimeMillis() + milliseconds); + public static void timedWait(final long duration, long interval, final WaitCondition waitCondition) { + final long timeout = (System.currentTimeMillis() + duration); - interval = Math.min(interval, milliseconds); + interval = Math.min(interval, duration); while (waitCondition.waiting() && (System.currentTimeMillis() < timeout)) { try { diff --git a/src/test/resources/org/springframework/data/gemfire/basic-subregion.xml b/src/test/resources/org/springframework/data/gemfire/basic-subregion.xml index 07551124..059573f7 100644 --- a/src/test/resources/org/springframework/data/gemfire/basic-subregion.xml +++ b/src/test/resources/org/springframework/data/gemfire/basic-subregion.xml @@ -12,7 +12,7 @@ BasicSubRegionConfig 0 - config + warning diff --git a/src/test/resources/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest-context.xml b/src/test/resources/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest-context.xml new file mode 100644 index 00000000..23535c03 --- /dev/null +++ b/src/test/resources/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest-context.xml @@ -0,0 +1,45 @@ + + + + + localhost + 24842 + + + + + + + + DurableClientCacheIntegrationTestClientId + 300 + warning + 0 + DurableClientCacheIntegrationTestClient + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest-server-context.xml b/src/test/resources/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest-server-context.xml new file mode 100644 index 00000000..53f15a55 --- /dev/null +++ b/src/test/resources/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest-server-context.xml @@ -0,0 +1,50 @@ + + + + + localhost + 24842 + + + + + + DurableClientCacheIntegrationTestServer + 0 + warning + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/springframework/data/gemfire/config/ClientCacheNamespaceTest-context.xml b/src/test/resources/org/springframework/data/gemfire/config/ClientCacheNamespaceTest-context.xml new file mode 100644 index 00000000..5c4eac2b --- /dev/null +++ b/src/test/resources/org/springframework/data/gemfire/config/ClientCacheNamespaceTest-context.xml @@ -0,0 +1,29 @@ + + + + + ClientCacheNamespaceTest + 0 + warning + + + + + + + + + + +