diff --git a/src/main/java/org/springframework/data/gemfire/GemfireAccessor.java b/src/main/java/org/springframework/data/gemfire/GemfireAccessor.java index 0548db81..50d4e3ff 100644 --- a/src/main/java/org/springframework/data/gemfire/GemfireAccessor.java +++ b/src/main/java/org/springframework/data/gemfire/GemfireAccessor.java @@ -16,21 +16,22 @@ package org.springframework.data.gemfire; +import com.gemstone.gemfire.GemFireCheckedException; +import com.gemstone.gemfire.GemFireException; +import com.gemstone.gemfire.cache.Region; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.dao.DataAccessException; import org.springframework.util.Assert; -import com.gemstone.gemfire.GemFireCheckedException; -import com.gemstone.gemfire.GemFireException; -import com.gemstone.gemfire.cache.Region; - /** - * Base class for GemfireTemplate and GemfireInterceptor, defining common properties such as {@link Region}. + * {@link GemfireAccessor} is a base class for {@link GemfireTemplate} defining common operations and properties, + * such as {@link Region}. + * + * This class is not intended to be used directly. * - * Not intended to be used directly. - * * @author Costin Leau * @author John Blum * @see org.springframework.beans.factory.InitializingBean @@ -66,7 +67,7 @@ public class GemfireAccessor implements InitializingBean { } public void afterPropertiesSet() { - Assert.notNull(getRegion(), "The GemFire Cache Region is required."); + Assert.notNull(getRegion(), "Region is required"); } /** @@ -96,12 +97,11 @@ public class GemfireAccessor implements InitializingBean { * org.springframework.dao hierarchy. Note that this particular implementation * is called only for GemFire querying exception that do NOT extend from GemFire exception. * May be overridden in subclasses. - * + * * @param ex GemFireException that occurred * @return the corresponding DataAccessException instance */ public DataAccessException convertGemFireQueryException(RuntimeException ex) { return GemfireCacheUtils.convertQueryExceptions(ex); } - } diff --git a/src/main/java/org/springframework/data/gemfire/GemfireTemplate.java b/src/main/java/org/springframework/data/gemfire/GemfireTemplate.java index c3dbf140..cb16331d 100644 --- a/src/main/java/org/springframework/data/gemfire/GemfireTemplate.java +++ b/src/main/java/org/springframework/data/gemfire/GemfireTemplate.java @@ -21,13 +21,9 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Collection; +import java.util.List; import java.util.Map; -import org.springframework.dao.DataAccessException; -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; - import com.gemstone.gemfire.GemFireCheckedException; import com.gemstone.gemfire.GemFireException; import com.gemstone.gemfire.cache.Region; @@ -40,6 +36,12 @@ import com.gemstone.gemfire.cache.query.QueryService; import com.gemstone.gemfire.cache.query.SelectResults; import com.gemstone.gemfire.internal.cache.LocalRegion; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + /** * Helper class that simplifies GemFire data access code and converts {@link GemFireCheckedException} and * {@link GemFireException} into Spring {@link DataAccessException}, following the org.springframework.dao @@ -50,7 +52,7 @@ import com.gemstone.gemfire.internal.cache.LocalRegion; * explicitly care about handling {@link Region} life-cycle exceptions. * Typically used to implement data access or business logic services that use GemFire within their implementation but * are GemFire-agnostic in their interface. The latter or code calling the latter only have to deal with business - * objects, query objects, and org.springframework.dao exceptions. + * objects, query objects, and org.springframework.dao exceptions. * * @author Costin Leau * @author John Blum @@ -111,7 +113,7 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation * @see org.springframework.data.gemfire.GemfireOperations#containsKey(java.lang.Object) */ @Override - public boolean containsKey(final Object key) { + public boolean containsKey(Object key) { return getRegion().containsKey(key); } @@ -119,7 +121,7 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation * @see org.springframework.data.gemfire.GemfireOperations#containsKeyOnServer(java.lang.Object) */ @Override - public boolean containsKeyOnServer(final Object key) { + public boolean containsKeyOnServer(Object key) { return getRegion().containsKeyOnServer(key); } @@ -127,7 +129,7 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation * @see org.springframework.data.gemfire.GemfireOperations#containsValue(java.lang.Object) */ @Override - public boolean containsValue(final Object value) { + public boolean containsValue(Object value) { return getRegion().containsValue(value); } @@ -135,7 +137,7 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation * @see org.springframework.data.gemfire.GemfireOperations#containsValueForKey(java.lang.Object) */ @Override - public boolean containsValueForKey(final Object key) { + public boolean containsValueForKey(Object key) { return getRegion().containsValueForKey(key); } @@ -143,7 +145,7 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation * @see org.springframework.data.gemfire.GemfireOperations#create(K, V) */ @Override - public void create(final K key, final V value) { + public void create(K key, V value) { try { getRegion().create(key, value); } @@ -156,7 +158,7 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation * @see org.springframework.data.gemfire.GemfireOperations#get(K) */ @Override - public V get(final K key) { + public V get(K key) { try { return this.getRegion().get(key); } @@ -169,7 +171,7 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation * @see org.springframework.data.gemfire.GemfireOperations#getAll(java.util.Collection) */ @Override - public Map getAll(final Collection keys) { + public Map getAll(Collection keys) { try { return this.getRegion().getAll(keys); } @@ -182,7 +184,7 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation * @see org.springframework.data.gemfire.GemfireOperations#put(K, V) */ @Override - public V put(final K key, final V value) { + public V put(K key, V value) { try { return this.getRegion().put(key, value); } @@ -195,7 +197,7 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation * @see org.springframework.data.gemfire.GemfireOperations#putAll(java.util.Map) */ @Override - public void putAll(final Map map) { + public void putAll(Map map) { try { this.getRegion().putAll(map); } @@ -208,7 +210,7 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation * @see org.springframework.data.gemfire.GemfireOperations#putIfAbsent(K, V) */ @Override - public V putIfAbsent(final K key, final V value) { + public V putIfAbsent(K key, V value) { try { return this.getRegion().putIfAbsent(key, value); } @@ -221,7 +223,7 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation * @see org.springframework.data.gemfire.GemfireOperations#remove(K) */ @Override - public V remove(final K key) { + public V remove(K key) { try { return this.getRegion().remove(key); } @@ -234,7 +236,7 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation * @see org.springframework.data.gemfire.GemfireOperations#replace(K, V) */ @Override - public V replace(final K key, final V value) { + public V replace(K key, V value) { try { return this.getRegion().replace(key, value); } @@ -247,7 +249,7 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation * @see org.springframework.data.gemfire.GemfireOperations#replace(K, V, V) */ @Override - public boolean replace(final K key, final V oldValue, final V newValue) { + public boolean replace(K key, V oldValue, V newValue) { try { return this.getRegion().replace(key, oldValue, newValue); } @@ -256,19 +258,20 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation } } - /* (non-Javadoc) + /* + * (non-Javadoc) * @see org.springframework.data.gemfire.GemfireOperations#query(java.lang.String) */ @Override - public SelectResults query(final String query) { + public SelectResults query(String query) { try { return this.getRegion().query(query); } - catch (IndexInvalidException ex) { - throw convertGemFireQueryException(ex); + catch (IndexInvalidException e) { + throw convertGemFireQueryException(e); } - catch (QueryInvalidException ex) { - throw convertGemFireQueryException(ex); + catch (QueryInvalidException e) { + throw convertGemFireQueryException(e); } catch (GemFireCheckedException e) { throw convertGemFireAccessException(e); @@ -287,14 +290,15 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation } } - /* (non-Javadoc) + /* + * (non-Javadoc) * @see org.springframework.data.gemfire.GemfireOperations#find(java.lang.String, java.lang.Object) */ @Override @SuppressWarnings("unchecked") - public SelectResults find(final String queryString, final Object... params) throws InvalidDataAccessApiUsageException { + public SelectResults find(String queryString, Object... params) throws InvalidDataAccessApiUsageException { try { - QueryService queryService = lookupQueryService(getRegion()); + QueryService queryService = resolveQueryService(getRegion()); Query query = queryService.newQuery(queryString); Object result = query.execute(params); @@ -302,8 +306,9 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation return (SelectResults) result; } else { - throw new InvalidDataAccessApiUsageException( - "Result object returned from GemfireCallback isn't a SelectResult: [" + result + "]"); + throw new InvalidDataAccessApiUsageException(String.format( + "The result from executing query [%1$s] was not an instance of SelectResults [%2$s]", + queryString, result)); } } catch (IndexInvalidException ex) { @@ -329,26 +334,28 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation } } - /* (non-Javadoc) + /* + * (non-Javadoc) * @see org.springframework.data.gemfire.GemfireOperations#findUnique(java.lang.String, java.lang.Object) */ @Override @SuppressWarnings("unchecked") - public T findUnique(final String queryString, final Object... params) throws InvalidDataAccessApiUsageException { + public T findUnique(String queryString, Object... params) throws InvalidDataAccessApiUsageException { try { - QueryService queryService = lookupQueryService(getRegion()); + QueryService queryService = resolveQueryService(getRegion()); Query query = queryService.newQuery(queryString); Object result = query.execute(params); if (result instanceof SelectResults) { SelectResults selectResults = (SelectResults) result; + List results = selectResults.asList(); - if (selectResults.asList().size() == 1) { - result = selectResults.iterator().next(); + if (results.size() == 1) { + result = results.get(0); } else { throw new InvalidDataAccessApiUsageException(String.format( - "The result returned from query (%1$s) is not unique: (%2$s).", queryString, result)); + "The result returned from query [%1$s]) was not unique [%2$s]", queryString, result)); } } @@ -378,43 +385,51 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation } /** - * Returns the query service used by the template in its find methods. - * - * @param region region to find the local query service from - * @return query service to use, local or generic + * Returns the {@link QueryService} used by this template in its query/finder methods. + * + * @param region {@link Region} used to acquire the {@link QueryService}. + * @return the {@link QueryService} that will perform the query. * @see com.gemstone.gemfire.cache.Region * @see com.gemstone.gemfire.cache.Region#getRegionService() * @see com.gemstone.gemfire.cache.RegionService#getQueryService() * @see com.gemstone.gemfire.cache.client.ClientCache#getLocalQueryService() */ - protected QueryService lookupQueryService(final Region region) { - return (requiresLocalQueryService(region) ? ((ClientCache) region.getRegionService()).getLocalQueryService() - : region.getRegionService().getQueryService()); + protected QueryService resolveQueryService(Region region) { + return (region.getRegionService() instanceof ClientCache ? resolveClientQueryService(region) + : queryServiceFrom(region)); } - /* - * (non-Javadoc) - * @see com.gemstone.gemfire.cache.Region - * @see com.gemstone.gemfire.internal.cache.LocalRegion - */ - /* package-private */ boolean isLocalWithNoServerProxy(final Region region) { + /* (non-Javadoc) */ + QueryService resolveClientQueryService(Region region) { + ClientCache clientCache = (ClientCache) region.getRegionService(); + + return (requiresLocalQueryService(region) ? clientCache.getLocalQueryService() + : (requiresPooledQueryService(region) ? clientCache.getQueryService(poolNameFrom(region)) + : queryServiceFrom(region))); + } + + /* (non-Javadoc) */ + boolean requiresLocalQueryService(Region region) { + return (Scope.LOCAL.equals(region.getAttributes().getScope()) && isLocalWithNoServerProxy(region)); + } + + /* (non-Javadoc) */ + boolean isLocalWithNoServerProxy(Region region) { return (region instanceof LocalRegion && !((LocalRegion) region).hasServerProxy()); } - /* - * (non-Javadoc) - * @see #isLocalWithNoServerProxy(:Region) - * @see com.gemstone.gemfire.cache.Region - * @see com.gemstone.gemfire.cache.RegionAttributes - * @see com.gemstone.gemfire.cache.RegionAttributes#getScope() - * @see com.gemstone.gemfire.cache.Scope - * @see com.gemstone.gemfire.cache.client.ClientCache - * @see com.gemstone.gemfire.internal.cache.LocalRegion - * @see com.gemstone.gemfire.internal.cache.LocalRegion#hasServerProxy() - */ - private boolean requiresLocalQueryService(final Region region) { - return (region.getRegionService() instanceof ClientCache && isLocalWithNoServerProxy(region) - && Scope.LOCAL.equals(region.getAttributes().getScope())); + boolean requiresPooledQueryService(Region region) { + return StringUtils.hasText(poolNameFrom(region)); + } + + /* (non-Javadoc) */ + QueryService queryServiceFrom(Region region) { + return region.getRegionService().getQueryService(); + } + + /* (non-Javadoc) */ + String poolNameFrom(Region region) { + return region.getAttributes().getPoolName(); } /* @@ -433,9 +448,11 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation @Override public T execute(GemfireCallback action, boolean exposeNativeRegion) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); + try { - Region regionToExpose = (exposeNativeRegion ? getRegion() : regionProxy); - return action.doInGemfire(regionToExpose); + Region regionArgument = (exposeNativeRegion ? getRegion() : regionProxy); + + return action.doInGemfire(regionArgument); } catch (IndexInvalidException ex) { throw convertGemFireQueryException(ex); @@ -472,9 +489,11 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation * @see #execute(GemfireCallback, boolean) */ @SuppressWarnings("unchecked") - protected Region createRegionProxy(final Region region) { - return (Region) Proxy.newProxyInstance(region.getClass().getClassLoader(), - ClassUtils.getAllInterfacesForClass(region.getClass(), getClass().getClassLoader()), + protected Region createRegionProxy(Region region) { + Class regionType = region.getClass(); + + return (Region) Proxy.newProxyInstance(regionType.getClassLoader(), + ClassUtils.getAllInterfacesForClass(regionType, getClass().getClassLoader()), new RegionCloseSuppressingInvocationHandler(region)); } @@ -516,5 +535,4 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation } } } - } diff --git a/src/main/java/org/springframework/data/gemfire/util/PropertiesBuilder.java b/src/main/java/org/springframework/data/gemfire/util/PropertiesBuilder.java index 7683e19f..924e7e94 100644 --- a/src/main/java/org/springframework/data/gemfire/util/PropertiesBuilder.java +++ b/src/main/java/org/springframework/data/gemfire/util/PropertiesBuilder.java @@ -50,6 +50,18 @@ public class PropertiesBuilder implements FactoryBean { return new PropertiesBuilder(); } + /** + * Factory method to create an instance of {@link PropertiesBuilder} initialized with the given {@link Properties}. + * + * @param properties {@link Properties} used as the default properties of the constructed {@link PropertiesBuilder}. + * @return an instance of {@link PropertiesBuilder} initialized with the given {@link Properties}. + * @see java.util.Properties + * @see #PropertiesBuilder(Properties) + */ + public static PropertiesBuilder from(Properties properties) { + return new PropertiesBuilder(properties); + } + /** * Constructs and initializes a {@link PropertiesBuilder} containing all properties * from the given {@link InputStream}. diff --git a/src/test/java/org/springframework/data/gemfire/GemfireTemplateIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/GemfireTemplateIntegrationTest.java deleted file mode 100644 index 585ddd58..00000000 --- a/src/test/java/org/springframework/data/gemfire/GemfireTemplateIntegrationTest.java +++ /dev/null @@ -1,336 +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; - -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.assertTrue; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.annotation.Resource; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.gemfire.repository.sample.User; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import com.gemstone.gemfire.cache.Region; -import com.gemstone.gemfire.cache.query.SelectResults; - -/** - * The GemfireTemplateIntegrationTest class is a test suite of test cases testing the contract and functionality - * of the SDG GemfireTemplate class for wrapping a GemFire Cache Region and performing Cache Region operations. - * - * @author John Blum - * @see org.junit.Test - * @see org.springframework.data.gemfire.GemfireTemplate - * @see org.springframework.test.context.ContextConfiguration - * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner - * @since 1.4.0 - */ -@ContextConfiguration -@RunWith(SpringJUnit4ClassRunner.class) -@SuppressWarnings("unused") -public class GemfireTemplateIntegrationTest { - - protected static final List TEST_USER_LIST = new ArrayList(11); - - static { - TEST_USER_LIST.add(createUser("jonDoe")); - TEST_USER_LIST.add(createUser("janeDoe", false)); - TEST_USER_LIST.add(createUser("pieDoe", false)); - TEST_USER_LIST.add(createUser("cookieDoe")); - TEST_USER_LIST.add(createUser("jackHandy")); - TEST_USER_LIST.add(createUser("mandyHandy", false)); - TEST_USER_LIST.add(createUser("randyHandy", false)); - TEST_USER_LIST.add(createUser("sandyHandy")); - TEST_USER_LIST.add(createUser("imaPigg")); - } - - @Resource(name = "Users") - private Region users; - - @Autowired - private GemfireTemplate usersTemplate; - - protected static User createUser(final String username) { - return createUser(username, true); - } - - protected static User createUser(final String username, final Boolean active) { - return createUser(username, String.format("%1$s@companyx.com", username), Calendar.getInstance(), active); - } - - protected static User createUser(final String username, final String email, final Calendar since, final Boolean active) { - User user = new User(username); - user.setActive(Boolean.TRUE.equals(active)); - user.setEmail(email); - user.setSince(since); - return user; - } - - protected String getKey(final User user) { - return (user != null ? user.getUsername() : null); - } - - protected User getUser(final String username) { - for (User user : TEST_USER_LIST) { - if (user.getUsername().equals(username)) { - return user; - } - } - - return null; - } - - protected List getUsers(final String... usernames) { - List users = new ArrayList(usernames.length); - List usernameList = Arrays.asList(usernames); - - for (User user : TEST_USER_LIST) { - if (usernameList.contains(user.getUsername())) { - users.add(user); - } - } - - return users; - } - - protected Map getUsersAsMap(final User... users) { - Map userMap = new HashMap(users.length); - - for (User user : users) { - userMap.put(getKey(user), user); - } - - return userMap; - } - - protected void assertNullEquals(final Object value1, final Object value2) { - Assert.assertTrue(value1 == null ? value2 == null : value1.equals(value2)); - } - - @Before - public void setup() { - assertNotNull("The 'Users' Region was not properly configured and initialized!", users); - - if (users.isEmpty()) { - for (User user : TEST_USER_LIST) { - users.put(getKey(user), user); - } - - assertFalse(users.isEmpty()); - assertEquals(TEST_USER_LIST.size(), users.size()); - } - } - - @Test - public void testContainsKey() { - assertTrue(usersTemplate.containsKey(getKey(getUser("jonDoe")))); - assertFalse(usersTemplate.containsKey("dukeNukem")); - } - - @Test - @Ignore - public void testContainsKeyOnServer() { - assertTrue(usersTemplate.containsKeyOnServer(getKey(getUser("jackHandy")))); - assertFalse(usersTemplate.containsKeyOnServer("maxPayne")); - } - - @Test - public void testContainsValue() { - assertTrue(usersTemplate.containsValue(getUser("pieDoe"))); - assertFalse(usersTemplate.containsValue(createUser("pieDough"))); - } - - @Test - public void testContainsValueForKey() { - assertTrue(usersTemplate.containsValueForKey(getKey(getUser("cookieDoe")))); - assertFalse(usersTemplate.containsValueForKey("chocolateChipCookieDoe")); - } - - @Test - public void testCreate() { - User bartSimpson = createUser("bartSimpson"); - - usersTemplate.create(getKey(bartSimpson), bartSimpson); - - assertTrue(users.containsKey(getKey(bartSimpson))); - assertTrue(users.containsValueForKey(getKey(bartSimpson))); - assertTrue(users.containsValue(bartSimpson)); - assertEquals(bartSimpson, users.get(getKey(bartSimpson))); - } - - @Test - public void testGet() { - assertEquals(users.get(getKey(getUser("imaPigg"))), usersTemplate.get(getKey(getUser("imaPigg")))); - assertNullEquals(users.get("mrT"), usersTemplate.get("mrT")); - } - - @Test - public void testPut() { - User peterGriffon = createUser("peterGriffon"); - - assertNull(usersTemplate.put(getKey(peterGriffon), peterGriffon)); - assertEquals(peterGriffon, users.get(getKey(peterGriffon))); - } - - @Test - public void testPutIfAbsent() { - User stewieGriffon = createUser("stewieGriffon"); - - assertFalse(users.containsValue(stewieGriffon)); - assertNull(usersTemplate.putIfAbsent(getKey(stewieGriffon), stewieGriffon)); - assertTrue(users.containsValue(stewieGriffon)); - assertEquals(stewieGriffon, usersTemplate.putIfAbsent(getKey(stewieGriffon), createUser("megGriffon"))); - assertEquals(stewieGriffon, users.get(getKey(stewieGriffon))); - } - - @Test - public void testRemove() { - User mandyHandy = users.get(getKey(getUser("mandyHandy"))); - - assertNotNull(mandyHandy); - assertEquals(mandyHandy, usersTemplate.remove(getKey(mandyHandy))); - assertFalse(users.containsKey(getKey(mandyHandy))); - assertFalse(users.containsValue(mandyHandy)); - assertFalse(users.containsKey("loisGriffon")); - assertNull(usersTemplate.remove("loisGriffon")); - assertFalse(users.containsKey("loisGriffon")); - } - - @Test - public void testReplace() { - User randyHandy = users.get(getKey(getUser("randyHandy"))); - User lukeFluke = createUser("lukeFluke"); - User chrisGriffon = createUser("chrisGriffon"); - - assertNotNull(randyHandy); - assertEquals(randyHandy, usersTemplate.replace(getKey(randyHandy), lukeFluke)); - assertEquals(lukeFluke, users.get(getKey(randyHandy))); - assertFalse(users.containsValue(randyHandy)); - assertFalse(users.containsValue(chrisGriffon)); - assertNull(usersTemplate.replace(getKey(chrisGriffon), chrisGriffon)); - assertFalse(users.containsValue(chrisGriffon)); - } - - @Test - public void testReplaceOldValueWithNewValue() { - User jackHandy = getUser("jackHandy"); - User imaPigg = getUser("imaPigg"); - - assertTrue(users.containsValue(jackHandy)); - assertFalse(usersTemplate.replace(getKey(jackHandy), null, imaPigg)); - assertTrue(users.containsValue(jackHandy)); - assertEquals(jackHandy, users.get(getKey(jackHandy))); - assertTrue(usersTemplate.replace(getKey(jackHandy), jackHandy, imaPigg)); - assertFalse(users.containsValue(jackHandy)); - assertEquals(imaPigg, users.get(getKey(jackHandy))); - } - - @Test - public void testGetAllReturnsNoResults() { - List keys = Arrays.asList("keyOne", "keyTwo", "keyThree"); - - Map actualUserMapping = usersTemplate.getAll(keys); - Map expectedUserMapping = users.getAll(keys); - - assertEquals(expectedUserMapping, actualUserMapping); - } - - @Test - public void testGetAllReturnsResults() { - List keys = Arrays.asList(getKey(getUser("jonDoe")), getKey(getUser("pieDoe"))); - - Map actualUserMapping = usersTemplate.getAll(keys); - Map expectedUserMapping = users.getAll(keys); - - assertEquals(actualUserMapping, expectedUserMapping); - } - - @Test - public void testPutAll() { - User batMan = createUser("batMap"); - User spiderMan = createUser("spiderMan"); - User superMan = createUser("superMan"); - - Map userMap = getUsersAsMap(batMan, spiderMan, superMan); - - assertFalse(users.keySet().containsAll(userMap.keySet())); - assertFalse(users.values().containsAll(userMap.values())); - - usersTemplate.putAll(userMap); - - assertTrue(users.keySet().containsAll(userMap.keySet())); - assertTrue(users.values().containsAll(userMap.values())); - } - - @Test - public void testQuery() { - SelectResults queryResults = usersTemplate.query("username LIKE '%Doe'"); - - assertNotNull(queryResults); - - List queriedUsers = queryResults.asList(); - - assertNotNull(queriedUsers); - assertFalse(queriedUsers.isEmpty()); - assertEquals(4, queriedUsers.size()); - assertTrue(queriedUsers.containsAll(getUsers("jonDoe", "janeDoe", "pieDoe", "cookieDoe"))); - } - - @Test - public void testFind() { - SelectResults findResults = usersTemplate.find("SELECT u FROM /Users u WHERE u.username LIKE $1 AND u.active = $2", "%Doe", true); - - assertNotNull(findResults); - - List usersFound = findResults.asList(); - - assertNotNull(usersFound); - assertFalse(usersFound.isEmpty()); - assertEquals(2, usersFound.size()); - assertTrue(usersFound.containsAll(getUsers("jonDoe", "cookieDoe"))); - } - - @Test - public void testFindUniqueReturnsResult() { - User jonDoe = usersTemplate.findUnique("SELECT u FROM /Users u WHERE u.username = $1", "jonDoe"); - - assertNotNull(jonDoe); - assertEquals(getUser("jonDoe"), jonDoe); - } - - @Test(expected = InvalidDataAccessApiUsageException.class) - public void testFindUniqueReturnsNoResult() { - usersTemplate.findUnique("SELECT u FROM /Users u WHERE u.username = $1", "benDover"); - } - -} diff --git a/src/test/java/org/springframework/data/gemfire/GemfireTemplateIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/GemfireTemplateIntegrationTests.java new file mode 100644 index 00000000..bccfb3a3 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/GemfireTemplateIntegrationTests.java @@ -0,0 +1,399 @@ +/* + * 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.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.Assume.assumeThat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.annotation.Resource; + +import com.gemstone.gemfire.cache.GemFireCache; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.query.SelectResults; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.gemfire.repository.sample.User; +import org.springframework.data.gemfire.util.CacheUtils; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Integration tests for {@link GemfireTemplate}. + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.data.gemfire.GemfireTemplate + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner + * @since 1.4.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@SuppressWarnings("unused") +public class GemfireTemplateIntegrationTests { + + protected static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning"; + + protected static final List TEST_USERS = new ArrayList(9); + + static { + TEST_USERS.add(newUser("jonDoe")); + TEST_USERS.add(newUser("janeDoe", false)); + TEST_USERS.add(newUser("pieDoe", false)); + TEST_USERS.add(newUser("cookieDoe")); + TEST_USERS.add(newUser("jackHandy")); + TEST_USERS.add(newUser("mandyHandy", false)); + TEST_USERS.add(newUser("randyHandy", false)); + TEST_USERS.add(newUser("sandyHandy")); + TEST_USERS.add(newUser("imaPigg")); + } + + @Autowired + private GemFireCache gemfireCache; + + @Autowired + private GemfireTemplate usersTemplate; + + @Resource(name = "Users") + private Region users; + + protected static User newUser(String username) { + return newUser(username, true); + } + + protected static User newUser(String username, Boolean active) { + return newUser(username, String.format("%1$s@companyx.com", username), Calendar.getInstance(), active); + } + + protected static User newUser(String username, String email, Calendar since, Boolean active) { + User user = new User(username); + user.setActive(Boolean.TRUE.equals(active)); + user.setEmail(email); + user.setSince(since); + return user; + } + + protected String getKey(User user) { + return (user != null ? user.getUsername() : null); + } + + protected User getUser(String username) { + for (User user : TEST_USERS) { + if (user.getUsername().equals(username)) { + return user; + } + } + + return null; + } + + protected List getUsers(String... usernames) { + List usernameList = Arrays.asList(usernames); + List users = new ArrayList(usernames.length); + + for (User user : TEST_USERS) { + if (usernameList.contains(user.getUsername())) { + users.add(user); + } + } + + return users; + } + + protected Map getUsersAsMap(String... usernames) { + return getUsersAsMap(getUsers(usernames)); + } + + protected Map getUsersAsMap(User... users) { + return getUsersAsMap(Arrays.asList(users)); + } + + protected Map getUsersAsMap(Iterable users) { + Map userMap = new HashMap(); + + for (User user : users) { + userMap.put(getKey(user), user); + } + + return userMap; + } + + protected void assertNullEquals(Object value1, Object value2) { + assertThat(value1 == null ? value2 == null : value1.equals(value2)).isTrue(); + } + + @Before + public void setup() { + assertThat(users).isNotNull(); + + if (users.isEmpty()) { + for (User user : TEST_USERS) { + users.put(getKey(user), user); + } + + assertThat(users.isEmpty()).isFalse(); + assertThat(users.size()).isEqualTo(TEST_USERS.size()); + } + } + + @Test + public void containsKey() { + assertThat(usersTemplate.containsKey(getKey(getUser("jonDoe")))).isTrue(); + assertThat(usersTemplate.containsKey("dukeNukem")).isFalse(); + } + + @Test + public void containsKeyOnServer() { + assumeThat(CacheUtils.isClient(gemfireCache), is(true)); + assertThat(usersTemplate.containsKeyOnServer(getKey(getUser("jackHandy")))).isTrue(); + assertThat(usersTemplate.containsKeyOnServer("maxPayne")).isFalse(); + } + + @Test + public void containsValue() { + assertThat(usersTemplate.containsValue(getUser("pieDoe"))).isTrue(); + assertThat(usersTemplate.containsValue(newUser("pieDough"))).isFalse(); + } + + @Test + public void containsValueForKey() { + assertThat(usersTemplate.containsValueForKey(getKey(getUser("cookieDoe")))).isTrue(); + assertThat(usersTemplate.containsValueForKey("chocolateChipCookieDoe")).isFalse(); + } + + @Test + public void create() { + User bartSimpson = newUser("bartSimpson"); + + usersTemplate.create(getKey(bartSimpson), bartSimpson); + + assertThat(users.containsKey(getKey(bartSimpson))).isTrue(); + assertThat(users.containsValueForKey(getKey(bartSimpson))).isTrue(); + assertThat(users.containsValue(bartSimpson)).isTrue(); + assertThat(users.get(getKey(bartSimpson))).isEqualTo(bartSimpson); + } + + @Test + public void get() { + String key = getKey(getUser("imaPigg")); + + assertThat(usersTemplate.get(key)).isEqualTo(users.get(key)); + assertNullEquals(users.get("mrT"), usersTemplate.get("mrT")); + } + + @Test + public void put() { + User peterGriffon = newUser("peterGriffon"); + + assertThat(usersTemplate.put(getKey(peterGriffon), peterGriffon)).isNull(); + assertThat(users.get(getKey(peterGriffon))).isEqualTo(peterGriffon); + } + + @Test + public void putIfAbsent() { + User stewieGriffon = newUser("stewieGriffon"); + + assertThat(users.containsValue(stewieGriffon)).isFalse(); + assertThat(usersTemplate.putIfAbsent(getKey(stewieGriffon), stewieGriffon)).isNull(); + assertThat(users.containsValue(stewieGriffon)).isTrue(); + assertThat(usersTemplate.putIfAbsent(getKey(stewieGriffon), newUser("megGriffon"))).isEqualTo(stewieGriffon); + assertThat(users.get(getKey(stewieGriffon))).isEqualTo(stewieGriffon); + } + + @Test + public void remove() { + User mandyHandy = users.get(getKey(getUser("mandyHandy"))); + + assertThat(mandyHandy).isNotNull(); + assertThat(usersTemplate.remove(getKey(mandyHandy))).isEqualTo(mandyHandy); + assertThat(users.containsKey(getKey(mandyHandy))).isFalse(); + assertThat(users.containsValue(mandyHandy)).isFalse(); + assertThat(users.containsKey("loisGriffon")).isFalse(); + assertThat(usersTemplate.remove("loisGriffon")).isNull(); + assertThat(users.containsKey("loisGriffon")).isFalse(); + } + + @Test + public void replace() { + User randyHandy = users.get(getKey(getUser("randyHandy"))); + User lukeFluke = newUser("lukeFluke"); + User chrisGriffon = newUser("chrisGriffon"); + + assertThat(randyHandy).isNotNull(); + assertThat(usersTemplate.replace(getKey(randyHandy), lukeFluke)).isEqualTo(randyHandy); + assertThat(users.get(getKey(randyHandy))).isEqualTo(lukeFluke); + assertThat(users.containsValue(randyHandy)).isFalse(); + assertThat(users.containsValue(chrisGriffon)).isFalse(); + assertThat(usersTemplate.replace(getKey(chrisGriffon), chrisGriffon)).isNull(); + assertThat(users.containsValue(chrisGriffon)).isFalse(); + } + + @Test + public void replaceOldValueWithNewValue() { + User jackHandy = getUser("jackHandy"); + User imaPigg = getUser("imaPigg"); + + assertThat(users.containsValue(jackHandy)).isTrue(); + assertThat(usersTemplate.replace(getKey(jackHandy), null, imaPigg)).isFalse(); + assertThat(users.containsValue(jackHandy)).isTrue(); + assertThat(users.get(getKey(jackHandy))).isEqualTo(jackHandy); + assertThat(usersTemplate.replace(getKey(jackHandy), jackHandy, imaPigg)).isTrue(); + assertThat(users.containsValue(jackHandy)).isFalse(); + assertThat(users.get(getKey(jackHandy))).isEqualTo(imaPigg); + } + + @Test + public void getAllReturnsNoResults() { + List keys = Arrays.asList("keyOne", "keyTwo", "keyThree"); + Map users = usersTemplate.getAll(keys); + + assertThat(users).isNotNull(); + assertThat(users).isEqualTo(this.users.getAll(keys)); + } + + @Test + public void getAllReturnsResults() { + Map users = usersTemplate.getAll(Arrays.asList( + getKey(getUser("jonDoe")), getKey(getUser("pieDoe")))); + + assertThat(users).isNotNull(); + assertThat(users).isEqualTo(getUsersAsMap(getUser("jonDoe"), getUser("pieDoe"))); + } + + @Test + public void putAll() { + User batMan = newUser("batMan"); + User spiderMan = newUser("spiderMan"); + User superMan = newUser("superMan"); + + Map userMap = getUsersAsMap(batMan, spiderMan, superMan); + + assertThat(users.keySet().containsAll(userMap.keySet())).isFalse(); + assertThat(users.values().containsAll(userMap.values())).isFalse(); + + usersTemplate.putAll(userMap); + + assertThat(users.keySet().containsAll(userMap.keySet())).isTrue(); + assertThat(users.values().containsAll(userMap.values())).isTrue(); + } + + @Test + public void query() { + SelectResults queryResults = usersTemplate.query("username LIKE '%Doe'"); + + assertThat(queryResults).isNotNull(); + + List usersFound = queryResults.asList(); + + assertThat(usersFound).isNotNull(); + assertThat(usersFound.size()).isEqualTo(4); + assertThat(usersFound.containsAll(getUsers("jonDoe", "janeDoe", "pieDoe", "cookieDoe"))).isTrue(); + } + + @Test + public void find() { + SelectResults findResults = usersTemplate.find("SELECT u FROM /Users u WHERE u.username LIKE $1 AND u.active = $2", "%Doe", true); + + assertThat(findResults).isNotNull(); + + List usersFound = findResults.asList(); + + assertThat(usersFound).isNotNull(); + assertThat(usersFound.size()).isEqualTo(2); + assertThat(usersFound.containsAll(getUsers("jonDoe", "cookieDoe"))).isTrue(); + } + + @Test + public void findUniqueReturnsResult() { + User jonDoe = usersTemplate.findUnique("SELECT u FROM /Users u WHERE u.username = $1", "jonDoe"); + + assertThat(jonDoe).isNotNull(); + assertThat(jonDoe).isEqualTo(getUser("jonDoe")); + } + + @Test(expected = InvalidDataAccessApiUsageException.class) + public void findUniqueReturnsNoResult() { + usersTemplate.findUnique("SELECT u FROM /Users u WHERE u.username = $1", "benDover"); + } + + @Test(expected = InvalidDataAccessApiUsageException.class) + public void findUnqiueReturnsTooManyResults() { + usersTemplate.findUnique("SELECT u FROM /Users u WHERE u.username LIKE $1", "%Doe"); + } + + @Configuration + static class GemfireTemplateConfiguration { + + Properties gemfireProperties() { + Properties gemfireProperties = new Properties(); + + gemfireProperties.setProperty("name", applicationName()); + gemfireProperties.setProperty("mcast-port", "0"); + gemfireProperties.setProperty("log-level", logLevel()); + return gemfireProperties; + } + + String applicationName() { + return GemfireTemplateIntegrationTests.class.getName(); + } + + String logLevel() { + return System.getProperty("gemfire.log-level", DEFAULT_GEMFIRE_LOG_LEVEL); + } + + @Bean + CacheFactoryBean gemfireCache() { + CacheFactoryBean gemfireCache = new CacheFactoryBean(); + + gemfireCache.setClose(false); + gemfireCache.setProperties(gemfireProperties()); + + return gemfireCache; + } + + @Bean(name = "Users") + LocalRegionFactoryBean usersRegion(GemFireCache gemfireCache) { + LocalRegionFactoryBean usersRegion = new LocalRegionFactoryBean(); + + usersRegion.setCache(gemfireCache); + usersRegion.setClose(false); + usersRegion.setPersistent(false); + + return usersRegion; + } + + @Bean + GemfireTemplate usersRegionTemplate(Region simple) { + return new GemfireTemplate(simple); + } + } +} diff --git a/src/test/java/org/springframework/data/gemfire/GemfireTemplateQueriesOnGroupedPooledClientCacheRegionsIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/GemfireTemplateQueriesOnGroupedPooledClientCacheRegionsIntegrationTests.java new file mode 100644 index 00000000..d638cf49 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/GemfireTemplateQueriesOnGroupedPooledClientCacheRegionsIntegrationTests.java @@ -0,0 +1,401 @@ +/* + * Copyright 2016 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.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; + +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.GemFireCache; +import com.gemstone.gemfire.cache.client.ClientRegionShortcut; +import com.gemstone.gemfire.cache.client.Pool; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import org.springframework.data.annotation.Id; +import org.springframework.data.gemfire.client.ClientCacheFactoryBean; +import org.springframework.data.gemfire.client.ClientRegionFactoryBean; +import org.springframework.data.gemfire.client.PoolFactoryBean; +import org.springframework.data.gemfire.mapping.Region; +import org.springframework.data.gemfire.process.ProcessExecutor; +import org.springframework.data.gemfire.process.ProcessWrapper; +import org.springframework.data.gemfire.server.CacheServerFactoryBean; +import org.springframework.data.gemfire.support.ConnectionEndpoint; +import org.springframework.data.gemfire.support.ConnectionEndpointList; +import org.springframework.data.gemfire.test.support.ClientServerIntegrationTestsSupport; +import org.springframework.data.gemfire.util.PropertiesBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import lombok.Data; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +/** + * Integrations tests for {@link GemfireTemplate} testing proper function and behavior of executing (OQL) queries + * from a cache client application using the {@link GemfireTemplate} to a cluster of GemFire servers that have + * been grouped according to business function and data access, primarily to distribute the load. + * + * Each GemFire {@link Pool} is configured to target a specific server group. Each group of servers in the cluster + * defines specific {@link Region Regions} to manage data independently and separately from other data that might + * garner high frequency access. + * + * Spring Data GemFire's {@link GemfireTemplate} should intelligently employ the right + * {@link com.gemstone.gemfire.cache.query.QueryService} configured with the {@link Region Region's} {@link Pool} + * meta-data when executing the query in order to ensure the right servers containing the {@link Region Region's} + * with the data of interest are targeted. + * + * @author John Blum + * @see org.springframework.data.gemfire.GemfireTemplate + * @see Repository queries on client Regions associated with a Pool configured with a specified server group can lead to a RegionNotFoundException. + * @since 1.9.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = + GemfireTemplateQueriesOnGroupedPooledClientCacheRegionsIntegrationTests.GemFireClientCacheConfiguration.class) +@SuppressWarnings("unused") +public class GemfireTemplateQueriesOnGroupedPooledClientCacheRegionsIntegrationTests + extends ClientServerIntegrationTestsSupport{ + + protected static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning"; + + private static ProcessWrapper serverOne; + private static ProcessWrapper serverTwo; + + @Autowired + @Qualifier("catsTemplate") + private GemfireTemplate catsTemplate; + + @Autowired + @Qualifier("dogsTemplate") + private GemfireTemplate dogsTemplate; + + @BeforeClass + public static void setupGemFireCluster() throws Exception { + serverOne = ProcessExecutor.launch(createDirectory("serverOne"), GemFireCacheServerOneConfiguration.class); + waitForCacheServerToStart("localhost", 41414); + serverTwo = ProcessExecutor.launch(createDirectory("serverTwo"), GemFireCacheServerTwoConfiguration.class); + waitForCacheServerToStart("localhost", 42424); + } + + @AfterClass + public static void shutdownGemFireCluster() { + serverOne.stop(); + serverTwo.stop(); + } + + @Test + public void findsAllCats() { + List catNames = catsTemplate.find("SELECT c.name FROM /Cats c").asList(); + + assertThat(catNames).isNotNull(); + assertThat(catNames.size()).isEqualTo(5); + assertThat(catNames).containsAll(Arrays.asList("Grey", "Patchit", "Tyger", "Molly", "Sammy")); + } + + @Test + public void findsAllDogs() { + List dogNames = dogsTemplate.find("SELECT d.name FROM /Dogs d").asList(); + + assertThat(dogNames).isNotNull(); + assertThat(dogNames.size()).isEqualTo(2); + assertThat(dogNames).containsAll(Arrays.asList("Spuds", "Maha")); + } + + @Data + @Region("Cats") + @RequiredArgsConstructor(staticName = "newCat") + static class Cat { + @Id @NonNull private String name; + } + + @Data + @Region("Dogs") + @RequiredArgsConstructor(staticName = "newDog") + static class Dog { + @Id @NonNull private String name; + } + + @Configuration + static class GemFireClientCacheConfiguration { + + Properties gemfireProperties() { + return PropertiesBuilder.create() + .setProperty("name", applicationName()) + .setProperty("log-level", logLevel()) + .build(); + } + + String applicationName() { + return GemFireClientCacheConfiguration.class.getName(); + } + + String logLevel() { + return System.getProperty("gemfire.log.level", DEFAULT_GEMFIRE_LOG_LEVEL); + } + + @Bean + ClientCacheFactoryBean gemfireCache() { + ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean(); + + gemfireCache.setClose(true); + gemfireCache.setPoolName("ServerOnePool"); + gemfireCache.setProperties(gemfireProperties()); + + return gemfireCache; + } + + @Bean(name = "ServerOnePool") + PoolFactoryBean serverOnePool() { + PoolFactoryBean serverOnePool = new PoolFactoryBean(); + + serverOnePool.setMaxConnections(2); + serverOnePool.setPingInterval(TimeUnit.SECONDS.toMillis(5)); + serverOnePool.setReadTimeout(Long.valueOf(TimeUnit.SECONDS.toMillis(20)).intValue()); + serverOnePool.setRetryAttempts(1); + serverOnePool.setServerGroup("serverOne"); + serverOnePool.setLocators(ConnectionEndpointList.from(newConnectionEndpoint("localhost", 11235))); + + return serverOnePool; + } + + @Bean(name = "ServerTwoPool") + PoolFactoryBean serverTwoPool() { + PoolFactoryBean serverOnePool = new PoolFactoryBean(); + + serverOnePool.setMaxConnections(2); + serverOnePool.setPingInterval(TimeUnit.SECONDS.toMillis(5)); + serverOnePool.setReadTimeout(Long.valueOf(TimeUnit.SECONDS.toMillis(20)).intValue()); + serverOnePool.setRetryAttempts(1); + serverOnePool.setServerGroup("serverTwo"); + serverOnePool.setLocators(ConnectionEndpointList.from(newConnectionEndpoint("localhost", 11235))); + + return serverOnePool; + } + + @Bean(name = "Cats") + ClientRegionFactoryBean catsRegion(GemFireCache gemfireCache, + @Qualifier("ServerOnePool") Pool serverOnePool) { + + ClientRegionFactoryBean catsRegion = new ClientRegionFactoryBean(); + + catsRegion.setCache(gemfireCache); + catsRegion.setClose(false); + catsRegion.setPoolName(serverOnePool.getName()); + catsRegion.setShortcut(ClientRegionShortcut.PROXY); + + return catsRegion; + } + + @Bean(name = "Dogs") + ClientRegionFactoryBean dogsRegion(GemFireCache gemfireCache, + @Qualifier("ServerTwoPool") Pool serverTwoPool) { + + ClientRegionFactoryBean dogsRegion = new ClientRegionFactoryBean(); + + dogsRegion.setCache(gemfireCache); + dogsRegion.setClose(false); + dogsRegion.setPoolName(serverTwoPool.getName()); + dogsRegion.setShortcut(ClientRegionShortcut.PROXY); + + return dogsRegion; + } + + @Bean + @DependsOn("Cats") + GemfireTemplate catsTemplate(GemFireCache gemfireCache) { + return new GemfireTemplate(gemfireCache.getRegion("Cats")); + } + + @Bean + @DependsOn("Dogs") + GemfireTemplate dogsTemplate(GemFireCache gemfireCache) { + return new GemfireTemplate(gemfireCache.getRegion("Dogs")); + } + + ConnectionEndpoint newConnectionEndpoint(String host, int port) { + return new ConnectionEndpoint(host, port); + } + } + + static abstract class AbstractGemFireCacheServerConfiguration { + + Properties gemfireProperties() { + return PropertiesBuilder.create() + .setProperty("name", applicationName()) + .setProperty("mcast-port", "0") + .setProperty("log-level", logLevel()) + .setProperty("locators", "localhost[11235]") + .setProperty("groups", groups()) + .setProperty("start-locator", startLocator()) + .build(); + } + + String applicationName() { + return getClass().getName(); + } + + abstract String groups(); + + String logLevel() { + return System.getProperty("gemfire.log.level", DEFAULT_GEMFIRE_LOG_LEVEL); + } + + String startLocator() { + return ""; + } + + @Bean + CacheFactoryBean gemfireCache() { + CacheFactoryBean gemfireCache = new CacheFactoryBean(); + + gemfireCache.setClose(true); + gemfireCache.setProperties(gemfireProperties()); + + return gemfireCache; + } + + @Bean + CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache) { + CacheServerFactoryBean gemfireCacheServer = new CacheServerFactoryBean(); + + gemfireCacheServer.setAutoStartup(true); + gemfireCacheServer.setCache(gemfireCache); + gemfireCacheServer.setMaxTimeBetweenPings(Long.valueOf(TimeUnit.SECONDS.toMillis(60)).intValue()); + gemfireCacheServer.setPort(cacheServerPort()); + + return gemfireCacheServer; + } + + abstract int cacheServerPort(); + + } + + @Configuration + @SuppressWarnings("all") + static class GemFireCacheServerOneConfiguration extends AbstractGemFireCacheServerConfiguration { + + @Resource(name = "Cats") + private com.gemstone.gemfire.cache.Region cats; + + Cat save(Cat cat) { + cats.put(cat.getName(), cat); + return cat; + } + + @PostConstruct + public void postConstruct() { + save(Cat.newCat("Grey")); + save(Cat.newCat("Patchit")); + save(Cat.newCat("Tyger")); + save(Cat.newCat("Molly")); + save(Cat.newCat("Sammy")); + } + + @Override + int cacheServerPort() { + return 41414; + } + + @Override + String groups() { + return "serverOne"; + } + + @Override + String startLocator() { + return "localhost[11235]"; + } + + @Bean(name = "Cats") + LocalRegionFactoryBean catsRegion(Cache gemfireCache) { + LocalRegionFactoryBean catsRegion = new LocalRegionFactoryBean(); + + catsRegion.setCache(gemfireCache); + catsRegion.setClose(false); + catsRegion.setPersistent(false); + + return catsRegion; + } + + public static void main(String[] args) { + new AnnotationConfigApplicationContext(GemFireCacheServerOneConfiguration.class) + .registerShutdownHook(); + } + } + + @Configuration + @SuppressWarnings("all") + static class GemFireCacheServerTwoConfiguration extends AbstractGemFireCacheServerConfiguration { + + @Resource(name = "Dogs") + private com.gemstone.gemfire.cache.Region dogs; + + Dog save(Dog dog) { + dogs.put(dog.getName(), dog); + return dog; + } + + @PostConstruct + public void postConstruct() { + save(Dog.newDog("Spuds")); + save(Dog.newDog("Maha")); + } + + @Override + int cacheServerPort() { + return 42424; + } + + @Override + String groups() { + return "serverTwo"; + } + + @Bean(name = "Dogs") + LocalRegionFactoryBean dogsRegion(Cache gemfireCache) { + LocalRegionFactoryBean dogsRegion = new LocalRegionFactoryBean(); + + dogsRegion.setCache(gemfireCache); + dogsRegion.setClose(false); + dogsRegion.setPersistent(false); + + return dogsRegion; + } + + public static void main(String[] args) { + new AnnotationConfigApplicationContext(GemFireCacheServerTwoConfiguration.class) + .registerShutdownHook(); + } + } +} diff --git a/src/test/java/org/springframework/data/gemfire/GemfireTemplateTest.java b/src/test/java/org/springframework/data/gemfire/GemfireTemplateTest.java deleted file mode 100644 index 0af6f2ac..00000000 --- a/src/test/java/org/springframework/data/gemfire/GemfireTemplateTest.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright 2011-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.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.assertSame; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -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.util.concurrent.atomic.AtomicBoolean; -import javax.annotation.Resource; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.gemfire.test.AbstractMockerySupport; -import org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import com.gemstone.gemfire.GemFireCheckedException; -import com.gemstone.gemfire.GemFireException; -import com.gemstone.gemfire.cache.Region; -import com.gemstone.gemfire.cache.RegionAttributes; -import com.gemstone.gemfire.cache.Scope; -import com.gemstone.gemfire.cache.client.ClientCache; -import com.gemstone.gemfire.cache.query.FunctionDomainException; -import com.gemstone.gemfire.cache.query.NameResolutionException; -import com.gemstone.gemfire.cache.query.Query; -import com.gemstone.gemfire.cache.query.QueryInvocationTargetException; -import com.gemstone.gemfire.cache.query.QueryService; -import com.gemstone.gemfire.cache.query.SelectResults; -import com.gemstone.gemfire.cache.query.TypeMismatchException; - -/** - * The GemfireTemplateTest class is a test suite of test cases testing the contract and functionality of the SDG - * GemfireTemplate class. - * - * @author Costin Leau - * @author David Turanski - * @author John Blum - * @see org.junit.Test - * @see org.junit.runner.RunWith - * @see org.springframework.data.gemfire.GemfireTemplate - * @see org.springframework.data.gemfire.test.AbstractMockerySupport - * @see org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer - * @see org.springframework.test.context.ContextConfiguration - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = "basic-template.xml", initializers = GemfireTestApplicationContextInitializer.class) -@SuppressWarnings("unused") -public class GemfireTemplateTest extends AbstractMockerySupport { - - private static final String MULTI_QUERY = "SELECT * FROM /simple"; - private static final String SINGLE_QUERY = "(SELECT * FROM /simple).size"; - - @Autowired - private GemfireTemplate template; - - @Resource(name = "simple") - private Region simple; - - @Before - @SuppressWarnings("rawtypes") - public void setUp() throws FunctionDomainException, TypeMismatchException, NameResolutionException, QueryInvocationTargetException { - if (isMocking()) { - QueryService queryService = simple.getRegionService().getQueryService(); - Query singleQuery = mock(Query.class); - - when(singleQuery.execute(any(Object[].class))).thenReturn(0); - when(queryService.newQuery(SINGLE_QUERY)).thenReturn(singleQuery); - - Query multipleQuery = mock(Query.class); - SelectResults selectResults = mock(SelectResults.class); - - when(multipleQuery.execute(any(Object[].class))).thenReturn(selectResults); - when(queryService.newQuery(MULTI_QUERY)).thenReturn(multipleQuery); - } - } - - @After - public void tearDown() { - template.setExposeNativeRegion(false); - } - - @Test - public void testConstructWithNonNullRegion() { - GemfireTemplate localTemplate = new GemfireTemplate(simple); - - assertNotNull(localTemplate); - assertSame(simple, localTemplate.getRegion()); - assertFalse(localTemplate.isExposeNativeRegion()); - } - - @Test(expected = IllegalArgumentException.class) - public void testConstructWithNullRegion() { - try { - new GemfireTemplate(null); - } - catch (IllegalArgumentException expected) { - assertEquals("The GemFire Cache Region is required.", expected.getMessage()); - throw expected; - } - } - - @Test - public void testExecuteUsingNativeRegion() { - template.setExposeNativeRegion(true); - - assertTrue(template.isExposeNativeRegion()); - assertSame(simple, template.getRegion()); - - final AtomicBoolean callbackInvoked = new AtomicBoolean(false); - - template.execute(new GemfireCallback() { - @Override - public Object doInGemfire(final Region region) throws GemFireCheckedException, GemFireException { - callbackInvoked.set(true); - assertSame(simple, region); - return null; - } - }); - - assertTrue(callbackInvoked.get()); - } - - @Test - public void testExecuteUsingProxyRegion() { - assertFalse(template.isExposeNativeRegion()); - - final AtomicBoolean callbackInvoked = new AtomicBoolean(false); - - template.execute(new GemfireCallback() { - @Override - public Object doInGemfire(final Region region) throws GemFireCheckedException, GemFireException { - callbackInvoked.set(true); - assertNotSame(simple, region); - return null; - } - }); - - assertTrue(callbackInvoked.get()); - } - - @Test(expected = InvalidDataAccessApiUsageException.class) - public void testFindMultiException() throws Exception { - template.find(SINGLE_QUERY); - } - - @Test(expected = InvalidDataAccessApiUsageException.class) - public void testFindMultiOne() throws Exception { - template.findUnique(MULTI_QUERY); - } - - @Test - public void testFind() throws Exception { - assertNotNull(template.find(MULTI_QUERY)); - } - - @Test - public void testFindUnique() throws Exception { - assertEquals(0, template.findUnique(SINGLE_QUERY)); - } - - @Test - @SuppressWarnings("unchecked") - public void testLookupQueryService() { - ClientCache mockClientCache = mock(ClientCache.class, "testLookupQueryService.ClientCache"); - QueryService mockQueryService = mock(QueryService.class, "testLookupQueryService.QueryService"); - Region mockRegion = mock(Region.class, "testLookupQueryService.Region"); - - when(mockRegion.getRegionService()).thenReturn(mockClientCache); - when(mockClientCache.getQueryService()).thenReturn(mockQueryService); - - GemfireTemplate localTemplate = new GemfireTemplate(mockRegion) { - @Override boolean isLocalWithNoServerProxy(final Region region) { - return false; - } - }; - - assertSame(mockQueryService, localTemplate.lookupQueryService(mockRegion)); - - verify(mockRegion, times(2)).getRegionService(); - verify(mockClientCache, times(1)).getQueryService(); - verify(mockClientCache, never()).getLocalQueryService(); - } - - @Test - @SuppressWarnings("unchecked") - public void testLookupLocalQueryService() { - ClientCache mockClientCache = mock(ClientCache.class, "testLookupLocalQueryService.ClientCache"); - QueryService mockQueryService = mock(QueryService.class, "testLookupLocalQueryService.QueryService"); - Region mockRegion = mock(Region.class, "testLookupLocalQueryService.Region"); - RegionAttributes mockRegionAttributes = mock(RegionAttributes.class, "testLookupLocalQueryService.RegionAttributes"); - - when(mockClientCache.getLocalQueryService()).thenReturn(mockQueryService); - when(mockRegion.getRegionService()).thenReturn(mockClientCache); - when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); - when(mockRegionAttributes.getScope()).thenReturn(Scope.LOCAL); - - GemfireTemplate localTemplate = new GemfireTemplate(mockRegion) { - @Override boolean isLocalWithNoServerProxy(final Region region) { - return true; - } - }; - - assertSame(mockQueryService, localTemplate.lookupQueryService(mockRegion)); - - verify(mockClientCache, never()).getQueryService(); - verify(mockClientCache, times(1)).getLocalQueryService(); - verify(mockRegion, times(2)).getRegionService(); - verify(mockRegionAttributes, times(1)).getScope(); - } - -} diff --git a/src/test/java/org/springframework/data/gemfire/GemfireTemplateUnitTests.java b/src/test/java/org/springframework/data/gemfire/GemfireTemplateUnitTests.java new file mode 100644 index 00000000..179f6d05 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/GemfireTemplateUnitTests.java @@ -0,0 +1,356 @@ +/* + * Copyright 2011-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.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.atLeastOnce; +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.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.gemstone.gemfire.GemFireCheckedException; +import com.gemstone.gemfire.GemFireException; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.RegionAttributes; +import com.gemstone.gemfire.cache.RegionService; +import com.gemstone.gemfire.cache.Scope; +import com.gemstone.gemfire.cache.client.ClientCache; +import com.gemstone.gemfire.cache.query.Query; +import com.gemstone.gemfire.cache.query.QueryService; +import com.gemstone.gemfire.cache.query.SelectResults; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.gemfire.test.support.AbstractUnitAndIntegrationTestsWithMockSupport; + +/** + * Unit tests for {@link GemfireTemplate} + * + * @author Costin Leau + * @author David Turanski + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mock + * @see org.mockito.Mockito + * @see org.mockito.runners.MockitoJUnitRunner + * @see org.springframework.data.gemfire.GemfireTemplate + * @see AbstractUnitAndIntegrationTestsWithMockSupport + * @see org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer + * @see org.springframework.test.context.ContextConfiguration + */ +@RunWith(MockitoJUnitRunner.class) +@SuppressWarnings("unused") +public class GemfireTemplateUnitTests extends AbstractUnitAndIntegrationTestsWithMockSupport { + + @Rule + public ExpectedException exception = ExpectedException.none(); + + private GemfireTemplate template; + + @Mock + private Query mockQuery; + + @Mock + private QueryService mockQueryService; + + @Mock + private Region mockRegion; + + @Mock + private RegionService mockRegionService; + + @Before + public void setUp() throws Exception { + when(mockRegion.getRegionService()).thenReturn(mockRegionService); + when(mockRegionService.getQueryService()).thenReturn(mockQueryService); + when(mockQueryService.newQuery(anyString())).thenReturn(mockQuery); + + template = new GemfireTemplate(mockRegion); + } + + @Test + public void constructWithNonNullRegionIsSuccessful() { + GemfireTemplate localTemplate = new GemfireTemplate(mockRegion); + + assertThat(localTemplate).isNotNull(); + assertThat(localTemplate.getRegion()).isSameAs(mockRegion); + assertThat(localTemplate.isExposeNativeRegion()).isFalse(); + } + + @Test + public void constructWithNullRegionThrowsIllegalArgumentException() { + exception.expect(IllegalArgumentException.class); + exception.expectCause(is(nullValue(Throwable.class))); + exception.expectMessage("Region is required"); + + new GemfireTemplate(null); + } + + @Test + public void executeWithGemfireCallbackUsesNativeRegion() { + template.setExposeNativeRegion(true); + + assertThat(template.isExposeNativeRegion()).isTrue(); + assertThat(template.getRegion()).isSameAs(mockRegion); + + final AtomicBoolean callbackInvoked = new AtomicBoolean(false); + + template.execute(new GemfireCallback() { + @Override + public Object doInGemfire(Region region) throws GemFireCheckedException, GemFireException { + callbackInvoked.set(true); + assertThat(region).isSameAs(mockRegion); + return null; + } + }); + + assertThat(callbackInvoked.get()).isTrue(); + } + + @Test + public void executeWithGemfireCallbackUsesProxyRegion() { + assertThat(template.isExposeNativeRegion()).isFalse(); + + final AtomicBoolean callbackInvoked = new AtomicBoolean(false); + + template.execute(new GemfireCallback() { + @Override + public Object doInGemfire(Region region) throws GemFireCheckedException, GemFireException { + callbackInvoked.set(true); + assertThat(region).isNotSameAs(mockRegion); + return null; + } + }); + + assertThat(callbackInvoked.get()).isTrue(); + } + + @Test + public void queryCallsRegionQuery() throws Exception { + String expectedQuery = "SELECT * FROM /Example"; + + template.query(expectedQuery); + + verify(mockRegion, times(1)).query(eq(expectedQuery)); + } + + @Test + public void findIsSuccessful() throws Exception { + Object[] expectedParams = { "arg" }; + String expectedQuery = "SELECT * FROM /Example"; + + SelectResults mockSelectResults = mock(SelectResults.class); + + when(mockQuery.execute(any(Object[].class))).thenReturn(mockSelectResults); + + assertThat(template.find(expectedQuery, expectedParams)).isEqualTo(mockSelectResults); + + verify(mockRegion, atLeastOnce()).getRegionService(); + verify(mockRegionService, times(1)).getQueryService(); + verify(mockQueryService, times(1)).newQuery(eq(expectedQuery)); + verify(mockQuery, times(1)).execute(eq(expectedParams)); + verifyZeroInteractions(mockSelectResults); + } + + @Test(expected = InvalidDataAccessApiUsageException.class) + public void findWithSingleResultQueryThrowsInvalidDataAccessApiUsageException() throws Exception { + Object[] expectedParams = { "arg" }; + String expectedQuery = "SELECT 1 FROM /Example"; + + when(mockQuery.execute(any(Object[].class))).thenReturn(1); + + try { + template.find(expectedQuery, expectedParams); + } + finally { + verify(mockRegion, atLeastOnce()).getRegionService(); + verify(mockRegionService, times(1)).getQueryService(); + verify(mockQueryService, times(1)).newQuery(eq(expectedQuery)); + verify(mockQuery, times(1)).execute(eq(expectedParams)); + } + } + + @Test + public void findUniqueReturnsSelectResultsIsSuccessful() throws Exception { + Object[] expectedParams = { "arg" }; + String expectedQuery = "SELECT 1 FROM /Example"; + + SelectResults mockSelectResults = mock(SelectResults.class); + + when(mockQuery.execute(eq(expectedParams))).thenReturn(mockSelectResults); + when(mockSelectResults.asList()).thenReturn(Collections.singletonList(1)); + + assertThat(template.findUnique(expectedQuery, expectedParams)).isEqualTo(1); + + verify(mockRegion, atLeastOnce()).getRegionService(); + verify(mockRegionService, times(1)).getQueryService(); + verify(mockQueryService, times(1)).newQuery(eq(expectedQuery)); + verify(mockQuery, times(1)).execute(eq(expectedParams)); + verify(mockSelectResults, times(1)).asList(); + } + + @Test + public void findUniqueReturnsObjectIsSuccessful() throws Exception { + Object[] expectedParams = { "arg" }; + String expectedQuery = "SELECT 1 FROM /Example"; + + when(mockQuery.execute(eq(expectedParams))).thenReturn("test"); + + assertThat(template.findUnique(expectedQuery, expectedParams)).isEqualTo("test"); + + verify(mockRegion, atLeastOnce()).getRegionService(); + verify(mockRegionService, times(1)).getQueryService(); + verify(mockQueryService, times(1)).newQuery(eq(expectedQuery)); + verify(mockQuery, times(1)).execute(eq(expectedParams)); + } + + @Test(expected = InvalidDataAccessApiUsageException.class) + public void findUniqueWithMultiResultQueryThrowsInvalidDataAccessApiUsageException() throws Exception { + Object[] expectedParams = { "arg" }; + String expectedQuery = "SELECT 1 FROM /Example"; + + SelectResults mockSelectResults = mock(SelectResults.class); + + when(mockQuery.execute(eq(expectedParams))).thenReturn(mockSelectResults); + when(mockSelectResults.asList()).thenReturn(Arrays.asList(1, 2)); + + try { + assertThat(template.findUnique(expectedQuery, expectedParams)).isEqualTo(1); + } + finally { + verify(mockRegion, atLeastOnce()).getRegionService(); + verify(mockRegionService, times(1)).getQueryService(); + verify(mockQueryService, times(1)).newQuery(eq(expectedQuery)); + verify(mockQuery, times(1)).execute(eq(expectedParams)); + verify(mockSelectResults, times(1)).asList(); + } + } + + @Test + @SuppressWarnings("unchecked") + public void resolveClientQueryService() { + ClientCache mockClientCache = mock(ClientCache.class); + Region mockRegion = mock(Region.class); + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); + + when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); + when(mockRegionAttributes.getScope()).thenReturn(Scope.GLOBAL); + when(mockRegion.getRegionService()).thenReturn(mockClientCache); + when(mockClientCache.getQueryService()).thenReturn(mockQueryService); + + GemfireTemplate localTemplate = new GemfireTemplate(mockRegion) { + @Override boolean isLocalWithNoServerProxy(Region region) { + return false; + } + }; + + assertThat(localTemplate.resolveQueryService(mockRegion)).isSameAs(mockQueryService); + + verify(mockClientCache, never()).getLocalQueryService(); + verify(mockClientCache, times(1)).getQueryService(); + verify(mockClientCache, never()).getQueryService(anyString()); + verify(mockRegion, times(2)).getAttributes(); + verify(mockRegion, times(3)).getRegionService(); + verify(mockRegionAttributes, times(1)).getScope(); + verifyZeroInteractions(mockQueryService); + } + + @Test + @SuppressWarnings("unchecked") + public void resolvesClientLocalQueryService() { + ClientCache mockClientCache = mock(ClientCache.class); + Region mockRegion = mock(Region.class); + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); + + when(mockClientCache.getLocalQueryService()).thenReturn(mockQueryService); + when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); + when(mockRegion.getRegionService()).thenReturn(mockClientCache); + when(mockRegionAttributes.getScope()).thenReturn(Scope.LOCAL); + + GemfireTemplate localTemplate = new GemfireTemplate(mockRegion) { + @Override boolean isLocalWithNoServerProxy(Region region) { + return true; + } + }; + + assertThat(localTemplate.resolveQueryService(mockRegion)).isSameAs(mockQueryService); + + verify(mockClientCache, times(1)).getLocalQueryService(); + verify(mockClientCache, never()).getQueryService(); + verify(mockClientCache, never()).getQueryService(anyString()); + verify(mockRegion, times(2)).getRegionService(); + verify(mockRegionAttributes, times(1)).getScope(); + verifyZeroInteractions(mockQueryService); + } + + @Test + @SuppressWarnings("unchecked") + public void resolvesClientPooledQueryService() { + ClientCache mockClientCache = mock(ClientCache.class); + Region mockRegion = mock(Region.class); + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); + + when(mockClientCache.getQueryService(anyString())).thenReturn(mockQueryService); + when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); + when(mockRegion.getRegionService()).thenReturn(mockClientCache); + when(mockRegionAttributes.getPoolName()).thenReturn("TestPool"); + when(mockRegionAttributes.getScope()).thenReturn(Scope.LOCAL); + + GemfireTemplate localTemplate = new GemfireTemplate(mockRegion) { + @Override boolean isLocalWithNoServerProxy(Region region) { + return false; + } + }; + + assertThat(localTemplate.resolveQueryService(mockRegion)).isSameAs(mockQueryService); + + verify(mockClientCache, never()).getLocalQueryService(); + verify(mockClientCache, never()).getQueryService(); + verify(mockClientCache, times(1)).getQueryService(eq("TestPool")); + verify(mockRegion, times(2)).getRegionService(); + verify(mockRegionAttributes, times(2)).getPoolName(); + verify(mockRegionAttributes, times(1)).getScope(); + verifyZeroInteractions(mockQueryService); + } + + @Test + public void resolvePeerQueryService() { + assertThat(template.resolveQueryService(mockRegion)).isEqualTo(mockQueryService); + + verify(mockRegion, times(2)).getRegionService(); + verify(mockRegion, never()).getAttributes(); + verify(mockRegionService, times(1)).getQueryService(); + } +} diff --git a/src/test/java/org/springframework/data/gemfire/process/ProcessConfiguration.java b/src/test/java/org/springframework/data/gemfire/process/ProcessConfiguration.java index f7dbac27..edb9ac59 100644 --- a/src/test/java/org/springframework/data/gemfire/process/ProcessConfiguration.java +++ b/src/test/java/org/springframework/data/gemfire/process/ProcessConfiguration.java @@ -28,10 +28,12 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** - * The ProcessContext class is a container encapsulating configuration and context meta-data for a running process. + * The {@link ProcessConfiguration} class is a container encapsulating configuration and context meta-data + * for a running process. * * @author John Blum * @see java.lang.ProcessBuilder + * @see org.springframework.data.gemfire.process.ProcessExecutor * @since 1.5.0 */ @SuppressWarnings("unused") @@ -46,23 +48,23 @@ public class ProcessConfiguration { private final Map environment; public static ProcessConfiguration create(ProcessBuilder processBuilder) { - Assert.notNull(processBuilder, "The ProcessBuilder used to configure and start the Process must not be null!"); + Assert.notNull(processBuilder, "The ProcessBuilder used to configure and start the Process must not be null"); return new ProcessConfiguration(processBuilder.command(), processBuilder.directory(), processBuilder.environment(), processBuilder.redirectErrorStream()); } public ProcessConfiguration(List command, File workingDirectory, Map environment, - boolean redirectingErrorStream) { + boolean redirectErrorStream) { - Assert.notEmpty(command, "process command must be specified"); + Assert.notEmpty(command, "Process command must be specified"); Assert.isTrue(FileSystemUtils.isDirectory(workingDirectory), String.format( - "process working directory [%1$s] is not valid", workingDirectory)); + "Process working directory [%s] is not valid", workingDirectory)); this.command = new ArrayList(command); this.workingDirectory = workingDirectory; - this.redirectingErrorStream = redirectingErrorStream; + this.redirectingErrorStream = redirectErrorStream; this.environment = (environment != null ? Collections.unmodifiableMap(new HashMap(environment)) @@ -93,9 +95,8 @@ public class ProcessConfiguration { public String toString() { return "{ command = ".concat(getCommandString()) .concat(", workingDirectory = ".concat(getWorkingDirectory().getAbsolutePath())) - .concat(", redirectingErrorStream = ".concat(String.valueOf(isRedirectingErrorStream()))) .concat(", environment = ".concat(String.valueOf(getEnvironment()))) + .concat(", redirectingErrorStream = ".concat(String.valueOf(isRedirectingErrorStream()))) .concat(" }"); } - } diff --git a/src/test/java/org/springframework/data/gemfire/process/ProcessExecutor.java b/src/test/java/org/springframework/data/gemfire/process/ProcessExecutor.java index fae23cd1..8869cffb 100644 --- a/src/test/java/org/springframework/data/gemfire/process/ProcessExecutor.java +++ b/src/test/java/org/springframework/data/gemfire/process/ProcessExecutor.java @@ -28,11 +28,12 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** - * The ProcessExecutor class is a utility class for launching and running Java processes. + * The {@link ProcessExecutor} class is a utility class for launching and running Java processes. * * @author John Blum * @see java.lang.Process * @see java.lang.ProcessBuilder + * @see java.lang.System * @see org.springframework.data.gemfire.process.ProcessConfiguration * @see org.springframework.data.gemfire.process.ProcessWrapper * @since 1.5.0 @@ -68,7 +69,7 @@ public abstract class ProcessExecutor { processWrapper.register(new ProcessInputStreamListener() { @Override public void onInput(final String input) { - System.err.printf("[FORK-OUT] - %1$s%n", input); + System.err.printf("[FORK-OUT] - %s%n", input); } }); @@ -76,7 +77,7 @@ public abstract class ProcessExecutor { } protected static String[] buildCommand(String classpath, Class type, String... args) { - Assert.notNull(type, "The main Java class to launch must not be null!"); + Assert.notNull(type, "The main Java class to launch must not be null"); List command = new ArrayList(); List programArgs = Collections.emptyList(); @@ -125,7 +126,8 @@ public abstract class ProcessExecutor { protected static File validateDirectory(File workingDirectory) { Assert.isTrue(workingDirectory != null && (workingDirectory.isDirectory() || workingDirectory.mkdirs()), - String.format("Failed to create working directory [%1$s]", workingDirectory)); + String.format("Failed to create working directory [%s]", workingDirectory)); + return workingDirectory; } } diff --git a/src/test/java/org/springframework/data/gemfire/process/ProcessInputStreamListener.java b/src/test/java/org/springframework/data/gemfire/process/ProcessInputStreamListener.java index 75a4c9dc..bca05283 100644 --- a/src/test/java/org/springframework/data/gemfire/process/ProcessInputStreamListener.java +++ b/src/test/java/org/springframework/data/gemfire/process/ProcessInputStreamListener.java @@ -19,8 +19,8 @@ package org.springframework.data.gemfire.process; import java.util.EventListener; /** - * The ProcessStreamListener interface is a callback listener that gets called when input arrives from either a - * process's standard output steam or standard error stream. + * The {@link ProcessInputStreamListener} is a callback interface that gets called when input arrives from either a + * {@link Process process's} standard output steam or standard error stream. * * @author John Blum * @see java.util.EventListener @@ -28,6 +28,14 @@ import java.util.EventListener; */ public interface ProcessInputStreamListener extends EventListener { + /** + * Callback method that gets called when the {@link Process} sends output from either its standard out + * or standard error streams. + * + * @param input {@link String} containing output from the {@link Process} that this listener is listening to. + * @see java.lang.Process#getErrorStream() + * @see java.lang.Process#getInputStream() + */ void onInput(String input); } diff --git a/src/test/java/org/springframework/data/gemfire/process/ProcessWrapper.java b/src/test/java/org/springframework/data/gemfire/process/ProcessWrapper.java index 9072138c..fe84e331 100644 --- a/src/test/java/org/springframework/data/gemfire/process/ProcessWrapper.java +++ b/src/test/java/org/springframework/data/gemfire/process/ProcessWrapper.java @@ -73,7 +73,7 @@ public class ProcessWrapper { Assert.notNull(process, "Process must not be null"); Assert.notNull(processConfiguration, "The context and configuration meta-data providing details about" - + " the environment in which the process is running and how the process was configured must not be null!"); + + " the environment in which the process is running and how the process was configured must not be null"); this.process = process; this.processConfiguration = processConfiguration; @@ -103,8 +103,8 @@ public class ProcessWrapper { } } catch (IOException ignore) { - // ignore IO error and just stop reading from the process input stream - // IO error occurred most likely because the process was terminated + // Ignore IO error and just stop reading from the process input stream + // An IO error occurred most likely because the process was terminated } finally { IOUtils.close(inputReader); @@ -115,11 +115,14 @@ public class ProcessWrapper { } protected Thread newThread(String name, Runnable task) { - Assert.isTrue(!StringUtils.isEmpty(name), "Thread name must be specified"); + Assert.hasText(name, "Thread name must be specified"); Assert.notNull(task, "Thread task must not be null"); + Thread thread = new Thread(task, name); + thread.setDaemon(DEFAULT_DAEMON_THREAD); thread.setPriority(Thread.NORM_PRIORITY); + return thread; } @@ -175,7 +178,7 @@ public class ProcessWrapper { public String readLogFile() throws IOException { File[] logFiles = FileSystemUtils.listFiles(getWorkingDirectory(), new FileFilter() { - @Override public boolean accept(final File pathname) { + @Override public boolean accept(File pathname) { return (pathname != null && (pathname.isDirectory() || pathname.getAbsolutePath().endsWith(".log"))); } }); @@ -185,11 +188,11 @@ public class ProcessWrapper { } else { throw new FileNotFoundException(String.format( - "No log files were found in the process's working directory (%1$s)!", getWorkingDirectory())); + "No log files found in process's working directory [%s]", getWorkingDirectory())); } } - public String readLogFile(final File log) throws IOException { + public String readLogFile(File log) throws IOException { return FileUtils.read(log); } @@ -210,7 +213,7 @@ public class ProcessWrapper { ProcessUtils.signalStop(process); } catch (IOException e) { - log.warning("Failed to signal the process to stop!"); + log.warning("Failed to signal the process to stop"); if (log.isLoggable(Level.FINE)) { log.fine(ThrowableUtils.toString(e)); @@ -245,7 +248,7 @@ public class ProcessWrapper { while (!exited.get() && System.currentTimeMillis() < timeout) { try { exitValue = futureExitValue.get(milliseconds, TimeUnit.MILLISECONDS); - log.info(String.format("Process [%1$s] has stopped.%n", pid)); + log.info(String.format("Process [%s] has stopped%n", pid)); } catch (InterruptedException ignore) { interrupted = true; @@ -254,7 +257,7 @@ public class ProcessWrapper { } catch (TimeoutException e) { exitValue = -1; - log.warning(String.format("Process [%1$d] did not stop within the allotted timeout of %2$d seconds.%n", + log.warning(String.format("Process [%1$d] did not stop within the allotted timeout of %2$d seconds%n", pid, TimeUnit.MILLISECONDS.toSeconds(milliseconds))); } catch (Exception ignore) { @@ -277,7 +280,7 @@ public class ProcessWrapper { public int shutdown() { if (isRunning()) { - log.info(String.format("Stopping process [%1$d]...%n", safeGetPid())); + log.info(String.format("Stopping process [%d]...%n", safeGetPid())); signalStop(); waitFor(); } @@ -300,5 +303,4 @@ public class ProcessWrapper { } }); } - } diff --git a/src/test/java/org/springframework/data/gemfire/process/support/ProcessUtils.java b/src/test/java/org/springframework/data/gemfire/process/support/ProcessUtils.java index dcf238fd..03570895 100644 --- a/src/test/java/org/springframework/data/gemfire/process/support/ProcessUtils.java +++ b/src/test/java/org/springframework/data/gemfire/process/support/ProcessUtils.java @@ -37,8 +37,7 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** - * The ProcessUtils class is a utility class for working with process, or specifically instances - * of the Java Process class. + * The {@link ProcessUtils} class is a utility class for working with Operating System (OS) {@link Process processes}. * * @author John Blum * @see java.io.File @@ -74,8 +73,8 @@ public abstract class ProcessUtils { } } - throw new PidUnavailableException(String.format("process ID (PID) not available [%1$s]", - runtimeMXBeanName), cause); + throw new PidUnavailableException(String.format("Process ID (PID) not available [%s]", runtimeMXBeanName), + cause); } public static boolean isRunning(int processId) { @@ -120,7 +119,7 @@ public abstract class ProcessUtils { if (pidFile == null) { throw new PidUnavailableException(String.format( - "no PID file was found in working directory [%1$s] or any of it's sub-directories", + "No PID file was found in working directory [%s] or any of it's sub-directories", workingDirectory)); } @@ -130,7 +129,7 @@ public abstract class ProcessUtils { @SuppressWarnings("all") protected static File findPidFile(File workingDirectory) { Assert.isTrue(FileSystemUtils.isDirectory(workingDirectory), String.format( - "File [%1$s] is not a valid directory", workingDirectory)); + "File [%s] is not a valid directory", workingDirectory)); for (File file : workingDirectory.listFiles(DirectoryPidFileFilter.INSTANCE)) { if (file.isDirectory()) { @@ -148,7 +147,7 @@ public abstract class ProcessUtils { @SuppressWarnings("all") public static int readPid(File pidFile) { Assert.isTrue(pidFile != null && pidFile.isFile(), String.format( - "File [%1$s] is not a valid file", pidFile)); + "File [%s] is not a valid file", pidFile)); BufferedReader fileReader = null; String pidValue = null; @@ -156,6 +155,7 @@ public abstract class ProcessUtils { try { fileReader = new BufferedReader(new FileReader(pidFile)); pidValue = String.valueOf(fileReader.readLine()).trim(); + return Integer.parseInt(pidValue); } catch (FileNotFoundException e) { @@ -176,9 +176,9 @@ public abstract class ProcessUtils { @SuppressWarnings("all") public static void writePid(File pidFile, int pid) throws IOException { Assert.isTrue(pidFile != null && (pidFile.isFile() || pidFile.createNewFile()), String.format( - "File [%1$s] is not a valid file", pidFile)); + "File [%s] is not a valid file", pidFile)); - Assert.isTrue(pid > 0, String.format("PID [%1$d] must greater than 0", pid)); + Assert.isTrue(pid > 0, String.format("PID [%d] must greater than 0", pid)); PrintWriter fileWriter = new PrintWriter(new BufferedWriter(new FileWriter(pidFile, false), 16), true); @@ -210,5 +210,4 @@ public abstract class ProcessUtils { return (path != null && path.isFile() && path.getName().toLowerCase().endsWith(".pid")); } } - } diff --git a/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTest.java deleted file mode 100644 index ad39fc8a..00000000 --- a/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTest.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2012 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.repository.support; - -import static org.hamcrest.Matchers.hasItems; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.Collection; -import javax.annotation.Resource; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.gemfire.GemfireTemplate; -import org.springframework.data.gemfire.repository.sample.Person; -import org.springframework.data.repository.core.EntityInformation; -import org.springframework.data.repository.core.support.ReflectionEntityInformation; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import com.gemstone.gemfire.cache.Region; -import com.gemstone.gemfire.cache.RegionEvent; -import com.gemstone.gemfire.cache.query.SelectResults; -import com.gemstone.gemfire.cache.util.CacheListenerAdapter; - -/** - * Integration tests for {@link SimpleGemfireRepository}. - * - * @author Oliver Gierke - * @author John Blum - * @see org.junit.Test - * @see org.springframework.data.gemfire.repository.support.SimpleGemfireRepository - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration("../../basic-template.xml") -public class SimpleGemfireRepositoryIntegrationTest { - - @Autowired - GemfireTemplate template; - - @Resource(name = "simple") - Region simpleRegion; - - SimpleGemfireRepository repository; - - RegionClearListener regionClearListener; - - @Before - @SuppressWarnings("unchecked") - public void setUp() { - simpleRegion.clear(); - regionClearListener = new RegionClearListener(); - simpleRegion.getAttributesMutator().addCacheListener(regionClearListener); - EntityInformation information = new ReflectionEntityInformation(Person.class); - repository = new SimpleGemfireRepository(template, information); - } - - @Test - public void storeAndDeleteEntity() { - - Person person = new Person(1L, "Oliver", "Gierke"); - - repository.save(person); - - assertThat(repository.count(), is(1L)); - assertThat(repository.findOne(person.id), is(person)); - assertThat(repository.findAll().size(), is(1)); - - repository.delete(person); - - assertThat(repository.count(), is(0L)); - assertThat(repository.findOne(person.id), is(nullValue())); - assertThat(repository.findAll().size(), is(0)); - } - - @Test - public void testDeleteAllFiresClearEvent() { - assertFalse(regionClearListener.eventFired); - repository.deleteAll(); - assertTrue(regionClearListener.eventFired); - } - - @Test - public void queryRegion() throws Exception { - - Person person = new Person(1L, "Oliver", "Gierke"); - - template.put(1L, person); - - SelectResults persons = template.find("SELECT * FROM /simple s WHERE s.firstname = $1", - person.firstname); - - assertThat(persons.size(), is(1)); - assertThat(persons.iterator().next(), is(person)); - } - - @Test - public void findAllWithGivenIds() { - - Person dave = new Person(1L, "Dave", "Matthews"); - Person carter = new Person(2L, "Carter", "Beauford"); - Person leroi = new Person(3L, "Leroi", "Moore"); - - template.put(dave.id, dave); - template.put(carter.id, carter); - template.put(leroi.id, leroi); - - Collection result = repository.findAll(Arrays.asList(carter.id, leroi.id)); - assertThat(result, hasItems(carter, leroi)); - assertThat(result, not(hasItems(dave))); - } - - @Test - public void testSaveEntities() { - assertTrue(template.getRegion().isEmpty()); - - Person johnBlum = new Person(1l, "John", "Blum"); - Person jonBloom = new Person(2l, "Jon", "Bloom"); - Person juanBlume = new Person(3l, "Juan", "Blume"); - - repository.save(Arrays.asList(johnBlum, jonBloom, juanBlume)); - - assertFalse(template.getRegion().isEmpty()); - assertEquals(3, template.getRegion().size()); - - assertEquals(johnBlum, template.get(johnBlum.id)); - assertEquals(jonBloom, template.get(jonBloom.id)); - assertEquals(juanBlume, template.get(juanBlume.id)); - } - - @SuppressWarnings("rawtypes") - public static class RegionClearListener extends CacheListenerAdapter { - public boolean eventFired; - - @Override - public void afterRegionClear(RegionEvent ev) { - eventFired = true; - } - } - -} diff --git a/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTests.java new file mode 100644 index 00000000..8509c7a6 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTests.java @@ -0,0 +1,209 @@ +/* + * Copyright 2012 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.repository.support; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Properties; + +import javax.annotation.Resource; + +import com.gemstone.gemfire.cache.GemFireCache; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.RegionEvent; +import com.gemstone.gemfire.cache.query.SelectResults; +import com.gemstone.gemfire.cache.util.CacheListenerAdapter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.gemfire.CacheFactoryBean; +import org.springframework.data.gemfire.GemfireTemplate; +import org.springframework.data.gemfire.LocalRegionFactoryBean; +import org.springframework.data.gemfire.repository.sample.Person; +import org.springframework.data.repository.core.EntityInformation; +import org.springframework.data.repository.core.support.ReflectionEntityInformation; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Integration tests for {@link SimpleGemfireRepository}. + * + * @author Oliver Gierke + * @author John Blum + * @see org.junit.Test + * @see org.springframework.data.gemfire.repository.support.SimpleGemfireRepository + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@SuppressWarnings("unused") +public class SimpleGemfireRepositoryIntegrationTests { + + protected static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning"; + + @Autowired + private GemfireTemplate template; + + @Resource(name = "People") + private Region people; + + private RegionClearListener regionClearListener; + + private SimpleGemfireRepository repository; + + @Before + @SuppressWarnings("unchecked") + public void setUp() { + people.clear(); + regionClearListener = new RegionClearListener(); + people.getAttributesMutator().addCacheListener(regionClearListener); + EntityInformation information = new ReflectionEntityInformation(Person.class); + repository = new SimpleGemfireRepository(template, information); + } + + @Test + public void saveAndDeleteEntity() { + Person oliverGierke = new Person(1L, "Oliver", "Gierke"); + + assertThat(repository.save(oliverGierke)).isEqualTo(oliverGierke); + assertThat(repository.count()).isEqualTo(1L); + assertThat(repository.findOne(oliverGierke.getId())).isEqualTo(oliverGierke); + assertThat(repository.findAll()).isEqualTo(Collections.singletonList(oliverGierke)); + + repository.delete(oliverGierke); + + assertThat(repository.count()).isEqualTo(0L); + assertThat(repository.findOne(oliverGierke.getId())).isNull(); + assertThat(repository.findAll()).isEmpty(); + } + + @Test + public void deleteAllFiresClearEvent() { + assertThat(regionClearListener.eventFired).isFalse(); + repository.deleteAll(); + assertThat(regionClearListener.eventFired).isTrue(); + } + + @Test + public void queryRegion() throws Exception { + Person oliverGierke = new Person(1L, "Oliver", "Gierke"); + + assertThat(template.put(oliverGierke.getId(), oliverGierke)).isNull(); + + SelectResults people = template.find("SELECT * FROM /People p WHERE p.firstname = $1", + oliverGierke.getFirstname()); + + assertThat(people.size()).isEqualTo(1); + assertThat(people.iterator().next()).isEqualTo(oliverGierke); + } + + @Test + public void findAllWithGivenIds() { + Person dave = new Person(1L, "Dave", "Matthews"); + Person carter = new Person(2L, "Carter", "Beauford"); + Person leroi = new Person(3L, "Leroi", "Moore"); + + template.put(dave.getId(), dave); + template.put(carter.getId(), carter); + template.put(leroi.getId(), leroi); + + Collection result = repository.findAll(Arrays.asList(carter.getId(), leroi.getId())); + + assertThat(result).isNotNull(); + assertThat(result.size()).isEqualTo(2); + assertThat(result).containsAll(Arrays.asList(carter, leroi)); + } + + @Test + public void saveEntities() { + assertThat(template.getRegion()).isEmpty(); + + Person johnBlum = new Person(1l, "John", "Blum"); + Person jonBloom = new Person(2l, "Jon", "Bloom"); + Person juanBlume = new Person(3l, "Juan", "Blume"); + + repository.save(Arrays.asList(johnBlum, jonBloom, juanBlume)); + + assertThat(template.getRegion().size()).isEqualTo(3); + assertThat(template.get(johnBlum.getId())).isEqualTo(johnBlum); + assertThat(template.get(jonBloom.getId())).isEqualTo(jonBloom); + assertThat(template.get(juanBlume.getId())).isEqualTo(juanBlume); + } + + @SuppressWarnings("rawtypes") + public static class RegionClearListener extends CacheListenerAdapter { + + public volatile boolean eventFired; + + @Override + public void afterRegionClear(RegionEvent ev) { + eventFired = true; + } + } + + @Configuration + static class SimpleGemfireRepositoryConfiguration { + + Properties gemfireProperties() { + Properties gemfireProperties = new Properties(); + + gemfireProperties.setProperty("name", applicationName()); + gemfireProperties.setProperty("mcast-port", "0"); + gemfireProperties.setProperty("log-level", logLevel()); + return gemfireProperties; + } + + String applicationName() { + return SimpleGemfireRepositoryIntegrationTests.class.getName(); + } + + String logLevel() { + return System.getProperty("gemfire.log-level", DEFAULT_GEMFIRE_LOG_LEVEL); + } + + @Bean + CacheFactoryBean gemfireCache() { + CacheFactoryBean gemfireCache = new CacheFactoryBean(); + + gemfireCache.setClose(false); + gemfireCache.setProperties(gemfireProperties()); + + return gemfireCache; + } + + @Bean(name = "People") + LocalRegionFactoryBean peopleRegion(GemFireCache gemfireCache) { + LocalRegionFactoryBean peopleRegion = new LocalRegionFactoryBean(); + + peopleRegion.setCache(gemfireCache); + peopleRegion.setClose(false); + peopleRegion.setPersistent(false); + + return peopleRegion; + } + + @Bean + GemfireTemplate peopleRegionTemplate(Region people) { + return new GemfireTemplate(people); + } + } +} diff --git a/src/test/java/org/springframework/data/gemfire/test/AbstractGemFireClientServerIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/test/AbstractGemFireClientServerIntegrationTest.java index cf6654f8..2b49eb09 100644 --- a/src/test/java/org/springframework/data/gemfire/test/AbstractGemFireClientServerIntegrationTest.java +++ b/src/test/java/org/springframework/data/gemfire/test/AbstractGemFireClientServerIntegrationTest.java @@ -39,6 +39,7 @@ import org.springframework.util.Assert; * @see org.springframework.data.gemfire.process.ProcessWrapper * @since 1.8.0 */ +@Deprecated @SuppressWarnings("unused") public abstract class AbstractGemFireClientServerIntegrationTest { diff --git a/src/test/java/org/springframework/data/gemfire/test/AbstractMockerySupport.java b/src/test/java/org/springframework/data/gemfire/test/support/AbstractUnitAndIntegrationTestsWithMockSupport.java similarity index 57% rename from src/test/java/org/springframework/data/gemfire/test/AbstractMockerySupport.java rename to src/test/java/org/springframework/data/gemfire/test/support/AbstractUnitAndIntegrationTestsWithMockSupport.java index 887a6366..ab24dde1 100644 --- a/src/test/java/org/springframework/data/gemfire/test/AbstractMockerySupport.java +++ b/src/test/java/org/springframework/data/gemfire/test/support/AbstractUnitAndIntegrationTestsWithMockSupport.java @@ -14,39 +14,45 @@ * limitations under the License. */ -package org.springframework.data.gemfire.test; +package org.springframework.data.gemfire.test.support; -import org.springframework.data.gemfire.test.support.IdentifierSequence; -import org.springframework.data.gemfire.test.support.StackTraceUtils; +import org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer; import org.springframework.util.StringUtils; /** - * The AbstractMockery class is an abstract base class supporting the creation and use of mock objects in unit tests. + * {@link AbstractUnitAndIntegrationTestsWithMockSupport} is an abstract base class for test classes + * having support for the creation and use of mock objects in test cases, but that may also be ran + * as integration tests using actual GemFire components (e.g. Regions, etc). * * @author John Blum + * @see org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer * @since 1.5.3 */ @SuppressWarnings("unused") -public abstract class AbstractMockerySupport { +public abstract class AbstractUnitAndIntegrationTestsWithMockSupport { + + protected static final String GEMFIRE_TEST_RUNNER_NO_MOCK = + System.getProperty(GemfireTestApplicationContextInitializer.GEMFIRE_TEST_RUNNER_DISABLED, "false"); protected static final String NOT_IMPLEMENTED = "Not Implemented"; + /* (non-Javadoc) */ protected boolean isMocking() { - String gemfireTestRunnerNoMock = StringUtils.trimWhitespace(System.getProperty( - GemfireTestApplicationContextInitializer.GEMFIRE_TEST_RUNNER_DISABLED, "false")); + String gemfireTestRunnerNoMock = StringUtils.trimWhitespace(GEMFIRE_TEST_RUNNER_NO_MOCK); return !(Boolean.parseBoolean(gemfireTestRunnerNoMock) || "yes".equalsIgnoreCase(gemfireTestRunnerNoMock) || "y".equalsIgnoreCase(gemfireTestRunnerNoMock)); } + /* (non-Javadoc) */ protected boolean isNotMocking() { return !isMocking(); } + /* (non-Javadoc) */ protected Object getMockId() { StackTraceElement element = StackTraceUtils.getTestCaller(); - return (element != null ? StackTraceUtils.getCallerSimpleName(element): IdentifierSequence.nextId()); + return (element != null ? StackTraceUtils.getCallerSimpleName(element) : IdentifierSequence.nextId()); } - } diff --git a/src/test/java/org/springframework/data/gemfire/test/support/ClientServerIntegrationTestsSupport.java b/src/test/java/org/springframework/data/gemfire/test/support/ClientServerIntegrationTestsSupport.java new file mode 100644 index 00000000..09240ecc --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/test/support/ClientServerIntegrationTestsSupport.java @@ -0,0 +1,97 @@ +/* + * Copyright 2016 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.support; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.net.Socket; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.gemstone.gemfire.cache.server.CacheServer; + +/** + * The {@link ClientServerIntegrationTestsSupport} class is a abstract base class encapsulating common functionality + * to support the implementation of GemFire client/server tests. + * + * @author John Blum + * @see com.gemstone.gemfire.cache.server.CacheServer + * @see org.springframework.data.gemfire.test.support.SocketUtils + * @see org.springframework.data.gemfire.test.support.ThreadUtils + * @since 1.9.0 + */ +@SuppressWarnings("unused") +public class ClientServerIntegrationTestsSupport { + + protected static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(20); + protected static final long DEFAULT_WAIT_INTERVAL = 500L; + + /* (non-Javadoc) */ + protected static File createDirectory(String pathname) { + File directory = new File(pathname); + + assertThat(directory.isDirectory() || directory.mkdirs()) + .as(String.format("Failed to create directory [%s]", directory)).isTrue(); + + directory.deleteOnExit(); + + return directory; + } + + /* (non-Javadoc) */ + protected static boolean waitForCacheServerToStart(CacheServer cacheServer) { + return waitForCacheServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), DEFAULT_WAIT_DURATION); + } + + /* (non-Javadoc) */ + protected static boolean waitForCacheServerToStart(CacheServer cacheServer, long duration) { + return waitForCacheServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), duration); + } + + /* (non-Javadoc) */ + protected static boolean waitForCacheServerToStart(String host, int port) { + return waitForCacheServerToStart(host, port, DEFAULT_WAIT_DURATION); + } + + /* (non-Javadoc) */ + protected static boolean waitForCacheServerToStart(final String host, final int port, long duration) { + return ThreadUtils.timedWait(duration, DEFAULT_WAIT_INTERVAL, new ThreadUtils.WaitCondition() { + AtomicBoolean connected = new AtomicBoolean(false); + + public boolean waiting() { + Socket socket = null; + + try { + if (!connected.get()) { + socket = new Socket(host, port); + connected.set(true); + } + } + catch (IOException ignore) { + } + finally { + SocketUtils.close(socket); + } + + return !connected.get(); + } + }); + } +} diff --git a/src/test/java/org/springframework/data/gemfire/test/support/SocketUtils.java b/src/test/java/org/springframework/data/gemfire/test/support/SocketUtils.java new file mode 100644 index 00000000..fd761a1d --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/test/support/SocketUtils.java @@ -0,0 +1,69 @@ +/* + * Copyright 2016 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.support; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.logging.Logger; + +/** + * {@link SocketUtils} is a utility class for managing {@link Socket} and {@link ServerSocket} objects. + * + * @author John Blum + * @see java.net.ServerSocket + * @see java.net.Socket + * @since 1.9.0 + */ +@SuppressWarnings("unused") +public abstract class SocketUtils { + + private static final Logger log = Logger.getLogger(SocketUtils.class.getName()); + + /* (non-Javadoc) */ + public static boolean close(Socket socket) { + try { + if (socket != null) { + socket.close(); + return true; + } + } + catch (IOException ignore) { + log.warning(String.format("Failed to close Socket [%s]", socket)); + log.warning(ThrowableUtils.toString(ignore)); + } + + return false; + } + + /* (non-Javadoc) */ + public static boolean close(ServerSocket serverSocket) { + try { + if (serverSocket != null) { + serverSocket.close(); + return true; + } + } + catch (IOException ignore) { + log.warning(String.format("Failed to close ServerSocket [%s]", serverSocket)); + log.warning(ThrowableUtils.toString(ignore)); + } + + return false; + } +} 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 3e9d97dc..a516196b 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 @@ -19,7 +19,7 @@ package org.springframework.data.gemfire.test.support; import java.util.concurrent.TimeUnit; /** - * The ThreadUtils class is a utility class for working with Java Threads. + * {@link ThreadUtils} is an abstract utility class for managing Java {@link Thread Threads}. * * @author John Blum * @see java.lang.Thread @@ -28,46 +28,52 @@ import java.util.concurrent.TimeUnit; @SuppressWarnings("unused") public abstract class ThreadUtils { - public static boolean sleep(final long milliseconds) { + /* (non-Javadoc) */ + public static boolean sleep(long milliseconds) { try { Thread.sleep(milliseconds); return true; } catch (InterruptedException ignore) { + Thread.currentThread().interrupt(); return false; } } - public static void timedWait(final long duration) { - timedWait(duration, duration); + public static boolean timedWait(long duration) { + return timedWait(duration, duration); } - public static void timedWait(final long duration, final long interval) { - timedWait(duration, interval, new WaitCondition() { + public static boolean timedWait(long duration, long interval) { + return timedWait(duration, interval, new WaitCondition() { @Override public boolean waiting() { return true; } }); } - public static void timedWait(final long duration, long interval, final WaitCondition waitCondition) { + @SuppressWarnings("all") + public static boolean timedWait(long duration, long interval, WaitCondition waitCondition) { final long timeout = (System.currentTimeMillis() + duration); interval = Math.min(interval, duration); - while (waitCondition.waiting() && (System.currentTimeMillis() < timeout)) { - try { + try { + while (waitCondition.waiting() && (System.currentTimeMillis() < timeout)) { synchronized (waitCondition) { TimeUnit.MILLISECONDS.timedWait(waitCondition, interval); } } - catch (InterruptedException ignore) { - } } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + return !waitCondition.waiting(); } - public static interface WaitCondition { + // TODO rename interface to Condition and waiting() method to evaluate() + public interface WaitCondition { boolean waiting(); } - } diff --git a/src/test/resources/org/springframework/data/gemfire/GemfireTemplateIntegrationTest-context.xml b/src/test/resources/org/springframework/data/gemfire/GemfireTemplateIntegrationTest-context.xml deleted file mode 100644 index 4f0b7f22..00000000 --- a/src/test/resources/org/springframework/data/gemfire/GemfireTemplateIntegrationTest-context.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - GemfireTemplateIntegrationTest - 0 - warning - - - - - - - - - diff --git a/src/test/resources/org/springframework/data/gemfire/basic-template.xml b/src/test/resources/org/springframework/data/gemfire/basic-template.xml deleted file mode 100644 index 60b0e7ee..00000000 --- a/src/test/resources/org/springframework/data/gemfire/basic-template.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - SpringGemFireBasicTemplate - 0 - warning - - - - - - - - -