From 0fc527e399ceb28d98851bdd84a18c46cb675358 Mon Sep 17 00:00:00 2001 From: John Blum Date: Sat, 3 Oct 2015 18:34:30 -0700 Subject: [PATCH] SGF-434 - Add a durable GemFire client cache test to assert proper behavior by SDG. (cherry picked from commit b52a185) Signed-off-by: John Blum --- .../data/gemfire/CacheFactoryBean.java | 23 +- .../data/gemfire/GemfireUtils.java | 22 + .../client/ClientCacheFactoryBean.java | 62 +- .../client/ClientRegionFactoryBean.java | 32 +- .../data/gemfire/client/PoolFactoryBean.java | 7 - .../gemfire/config/ClientCacheParser.java | 1 + .../data/gemfire/CacheFactoryBeanTest.java | 636 ++++++++++++++++++ .../data/gemfire/CacheIntegrationTest.java | 2 - .../data/gemfire/SubRegionTest.java | 4 +- .../client/ClientCacheFactoryBeanTest.java | 579 ++++++++++++++++ .../client/ClientRegionFactoryBeanTest.java | 151 ++++- .../DurableClientCacheIntegrationTest.java | 291 ++++++++ .../config/ClientCacheNamespaceTest.java | 80 +++ .../data/gemfire/fork/ServerProcess.java | 13 +- ...actGemFireClientServerIntegrationTest.java | 109 +++ .../test/MockClientCacheFactoryBean.java | 54 +- .../gemfire/test/support/ThreadUtils.java | 14 +- .../data/gemfire/basic-subregion.xml | 2 +- ...ableClientCacheIntegrationTest-context.xml | 45 ++ ...entCacheIntegrationTest-server-context.xml | 50 ++ .../ClientCacheNamespaceTest-context.xml | 29 + 21 files changed, 2118 insertions(+), 88 deletions(-) create mode 100644 src/test/java/org/springframework/data/gemfire/CacheFactoryBeanTest.java create mode 100644 src/test/java/org/springframework/data/gemfire/client/ClientCacheFactoryBeanTest.java create mode 100644 src/test/java/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest.java create mode 100644 src/test/java/org/springframework/data/gemfire/config/ClientCacheNamespaceTest.java create mode 100644 src/test/java/org/springframework/data/gemfire/test/AbstractGemFireClientServerIntegrationTest.java create mode 100644 src/test/resources/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest-context.xml create mode 100644 src/test/resources/org/springframework/data/gemfire/client/DurableClientCacheIntegrationTest-server-context.xml create mode 100644 src/test/resources/org/springframework/data/gemfire/config/ClientCacheNamespaceTest-context.xml diff --git a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java index e44be6a9..f1bf8ef6 100644 --- a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java @@ -231,12 +231,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, /* (non-Javadoc) */ private Cache init() throws Exception { - if (useBeanFactoryLocator && beanFactoryLocator == null) { - beanFactoryLocator = new GemfireBeanFactoryLocator(); - beanFactoryLocator.setBeanFactory(beanFactory); - beanFactoryLocator.setBeanName(beanName); - beanFactoryLocator.afterPropertiesSet(); - } + initBeanFactoryLocator(); final ClassLoader originalThreadContextClassLoader = Thread.currentThread().getContextClassLoader(); @@ -265,6 +260,16 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, } } + /* (non-Javadoc) */ + private void initBeanFactoryLocator() { + if (useBeanFactoryLocator && beanFactoryLocator == null) { + beanFactoryLocator = new GemfireBeanFactoryLocator(); + beanFactoryLocator.setBeanFactory(beanFactory); + beanFactoryLocator.setBeanName(beanName); + beanFactoryLocator.afterPropertiesSet(); + } + } + /** * If Dynamic Regions are enabled, create and initialize a DynamicRegionFactory before creating the Cache. */ @@ -496,7 +501,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, Cache localCache = fetchCache(); if (localCache != null && !localCache.isClosed()) { - localCache.close(); + close(localCache); } this.cache = null; @@ -508,6 +513,10 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, } } + protected void close(GemFireCache cache) { + cache.close(); + } + @Override public DataAccessException translateExceptionIfPossible(final RuntimeException e) { if (e instanceof GemFireException) { diff --git a/src/main/java/org/springframework/data/gemfire/GemfireUtils.java b/src/main/java/org/springframework/data/gemfire/GemfireUtils.java index 2c84791a..e7e87f0a 100644 --- a/src/main/java/org/springframework/data/gemfire/GemfireUtils.java +++ b/src/main/java/org/springframework/data/gemfire/GemfireUtils.java @@ -23,6 +23,7 @@ import org.springframework.util.ClassUtils; import com.gemstone.gemfire.cache.CacheFactory; import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.client.ClientCacheFactory; /** * GemfireUtils is an abstract utility class encapsulating common functionality to access features and capabilities @@ -32,10 +33,31 @@ import com.gemstone.gemfire.cache.Region; * @see org.springframework.data.gemfire.util.DistributedSystemUtils * @since 1.3.3 */ +@SuppressWarnings("unused") public abstract class GemfireUtils extends DistributedSystemUtils { public final static String GEMFIRE_VERSION = CacheFactory.getVersion(); + 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 2f88f563..5d02f1cf 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."); @@ -253,6 +262,26 @@ public class ClientCacheFactoryBean extends CacheFactoryBean { return Boolean.FALSE; } + /** + * 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. * @@ -273,6 +302,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 f2e62324..a76e51b1 100644 --- a/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java @@ -244,17 +244,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/test/java/org/springframework/data/gemfire/CacheFactoryBeanTest.java b/src/test/java/org/springframework/data/gemfire/CacheFactoryBeanTest.java new file mode 100644 index 00000000..ed3bba09 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/CacheFactoryBeanTest.java @@ -0,0 +1,636 @@ +/* + * Copyright 2010-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.gemfire; + +import static org.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.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.same; +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.io.InputStream; +import java.util.Collections; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.Test; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.access.BeanFactoryReference; +import org.springframework.core.io.Resource; +import org.springframework.data.util.ReflectionUtils; + +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.CacheFactory; +import com.gemstone.gemfire.cache.CacheTransactionManager; +import com.gemstone.gemfire.cache.GemFireCache; +import com.gemstone.gemfire.cache.TransactionListener; +import com.gemstone.gemfire.cache.TransactionWriter; +import com.gemstone.gemfire.cache.control.ResourceManager; +import com.gemstone.gemfire.cache.util.GatewayConflictResolver; +import com.gemstone.gemfire.distributed.DistributedMember; +import com.gemstone.gemfire.distributed.DistributedSystem; +import com.gemstone.gemfire.distributed.Role; +import com.gemstone.gemfire.pdx.PdxSerializer; + +/** + * The CacheFactoryBeanTest class is a test suite of test cases testing the contract and functionality + * of the CacheFactoryBean class. + * + * @author John Blum + * @see org.mockito.Mockito + * @see org.junit.Test + * @see org.springframework.data.gemfire.CacheFactoryBean + * @see com.gemstone.gemfire.cache.Cache + * @since 1.7.0 + */ +public class CacheFactoryBeanTest { + + @Test + public void afterPropertiesSet() throws Exception { + BeanFactory mockBeanFactory = mock(BeanFactory.class, "SpringBeanFactory"); + Cache mockCache = mock(Cache.class, "GemFireCache"); + CacheTransactionManager mockCacheTransactionManager = mock(CacheTransactionManager.class, "GemFireTransactionManager"); + DistributedMember mockDistributedMember = mock(DistributedMember.class, "GemFireDistributedMember"); + DistributedSystem mockDistributedSystem = mock(DistributedSystem.class, "GemFireDistributedSystem"); + GatewayConflictResolver mockGatewayConflictResolver = mock(GatewayConflictResolver.class, "GemFireGatewayConflictResolver"); + PdxSerializer mockPdxSerializer = mock(PdxSerializer.class, "GemFirePdxSerializer"); + Resource mockCacheXml = mock(Resource.class, "GemFireCacheXml"); + ResourceManager mockResourceManager = mock(ResourceManager.class, "GemFireResourceManager"); + TransactionListener mockTransactionLister = mock(TransactionListener.class, "GemFireTransactionListener"); + TransactionWriter mockTransactionWriter = mock(TransactionWriter.class, "GemFireTransactionWriter"); + + final CacheFactory mockCacheFactory = mock(CacheFactory.class, "GemFireCacheFactory"); + + when(mockCacheFactory.create()).thenReturn(mockCache); + when(mockCache.getCacheTransactionManager()).thenReturn(mockCacheTransactionManager); + when(mockCache.getDistributedSystem()).thenReturn(mockDistributedSystem); + when(mockCache.getResourceManager()).thenReturn(mockResourceManager); + when(mockCacheXml.getInputStream()).thenReturn(mock(InputStream.class)); + when(mockDistributedSystem.getDistributedMember()).thenReturn(mockDistributedMember); + when(mockDistributedSystem.getName()).thenReturn("MockDistributedSystem"); + when(mockDistributedMember.getId()).thenReturn("MockDistributedMember"); + when(mockDistributedMember.getGroups()).thenReturn(Collections.emptyList()); + when(mockDistributedMember.getRoles()).thenReturn(Collections.emptySet()); + when(mockDistributedMember.getHost()).thenReturn("skullbox"); + when(mockDistributedMember.getProcessId()).thenReturn(12345); + + final ClassLoader expectedThreadContextClassLoader = Thread.currentThread().getContextClassLoader(); + + final Properties gemfireProperties = new Properties(); + + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean() { + @Override protected Object createFactory(final Properties actualGemfireProperties) { + assertSame(gemfireProperties, actualGemfireProperties); + assertSame(ClassLoader.getSystemClassLoader(), getBeanClassLoader()); + return mockCacheFactory; + } + }; + + cacheFactoryBean.setBeanClassLoader(ClassLoader.getSystemClassLoader()); + cacheFactoryBean.setBeanFactory(mockBeanFactory); + cacheFactoryBean.setBeanName("TestGemFireCache"); + cacheFactoryBean.setCacheXml(mockCacheXml); + cacheFactoryBean.setCopyOnRead(true); + cacheFactoryBean.setCriticalHeapPercentage(0.90f); + cacheFactoryBean.setDynamicRegionSupport(null); + cacheFactoryBean.setEnableAutoReconnect(false); + cacheFactoryBean.setEvictionHeapPercentage(0.75f); + cacheFactoryBean.setGatewayConflictResolver(mockGatewayConflictResolver); + cacheFactoryBean.setJndiDataSources(null); + cacheFactoryBean.setLazyInitialize(false); + cacheFactoryBean.setLockLease(15000); + cacheFactoryBean.setLockTimeout(5000); + cacheFactoryBean.setMessageSyncInterval(20000); + cacheFactoryBean.setPdxSerializer(mockPdxSerializer); + cacheFactoryBean.setPdxReadSerialized(true); + cacheFactoryBean.setPdxPersistent(true); + cacheFactoryBean.setPdxIgnoreUnreadFields(false); + cacheFactoryBean.setPdxDiskStoreName("TestPdxDiskStore"); + cacheFactoryBean.setProperties(gemfireProperties); + cacheFactoryBean.setSearchTimeout(45000); + cacheFactoryBean.setTransactionListeners(Collections.singletonList(mockTransactionLister)); + cacheFactoryBean.setTransactionWriter(mockTransactionWriter); + + assertTrue(gemfireProperties.isEmpty()); + + cacheFactoryBean.afterPropertiesSet(); + + assertEquals(2, gemfireProperties.size()); + assertTrue(gemfireProperties.containsKey("disable-auto-reconnect")); + assertTrue(gemfireProperties.containsKey("use-cluster-configuration")); + assertEquals("true", gemfireProperties.getProperty("disable-auto-reconnect")); + assertEquals("false", gemfireProperties.getProperty("use-cluster-configuration")); + assertSame(expectedThreadContextClassLoader, Thread.currentThread().getContextClassLoader()); + + GemfireBeanFactoryLocator beanFactoryLocator = cacheFactoryBean.getBeanFactoryLocator(); + + assertNotNull(beanFactoryLocator); + + BeanFactoryReference beanFactoryReference = beanFactoryLocator.useBeanFactory("TestGemFireCache"); + + assertNotNull(beanFactoryReference); + assertSame(mockBeanFactory, beanFactoryReference.getFactory()); + + verify(mockCacheFactory, times(1)).setPdxDiskStore(eq("TestPdxDiskStore")); + verify(mockCacheFactory, times(1)).setPdxIgnoreUnreadFields(eq(false)); + verify(mockCacheFactory, times(1)).setPdxPersistent(eq(true)); + verify(mockCacheFactory, times(1)).setPdxReadSerialized(eq(true)); + verify(mockCacheFactory, times(1)).setPdxSerializer(eq(mockPdxSerializer)); + verify(mockCacheFactory, times(1)).create(); + verify(mockCache, times(1)).loadCacheXml(any(InputStream.class)); + verify(mockCache, times(1)).setCopyOnRead(eq(true)); + verify(mockCache, times(1)).setGatewayConflictResolver(same(mockGatewayConflictResolver)); + verify(mockCache, times(1)).setLockLease(eq(15000)); + verify(mockCache, times(1)).setLockTimeout(eq(5000)); + verify(mockCache, times(1)).setMessageSyncInterval(eq(20000)); + verify(mockCache, times(1)).setSearchTimeout(eq(45000)); + verify(mockCache, times(2)).getResourceManager(); + verify(mockResourceManager, times(1)).setCriticalHeapPercentage(eq(0.90f)); + verify(mockResourceManager, times(1)).setEvictionHeapPercentage(eq(0.75f)); + verify(mockCache, times(2)).getCacheTransactionManager(); + verify(mockCacheTransactionManager, times(1)).addListener(same(mockTransactionLister)); + verify(mockCacheTransactionManager, times(1)).setWriter(same(mockTransactionWriter)); + } + + @Test + public void postProcessPropertiesBeforeInitializationDefaults() { + assumeTrue(GemfireUtils.isGemfireVersion8OrAbove()); + + Properties gemfireProperties = new Properties(); + + assertTrue(gemfireProperties.isEmpty()); + + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean(); + + cacheFactoryBean.postProcessPropertiesBeforeInitialization(gemfireProperties); + + assertEquals(2, gemfireProperties.size()); + assertTrue(gemfireProperties.containsKey("disable-auto-reconnect")); + assertTrue(gemfireProperties.containsKey("use-cluster-configuration")); + assertEquals("true", gemfireProperties.getProperty("disable-auto-reconnect")); + assertEquals("false", gemfireProperties.getProperty("use-cluster-configuration")); + } + + @Test + public void postProcessPropertiesBeforeInitializationDisabled() { + assumeTrue(GemfireUtils.isGemfireVersion8OrAbove()); + + Properties gemfireProperties = new Properties(); + + assertTrue(gemfireProperties.isEmpty()); + + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean(); + + cacheFactoryBean.setEnableAutoReconnect(false); + cacheFactoryBean.setUseClusterConfiguration(false); + cacheFactoryBean.postProcessPropertiesBeforeInitialization(gemfireProperties); + + assertEquals(2, gemfireProperties.size()); + assertTrue(gemfireProperties.containsKey("disable-auto-reconnect")); + assertTrue(gemfireProperties.containsKey("use-cluster-configuration")); + assertEquals("true", gemfireProperties.getProperty("disable-auto-reconnect")); + assertEquals("false", gemfireProperties.getProperty("use-cluster-configuration")); + } + + @Test + public void postProcessPropertiesBeforeInitializationEnabled() { + assumeTrue(GemfireUtils.isGemfireVersion8OrAbove()); + + Properties gemfireProperties = new Properties(); + + assertTrue(gemfireProperties.isEmpty()); + + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean(); + + cacheFactoryBean.setEnableAutoReconnect(true); + cacheFactoryBean.setUseClusterConfiguration(true); + cacheFactoryBean.postProcessPropertiesBeforeInitialization(gemfireProperties); + + assertEquals(2, gemfireProperties.size()); + assertTrue(gemfireProperties.containsKey("disable-auto-reconnect")); + assertTrue(gemfireProperties.containsKey("use-cluster-configuration")); + assertEquals("false", gemfireProperties.getProperty("disable-auto-reconnect")); + assertEquals("true", gemfireProperties.getProperty("use-cluster-configuration")); + } + + @Test + public void fetchExistingCache() throws Exception { + Cache mockCache = mock(Cache.class, "GemFireCache"); + + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean(); + + ReflectionUtils.setField(CacheFactoryBean.class.getDeclaredField("cache"), cacheFactoryBean, mockCache); + + Cache actualCache = cacheFactoryBean.resolveCache(); + + assertSame(mockCache, actualCache); + } + + @Test + public void resolveProperties() { + Properties gemfireProperties = new Properties(); + + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean(); + + cacheFactoryBean.setProperties(gemfireProperties); + + assertSame(gemfireProperties, cacheFactoryBean.resolveProperties()); + } + + @Test + public void createFactory() { + Properties gemfireProperties = new Properties(); + Object cacheFactoryReference = new CacheFactoryBean().createFactory(gemfireProperties); + + assertTrue(gemfireProperties.isEmpty()); + assertTrue(cacheFactoryReference instanceof CacheFactory); + + CacheFactory cacheFactory = (CacheFactory) cacheFactoryReference; + + cacheFactory.set("name", "TestCreateCacheFactory"); + + assertTrue(gemfireProperties.containsKey("name")); + assertEquals("TestCreateCacheFactory", gemfireProperties.getProperty("name")); + } + + @Test + public void prepareFactoryWithUnspecifiedPdxOptions() { + CacheFactory mockCacheFactory = mock(CacheFactory.class, "MockGemFireCacheFactory"); + + assertSame(mockCacheFactory, new CacheFactoryBean().prepareFactory(mockCacheFactory)); + + verify(mockCacheFactory, never()).setPdxSerializer(any(PdxSerializer.class)); + verify(mockCacheFactory, never()).setPdxDiskStore(any(String.class)); + verify(mockCacheFactory, never()).setPdxIgnoreUnreadFields(any(Boolean.class)); + verify(mockCacheFactory, never()).setPdxPersistent(any(Boolean.class)); + verify(mockCacheFactory, never()).setPdxReadSerialized(any(Boolean.class)); + } + + @Test + public void prepareFactoryWithPartialPdxOptions() { + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean(); + + cacheFactoryBean.setPdxSerializer(mock(PdxSerializer.class, "MockGemFirePdxSerializer")); + cacheFactoryBean.setPdxReadSerialized(true); + cacheFactoryBean.setPdxIgnoreUnreadFields(false); + + CacheFactory mockCacheFactory = mock(CacheFactory.class, "MockGemFireCacheFactory"); + + assertSame(mockCacheFactory, cacheFactoryBean.prepareFactory(mockCacheFactory)); + + verify(mockCacheFactory, times(1)).setPdxSerializer(any(PdxSerializer.class)); + verify(mockCacheFactory, never()).setPdxDiskStore(any(String.class)); + verify(mockCacheFactory, times(1)).setPdxIgnoreUnreadFields(eq(false)); + verify(mockCacheFactory, never()).setPdxPersistent(any(Boolean.class)); + verify(mockCacheFactory, times(1)).setPdxReadSerialized(eq(true)); + } + + @Test + public void prepareFactoryWithAllPdxOptions() { + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean(); + + cacheFactoryBean.setPdxSerializer(mock(PdxSerializer.class, "MockGemFirePdxSerializer")); + cacheFactoryBean.setPdxDiskStoreName("testPdxDiskStoreName"); + cacheFactoryBean.setPdxIgnoreUnreadFields(false); + cacheFactoryBean.setPdxPersistent(true); + cacheFactoryBean.setPdxReadSerialized(true); + + CacheFactory mockCacheFactory = mock(CacheFactory.class, "MockGemFireCacheFactory"); + + assertSame(mockCacheFactory, cacheFactoryBean.prepareFactory(mockCacheFactory)); + + verify(mockCacheFactory, times(1)).setPdxSerializer(any(PdxSerializer.class)); + verify(mockCacheFactory, times(1)).setPdxDiskStore(eq("testPdxDiskStoreName")); + verify(mockCacheFactory, times(1)).setPdxIgnoreUnreadFields(eq(false)); + verify(mockCacheFactory, times(1)).setPdxPersistent(eq(true)); + verify(mockCacheFactory, times(1)).setPdxReadSerialized(eq(true)); + } + + @Test(expected = IllegalArgumentException.class) + public void prepareFactoryWithInvalidTypeForPdxSerializer() { + CacheFactory mockCacheFactory = mock(CacheFactory.class, "MockGemFireCacheFactory"); + + try { + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean(); + + cacheFactoryBean.setPdxSerializer(new Object()); + cacheFactoryBean.setPdxIgnoreUnreadFields(false); + cacheFactoryBean.setPdxReadSerialized(true); + cacheFactoryBean.prepareFactory(mockCacheFactory); + } + catch (IllegalArgumentException expected) { + assertTrue(expected.getMessage().startsWith("Invalid pdx serializer used")); + assertNull(expected.getCause()); + throw expected; + } + finally { + verify(mockCacheFactory, never()).setPdxSerializer(any(PdxSerializer.class)); + verify(mockCacheFactory, never()).setPdxDiskStore(any(String.class)); + verify(mockCacheFactory, never()).setPdxIgnoreUnreadFields(any(Boolean.class)); + verify(mockCacheFactory, never()).setPdxPersistent(any(Boolean.class)); + verify(mockCacheFactory, never()).setPdxReadSerialized(any(Boolean.class)); + } + } + + @Test + public void createCacheWithExistingCache() throws Exception { + Cache mockCache = mock(Cache.class, "MockGemFireCache"); + + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean(); + + ReflectionUtils.setField(CacheFactoryBean.class.getDeclaredField("cache"), cacheFactoryBean, mockCache); + + GemFireCache actualCache = cacheFactoryBean.createCache(null); + + assertSame(mockCache, actualCache); + } + + @Test + public void createCacheWithNoExistingCache() { + Cache mockCache = mock(Cache.class, "MockGemFireCache"); + CacheFactory mockCacheFactory = mock(CacheFactory.class, "MockGemFireCacheFactory"); + + when(mockCacheFactory.create()).thenReturn(mockCache); + + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean(); + + Cache actualCache = cacheFactoryBean.createCache(mockCacheFactory); + + assertSame(mockCache, actualCache); + + verify(mockCacheFactory, times(1)).create(); + } + + @Test(expected = IllegalArgumentException.class) + public void postProcessCacheWithInvalidCriticalHeapPercentage() throws Exception { + try { + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean(); + + cacheFactoryBean.setCriticalHeapPercentage(200.0f); + cacheFactoryBean.postProcess(null); + } + catch (IllegalArgumentException expected) { + assertEquals("'criticalHeapPercentage' (200.0) is invalid; must be > 0.0 and <= 100.0", + expected.getMessage()); + throw expected; + } + } + + @Test(expected = IllegalArgumentException.class) + public void postProcessCacheWithInvalidEvictionHeapPercentage() throws Exception { + try { + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean(); + + cacheFactoryBean.setEvictionHeapPercentage(-75.0f); + cacheFactoryBean.postProcess(null); + } + catch (IllegalArgumentException expected) { + assertEquals("'evictionHeapPercentage' (-75.0) is invalid; must be > 0.0 and <= 100.0", + expected.getMessage()); + throw expected; + } + } + + @Test + @SuppressWarnings("unchecked") + public void getObject() throws Exception { + final ClassLoader expectedThreadContextClassLoader = Thread.currentThread().getContextClassLoader(); + final Cache mockCache = mock(Cache.class, "GemFireCache"); + + DistributedMember mockDistributedMember = mock(DistributedMember.class, "GemFireDistributedMember"); + DistributedSystem mockDistributedSystem = mock(DistributedSystem.class, "GemFireDistributedSystem"); + + when(mockCache.getDistributedSystem()).thenReturn(mockDistributedSystem); + when(mockDistributedSystem.getDistributedMember()).thenReturn(mockDistributedMember); + when(mockDistributedSystem.getName()).thenReturn("MockDistributedSystem"); + when(mockDistributedMember.getId()).thenReturn("MockDistributedMember"); + when(mockDistributedMember.getGroups()).thenReturn(Collections.emptyList()); + when(mockDistributedMember.getRoles()).thenReturn(Collections.emptySet()); + when(mockDistributedMember.getHost()).thenReturn("skullbox"); + when(mockDistributedMember.getProcessId()).thenReturn(67890); + + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean() { + @Override protected GemFireCache fetchCache() { + assertSame(ClassLoader.getSystemClassLoader(), getBeanClassLoader()); + return mockCache; + } + }; + + cacheFactoryBean.setBeanClassLoader(ClassLoader.getSystemClassLoader()); + cacheFactoryBean.setBeanName("MockGemFireCache"); + cacheFactoryBean.setCopyOnRead(true); + cacheFactoryBean.setLockLease(15000); + cacheFactoryBean.setLockTimeout(5000); + cacheFactoryBean.setSearchTimeout(15000); + cacheFactoryBean.setUseBeanFactoryLocator(false); + + GemFireCache actualCache = cacheFactoryBean.getObject(); + + assertSame(mockCache, actualCache); + assertSame(expectedThreadContextClassLoader, Thread.currentThread().getContextClassLoader()); + + verify(mockCache, never()).loadCacheXml(any(InputStream.class)); + verify(mockCache, times(1)).setCopyOnRead(eq(true)); + verify(mockCache, never()).setGatewayConflictResolver(any(GatewayConflictResolver.class)); + verify(mockCache, times(1)).setLockLease(eq(15000)); + verify(mockCache, times(1)).setLockTimeout(eq(5000)); + verify(mockCache, never()).setMessageSyncInterval(anyInt()); + verify(mockCache, times(1)).setSearchTimeout(eq(15000)); + verify(mockCache, never()).getResourceManager(); + verify(mockCache, never()).getCacheTransactionManager(); + } + + @Test + public void getObjectType() { + assertEquals(Cache.class, new CacheFactoryBean().getObjectType()); + } + + @Test + public void isSingleton() { + assertTrue(new CacheFactoryBean().isSingleton()); + } + + @Test + @SuppressWarnings("unchecked") + public void destroy() throws Exception { + final AtomicBoolean fetchCacheCalled = new AtomicBoolean(false); + final Cache mockCache = mock(Cache.class, "GemFireCache"); + + GemfireBeanFactoryLocator mockGemfireBeanFactoryLocator = mock(GemfireBeanFactoryLocator.class); + + when(mockCache.isClosed()).thenReturn(false); + + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean() { + @Override protected GemFireCache fetchCache() { + fetchCacheCalled.set(true); + return mockCache; + } + }; + + ReflectionUtils.setField(CacheFactoryBean.class.getDeclaredField("beanFactoryLocator"), cacheFactoryBean, + mockGemfireBeanFactoryLocator); + + cacheFactoryBean.setClose(true); + 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); + + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean() { + @Override protected GemFireCache fetchCache() { + fetchCacheCalled.set(true); + return null; + } + }; + + cacheFactoryBean.setClose(true); + cacheFactoryBean.setUseBeanFactoryLocator(true); + cacheFactoryBean.destroy(); + + assertTrue(fetchCacheCalled.get()); + } + + @Test + @SuppressWarnings("unchecked") + public void destroyWhenCacheClosedIsTrue() throws Exception { + final AtomicBoolean fetchCacheCalled = new AtomicBoolean(false); + final Cache mockCache = mock(Cache.class, "GemFireCache"); + + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean() { + @Override protected GemFireCache fetchCache() { + fetchCacheCalled.set(true); + return mockCache; + } + }; + + cacheFactoryBean.setClose(false); + cacheFactoryBean.setUseBeanFactoryLocator(false); + cacheFactoryBean.destroy(); + + verify(mockCache, never()).isClosed(); + verify(mockCache, never()).close(); + + 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"); + GatewayConflictResolver mockGatewayConflictResolver = mock(GatewayConflictResolver.class, "GemFireGatewayConflictResolver"); + PdxSerializer mockPdxSerializer = mock(PdxSerializer.class, "GemFirePdxSerializer"); + Resource mockCacheXml = mock(Resource.class, "GemFireCacheXml"); + TransactionListener mockTransactionListener = mock(TransactionListener.class, "GemFireTransactionListener"); + TransactionWriter mockTransactionWriter = mock(TransactionWriter.class, "GemFireTransactionWriter"); + + Properties gemfireProperties = new Properties(); + + CacheFactoryBean cacheFactoryBean = new CacheFactoryBean(); + + cacheFactoryBean.setBeanClassLoader(Thread.currentThread().getContextClassLoader()); + cacheFactoryBean.setBeanFactory(mockBeanFactory); + cacheFactoryBean.setBeanName("TestCache"); + cacheFactoryBean.setCacheXml(mockCacheXml); + cacheFactoryBean.setProperties(gemfireProperties); + cacheFactoryBean.setLazyInitialize(false); + cacheFactoryBean.setUseBeanFactoryLocator(false); + cacheFactoryBean.setClose(false); + cacheFactoryBean.setCopyOnRead(true); + cacheFactoryBean.setDynamicRegionSupport(new CacheFactoryBean.DynamicRegionSupport()); + cacheFactoryBean.setEnableAutoReconnect(true); + cacheFactoryBean.setCriticalHeapPercentage(0.95f); + cacheFactoryBean.setEvictionHeapPercentage(0.70f); + cacheFactoryBean.setGatewayConflictResolver(mockGatewayConflictResolver); + cacheFactoryBean.setJndiDataSources(Collections.singletonList(new CacheFactoryBean.JndiDataSource())); + cacheFactoryBean.setLockLease(15000); + cacheFactoryBean.setLockTimeout(5000); + cacheFactoryBean.setMessageSyncInterval(10000); + cacheFactoryBean.setPdxSerializer(mockPdxSerializer); + cacheFactoryBean.setPdxReadSerialized(false); + cacheFactoryBean.setPdxPersistent(true); + cacheFactoryBean.setPdxIgnoreUnreadFields(true); + cacheFactoryBean.setPdxDiskStoreName("TestPdxDiskStore"); + cacheFactoryBean.setSearchTimeout(30000); + cacheFactoryBean.setTransactionListeners(Collections.singletonList(mockTransactionListener)); + cacheFactoryBean.setTransactionWriter(mockTransactionWriter); + cacheFactoryBean.setUseClusterConfiguration(true); + + assertEquals(Thread.currentThread().getContextClassLoader(), cacheFactoryBean.getBeanClassLoader()); + assertSame(mockBeanFactory, cacheFactoryBean.getBeanFactory()); + assertNull(cacheFactoryBean.getBeanFactoryLocator()); + assertEquals("TestCache", cacheFactoryBean.getBeanName()); + assertSame(mockCacheXml, cacheFactoryBean.getCacheXml()); + assertSame(gemfireProperties, cacheFactoryBean.getProperties()); + assertFalse(cacheFactoryBean.isLazyInitialize()); + assertTrue(Boolean.FALSE.equals(TestUtils.readField("useBeanFactoryLocator", cacheFactoryBean))); + assertTrue(Boolean.FALSE.equals(TestUtils.readField("close", cacheFactoryBean))); + assertTrue(cacheFactoryBean.getCopyOnRead()); + assertEquals(0.95f, cacheFactoryBean.getCriticalHeapPercentage().floatValue(), 0.0f); + assertNotNull(cacheFactoryBean.getDynamicRegionSupport()); + assertTrue(cacheFactoryBean.getEnableAutoReconnect()); + assertEquals(0.70f, cacheFactoryBean.getEvictionHeapPercentage().floatValue(), 0.0f); + assertSame(mockGatewayConflictResolver, cacheFactoryBean.getGatewayConflictResolver()); + assertNotNull(cacheFactoryBean.getJndiDataSources()); + assertEquals(1, cacheFactoryBean.getJndiDataSources().size()); + assertEquals(15000, cacheFactoryBean.getLockLease().intValue()); + assertEquals(5000, cacheFactoryBean.getLockTimeout().intValue()); + assertEquals(10000, cacheFactoryBean.getMessageSyncInterval().intValue()); + assertSame(mockPdxSerializer, cacheFactoryBean.getPdxSerializer()); + assertFalse(cacheFactoryBean.getPdxReadSerialized()); + assertTrue(cacheFactoryBean.getPdxPersistent()); + assertTrue(cacheFactoryBean.getPdxIgnoreUnreadFields()); + assertEquals("TestPdxDiskStore", cacheFactoryBean.getPdxDiskStoreName()); + assertEquals(30000, cacheFactoryBean.getSearchTimeout().intValue()); + assertNotNull(cacheFactoryBean.getTransactionListeners()); + assertEquals(1, cacheFactoryBean.getTransactionListeners().size()); + assertSame(mockTransactionListener, cacheFactoryBean.getTransactionListeners().get(0)); + assertSame(mockTransactionWriter, cacheFactoryBean.getTransactionWriter()); + assertTrue(cacheFactoryBean.getUseClusterConfiguration()); + } + +} 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 2a28a255..969d2687 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 new file mode 100644 index 00000000..507b6e39 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/client/ClientCacheFactoryBeanTest.java @@ -0,0 +1,579 @@ +/* + * 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.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; +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; +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; +import com.gemstone.gemfire.cache.client.ClientCacheFactory; +import com.gemstone.gemfire.cache.client.Pool; +import com.gemstone.gemfire.distributed.DistributedSystem; +import com.gemstone.gemfire.internal.lang.ClassUtils; +import com.gemstone.gemfire.pdx.PdxSerializer; + +/** + * The ClientCacheFactoryBeanTest class is a test suite of test cases testing the contract and functionality + * of the SDG ClientCacheFactoryBean class. + * + * @author John Blum + * @see org.mockito.Mockito + * @see org.junit.Test + * @see org.springframework.data.gemfire.client.ClientCacheFactoryBean + * @see org.springframework.data.gemfire.TestUtils + * @since 1.7.0 + */ +public class ClientCacheFactoryBeanTest { + + protected Properties createProperties(String key, String value) { + Properties properties = new Properties(); + properties.setProperty(key, value); + return properties; + } + + @Test + public void getObjectType() { + assertEquals(ClientCache.class, new ClientCacheFactoryBean().getObjectType()); + } + + @Test + public void isSingleton() { + assertTrue(new ClientCacheFactoryBean().isSingleton()); + } + + @Test + public void resolvePropertiesWhenDistributedSystemIsConnected() { + Properties gemfireProperties = createProperties("gf", "test"); + Properties distributedSystemProperties = createProperties("ds", "mock"); + + final DistributedSystem mockDistributedSystem = mock(DistributedSystem.class, "MockDistributedSystem"); + + when(mockDistributedSystem.isConnected()).thenReturn(true); + when(mockDistributedSystem.getProperties()).thenReturn(distributedSystemProperties); + + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean() { + @SuppressWarnings("unchecked") @Override T getDistributedSystem() { + return (T) mockDistributedSystem; + } + }; + + clientCacheFactoryBean.setProperties(gemfireProperties); + + Properties resolvedProperties = clientCacheFactoryBean.resolveProperties(); + + assertNotNull(resolvedProperties); + assertNotSame(gemfireProperties, resolvedProperties); + assertNotSame(distributedSystemProperties, resolvedProperties); + assertEquals(2, resolvedProperties.size()); + assertEquals("test", resolvedProperties.getProperty("gf")); + assertEquals("mock", resolvedProperties.getProperty("ds")); + + verify(mockDistributedSystem, times(1)).isConnected(); + verify(mockDistributedSystem, times(1)).getProperties(); + } + + @Test + public void resolvePropertiesWhenDistributedSystemIsDisconnected() { + Properties gemfireProperties = createProperties("gf", "test"); + Properties distributedSystemProperties = createProperties("ds", "mock"); + + final DistributedSystem mockDistributedSystem = mock(DistributedSystem.class, "MockDistributedSystem"); + + when(mockDistributedSystem.isConnected()).thenReturn(false); + when(mockDistributedSystem.getProperties()).thenReturn(distributedSystemProperties); + + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean() { + @SuppressWarnings("unchecked") @Override T getDistributedSystem() { + return (T) mockDistributedSystem; + } + }; + + clientCacheFactoryBean.setProperties(gemfireProperties); + + Properties resolvedProperties = clientCacheFactoryBean.resolveProperties(); + + assertSame(gemfireProperties, resolvedProperties); + + verify(mockDistributedSystem, times(1)).isConnected(); + verify(mockDistributedSystem, never()).getProperties(); + } + + @Test + public void resolvePropertiesWhenDistributedSystemIsNull() { + Properties gemfireProperties = createProperties("gf", "test"); + + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean() { + @Override T getDistributedSystem() { + return null; + } + }; + + clientCacheFactoryBean.setProperties(gemfireProperties); + + Properties resolvedProperties = clientCacheFactoryBean.resolveProperties(); + + assertSame(gemfireProperties, resolvedProperties); + } + + @Test + public void createClientCacheFactory() { + Properties gemfireProperties = new Properties(); + Object clientCacheFactoryReference = new ClientCacheFactoryBean().createFactory(gemfireProperties); + + assertTrue(gemfireProperties.isEmpty()); + assertTrue(clientCacheFactoryReference instanceof ClientCacheFactory); + + ClientCacheFactory clientCacheFactory = (ClientCacheFactory) clientCacheFactoryReference; + + clientCacheFactory.set("name", "TestCreateClientCacheFactory"); + + assertTrue(gemfireProperties.containsKey("name")); + assertEquals("TestCreateClientCacheFactory", gemfireProperties.get("name")); + } + + @Test + public void prepareClientCacheFactoryWithUnspecifiedPdxOptions() { + ClientCacheFactory mockClientCacheFactory = mock(ClientCacheFactory.class, "MockGemFireClientCacheFactory"); + + assertSame(mockClientCacheFactory, new ClientCacheFactoryBean().prepareFactory(mockClientCacheFactory)); + + verify(mockClientCacheFactory, never()).setPdxSerializer(any(PdxSerializer.class)); + verify(mockClientCacheFactory, never()).setPdxDiskStore(any(String.class)); + verify(mockClientCacheFactory, never()).setPdxIgnoreUnreadFields(any(Boolean.class)); + verify(mockClientCacheFactory, never()).setPdxPersistent(any(Boolean.class)); + verify(mockClientCacheFactory, never()).setPdxReadSerialized(any(Boolean.class)); + } + + @Test + public void prepareClientCacheFactoryWithPartialPdxOptions() { + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); + + clientCacheFactoryBean.setPdxSerializer(mock(PdxSerializer.class)); + clientCacheFactoryBean.setPdxReadSerialized(true); + clientCacheFactoryBean.setPdxIgnoreUnreadFields(false); + + ClientCacheFactory mockClientCacheFactory = mock(ClientCacheFactory.class, "MockGemFireClientCacheFactory"); + + assertSame(mockClientCacheFactory, clientCacheFactoryBean.prepareFactory(mockClientCacheFactory)); + + verify(mockClientCacheFactory, times(1)).setPdxSerializer(any(PdxSerializer.class)); + verify(mockClientCacheFactory, never()).setPdxDiskStore(any(String.class)); + verify(mockClientCacheFactory, times(1)).setPdxIgnoreUnreadFields(eq(false)); + verify(mockClientCacheFactory, never()).setPdxPersistent(any(Boolean.class)); + verify(mockClientCacheFactory, times(1)).setPdxReadSerialized(eq(true)); + } + + @Test + public void prepareClientCacheFactoryWithAllPdxOptions() { + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); + + clientCacheFactoryBean.setPdxSerializer(mock(PdxSerializer.class)); + clientCacheFactoryBean.setPdxDiskStoreName("mockPdxDiskStoreName"); + clientCacheFactoryBean.setPdxIgnoreUnreadFields(false); + clientCacheFactoryBean.setPdxPersistent(true); + clientCacheFactoryBean.setPdxReadSerialized(true); + + ClientCacheFactory mockClientCacheFactory = mock(ClientCacheFactory.class, "MockGemFireClientCacheFactory"); + + assertSame(mockClientCacheFactory, clientCacheFactoryBean.prepareFactory(mockClientCacheFactory)); + + verify(mockClientCacheFactory, times(1)).setPdxSerializer(any(PdxSerializer.class)); + verify(mockClientCacheFactory, times(1)).setPdxDiskStore(eq("mockPdxDiskStoreName")); + verify(mockClientCacheFactory, times(1)).setPdxIgnoreUnreadFields(eq(false)); + verify(mockClientCacheFactory, times(1)).setPdxPersistent(eq(true)); + verify(mockClientCacheFactory, times(1)).setPdxReadSerialized(eq(true)); + } + + @Test(expected = IllegalArgumentException.class) + public void prepareClientCacheFactoryWithInvalidTypeForPdxSerializer() { + ClientCacheFactory mockClientCacheFactory = mock(ClientCacheFactory.class, "MockGemFireClientCacheFactory"); + + try { + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); + + clientCacheFactoryBean.setPdxSerializer(new Object()); + clientCacheFactoryBean.setPdxReadSerialized(true); + clientCacheFactoryBean.setPdxIgnoreUnreadFields(true); + clientCacheFactoryBean.prepareFactory(mockClientCacheFactory); + } + catch (IllegalArgumentException expected) { + assertTrue(expected.getMessage().startsWith("Invalid pdx serializer used")); + assertNull(expected.getCause()); + throw expected; + } + finally { + verify(mockClientCacheFactory, never()).setPdxSerializer(any(PdxSerializer.class)); + verify(mockClientCacheFactory, never()).setPdxDiskStore(any(String.class)); + verify(mockClientCacheFactory, never()).setPdxIgnoreUnreadFields(any(Boolean.class)); + verify(mockClientCacheFactory, never()).setPdxPersistent(any(Boolean.class)); + verify(mockClientCacheFactory, never()).setPdxReadSerialized(any(Boolean.class)); + } + } + + @Test + public void createCache() { + BeanFactory mockBeanFactory = mock(BeanFactory.class, "MockSpringBeanFactory"); + ClientCacheFactory mockClientCacheFactory = mock(ClientCacheFactory.class, "MockGemFireClientCacheFactory"); + ClientCache mockClientCache = mock(ClientCache.class, "MockGemFireClientCache"); + Pool mockPool = mock(Pool.class, "MockGemFirePool"); + + when(mockClientCacheFactory.create()).thenReturn(mockClientCache); + when(mockBeanFactory.isTypeMatch(eq("testCreateCache.Pool"), eq(Pool.class))).thenReturn(true); + when(mockBeanFactory.getBean(eq("testCreateCache.Pool"), eq(Pool.class))).thenReturn(mockPool); + when(mockPool.getFreeConnectionTimeout()).thenReturn(30000); + when(mockPool.getIdleTimeout()).thenReturn(60000l); + when(mockPool.getLoadConditioningInterval()).thenReturn(45000); + when(mockPool.getMaxConnections()).thenReturn(100); + when(mockPool.getMinConnections()).thenReturn(10); + when(mockPool.getMultiuserAuthentication()).thenReturn(true); + when(mockPool.getPingInterval()).thenReturn(15000l); + when(mockPool.getPRSingleHopEnabled()).thenReturn(true); + when(mockPool.getReadTimeout()).thenReturn(20000); + when(mockPool.getRetryAttempts()).thenReturn(10); + when(mockPool.getServerGroup()).thenReturn("TestServerGroup"); + when(mockPool.getSocketBufferSize()).thenReturn(32768); + when(mockPool.getStatisticInterval()).thenReturn(5000); + when(mockPool.getSubscriptionAckInterval()).thenReturn(15000); + when(mockPool.getSubscriptionEnabled()).thenReturn(true); + when(mockPool.getSubscriptionMessageTrackingTimeout()).thenReturn(30000); + when(mockPool.getSubscriptionRedundancy()).thenReturn(2); + when(mockPool.getThreadLocalConnections()).thenReturn(false); + + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); + + clientCacheFactoryBean.setBeanFactory(mockBeanFactory); + clientCacheFactoryBean.setPoolName("testCreateCache.Pool"); + clientCacheFactoryBean.setReadyForEvents(false); + + GemFireCache actualCache = clientCacheFactoryBean.createCache(mockClientCacheFactory); + + assertSame(mockClientCache, actualCache); + + verify(mockClientCacheFactory, times(1)).create(); + verify(mockBeanFactory, times(1)).isTypeMatch(eq("testCreateCache.Pool"), eq(Pool.class)); + verify(mockBeanFactory, times(1)).getBean(eq("testCreateCache.Pool"), eq(Pool.class)); + verify(mockBeanFactory, never()).getBean(eq(Pool.class)); + verify(mockClientCacheFactory, never()).setPoolFreeConnectionTimeout(eq(30000)); + verify(mockClientCacheFactory, never()).setPoolIdleTimeout(eq(60000l)); + verify(mockClientCacheFactory, never()).setPoolLoadConditioningInterval(eq(45000)); + verify(mockClientCacheFactory, never()).setPoolMaxConnections(eq(100)); + verify(mockClientCacheFactory, never()).setPoolMinConnections(eq(10)); + verify(mockClientCacheFactory, never()).setPoolMultiuserAuthentication(eq(true)); + verify(mockClientCacheFactory, never()).setPoolPingInterval(eq(15000l)); + verify(mockClientCacheFactory, never()).setPoolPRSingleHopEnabled(eq(true)); + verify(mockClientCacheFactory, never()).setPoolReadTimeout(eq(20000)); + verify(mockClientCacheFactory, never()).setPoolRetryAttempts(eq(10)); + verify(mockClientCacheFactory, never()).setPoolServerGroup(eq("TestServerGroup")); + verify(mockClientCacheFactory, never()).setPoolSocketBufferSize(eq(32768)); + verify(mockClientCacheFactory, never()).setPoolStatisticInterval(eq(5000)); + verify(mockClientCacheFactory, never()).setPoolSubscriptionAckInterval(eq(15000)); + verify(mockClientCacheFactory, never()).setPoolSubscriptionEnabled(eq(true)); + verify(mockClientCacheFactory, never()).setPoolSubscriptionMessageTrackingTimeout(eq(30000)); + verify(mockClientCacheFactory, never()).setPoolSubscriptionRedundancy(eq(2)); + verify(mockClientCacheFactory, never()).setPoolThreadLocalConnections(eq(false)); + } + + @Test + public void resolvePoolWithUnresolvablePoolName() throws Exception { + BeanFactory mockBeanFactory = mock(BeanFactory.class, "MockSpringBeanFactory"); + ClientCacheFactory mockClientCacheFactory = mock(ClientCacheFactory.class, "MockGemFireClientCacheFactory"); + ClientCache mockClientCache = mock(ClientCache.class, "MockGemFireClientCache"); + Pool mockPool = mock(Pool.class, "MockGemFirePool"); + + when(mockBeanFactory.isTypeMatch(any(String.class), eq(Pool.class))).thenReturn(false); + when(mockBeanFactory.getBean(eq(Pool.class))).thenReturn(mockPool); + when(mockClientCacheFactory.create()).thenReturn(mockClientCache); + when(mockPool.getFreeConnectionTimeout()).thenReturn(120000); + when(mockPool.getIdleTimeout()).thenReturn(300000l); + when(mockPool.getLoadConditioningInterval()).thenReturn(15000); + when(mockPool.getMaxConnections()).thenReturn(50); + when(mockPool.getMinConnections()).thenReturn(5); + when(mockPool.getMultiuserAuthentication()).thenReturn(false); + when(mockPool.getName()).thenReturn("MockGemFirePool"); + when(mockPool.getPingInterval()).thenReturn(12000l); + when(mockPool.getPRSingleHopEnabled()).thenReturn(true); + when(mockPool.getReadTimeout()).thenReturn(60000); + when(mockPool.getRetryAttempts()).thenReturn(5); + when(mockPool.getServerGroup()).thenReturn("MockServerGroup"); + when(mockPool.getSocketBufferSize()).thenReturn(16384); + when(mockPool.getStatisticInterval()).thenReturn(1000); + when(mockPool.getSubscriptionAckInterval()).thenReturn(500); + when(mockPool.getSubscriptionEnabled()).thenReturn(true); + when(mockPool.getSubscriptionMessageTrackingTimeout()).thenReturn(15000); + when(mockPool.getSubscriptionRedundancy()).thenReturn(4); + when(mockPool.getThreadLocalConnections()).thenReturn(false); + + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); + + clientCacheFactoryBean.setBeanFactory(mockBeanFactory); + clientCacheFactoryBean.setPoolName("TestGemFirePool"); + clientCacheFactoryBean.setReadyForEvents(false); + + assertEquals("TestGemFirePool", TestUtils.readField("poolName", clientCacheFactoryBean)); + + GemFireCache actualClientCache = clientCacheFactoryBean.createCache(mockClientCacheFactory); + + assertSame(mockClientCache, actualClientCache); + assertEquals("MockGemFirePool", TestUtils.readField("poolName", clientCacheFactoryBean)); + + verify(mockClientCacheFactory, times(1)).create(); + verify(mockBeanFactory, times(1)).isTypeMatch(eq("TestGemFirePool"), eq(Pool.class)); + verify(mockBeanFactory, never()).getBean(eq("TestGemFirePool"), eq(Pool.class)); + verify(mockBeanFactory, times(1)).getBean(eq(Pool.class)); + verify(mockPool, times(1)).getName(); + verify(mockClientCacheFactory, never()).setPoolFreeConnectionTimeout(eq(120000)); + verify(mockClientCacheFactory, never()).setPoolIdleTimeout(eq(300000l)); + verify(mockClientCacheFactory, never()).setPoolLoadConditioningInterval(eq(15000)); + verify(mockClientCacheFactory, never()).setPoolMaxConnections(eq(50)); + verify(mockClientCacheFactory, never()).setPoolMinConnections(eq(5)); + verify(mockClientCacheFactory, never()).setPoolMultiuserAuthentication(eq(false)); + verify(mockClientCacheFactory, never()).setPoolPingInterval(eq(12000l)); + verify(mockClientCacheFactory, never()).setPoolPRSingleHopEnabled(eq(true)); + verify(mockClientCacheFactory, never()).setPoolReadTimeout(eq(60000)); + verify(mockClientCacheFactory, never()).setPoolRetryAttempts(eq(5)); + verify(mockClientCacheFactory, never()).setPoolServerGroup(eq("MockServerGroup")); + verify(mockClientCacheFactory, never()).setPoolSocketBufferSize(eq(16384)); + verify(mockClientCacheFactory, never()).setPoolStatisticInterval(eq(1000)); + verify(mockClientCacheFactory, never()).setPoolSubscriptionAckInterval(eq(500)); + verify(mockClientCacheFactory, never()).setPoolSubscriptionEnabled(eq(true)); + verify(mockClientCacheFactory, never()).setPoolSubscriptionMessageTrackingTimeout(eq(15000)); + verify(mockClientCacheFactory, never()).setPoolSubscriptionRedundancy(eq(4)); + verify(mockClientCacheFactory, never()).setPoolThreadLocalConnections(eq(false)); + } + + @Test(expected = BeanInitializationException.class) + public void resolveUnresolvablePool() { + ClientCacheFactory mockClientCacheFactory = mock(ClientCacheFactory.class, "MockGemFireClientCacheFactory"); + + try { + BeanFactory mockBeanFactory = mock(BeanFactory.class, "MockSpringBeanFactory"); + + when(mockBeanFactory.isTypeMatch(eq(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME), eq(Pool.class))) + .thenReturn(true); + when(mockBeanFactory.getBean(eq(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME), eq(Pool.class))) + .thenThrow(new IllegalArgumentException("TEST")); + + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); + + clientCacheFactoryBean.setBeanFactory(mockBeanFactory); + clientCacheFactoryBean.setPoolName(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME); + clientCacheFactoryBean.setReadyForEvents(false); + clientCacheFactoryBean.createCache(mockClientCacheFactory); + } + catch (BeanInitializationException expected) { + assertTrue(expected.getMessage(), expected.getMessage().startsWith(String.format( + "no bean of type '%1$s' having name '%2$s' was found; a ClientCache requires a Pool", + Pool.class.getName(), GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME))); + assertTrue(String.format("Cause was: %1$s!", ClassUtils.getClassName(expected.getCause())), + expected.getCause() instanceof IllegalArgumentException); + assertEquals("TEST", expected.getCause().getMessage()); + throw expected; + } + finally { + verify(mockClientCacheFactory, never()).create(); + } + } + + @Test + public void onApplicationEventSignalsReadyForEvents() throws Exception { + ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache"); + + when(mockClientCache.isClosed()).thenReturn(false); + + 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, 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()); + } + + @Test(expected = UnsupportedOperationException.class) + public void enableAutoReconnect() { + new ClientCacheFactoryBean().setEnableAutoReconnect(true); + } + + @Test(expected = IllegalArgumentException.class) + public void setPoolToNull() { + try { + new ClientCacheFactoryBean().setPool(null); + } + catch (IllegalArgumentException expected) { + assertEquals("GemFire Pool must not be null", expected.getMessage()); + throw expected; + } + } + + @Test(expected = IllegalArgumentException.class) + public void setPoolNameToInvalidValue() { + try { + new ClientCacheFactoryBean().setPoolName(" "); + } + catch (IllegalArgumentException expected) { + assertEquals("Pool 'name' is required", expected.getMessage()); + throw expected; + } + } + + @Test + public void setAndGetReadyForEvents() { + ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean(); + + assertFalse(clientCacheFactoryBean.getReadyForEvents()); + + clientCacheFactoryBean.setReadyForEvents(true); + + assertTrue(clientCacheFactoryBean.getReadyForEvents()); + + clientCacheFactoryBean.setReadyForEvents(null); + + assertNull(clientCacheFactoryBean.getReadyForEvents()); + } + + @Test + public void clusterConfigurationNotUsed() { + assertFalse(new ClientCacheFactoryBean().getUseClusterConfiguration()); + } + + @Test(expected = UnsupportedOperationException.class) + public void useClusterConfiguration() { + new ClientCacheFactoryBean().setUseClusterConfiguration(true); + } + +} 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 + + + + + + + + + + +