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 e12cdd45..4860ef41 100644 --- a/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java @@ -295,7 +295,7 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean /* (non-Javadoc) */ private String resolvePoolName() { - return Optional.of(getPoolName()).filter(this::isPoolResolvable).orElse(DEFAULT_POOL_NAME); + return Optional.of(getPoolName()).filter(this::isPoolResolvable).orElse(null); } /* (non-Javadoc) */ diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireCacheTypeAwareRegionFactoryBean.java b/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireCacheTypeAwareRegionFactoryBean.java index 6b58f267..256cfb56 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireCacheTypeAwareRegionFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireCacheTypeAwareRegionFactoryBean.java @@ -108,12 +108,13 @@ public class GemFireCacheTypeAwareRegionFactoryBean extends RegionLookupFa clientRegionFactory.setCache(gemfireCache); clientRegionFactory.setClose(isClose()); clientRegionFactory.setKeyConstraint(getKeyConstraint()); - clientRegionFactory.setPoolName(resolvePoolName()); clientRegionFactory.setRegionConfigurers(this.regionConfigurers); clientRegionFactory.setRegionName(regionName); clientRegionFactory.setShortcut(getClientRegionShortcut()); clientRegionFactory.setValueConstraint(getValueConstraint()); + resolvePoolName().ifPresent(clientRegionFactory::setPoolName); + clientRegionFactory.afterPropertiesSet(); return clientRegionFactory.getObject(); @@ -204,9 +205,8 @@ public class GemFireCacheTypeAwareRegionFactoryBean extends RegionLookupFa .orElse(ClientRegionFactoryBean.GEMFIRE_POOL_NAME); } - protected String resolvePoolName() { - return Optional.of(getPoolName()).filter(this::isPoolResolvable) - .orElse(ClientRegionFactoryBean.DEFAULT_POOL_NAME); + protected Optional resolvePoolName() { + return Optional.of(getPoolName()).filter(this::isPoolResolvable); } private boolean isPoolResolvable(String poolName) { diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireComponentClassTypeScanner.java b/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireComponentClassTypeScanner.java index fccc6d3d..aa027d79 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireComponentClassTypeScanner.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/support/GemFireComponentClassTypeScanner.java @@ -27,7 +27,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Optional; import java.util.Set; -import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.stream.Collectors; import org.apache.commons.logging.Log; @@ -201,7 +201,7 @@ public class GemFireComponentClassTypeScanner implements Iterable { */ public Set> scan() { - Set> componentClasses = new ConcurrentSkipListSet<>(); + Set> componentClasses = new CopyOnWriteArraySet<>(); ClassLoader entityClassLoader = getEntityClassLoader(); diff --git a/src/test/java/org/springframework/data/gemfire/client/ClientCacheIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/client/ClientCacheIntegrationTests.java new file mode 100644 index 00000000..2d65b63f --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/client/ClientCacheIntegrationTests.java @@ -0,0 +1,168 @@ +/* + * 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.assertj.core.api.Assertions.assertThat; + +import org.apache.geode.cache.GemFireCache; +import org.apache.geode.cache.Region; +import org.apache.geode.cache.client.ClientCache; +import org.apache.geode.cache.client.ClientRegionShortcut; +import org.junit.Test; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMocking; +import org.springframework.data.gemfire.test.support.IOUtils; + +/** + * Integration tests for {@link org.apache.geode.cache.client.ClientCache}. + * + * @author John Blum + * @see org.junit.Test + * @see org.apache.geode.cache.GemFireCache + * @see org.apache.geode.cache.Region + * @see org.apache.geode.cache.client.ClientCache + * @see org.apache.geode.cache.client.ClientCache + * @see org.springframework.context.ConfigurableApplicationContext + * @see org.springframework.context.annotation.Bean + * @see org.springframework.context.annotation.Configuration + * @see org.springframework.data.gemfire.client.ClientCacheFactoryBean + * @see org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMocking + */ +public class ClientCacheIntegrationTests { + + private ConfigurableApplicationContext newApplicationContext(Class... annotatedClasses) { + return new AnnotationConfigApplicationContext(annotatedClasses); + } + + private boolean testClientCacheClose(Class clientCacheConfiguration) { + + ConfigurableApplicationContext applicationContext = null; + + try { + applicationContext = newApplicationContext(clientCacheConfiguration); + + ClientCache clientCache = applicationContext.getBean(ClientCache.class); + + assertThat(clientCache.isClosed()).isFalse(); + + applicationContext.close(); + + return clientCache.isClosed(); + } + finally { + IOUtils.close(applicationContext); + } + } + + @Test + public void clientCacheIsClosed() { + assertThat(testClientCacheClose(ClosingClientCacheConfiguration.class)).isTrue(); + } + + @Test + public void clientCacheIsNotClosed() { + assertThat(testClientCacheClose(CloseSuppressingClientCacheConfiguration.class)).isFalse(); + } + + @Test + @SuppressWarnings("unchecked") + public void multipleClientCachesAreTheSame() { + + ConfigurableApplicationContext applicationContextOne = newApplicationContext(MultiClientCacheConfiguration.class); + ConfigurableApplicationContext applicationContextTwo = newApplicationContext(MultiClientCacheConfiguration.class); + + ClientCache clientCacheOne = applicationContextOne.getBean(ClientCache.class); + ClientCache clientCacheTwo = applicationContextTwo.getBean(ClientCache.class); + + assertThat(clientCacheOne).isNotNull(); + assertThat(clientCacheTwo).isSameAs(clientCacheOne); + + Region regionOne = applicationContextOne.getBean(Region.class); + Region regionTwo = applicationContextTwo.getBean(Region.class); + + assertThat(regionOne).isNotNull(); + assertThat(regionTwo).isSameAs(regionTwo); + assertThat(clientCacheOne.isClosed()).isFalse(); + assertThat(regionOne.isDestroyed()).isFalse(); + + applicationContextOne.close(); + + assertThat(clientCacheOne.isClosed()).describedAs("ClientCache was closed").isFalse(); + assertThat(regionOne.isDestroyed()).describedAs("Region was destroyed").isFalse(); + } + + @Configuration + @EnableGemFireMocking + static class ClosingClientCacheConfiguration { + + @Bean + ClientCacheFactoryBean gemfireCache() { + + ClientCacheFactoryBean clientCache = new ClientCacheFactoryBean(); + + clientCache.setClose(true); + + return clientCache; + } + } + + @Configuration + @EnableGemFireMocking + static class CloseSuppressingClientCacheConfiguration { + + @Bean + ClientCacheFactoryBean gemfireCache() { + + ClientCacheFactoryBean clientCache = new ClientCacheFactoryBean(); + + clientCache.setClose(false); + + return clientCache; + } + } + + @Configuration + @EnableGemFireMocking(useSingletonCache = true) + static class MultiClientCacheConfiguration { + + @Bean + ClientCacheFactoryBean gemfireCache() { + + ClientCacheFactoryBean clientCache = new ClientCacheFactoryBean(); + + clientCache.setClose(false); + + return clientCache; + } + + @Bean("Example") + ClientRegionFactoryBean exampleRegion(GemFireCache gemfireCache) { + + ClientRegionFactoryBean exampleRegion = new ClientRegionFactoryBean<>(); + + exampleRegion.setCache(gemfireCache); + exampleRegion.setClose(false); + exampleRegion.setDestroy(false); + exampleRegion.setLookupEnabled(true); + exampleRegion.setShortcut(ClientRegionShortcut.LOCAL); + + return exampleRegion; + } + } +} diff --git a/src/test/java/org/springframework/data/gemfire/client/ClientCacheTest.java b/src/test/java/org/springframework/data/gemfire/client/ClientCacheTest.java deleted file mode 100644 index 84d65d65..00000000 --- a/src/test/java/org/springframework/data/gemfire/client/ClientCacheTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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.Matchers.is; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; - -import javax.annotation.Resource; - -import org.apache.geode.cache.Cache; -import org.apache.geode.cache.Region; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -/** - * @author David Turanski - * @author John Blum - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = "client-cache.xml", initializers = GemfireTestApplicationContextInitializer.class) -public class ClientCacheTest { - - @Resource(name = "ChallengeQuestions") - @SuppressWarnings("all") - private Region region; - - @Test - public void clientCacheIsNotClosed() { - ConfigurableApplicationContext context = new ClassPathXmlApplicationContext( - "/org/springframework/data/gemfire/client/client-cache-no-close.xml"); - - Cache cache = context.getBean(Cache.class); - - context.close(); - - assertThat(cache.isClosed(), is(false)); - } - - @Test - public void poolNameEqualsDefault() { - assertThat(region.getAttributes().getPoolName(), is(nullValue())); - } - -} 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 9412d78c..1bd09c9f 100644 --- a/src/test/java/org/springframework/data/gemfire/client/ClientRegionFactoryBeanTest.java +++ b/src/test/java/org/springframework/data/gemfire/client/ClientRegionFactoryBeanTest.java @@ -89,10 +89,13 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings({ "deprecation", "unchecked" }) public void testLookupFallbackUsingDefaultShortcut() throws Exception { + String testRegionName = "TestRegion"; ClientCache mockClientCache = mock(ClientCache.class); + ClientRegionFactory mockClientRegionFactory = mock(ClientRegionFactory.class); + Region mockRegion = mock(Region.class); when(mockClientCache.createClientRegionFactory(eq(ClientRegionShortcut.LOCAL))).thenReturn(mockClientRegionFactory); @@ -124,6 +127,7 @@ public class ClientRegionFactoryBeanTest { Pool mockPool = mock(Pool.class); Resource mockSnapshot = mock(Resource.class, "Snapshot"); + when(mockBeanFactory.containsBean(eq("TestPoolTwo"))).thenReturn(true); when(mockBeanFactory.isTypeMatch(eq("TestPoolTwo"), eq(Pool.class))).thenReturn(true); when(mockBeanFactory.getBean(eq("TestPoolTwo"))).thenReturn(mockPool); when(mockPool.getName()).thenReturn("TestPoolTwo"); @@ -164,8 +168,8 @@ public class ClientRegionFactoryBeanTest { verify(mockClientRegionFactory, times(1)).setRegionTimeToLive(any(ExpirationAttributes.class)); verify(mockClientRegionFactory, times(1)).setStatisticsEnabled(eq(true)); verify(mockClientRegionFactory, times(1)).setValueConstraint(eq(Number.class)); - verify(mockClientRegionFactory, times(1)).setPoolName(eq("TestPoolTwo")); verify(mockClientRegionFactory, times(1)).setDiskStoreName(eq("TestDiskStoreTwo")); + verify(mockClientRegionFactory, times(1)).setPoolName(eq("TestPoolTwo")); verify(mockClientRegionFactory, times(1)).create(eq(testRegionName)); verify(mockRegion, never()).loadSnapshot(any(InputStream.class)); } @@ -173,8 +177,11 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings({ "deprecation", "unchecked" }) public void testLookupFallbackUsingDefaultPersistentShortcut() throws Exception { + ClientCache mockClientCache = mock(ClientCache.class); + ClientRegionFactory mockClientRegionFactory = mock(ClientRegionFactory.class); + Region mockRegion = mock(Region.class); when(mockClientCache.createClientRegionFactory(eq(ClientRegionShortcut.LOCAL_PERSISTENT))).thenReturn(mockClientRegionFactory); @@ -182,6 +189,7 @@ public class ClientRegionFactoryBeanTest { BeanFactory mockBeanFactory = mock(BeanFactory.class); + when(mockBeanFactory.containsBean(eq("TestPool"))).thenReturn(true); when(mockBeanFactory.isTypeMatch(eq("TestPool"), eq(Pool.class))).thenReturn(false); factoryBean.setAttributes(null); @@ -197,6 +205,7 @@ public class ClientRegionFactoryBeanTest { verify(mockClientCache, times(1)).createClientRegionFactory(eq(ClientRegionShortcut.LOCAL_PERSISTENT)); verify(mockClientRegionFactory, times(1)).setPoolName(eq("TestPool")); verify(mockClientRegionFactory, times(1)).create(eq("TestRegion")); + verify(mockBeanFactory, times(1)).containsBean(eq("TestPool")); verify(mockBeanFactory, never()).getBean(eq("TestPool")); verify(mockRegion, never()).loadSnapshot(any(InputStream.class)); } @@ -204,9 +213,13 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("unchecked") public void testLookupFallbackWithSpecifiedShortcut() throws Exception { + BeanFactory mockBeanFactory = mock(BeanFactory.class); + ClientCache mockClientCache = mock(ClientCache.class); + ClientRegionFactory mockClientRegionFactory = mock(ClientRegionFactory.class); + Region mockRegion = mock(Region.class); when(mockBeanFactory.containsBean(anyString())).thenReturn(true); @@ -230,9 +243,13 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("unchecked") public void testLookupFallbackWithSubRegionCreation() throws Exception { + BeanFactory mockBeanFactory = mock(BeanFactory.class); + ClientCache mockClientCache = mock(ClientCache.class); + ClientRegionFactory mockClientRegionFactory = mock(ClientRegionFactory.class); + Region mockRegion = mock(Region.class, "RootRegion"); Region mockSubRegion = mock(Region.class, "SubRegion"); @@ -258,9 +275,13 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("unchecked") public void testLookupFallbackWithUnspecifiedPool() throws Exception { + BeanFactory mockBeanFactory = mock(BeanFactory.class); + ClientCache mockClientCache = mock(ClientCache.class); + ClientRegionFactory mockClientRegionFactory = mock(ClientRegionFactory.class); + Region mockRegion = mock(Region.class); when(mockBeanFactory.containsBean(anyString())).thenReturn(false); @@ -284,6 +305,7 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("deprecation") public void testSetDataPolicyName() throws Exception { + factoryBean.setDataPolicyName("NORMAL"); assertEquals(DataPolicy.NORMAL, TestUtils.readField("dataPolicy", factoryBean)); } @@ -291,6 +313,7 @@ public class ClientRegionFactoryBeanTest { @Test(expected = IllegalArgumentException.class) @SuppressWarnings("deprecation") public void testSetDataPolicyNameWithInvalidName() throws Exception { + try { factoryBean.setDataPolicyName("INVALID"); } @@ -305,6 +328,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testIsPersistent() { + assertFalse(factoryBean.isPersistent()); factoryBean.setPersistent(false); assertFalse(factoryBean.isPersistent()); @@ -314,6 +338,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testIsPersistentUnspecified() { + assertTrue(factoryBean.isPersistentUnspecified()); factoryBean.setPersistent(true); assertTrue(factoryBean.isPersistent()); @@ -325,6 +350,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testIsNotPersistent() { + assertFalse(factoryBean.isNotPersistent()); factoryBean.setPersistent(true); assertFalse(factoryBean.isNotPersistent()); @@ -334,7 +360,8 @@ public class ClientRegionFactoryBeanTest { @Test public void testCloseDestroySettings() { - final ClientRegionFactoryBean factory = new ClientRegionFactoryBean<>(); + + ClientRegionFactoryBean factory = new ClientRegionFactoryBean<>(); assertNotNull(factory); assertFalse(factory.isClose()); @@ -383,6 +410,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcut() throws Exception { + assertNull(TestUtils.readField("dataPolicy", factoryBean)); assertNull(TestUtils.readField("persistent", factoryBean)); assertNull(TestUtils.readField("shortcut", factoryBean)); @@ -391,6 +419,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutWhenNotPersistent() throws Exception { + factoryBean.setPersistent(false); assertNull(TestUtils.readField("dataPolicy", factoryBean)); @@ -401,6 +430,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutWhenPersistent() throws Exception { + factoryBean.setPersistent(true); assertNull(TestUtils.readField("dataPolicy", factoryBean)); @@ -411,6 +441,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingShortcut() throws Exception { + factoryBean.setShortcut(ClientRegionShortcut.CACHING_PROXY_OVERFLOW); assertNull(TestUtils.readField("dataPolicy", factoryBean)); @@ -420,6 +451,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingShortcutWhenNotPersistent() throws Exception { + factoryBean.setPersistent(false); factoryBean.setShortcut(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU); @@ -430,6 +462,7 @@ public class ClientRegionFactoryBeanTest { @Test(expected = IllegalArgumentException.class) public void testResolveClientRegionShortcutUsingShortcutWhenPersistent() throws Exception { + try { factoryBean.setPersistent(true); factoryBean.setShortcut(ClientRegionShortcut.CACHING_PROXY); @@ -448,6 +481,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingPersistentShortcut() throws Exception { + factoryBean.setShortcut(ClientRegionShortcut.LOCAL_PERSISTENT); assertNull(TestUtils.readField("dataPolicy", factoryBean)); @@ -457,6 +491,7 @@ public class ClientRegionFactoryBeanTest { @Test(expected = IllegalArgumentException.class) public void testResolveClientRegionShortcutUsingPersistentShortcutWhenNotPersistent() throws Exception { + try { factoryBean.setPersistent(false); factoryBean.setShortcut(ClientRegionShortcut.LOCAL_PERSISTENT); @@ -475,6 +510,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingPersistentShortcutWhenPersistent() throws Exception { + factoryBean.setPersistent(true); factoryBean.setShortcut(ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW); @@ -485,6 +521,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingEmptyDataPolicy() throws Exception { + factoryBean.setDataPolicy(DataPolicy.EMPTY); assertNull(TestUtils.readField("persistent", factoryBean)); @@ -494,6 +531,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingNormalDataPolicyWhenNotPersistent() throws Exception { + factoryBean.setDataPolicy(DataPolicy.NORMAL); factoryBean.setPersistent(false); @@ -504,6 +542,7 @@ public class ClientRegionFactoryBeanTest { @Test(expected = IllegalArgumentException.class) public void testResolveClientRegionShortcutUsingNormalDataPolicyWhenPersistent() throws Exception { + try { factoryBean.setDataPolicy(DataPolicy.NORMAL); factoryBean.setPersistent(true); @@ -521,6 +560,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingPersistentReplicateDataPolicy() throws Exception { + factoryBean.setDataPolicy(DataPolicy.PERSISTENT_REPLICATE); assertNull(TestUtils.readField("persistent", factoryBean)); @@ -530,6 +570,7 @@ public class ClientRegionFactoryBeanTest { @Test(expected = IllegalArgumentException.class) public void testResolveClientRegionShortcutUsingPersistentReplicateDataPolicyWhenNotPersistent() throws Exception { + try { factoryBean.setDataPolicy(DataPolicy.PERSISTENT_REPLICATE); factoryBean.setPersistent(false); @@ -547,6 +588,7 @@ public class ClientRegionFactoryBeanTest { @Test public void testResolveClientRegionShortcutUsingPersistentReplicateDataPolicyWhenPersistent() throws Exception { + factoryBean.setDataPolicy(DataPolicy.PERSISTENT_REPLICATE); factoryBean.setPersistent(true); @@ -562,7 +604,8 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("unchecked") public void destroyCallsRegionClose() throws Exception { - final Region mockRegion = mock(Region.class, "MockRegion"); + + Region mockRegion = mock(Region.class, "MockRegion"); RegionService mockRegionService = mock(RegionService.class, "MockRegionService"); @@ -596,7 +639,8 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("unchecked") public void destroyCallsRegionDestroy() throws Exception { - final Region mockRegion = mock(Region.class, "MockRegion"); + + Region mockRegion = mock(Region.class, "MockRegion"); RegionService mockRegionService = mock(RegionService.class, "MockRegionService"); @@ -631,7 +675,8 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("unchecked") public void destroyDoesNothingWhenClientRegionFactoryBeanCloseIsTrueButRegionServiceIsClosed() throws Exception { - final Region mockRegion = mock(Region.class, "MockRegion"); + + Region mockRegion = mock(Region.class, "MockRegion"); RegionService mockRegionService = mock(RegionService.class, "MockRegionService"); @@ -665,7 +710,8 @@ public class ClientRegionFactoryBeanTest { @Test @SuppressWarnings("unchecked") public void destroyDoesNothingWhenClientRegionFactoryBeanCloseAndDestroyAreFalse() throws Exception { - final Region mockRegion = mock(Region.class, "MockRegion"); + + Region mockRegion = mock(Region.class, "MockRegion"); ClientRegionFactoryBean clientRegionFactoryBean = new ClientRegionFactoryBean() { @Override public Region getObject() throws Exception { @@ -684,6 +730,7 @@ public class ClientRegionFactoryBeanTest { @Test public void destroyDoesNothingWhenRegionIsNull() throws Exception { + ClientRegionFactoryBean clientRegionFactoryBean = new ClientRegionFactoryBean() { @Override public Region getObject() throws Exception { return null; diff --git a/src/test/java/org/springframework/data/gemfire/client/ClientRegionIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/client/ClientRegionIntegrationTests.java new file mode 100644 index 00000000..734ce568 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/client/ClientRegionIntegrationTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2017 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.assertj.core.api.Assertions.assertThat; + +import javax.annotation.Resource; + +import org.apache.geode.cache.GemFireCache; +import org.apache.geode.cache.Region; +import org.apache.geode.cache.client.ClientRegionShortcut; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.context.annotation.Bean; +import org.springframework.data.gemfire.config.annotation.ClientCacheApplication; +import org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMocking; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Integration tests for client {@link Region Regions} created with SDG's {@link ClientRegionFactoryBean}. + * + * @author John Blum + * @see org.junit.Test + * @see org.apache.geode.cache.GemFireCache + * @see org.apache.geode.cache.Region + * @see org.springframework.data.gemfire.client.ClientRegionFactoryBean + * @since 2.0.0 + */ +@RunWith(SpringRunner.class) +@ContextConfiguration +public class ClientRegionIntegrationTests { + + @Resource(name = "Example") + private Region example; + + @Test + public void clientRegionUsesDefaultPoolWhenUnspecified() { + assertThat(this.example.getAttributes().getPoolName()).isNull(); + } + + @ClientCacheApplication + @EnableGemFireMocking + static class ClientRegionConfiguration { + + @Bean("Example") + public ClientRegionFactoryBean exampleRegion(GemFireCache gemfireCache) { + + ClientRegionFactoryBean exampleRegion = new ClientRegionFactoryBean<>(); + + exampleRegion.setCache(gemfireCache); + exampleRegion.setClose(false); + exampleRegion.setShortcut(ClientRegionShortcut.LOCAL); + + return exampleRegion; + } + } +} diff --git a/src/test/java/org/springframework/data/gemfire/client/ClientRegionWithCacheLoaderWriterTest.java b/src/test/java/org/springframework/data/gemfire/client/ClientRegionWithCacheLoaderWriterTest.java index 1af5525f..ebd3813f 100644 --- a/src/test/java/org/springframework/data/gemfire/client/ClientRegionWithCacheLoaderWriterTest.java +++ b/src/test/java/org/springframework/data/gemfire/client/ClientRegionWithCacheLoaderWriterTest.java @@ -37,7 +37,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit4.SpringRunner; /** * The ClientRegionWithCacheLoaderWriterTest class is a test suite of test cases testing the addition of CacheLoaders @@ -54,8 +54,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * @see org.apache.geode.cache.Region * @since 1.3.3 */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = "clientcache-with-region-using-cache-loader-writer.xml") +@RunWith(SpringRunner.class) +@ContextConfiguration @SuppressWarnings("unused") public class ClientRegionWithCacheLoaderWriterTest { @@ -69,6 +69,7 @@ public class ClientRegionWithCacheLoaderWriterTest { @Test public void testCacheLoaderWriter() { + assertNotNull(localAppData); assertEquals(0, localAppData.size()); @@ -107,7 +108,7 @@ public class ClientRegionWithCacheLoaderWriterTest { public static class LocalAppDataCacheWriter extends CacheWriterAdapter { - private static final Map data = new ConcurrentHashMap(REGION_SIZE); + private static final Map data = new ConcurrentHashMap<>(REGION_SIZE); @Override public void beforeUpdate(final EntryEvent event) throws CacheWriterException { diff --git a/src/test/java/org/springframework/data/gemfire/client/LocalOnlyClientCacheIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/client/LocalOnlyClientCacheIntegrationTest.java index c654fd96..b3ceaffa 100644 --- a/src/test/java/org/springframework/data/gemfire/client/LocalOnlyClientCacheIntegrationTest.java +++ b/src/test/java/org/springframework/data/gemfire/client/LocalOnlyClientCacheIntegrationTest.java @@ -42,7 +42,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.data.gemfire.RegionAttributesFactoryBean; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit4.SpringRunner; /** * The LocalOnlyClientCacheIntegrationTest class... @@ -50,7 +50,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * @author John Blum * @since 1.0.0 */ -@RunWith(SpringJUnit4ClassRunner.class) +@RunWith(SpringRunner.class) @ContextConfiguration @SuppressWarnings("unused") public class LocalOnlyClientCacheIntegrationTest { @@ -71,26 +71,27 @@ public class LocalOnlyClientCacheIntegrationTest { @Test public void getAndPutsAreSuccessful() { - assertThat(example.put(1l, "one"), is(nullValue())); - assertThat(example.put(2l, "two"), is(nullValue())); - assertThat(example.put(3l, "three"), is(nullValue())); - assertThat(example.get(1l), is(equalTo("one"))); - assertThat(example.get(2l), is(equalTo("two"))); - assertThat(example.get(3l), is(equalTo("three"))); - assertThat(example.get(0l), is(nullValue())); - assertThat(example.get(4l), is(nullValue())); + assertThat(example.put(1L, "one"), is(nullValue())); + assertThat(example.put(2L, "two"), is(nullValue())); + assertThat(example.put(3L, "three"), is(nullValue())); + assertThat(example.get(1L), is(equalTo("one"))); + assertThat(example.get(2L), is(equalTo("two"))); + assertThat(example.get(3L), is(equalTo("three"))); + assertThat(example.get(0L), is(nullValue())); + assertThat(example.get(4L), is(nullValue())); } @Configuration static class GemFireClientCacheConfiguration { @Bean - PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { + static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } @Bean Properties gemfireProperties(@Value("${spring.data.gemfire.log.level:warning}") String logLevel) { + Properties gemfireProperties = new Properties(); gemfireProperties.setProperty("name", LocalOnlyClientCacheIntegrationTest.class.getSimpleName()); @@ -101,6 +102,7 @@ public class LocalOnlyClientCacheIntegrationTest { @Bean ClientCacheFactoryBean gemfireCache(@Qualifier("gemfireProperties") Properties gemfireProperties) { + ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean(); gemfireCache.setClose(true); @@ -128,6 +130,7 @@ public class LocalOnlyClientCacheIntegrationTest { @Bean @SuppressWarnings("unchecked") RegionAttributesFactoryBean exampleRegionAttributes() { + RegionAttributesFactoryBean exampleRegionAttributes = new RegionAttributesFactoryBean(); exampleRegionAttributes.setKeyConstraint(Long.class); diff --git a/src/test/java/org/springframework/data/gemfire/client/MultipleClientCacheTest.java b/src/test/java/org/springframework/data/gemfire/client/MultipleClientCacheTest.java deleted file mode 100644 index 2e0a3360..00000000 --- a/src/test/java/org/springframework/data/gemfire/client/MultipleClientCacheTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2002-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.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; - -import org.apache.geode.cache.Cache; -import org.apache.geode.cache.Region; -import org.junit.Test; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -/** - * @author David Turanski - * @author John Blum - */ -public class MultipleClientCacheTest { - - @Test - public void testMultipleCaches() { - String configLocation = "/org/springframework/data/gemfire/client/client-cache-no-close.xml"; - - ConfigurableApplicationContext context1 = new ClassPathXmlApplicationContext(configLocation); - ConfigurableApplicationContext context2 = new ClassPathXmlApplicationContext(configLocation); - - Cache cache1 = context1.getBean(Cache.class); - Cache cache2 = context2.getBean(Cache.class); - - assertNotNull(cache1); - assertSame(cache1, cache2); - - Region region1 = context1.getBean(Region.class); - Region region2 = context2.getBean(Region.class); - - assertNotNull(region1); - assertSame(region1, region2); - assertFalse(cache1.isClosed()); - assertFalse(region1.isDestroyed()); - - context1.close(); - - assertFalse(cache1.isClosed()); - assertFalse("region was destroyed", region1.isDestroyed()); - } - -} diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegionsUnitTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegionsUnitTests.java index f89a2fc0..4a217f51 100644 --- a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegionsUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegionsUnitTests.java @@ -71,7 +71,6 @@ import org.springframework.data.gemfire.config.annotation.test.entities.LocalReg import org.springframework.data.gemfire.config.annotation.test.entities.NonEntity; import org.springframework.data.gemfire.config.annotation.test.entities.PartitionRegionEntity; import org.springframework.data.gemfire.config.annotation.test.entities.ReplicateRegionEntity; -import org.springframework.data.gemfire.config.xml.GemfireConstants; import org.springframework.data.gemfire.mapping.annotation.ClientRegion; import org.springframework.data.gemfire.mapping.annotation.LocalRegion; import org.springframework.data.gemfire.mapping.annotation.PartitionRegion; @@ -193,20 +192,21 @@ public class EnableEntityDefinedRegionsUnitTests { @Test @SuppressWarnings("unchecked") public void entityClientRegionsDefined() { + applicationContext = newApplicationContext(ClientPersistentEntitiesConfiguration.class); Region sessions = applicationContext.getBean("Sessions", Region.class); assertRegion(sessions, "Sessions", String.class, ClientRegionEntity.class); assertRegionAttributes(sessions.getAttributes(), DataPolicy.NORMAL, null, true, - false, GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME, null); + false, null, null); Region genericRegionEntity = applicationContext.getBean("GenericRegionEntity", Region.class); assertRegion(genericRegionEntity, "GenericRegionEntity", Long.class, GenericRegionEntity.class); assertRegionAttributes(genericRegionEntity.getAttributes(), DataPolicy.EMPTY, null, - true, false, GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME, null); + true, false, null, null); assertThat(applicationContext.containsBean("CollocatedPartitionRegionEntity")).isFalse(); assertThat(applicationContext.containsBean("ContactEvents")).isFalse(); diff --git a/src/test/java/org/springframework/data/gemfire/config/xml/ClientRegionNamespaceTest.java b/src/test/java/org/springframework/data/gemfire/config/xml/ClientRegionNamespaceTest.java index 5727fb89..4b3ce6a9 100644 --- a/src/test/java/org/springframework/data/gemfire/config/xml/ClientRegionNamespaceTest.java +++ b/src/test/java/org/springframework/data/gemfire/config/xml/ClientRegionNamespaceTest.java @@ -236,6 +236,7 @@ public class ClientRegionNamespaceTest { @Test @SuppressWarnings("unchecked") public void testClientRegionWithAttributes() { + assertTrue(applicationContext.containsBean("client-with-attributes")); Region clientRegion = applicationContext.getBean("client-with-attributes", Region.class); diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java b/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java index 779fcd16..3d1eddf9 100644 --- a/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java +++ b/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java @@ -47,30 +47,40 @@ import java.util.Properties; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; +import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; import org.apache.geode.cache.Cache; import org.apache.geode.cache.CacheFactory; +import org.apache.geode.cache.CustomExpiry; +import org.apache.geode.cache.DataPolicy; import org.apache.geode.cache.DiskStore; import org.apache.geode.cache.DiskStoreFactory; +import org.apache.geode.cache.EvictionAttributes; +import org.apache.geode.cache.ExpirationAction; +import org.apache.geode.cache.ExpirationAttributes; import org.apache.geode.cache.GemFireCache; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; import org.apache.geode.cache.RegionService; import org.apache.geode.cache.client.ClientCache; import org.apache.geode.cache.client.ClientCacheFactory; +import org.apache.geode.cache.client.ClientRegionFactory; +import org.apache.geode.cache.client.ClientRegionShortcut; import org.apache.geode.cache.client.Pool; import org.apache.geode.cache.client.PoolFactory; import org.apache.geode.cache.control.ResourceManager; import org.apache.geode.cache.server.CacheServer; import org.apache.geode.cache.server.ClientSubscriptionConfig; +import org.apache.geode.compression.Compressor; import org.apache.geode.distributed.DistributedSystem; import org.apache.geode.pdx.PdxSerializer; +import org.mockito.ArgumentMatchers; import org.springframework.data.gemfire.server.SubscriptionEvictionPolicy; import org.springframework.data.gemfire.test.support.FileSystemUtils; @@ -97,12 +107,25 @@ import org.springframework.data.gemfire.test.support.FileSystemUtils; @SuppressWarnings("unused") public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { + private static final boolean DEFAULT_USE_SINGLETON_CACHE = false; + + private static final AtomicReference singletonCache = new AtomicReference<>(null); + private static final Map diskStores = new ConcurrentHashMap<>(); private static final Map> regions = new ConcurrentHashMap<>(); private static final Map> regionAttributes = new ConcurrentHashMap<>(); + private static final String REPEATING_REGION_SEPARATOR = Region.SEPARATOR + "{2,}"; + + public static void destroy() { + singletonCache.set(null); + diskStores.clear(); + regions.clear(); + regionAttributes.clear(); + } + /* (non-Javadoc) */ private static boolean isRootRegion(Region region) { return isRootRegion(region.getFullPath()); @@ -113,9 +136,38 @@ public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { return (regionPath.lastIndexOf(Region.SEPARATOR) <= 0); } + /* (non-Javadoc) */ + private static String normalizeRegionPath(String regionPath) { + + regionPath = regionPath.replaceAll(REPEATING_REGION_SEPARATOR, Region.SEPARATOR); + regionPath = regionPath.endsWith(Region.SEPARATOR) + ? regionPath.substring(0, regionPath.length() - 1) : regionPath; + + return regionPath; + } + + /* (non-Javadoc) */ + private static String toRegionName(String regionName) { + + return Optional.ofNullable(regionName) + .map(String::trim) + .map(it -> { + int lastIndexOfRegionSeparator = it.lastIndexOf(Region.SEPARATOR); + return lastIndexOfRegionSeparator < 0 ? it : it.substring(lastIndexOfRegionSeparator); + }) + .filter(it -> !it.isEmpty()) + .orElseThrow(() -> newIllegalArgumentException("Region name [%s] is required", regionName)); + } + + /* (non-Javadoc) */ private static String toRegionPath(String regionPath) { - return (StringUtils.startsWith(regionPath, Region.SEPARATOR) ? regionPath - : String.format("%1$s%2$s", Region.SEPARATOR, regionPath)); + + return Optional.ofNullable(regionPath) + .map(String::trim) + .map(it -> it.startsWith(Region.SEPARATOR) ? it : String.format("%1$s%2$s", Region.SEPARATOR, it)) + .map(MockGemFireObjectsSupport::normalizeRegionPath) + .filter(it -> !it.isEmpty()) + .orElseThrow(() -> newIllegalArgumentException("Region path [%s] is required", regionPath)); } /* (non-Javadoc) */ @@ -183,18 +235,8 @@ public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { when(mockRegionService.createPdxInstanceFactory(anyString())) .thenThrow(newUnsupportedOperationException(NOT_SUPPORTED)); - when(mockRegionService.rootRegions()).thenAnswer(invocation -> { - - Set> rootRegions = new HashSet<>(); - - for (Region region : regions.values()) { - if (isRootRegion(region)) { - rootRegions.add(region); - } - } - - return rootRegions; - }); + when(mockRegionService.rootRegions()).thenAnswer(invocation -> + regions.values().stream().filter(MockGemFireObjectsSupport::isRootRegion).collect(Collectors.toSet())); return mockRegionService; } @@ -203,7 +245,9 @@ public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { ClientCache mockClientCache = mock(ClientCache.class); - return mockCacheApi(mockClientCache); + doAnswer(newVoidAnswer(invocation -> mockClientCache.close())).when(mockClientCache).close(anyBoolean()); + + return mockClientRegionFactory(mockCacheApi(mockClientCache)); } public static GemFireCache mockGemFireCache() { @@ -320,6 +364,131 @@ public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { return mockCacheServer; } + @SuppressWarnings("unchecked") + public static ClientCache mockClientRegionFactory(ClientCache mockClientCache) { + + ClientRegionFactory mockClientRegionFactory = + mock(ClientRegionFactory.class, mockObjectIdentifier("MockClientRegionFactory")); + + when(mockClientCache.createClientRegionFactory(any(ClientRegionShortcut.class))) + .thenReturn(mockClientRegionFactory); + + ExpirationAttributes DEFAULT_EXPIRATION_ATTRIBUTES = + new ExpirationAttributes(0, ExpirationAction.INVALIDATE); + + AtomicBoolean cloningEnabled = new AtomicBoolean(false); + AtomicBoolean concurrencyChecksEnabled = new AtomicBoolean(false); + AtomicBoolean diskSynchronous = new AtomicBoolean(true); + AtomicBoolean statisticsEnabled = new AtomicBoolean(false); + + AtomicInteger concurrencyLevel = new AtomicInteger(16); + AtomicInteger initialCapacity = new AtomicInteger(16); + + AtomicReference compressor = new AtomicReference<>(null); + AtomicReference> customEntryIdleTimeout = new AtomicReference<>(null); + AtomicReference> customEntryTimeToLive = new AtomicReference<>(null); + AtomicReference diskStoreName = new AtomicReference<>(null); + AtomicReference entryIdleTimeout = new AtomicReference<>(DEFAULT_EXPIRATION_ATTRIBUTES); + AtomicReference entryTimeToLive = new AtomicReference<>(DEFAULT_EXPIRATION_ATTRIBUTES); + AtomicReference evictionAttributes = + new AtomicReference<>(EvictionAttributes.createLRUEntryAttributes()); + AtomicReference> keyConstraint = new AtomicReference<>(); + AtomicReference loadFactor = new AtomicReference<>(0.75f); + AtomicReference poolName = new AtomicReference<>(null); + AtomicReference regionIdleTimeout = new AtomicReference<>(DEFAULT_EXPIRATION_ATTRIBUTES); + AtomicReference regionTimeToLive = new AtomicReference<>(DEFAULT_EXPIRATION_ATTRIBUTES); + AtomicReference> valueConstraint = new AtomicReference<>(); + + when(mockClientRegionFactory.setCloningEnabled(anyBoolean())) + .thenAnswer(newSetter(cloningEnabled, mockClientRegionFactory)); + + when(mockClientRegionFactory.setCompressor(any(Compressor.class))) + .thenAnswer(newSetter(compressor, mockClientRegionFactory)); + + doAnswer(newSetter(concurrencyChecksEnabled, mockClientRegionFactory)) + .when(mockClientRegionFactory).setConcurrencyChecksEnabled(anyBoolean()); + + when(mockClientRegionFactory.setConcurrencyLevel(anyInt())) + .thenAnswer(newSetter(concurrencyLevel, mockClientRegionFactory)); + + when(mockClientRegionFactory.setCustomEntryIdleTimeout(any(CustomExpiry.class))) + .thenAnswer(newSetter(customEntryIdleTimeout, mockClientRegionFactory)); + + when(mockClientRegionFactory.setCustomEntryTimeToLive(any(CustomExpiry.class))) + .thenAnswer(newSetter(customEntryTimeToLive, mockClientRegionFactory)); + + when(mockClientRegionFactory.setDiskStoreName(anyString())) + .thenAnswer(newSetter(diskStoreName, mockClientRegionFactory)); + + when(mockClientRegionFactory.setDiskSynchronous(anyBoolean())) + .thenAnswer(newSetter(diskSynchronous, mockClientRegionFactory)); + + when(mockClientRegionFactory.setEntryIdleTimeout(any(ExpirationAttributes.class))) + .thenAnswer(newSetter(entryIdleTimeout, mockClientRegionFactory)); + + when(mockClientRegionFactory.setEntryTimeToLive(any(ExpirationAttributes.class))) + .thenAnswer(newSetter(entryTimeToLive, mockClientRegionFactory)); + + when(mockClientRegionFactory.setEvictionAttributes(any(EvictionAttributes.class))) + .thenAnswer(newSetter(evictionAttributes, mockClientRegionFactory)); + + when(mockClientRegionFactory.setInitialCapacity(anyInt())) + .thenAnswer(newSetter(initialCapacity, mockClientRegionFactory)); + + when(mockClientRegionFactory.setKeyConstraint(any(Class.class))) + .thenAnswer(newSetter(keyConstraint, mockClientRegionFactory)); + + when(mockClientRegionFactory.setLoadFactor(anyFloat())) + .thenAnswer(newSetter(loadFactor, mockClientRegionFactory)); + + when(mockClientRegionFactory.setPoolName(anyString())) + .thenAnswer(newSetter(poolName, mockClientRegionFactory)); + + when(mockClientRegionFactory.setRegionIdleTimeout(any(ExpirationAttributes.class))) + .thenAnswer(newSetter(regionIdleTimeout, mockClientRegionFactory)); + + when(mockClientRegionFactory.setRegionTimeToLive(any(ExpirationAttributes.class))) + .thenAnswer(newSetter(regionTimeToLive, mockClientRegionFactory)); + + when(mockClientRegionFactory.setStatisticsEnabled(anyBoolean())) + .thenAnswer(newSetter(statisticsEnabled, mockClientRegionFactory)); + + when(mockClientRegionFactory.setValueConstraint(any(Class.class))) + .thenAnswer(newSetter(valueConstraint, mockClientRegionFactory)); + + RegionAttributes mockRegionAttributes = + mock(RegionAttributes.class, mockObjectIdentifier("MockRegionAttributes")); + + when(mockRegionAttributes.getCloningEnabled()).thenAnswer(newGetter(cloningEnabled)); + when(mockRegionAttributes.getCompressor()).thenAnswer(newGetter(compressor)); + when(mockRegionAttributes.getConcurrencyChecksEnabled()).thenAnswer(newGetter(concurrencyChecksEnabled)); + when(mockRegionAttributes.getConcurrencyLevel()).thenAnswer(newGetter(concurrencyLevel)); + when(mockRegionAttributes.getCustomEntryIdleTimeout()).thenAnswer(newGetter(customEntryIdleTimeout)); + when(mockRegionAttributes.getCustomEntryTimeToLive()).thenAnswer(newGetter(customEntryTimeToLive)); + when(mockRegionAttributes.getDataPolicy()).thenReturn(DataPolicy.NORMAL); + when(mockRegionAttributes.getDiskStoreName()).thenAnswer(newGetter(diskStoreName)); + when(mockRegionAttributes.isDiskSynchronous()).thenAnswer(newGetter(diskSynchronous)); + when(mockRegionAttributes.getEntryIdleTimeout()).thenAnswer(newGetter(entryIdleTimeout)); + when(mockRegionAttributes.getEntryTimeToLive()).thenAnswer(newGetter(entryTimeToLive)); + when(mockRegionAttributes.getEvictionAttributes()).thenAnswer(newGetter(evictionAttributes)); + when(mockRegionAttributes.getInitialCapacity()).thenAnswer(newGetter(initialCapacity)); + when(mockRegionAttributes.getKeyConstraint()).thenAnswer(newGetter(keyConstraint)); + when(mockRegionAttributes.getLoadFactor()).thenAnswer(newGetter(loadFactor)); + when(mockRegionAttributes.getPoolName()).thenAnswer(newGetter(poolName)); + when(mockRegionAttributes.getRegionIdleTimeout()).thenAnswer(newGetter(regionIdleTimeout)); + when(mockRegionAttributes.getRegionTimeToLive()).thenAnswer(newGetter(regionTimeToLive)); + when(mockRegionAttributes.getStatisticsEnabled()).thenAnswer(newGetter(statisticsEnabled)); + when(mockRegionAttributes.getValueConstraint()).thenAnswer(newGetter(valueConstraint)); + + when(mockClientRegionFactory.create(anyString())).thenAnswer(invocation -> + mockRegion(mockClientCache, invocation.getArgument(0), mockRegionAttributes)); + + when(mockClientRegionFactory.createSubregion(any(Region.class), anyString())).thenAnswer(invocation -> + mockSubRegion(invocation.getArgument(0), invocation.getArgument(1), mockRegionAttributes)); + + return mockClientCache; + } + public static ClientSubscriptionConfig mockClientSubscriptionConfig() { ClientSubscriptionConfig mockClientSubscriptionConfig = mock(ClientSubscriptionConfig.class); @@ -604,7 +773,66 @@ public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { }); return mockPoolFactory; + } + @SuppressWarnings("unchecked") + public static Region mockRegion(RegionService regionService, String name, + RegionAttributes regionAttributes) { + + Map data = new ConcurrentHashMap<>(); + + Region mockRegion = mock(Region.class, name); + + Set> subRegions = new CopyOnWriteArraySet<>(); + + when(mockRegion.getAttributes()).thenReturn(regionAttributes); + when(mockRegion.getFullPath()).thenReturn(toRegionPath(name)); + when(mockRegion.getName()).thenReturn(toRegionName(name)); + when(mockRegion.getRegionService()).thenReturn(regionService); + + when(mockRegion.getSubregion(anyString())).thenAnswer(invocation -> { + + String subRegionPath = toRegionPath(invocation.getArgument(0)); + String subRegionFullPath = String.format("%1$s%2$s", mockRegion.getFullPath(), subRegionPath); + + return regions.get(subRegionFullPath); + }); + + when(mockRegion.get(ArgumentMatchers.any())).thenAnswer(invocation -> + data.get(invocation.getArgument(0))); + + when(mockRegion.getEntry(ArgumentMatchers.any())).thenAnswer(invocation -> + data.entrySet().stream().filter(entry -> entry.getKey().equals(invocation.getArgument(0))).findFirst()); + + when(mockRegion.put(any(), any())).thenAnswer(invocation -> + data.put(invocation.getArgument(0), invocation.getArgument(1))); + + when(mockRegion.size()).thenAnswer(invocation -> data.size()); + + when(mockRegion.subregions(anyBoolean())).thenAnswer(invocation -> { + + boolean recursive = invocation.getArgument(0); + + return recursive ? subRegions.stream() + .flatMap(subRegion -> subRegion.subregions(true).stream()).collect(Collectors.toSet()) + : subRegions; + }); + + regions.put(mockRegion.getFullPath(), (Region) mockRegion); + + return mockRegion; + } + + public static Region mockSubRegion(Region parent, String name, + RegionAttributes regionAttributes) { + + String subRegionName = String.format("%1$s%2$s", parent.getFullPath(), toRegionPath(name)); + + Region mockSubRegion = mockRegion(parent.getRegionService(), subRegionName, regionAttributes); + + parent.subregions(false).add(mockSubRegion); + + return mockSubRegion; } public static ResourceManager mockResourceManager() { @@ -644,195 +872,235 @@ public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { return mockResourceManager; } + private static T rememberMockedGemFireCache(T mockedGemFireCache, + boolean useSingletonCache) { + + return Optional.ofNullable(mockedGemFireCache) + .map(it -> { + if (useSingletonCache) { + singletonCache.compareAndSet(null, mockedGemFireCache); + } + + return mockedGemFireCache; + }) + .orElseThrow(() -> newIllegalArgumentException("GemFireCache is required")); + } + + @SuppressWarnings("unchecked") + private static Optional resolveMockedGemFireCache(boolean useSingletonCache) { + return Optional.ofNullable((T) singletonCache.get()).filter(it -> useSingletonCache); + } + public static CacheFactory spyOn(CacheFactory cacheFactory) { + return spyOn(cacheFactory, DEFAULT_USE_SINGLETON_CACHE); + } + + public static CacheFactory spyOn(CacheFactory cacheFactory, boolean useSingletonCache) { CacheFactory cacheFactorySpy = spy(cacheFactory); - Cache mockCache = mockPeerCache(); + Cache resolvedMockCache = MockGemFireObjectsSupport.resolveMockedGemFireCache(useSingletonCache) + .orElseGet(() -> { - AtomicBoolean pdxIgnoreUnreadFields = new AtomicBoolean(false); - AtomicBoolean pdxPersistent = new AtomicBoolean(false); - AtomicBoolean pdxReadSerialized = new AtomicBoolean(false); + Cache mockCache = mockPeerCache(); - AtomicReference pdxDiskStoreName = new AtomicReference<>(null); - AtomicReference pdxSerializer = new AtomicReference<>(null); + AtomicBoolean pdxIgnoreUnreadFields = new AtomicBoolean(false); + AtomicBoolean pdxPersistent = new AtomicBoolean(false); + AtomicBoolean pdxReadSerialized = new AtomicBoolean(false); - doAnswer(newSetter(pdxDiskStoreName, cacheFactorySpy)) - .when(cacheFactorySpy).setPdxDiskStore(anyString()); + AtomicReference pdxDiskStoreName = new AtomicReference<>(null); + AtomicReference pdxSerializer = new AtomicReference<>(null); - doAnswer(newSetter(pdxIgnoreUnreadFields, cacheFactorySpy)) - .when(cacheFactorySpy).setPdxIgnoreUnreadFields(anyBoolean()); + doAnswer(newSetter(pdxDiskStoreName, cacheFactorySpy)) + .when(cacheFactorySpy).setPdxDiskStore(anyString()); - doAnswer(newSetter(pdxPersistent, cacheFactorySpy)) - .when(cacheFactorySpy).setPdxPersistent(anyBoolean()); + doAnswer(newSetter(pdxIgnoreUnreadFields, cacheFactorySpy)) + .when(cacheFactorySpy).setPdxIgnoreUnreadFields(anyBoolean()); - doAnswer(newSetter(pdxReadSerialized, cacheFactorySpy)) - .when(cacheFactorySpy).setPdxReadSerialized(anyBoolean()); + doAnswer(newSetter(pdxPersistent, cacheFactorySpy)) + .when(cacheFactorySpy).setPdxPersistent(anyBoolean()); - doAnswer(newSetter(pdxSerializer, cacheFactorySpy)) - .when(cacheFactorySpy).setPdxSerializer(any(PdxSerializer.class)); + doAnswer(newSetter(pdxReadSerialized, cacheFactorySpy)) + .when(cacheFactorySpy).setPdxReadSerialized(anyBoolean()); - doReturn(mockCache).when(cacheFactorySpy).create(); + doAnswer(newSetter(pdxSerializer, cacheFactorySpy)) + .when(cacheFactorySpy).setPdxSerializer(any(PdxSerializer.class)); - when(mockCache.getPdxDiskStore()).thenAnswer(newGetter(pdxDiskStoreName)); - when(mockCache.getPdxIgnoreUnreadFields()).thenAnswer(newGetter(pdxIgnoreUnreadFields)); - when(mockCache.getPdxPersistent()).thenAnswer(newGetter(pdxPersistent)); - when(mockCache.getPdxReadSerialized()).thenAnswer(newGetter(pdxReadSerialized)); - when(mockCache.getPdxSerializer()).thenAnswer(newGetter(pdxSerializer)); + when(mockCache.getPdxDiskStore()).thenAnswer(newGetter(pdxDiskStoreName)); + when(mockCache.getPdxIgnoreUnreadFields()).thenAnswer(newGetter(pdxIgnoreUnreadFields)); + when(mockCache.getPdxPersistent()).thenAnswer(newGetter(pdxPersistent)); + when(mockCache.getPdxReadSerialized()).thenAnswer(newGetter(pdxReadSerialized)); + when(mockCache.getPdxSerializer()).thenAnswer(newGetter(pdxSerializer)); + + return mockCache; + }); + + doReturn(rememberMockedGemFireCache(resolvedMockCache, useSingletonCache)).when(cacheFactorySpy).create(); return cacheFactorySpy; } public static ClientCacheFactory spyOn(ClientCacheFactory clientCacheFactory) { + return spyOn(clientCacheFactory, DEFAULT_USE_SINGLETON_CACHE); + } + + public static ClientCacheFactory spyOn(ClientCacheFactory clientCacheFactory, boolean useSingletonCache) { ClientCacheFactory clientCacheFactorySpy = spy(clientCacheFactory); - ClientCache mockClientCache = mockClientCache(); + ClientCache resolvedMockedClientCache = + MockGemFireObjectsSupport.resolveMockedGemFireCache(useSingletonCache).orElseGet(() -> { - AtomicBoolean pdxIgnoreUnreadFields = new AtomicBoolean(false); - AtomicBoolean pdxPersistent = new AtomicBoolean(false); - AtomicBoolean pdxReadSerialized = new AtomicBoolean(false); + ClientCache mockClientCache = mockClientCache(); - AtomicReference pdxDiskStoreName = new AtomicReference<>(null); - AtomicReference pdxSerializer = new AtomicReference<>(null); - AtomicReference defaultPool = new AtomicReference<>(null); + AtomicBoolean pdxIgnoreUnreadFields = new AtomicBoolean(false); + AtomicBoolean pdxPersistent = new AtomicBoolean(false); + AtomicBoolean pdxReadSerialized = new AtomicBoolean(false); - doAnswer(newSetter(pdxDiskStoreName, clientCacheFactorySpy)) - .when(clientCacheFactorySpy).setPdxDiskStore(anyString()); + AtomicReference pdxDiskStoreName = new AtomicReference<>(null); + AtomicReference pdxSerializer = new AtomicReference<>(null); + AtomicReference defaultPool = new AtomicReference<>(null); - doAnswer(newSetter(pdxIgnoreUnreadFields, clientCacheFactorySpy)) - .when(clientCacheFactorySpy).setPdxIgnoreUnreadFields(anyBoolean()); + doAnswer(newSetter(pdxDiskStoreName, clientCacheFactorySpy)) + .when(clientCacheFactorySpy).setPdxDiskStore(anyString()); - doAnswer(newSetter(pdxPersistent, clientCacheFactorySpy)) - .when(clientCacheFactorySpy).setPdxPersistent(anyBoolean()); + doAnswer(newSetter(pdxIgnoreUnreadFields, clientCacheFactorySpy)) + .when(clientCacheFactorySpy).setPdxIgnoreUnreadFields(anyBoolean()); - doAnswer(newSetter(pdxReadSerialized, clientCacheFactorySpy)) - .when(clientCacheFactorySpy).setPdxReadSerialized(anyBoolean()); + doAnswer(newSetter(pdxPersistent, clientCacheFactorySpy)) + .when(clientCacheFactorySpy).setPdxPersistent(anyBoolean()); - doAnswer(newSetter(pdxSerializer, clientCacheFactorySpy)) - .when(clientCacheFactorySpy).setPdxSerializer(any(PdxSerializer.class)); + doAnswer(newSetter(pdxReadSerialized, clientCacheFactorySpy)) + .when(clientCacheFactorySpy).setPdxReadSerialized(anyBoolean()); - PoolFactory mockPoolFactory = mockPoolFactory(); + doAnswer(newSetter(pdxSerializer, clientCacheFactorySpy)) + .when(clientCacheFactorySpy).setPdxSerializer(any(PdxSerializer.class)); - doAnswer(invocation -> { - mockPoolFactory.addLocator(invocation.getArgument(0), invocation.getArgument(1)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).addPoolLocator(anyString(), anyInt()); + PoolFactory mockPoolFactory = mockPoolFactory(); - doAnswer(invocation -> { - mockPoolFactory.addServer(invocation.getArgument(0), invocation.getArgument(1)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).addPoolServer(anyString(), anyInt()); + doAnswer(invocation -> { + mockPoolFactory.addLocator(invocation.getArgument(0), invocation.getArgument(1)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).addPoolLocator(anyString(), anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setFreeConnectionTimeout(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolFreeConnectionTimeout(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.addServer(invocation.getArgument(0), invocation.getArgument(1)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).addPoolServer(anyString(), anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setIdleTimeout(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolIdleTimeout(anyLong()); + doAnswer(invocation -> { + mockPoolFactory.setFreeConnectionTimeout(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolFreeConnectionTimeout(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setLoadConditioningInterval(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolLoadConditioningInterval(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setIdleTimeout(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolIdleTimeout(anyLong()); - doAnswer(invocation -> { - mockPoolFactory.setMaxConnections(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolMaxConnections(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setLoadConditioningInterval(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolLoadConditioningInterval(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setMinConnections(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolMinConnections(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setMaxConnections(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolMaxConnections(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setMultiuserAuthentication(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolMultiuserAuthentication(anyBoolean()); + doAnswer(invocation -> { + mockPoolFactory.setMinConnections(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolMinConnections(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setPingInterval(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolPingInterval(anyLong()); + doAnswer(invocation -> { + mockPoolFactory.setMultiuserAuthentication(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolMultiuserAuthentication(anyBoolean()); - doAnswer(invocation -> { - mockPoolFactory.setPRSingleHopEnabled(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolPRSingleHopEnabled(anyBoolean()); + doAnswer(invocation -> { + mockPoolFactory.setPingInterval(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolPingInterval(anyLong()); - doAnswer(invocation -> { - mockPoolFactory.setReadTimeout(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolReadTimeout(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setPRSingleHopEnabled(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolPRSingleHopEnabled(anyBoolean()); - doAnswer(invocation -> { - mockPoolFactory.setRetryAttempts(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolRetryAttempts(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setReadTimeout(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolReadTimeout(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setServerGroup(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolServerGroup(anyString()); + doAnswer(invocation -> { + mockPoolFactory.setRetryAttempts(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolRetryAttempts(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setSocketBufferSize(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolSocketBufferSize(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setServerGroup(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolServerGroup(anyString()); - doAnswer(invocation -> { - mockPoolFactory.setStatisticInterval(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolStatisticInterval(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setSocketBufferSize(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolSocketBufferSize(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setSubscriptionAckInterval(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolSubscriptionAckInterval(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setStatisticInterval(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolStatisticInterval(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setSubscriptionEnabled(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolSubscriptionEnabled(anyBoolean()); + doAnswer(invocation -> { + mockPoolFactory.setSubscriptionAckInterval(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolSubscriptionAckInterval(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setSubscriptionMessageTrackingTimeout(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolSubscriptionMessageTrackingTimeout(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setSubscriptionEnabled(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolSubscriptionEnabled(anyBoolean()); - doAnswer(invocation -> { - mockPoolFactory.setSubscriptionRedundancy(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolSubscriptionRedundancy(anyInt()); + doAnswer(invocation -> { + mockPoolFactory.setSubscriptionMessageTrackingTimeout(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolSubscriptionMessageTrackingTimeout(anyInt()); - doAnswer(invocation -> { - mockPoolFactory.setThreadLocalConnections(invocation.getArgument(0)); - return clientCacheFactorySpy; - }).when(clientCacheFactorySpy).setPoolThreadLocalConnections(anyBoolean()); + doAnswer(invocation -> { + mockPoolFactory.setSubscriptionRedundancy(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolSubscriptionRedundancy(anyInt()); - doReturn(mockClientCache).when(clientCacheFactorySpy).create(); + doAnswer(invocation -> { + mockPoolFactory.setThreadLocalConnections(invocation.getArgument(0)); + return clientCacheFactorySpy; + }).when(clientCacheFactorySpy).setPoolThreadLocalConnections(anyBoolean()); - when(mockClientCache.getCurrentServers()).thenAnswer(invocation -> - Collections.unmodifiableSet(new HashSet<>(defaultPool.get().getServers()))); + when(mockClientCache.getCurrentServers()).thenAnswer(invocation -> + Collections.unmodifiableSet(new HashSet<>(defaultPool.get().getServers()))); - when(mockClientCache.getDefaultPool()).thenAnswer(invocation -> { + when(mockClientCache.getDefaultPool()).thenAnswer(invocation -> { - if (defaultPool.get() == null) { - defaultPool.set(mockPoolFactory.create("DEFAULT")); - } + if (defaultPool.get() == null) { + defaultPool.set(mockPoolFactory.create("DEFAULT")); + } - return defaultPool.get(); - }); + return defaultPool.get(); + }); - when(mockClientCache.getPdxDiskStore()).thenAnswer(newGetter(pdxDiskStoreName)); - when(mockClientCache.getPdxIgnoreUnreadFields()).thenAnswer(newGetter(pdxIgnoreUnreadFields)); - when(mockClientCache.getPdxPersistent()).thenAnswer(newGetter(pdxPersistent)); - when(mockClientCache.getPdxReadSerialized()).thenAnswer(newGetter(pdxReadSerialized)); - when(mockClientCache.getPdxSerializer()).thenAnswer(newGetter(pdxSerializer)); + when(mockClientCache.getPdxDiskStore()).thenAnswer(newGetter(pdxDiskStoreName)); + when(mockClientCache.getPdxIgnoreUnreadFields()).thenAnswer(newGetter(pdxIgnoreUnreadFields)); + when(mockClientCache.getPdxPersistent()).thenAnswer(newGetter(pdxPersistent)); + when(mockClientCache.getPdxReadSerialized()).thenAnswer(newGetter(pdxReadSerialized)); + when(mockClientCache.getPdxSerializer()).thenAnswer(newGetter(pdxSerializer)); + + return mockClientCache; + }); + + doReturn(rememberMockedGemFireCache(resolvedMockedClientCache, useSingletonCache)) + .when(clientCacheFactorySpy).create(); return clientCacheFactorySpy; } diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/MockObjectsSupport.java b/src/test/java/org/springframework/data/gemfire/test/mock/MockObjectsSupport.java index 1f57fc89..c21c1aa5 100644 --- a/src/test/java/org/springframework/data/gemfire/test/mock/MockObjectsSupport.java +++ b/src/test/java/org/springframework/data/gemfire/test/mock/MockObjectsSupport.java @@ -17,13 +17,18 @@ package org.springframework.data.gemfire.test.mock; import java.util.Map; +import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; +import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import org.springframework.util.StringUtils; /** * The {@link MockObjectsSupport} class is an abstract base class encapsulating common operations and utilities @@ -35,6 +40,19 @@ import org.mockito.stubbing.Answer; @SuppressWarnings("unused") public abstract class MockObjectsSupport { + private static final AtomicLong mockObjectIdentifier = new AtomicLong(0L); + + private static final String DEFAULT_MOCK_OBJECT_NAME = "MockObject"; + + protected static String mockObjectIdentifier() { + return mockObjectIdentifier(DEFAULT_MOCK_OBJECT_NAME); + } + + protected static String mockObjectIdentifier(String mockObjectName) { + return String.format("%s%d", Optional.ofNullable(mockObjectName).filter(StringUtils::hasText) + .orElse(DEFAULT_MOCK_OBJECT_NAME), mockObjectIdentifier.incrementAndGet()); + } + /* (non-Javadoc) */ protected static Answer newGetter(AtomicBoolean returnValue) { return invocation -> returnValue.get(); @@ -60,6 +78,16 @@ public abstract class MockObjectsSupport { return invocation -> converter.apply(returnValue.get()); } + /* (non-Javadoc) */ + protected static Answer newGetter(Supplier returnValue) { + return invocation -> returnValue.get(); + } + + /* (non-Javadoc) */ + protected static Answer newGetter(Supplier returnValue, Function converter) { + return invocation -> converter.apply(returnValue.get()); + } + /* (non-Javadoc) */ protected static Answer newSetter(AtomicBoolean argument, R returnValue) { return invocation -> { @@ -139,4 +167,12 @@ public abstract class MockObjectsSupport { return returnValue; }; } + + /* (non-Javadoc) */ + protected static Answer newVoidAnswer(Consumer methodInvocation) { + return invocation -> { + methodInvocation.accept(invocation); + return null; + }; + } } diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/annotation/EnableGemFireMocking.java b/src/test/java/org/springframework/data/gemfire/test/mock/annotation/EnableGemFireMocking.java index bdf9499f..80f70bb8 100644 --- a/src/test/java/org/springframework/data/gemfire/test/mock/annotation/EnableGemFireMocking.java +++ b/src/test/java/org/springframework/data/gemfire/test/mock/annotation/EnableGemFireMocking.java @@ -23,6 +23,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.apache.geode.cache.GemFireCache; import org.springframework.context.annotation.Import; /** @@ -33,6 +34,7 @@ import org.springframework.context.annotation.Import; * @see java.lang.annotation.Inherited * @see java.lang.annotation.Retention * @see java.lang.annotation.Target + * @see org.apache.geode.cache.GemFireCache * @see org.springframework.context.annotation.Import * @since 2.0.0 */ @@ -44,4 +46,13 @@ import org.springframework.context.annotation.Import; @SuppressWarnings("unused") public @interface EnableGemFireMocking { + /** + * Determines whether the mock {@link GemFireCache} created for Unit Tests is a Singleton. + * + * Defaults to {@literal false}. + * + * @return a boolean value indicating whether the mock {@link GemFireCache} created for Unit Tests is a Singleton. + */ + boolean useSingletonCache() default false; + } diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireMockingConfiguration.java b/src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireMockingConfiguration.java index 34f42dfc..78d4e798 100644 --- a/src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireMockingConfiguration.java +++ b/src/test/java/org/springframework/data/gemfire/test/mock/annotation/GemFireMockingConfiguration.java @@ -16,9 +16,17 @@ package org.springframework.data.gemfire.test.mock.annotation; +import java.lang.annotation.Annotation; + import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportAware; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.data.gemfire.test.mock.MockGemFireObjectsSupport; import org.springframework.data.gemfire.test.mock.config.MockGemFireObjectsBeanPostProcessor; /** @@ -26,16 +34,64 @@ import org.springframework.data.gemfire.test.mock.config.MockGemFireObjectsBeanP * containing bean definitions to configure GemFire Object mocking. * * @author John Blum + * @see java.lang.annotation.Annotation + * @see org.springframework.beans.factory.config.BeanPostProcessor * @see org.springframework.context.annotation.Bean * @see org.springframework.context.annotation.Configuration + * @see org.springframework.context.annotation.ImportAware + * @see org.springframework.core.annotation.AnnotationAttributes + * @see org.springframework.core.type.AnnotationMetadata + * @see org.springframework.data.gemfire.test.mock.config.MockGemFireObjectsBeanPostProcessor * @since 2.0.0 */ @SuppressWarnings("unused") @Configuration -public class GemFireMockingConfiguration { +public class GemFireMockingConfiguration implements ImportAware { + + private boolean useSingletonCache = false; + + @Override + public void setImportMetadata(AnnotationMetadata importingClassMetadata) { + + if (isAnnotationPresent(importingClassMetadata)) { + + AnnotationAttributes enableGemFireMockingAttributes = getAnnotationAttributes(importingClassMetadata); + + this.useSingletonCache = enableGemFireMockingAttributes.getBoolean("useSingletonCache"); + } + } + + private Class getAnnotationType() { + return EnableGemFireMocking.class; + } + + private boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata) { + return isAnnotationPresent(importingClassMetadata, getAnnotationType()); + } + + private boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata, + Class annotationType) { + + return importingClassMetadata.hasAnnotation(annotationType.getName()); + } + + private AnnotationAttributes getAnnotationAttributes(AnnotationMetadata importingClassMetadata) { + return getAnnotationAttributes(importingClassMetadata, getAnnotationType()); + } + + private AnnotationAttributes getAnnotationAttributes(AnnotationMetadata importingClassMetadata, + Class annotationType) { + + return AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(annotationType.getName())); + } @Bean public BeanPostProcessor mockGemFireObjectsBeanPostProcessor() { - return MockGemFireObjectsBeanPostProcessor.INSTANCE; + return MockGemFireObjectsBeanPostProcessor.newInstance(this.useSingletonCache); + } + + @EventListener + public void releaseMockResources(ContextClosedEvent event) { + MockGemFireObjectsSupport.destroy(); } } diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/config/MockGemFireObjectsBeanPostProcessor.java b/src/test/java/org/springframework/data/gemfire/test/mock/config/MockGemFireObjectsBeanPostProcessor.java index 2af9ee72..4e166cf8 100644 --- a/src/test/java/org/springframework/data/gemfire/test/mock/config/MockGemFireObjectsBeanPostProcessor.java +++ b/src/test/java/org/springframework/data/gemfire/test/mock/config/MockGemFireObjectsBeanPostProcessor.java @@ -51,17 +51,32 @@ import org.springframework.lang.Nullable; */ public class MockGemFireObjectsBeanPostProcessor implements BeanPostProcessor { - public static final MockGemFireObjectsBeanPostProcessor INSTANCE = new MockGemFireObjectsBeanPostProcessor(); + private static final boolean DEFAULT_USE_SINGLETON_CACHE = false; private static final String GEMFIRE_PROPERTIES_BEAN_NAME = "gemfireProperties"; + private volatile boolean useSingletonCache; + private final AtomicReference gemfireProperties = new AtomicReference<>(new Properties()); + public static MockGemFireObjectsBeanPostProcessor newInstance() { + return newInstance(DEFAULT_USE_SINGLETON_CACHE); + } + + public static MockGemFireObjectsBeanPostProcessor newInstance(boolean useSingletonCache) { + + MockGemFireObjectsBeanPostProcessor beanPostProcessor = new MockGemFireObjectsBeanPostProcessor(); + + beanPostProcessor.useSingletonCache = useSingletonCache; + + return beanPostProcessor; + } + @Nullable @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return (isGemFireProperties(bean, beanName) ? set((Properties) bean) - : (bean instanceof CacheFactoryBean ? spyOnCacheFactoryBean((CacheFactoryBean) bean) + : (bean instanceof CacheFactoryBean ? spyOnCacheFactoryBean((CacheFactoryBean) bean, this.useSingletonCache) : (bean instanceof PoolFactoryBean ? mockThePoolFactoryBean((PoolFactoryBean) bean) : bean))); } @@ -88,11 +103,11 @@ public class MockGemFireObjectsBeanPostProcessor implements BeanPostProcessor { return gemfireProperties; } - private Object spyOnCacheFactoryBean(CacheFactoryBean bean) { + private Object spyOnCacheFactoryBean(CacheFactoryBean bean, boolean useSingletonCache) { return (bean instanceof ClientCacheFactoryBean - ? SpyingClientCacheFactoryInitializer.spyOn((ClientCacheFactoryBean) bean) - : SpyingCacheFactoryInitializer.spyOn(bean)); + ? SpyingClientCacheFactoryInitializer.spyOn((ClientCacheFactoryBean) bean, useSingletonCache) + : SpyingCacheFactoryInitializer.spyOn(bean, useSingletonCache)); } private Object mockThePoolFactoryBean(PoolFactoryBean bean) { @@ -102,28 +117,44 @@ public class MockGemFireObjectsBeanPostProcessor implements BeanPostProcessor { protected static class SpyingCacheFactoryInitializer implements CacheFactoryBean.CacheFactoryInitializer { - public static CacheFactoryBean spyOn(CacheFactoryBean cacheFactoryBean) { - cacheFactoryBean.setCacheFactoryInitializer(new SpyingCacheFactoryInitializer()); + public static CacheFactoryBean spyOn(CacheFactoryBean cacheFactoryBean, boolean useSingletonCache) { + cacheFactoryBean.setCacheFactoryInitializer(new SpyingCacheFactoryInitializer(useSingletonCache)); return cacheFactoryBean; } + private final boolean useSingletonCache; + + protected SpyingCacheFactoryInitializer(boolean useSingletonCache) { + this.useSingletonCache = useSingletonCache; + } + @Override public CacheFactory initialize(CacheFactory cacheFactory) { - return MockGemFireObjectsSupport.spyOn(cacheFactory); + return MockGemFireObjectsSupport.spyOn(cacheFactory, useSingletonCache); } } protected static class SpyingClientCacheFactoryInitializer implements CacheFactoryBean.CacheFactoryInitializer { - public static ClientCacheFactoryBean spyOn(ClientCacheFactoryBean clientCacheFactoryBean) { - clientCacheFactoryBean.setCacheFactoryInitializer(new SpyingClientCacheFactoryInitializer()); + public static ClientCacheFactoryBean spyOn(ClientCacheFactoryBean clientCacheFactoryBean, + boolean useSingletonCache) { + + clientCacheFactoryBean.setCacheFactoryInitializer( + new SpyingClientCacheFactoryInitializer(useSingletonCache)); + return clientCacheFactoryBean; } + private final boolean useSingletonCache; + + protected SpyingClientCacheFactoryInitializer(boolean useSingletonCache) { + this.useSingletonCache = useSingletonCache; + } + @Override public ClientCacheFactory initialize(ClientCacheFactory clientCacheFactory) { - return MockGemFireObjectsSupport.spyOn(clientCacheFactory); + return MockGemFireObjectsSupport.spyOn(clientCacheFactory, this.useSingletonCache); } } diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/context/MockGemFireObjectsApplicationContextInitializer.java b/src/test/java/org/springframework/data/gemfire/test/mock/context/MockGemFireObjectsApplicationContextInitializer.java index 4cefce2f..cfae6a5e 100644 --- a/src/test/java/org/springframework/data/gemfire/test/mock/context/MockGemFireObjectsApplicationContextInitializer.java +++ b/src/test/java/org/springframework/data/gemfire/test/mock/context/MockGemFireObjectsApplicationContextInitializer.java @@ -36,6 +36,6 @@ public class MockGemFireObjectsApplicationContextInitializer @Override @SuppressWarnings("all") public void initialize(ConfigurableApplicationContext applicationContext) { - applicationContext.getBeanFactory().addBeanPostProcessor(MockGemFireObjectsBeanPostProcessor.INSTANCE); + applicationContext.getBeanFactory().addBeanPostProcessor(MockGemFireObjectsBeanPostProcessor.newInstance()); } } diff --git a/src/test/resources/org/springframework/data/gemfire/client/clientcache-with-region-using-cache-loader-writer.xml b/src/test/resources/org/springframework/data/gemfire/client/ClientRegionWithCacheLoaderWriterTest-context.xml similarity index 97% rename from src/test/resources/org/springframework/data/gemfire/client/clientcache-with-region-using-cache-loader-writer.xml rename to src/test/resources/org/springframework/data/gemfire/client/ClientRegionWithCacheLoaderWriterTest-context.xml index 6a528241..96526a9f 100644 --- a/src/test/resources/org/springframework/data/gemfire/client/clientcache-with-region-using-cache-loader-writer.xml +++ b/src/test/resources/org/springframework/data/gemfire/client/ClientRegionWithCacheLoaderWriterTest-context.xml @@ -11,7 +11,6 @@ ClientCacheWithRegionUsingCacheLoaderWriterTest - 0 warning diff --git a/src/test/resources/org/springframework/data/gemfire/client/client-cache-no-close.xml b/src/test/resources/org/springframework/data/gemfire/client/client-cache-no-close.xml deleted file mode 100644 index 81ed01a9..00000000 --- a/src/test/resources/org/springframework/data/gemfire/client/client-cache-no-close.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - warning - - - - - - - diff --git a/src/test/resources/org/springframework/data/gemfire/client/client-cache.xml b/src/test/resources/org/springframework/data/gemfire/client/client-cache.xml deleted file mode 100644 index d7336153..00000000 --- a/src/test/resources/org/springframework/data/gemfire/client/client-cache.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - warning - - - - - - - diff --git a/src/test/resources/org/springframework/data/gemfire/config/xml/client-ns.xml b/src/test/resources/org/springframework/data/gemfire/config/xml/client-ns.xml index a58a3e9b..82a3c4c8 100644 --- a/src/test/resources/org/springframework/data/gemfire/config/xml/client-ns.xml +++ b/src/test/resources/org/springframework/data/gemfire/config/xml/client-ns.xml @@ -95,6 +95,7 @@ result-policy="${client.regex.interests.result-policy}"/> +