SGF-555 - Repository queries on client Regions associated with a Pool configured for a specified server group can lead to a RegionNotFoundException.

This commit is contained in:
John Blum
2016-10-07 22:41:34 -07:00
parent 6a5f2f7a36
commit 6ee35c488d
24 changed files with 1728 additions and 928 deletions

View File

@@ -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 {
* <code>org.springframework.dao</code> hierarchy. Note that this particular implementation
* is called only for GemFire querying exception that do <b>NOT</b> 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);
}
}

View File

@@ -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 <tt>org.springframework.dao</tt>
@@ -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 <tt>org.springframework.dao</tt> exceptions.
* objects, query objects, and <tt>org.springframework.dao</tt> 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 <K, V> void create(final K key, final V value) {
public <K, V> 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 <K, V> V get(final K key) {
public <K, V> V get(K key) {
try {
return this.<K, V>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 <K, V> Map<K, V> getAll(final Collection<?> keys) {
public <K, V> Map<K, V> getAll(Collection<?> keys) {
try {
return this.<K, V>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 <K, V> V put(final K key, final V value) {
public <K, V> V put(K key, V value) {
try {
return this.<K, V>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 <K, V> void putAll(final Map<? extends K, ? extends V> map) {
public <K, V> void putAll(Map<? extends K, ? extends V> map) {
try {
this.<K, V>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 <K, V> V putIfAbsent(final K key, final V value) {
public <K, V> V putIfAbsent(K key, V value) {
try {
return this.<K, V>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 <K, V> V remove(final K key) {
public <K, V> V remove(K key) {
try {
return this.<K, V>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 <K, V> V replace(final K key, final V value) {
public <K, V> V replace(K key, V value) {
try {
return this.<K, V>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 <K, V> boolean replace(final K key, final V oldValue, final V newValue) {
public <K, V> boolean replace(K key, V oldValue, V newValue) {
try {
return this.<K, V>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 <E> SelectResults<E> query(final String query) {
public <E> SelectResults<E> 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 <E> SelectResults<E> find(final String queryString, final Object... params) throws InvalidDataAccessApiUsageException {
public <E> SelectResults<E> 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<E>) 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> T findUnique(final String queryString, final Object... params) throws InvalidDataAccessApiUsageException {
public <T> 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<T> selectResults = (SelectResults<T>) result;
List<T> 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> T execute(GemfireCallback<T> 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 <K, V> Region<K, V> createRegionProxy(final Region<K, V> region) {
return (Region<K, V>) Proxy.newProxyInstance(region.getClass().getClassLoader(),
ClassUtils.getAllInterfacesForClass(region.getClass(), getClass().getClassLoader()),
protected <K, V> Region<K, V> createRegionProxy(Region<K, V> region) {
Class<?> regionType = region.getClass();
return (Region<K, V>) Proxy.newProxyInstance(regionType.getClassLoader(),
ClassUtils.getAllInterfacesForClass(regionType, getClass().getClassLoader()),
new RegionCloseSuppressingInvocationHandler(region));
}
@@ -516,5 +535,4 @@ public class GemfireTemplate extends GemfireAccessor implements GemfireOperation
}
}
}
}

View File

@@ -50,6 +50,18 @@ public class PropertiesBuilder implements FactoryBean<Properties> {
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}.