Updated reference and JSON support for GemfireTemplate queries

This commit is contained in:
David Turanski
2013-03-13 18:00:15 -04:00
parent fb18299759
commit 0e81e9d4c8
14 changed files with 382 additions and 106 deletions

View File

@@ -0,0 +1,121 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.data.gemfire;
import java.util.Collection;
import java.util.Map;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.query.Query;
import com.gemstone.gemfire.cache.query.QueryService;
import com.gemstone.gemfire.cache.query.SelectResults;
/**
* @author David Turanski
*
*/
public interface GemfireOperations {
public abstract boolean containsKey(Object key);
public abstract boolean containsKeyOnServer(Object key);
public abstract boolean containsValue(Object value);
public abstract boolean containsValueForKey(Object key);
public abstract <K, V> void create(K key, V value);
public abstract <K, V> V get(K key);
public abstract <K, V> V put(K key, V value);
public abstract <K, V> V putIfAbsent(K key, V value);
public abstract <K, V> V remove(K key);
public abstract <K, V> V replace(K key, V value);
public abstract <K, V> boolean replace(K key, V oldValue, V newValue);
public abstract <K, V> Map<K, V> getAll(Collection<?> keys);
public abstract <K, V> void putAll(Map<? extends K, ? extends V> map);
/**
* Shortcut for {@link Region#query(String)} method. Filters the values of this region using the predicate given as a string with the syntax of the WHERE clause of the query language.
* The predefined variable this may be used inside the predicate to denote the current element being filtered.
* This method evaluates the passed in where clause and returns results. It is supported on servers as well as clients.
* When executed on a client, this method always runs on the server and returns results.
* When invoking this method from the client, applications can pass in a where clause or a complete query.
* @see Region#query(String)
* @param query A query language boolean query predicate.
* @return A SelectResults containing the values of this Region that match the predicate.
*/
public abstract <E> SelectResults<E> query(String query);
/**
* Executes a GemFire query with the given (optional) parameters and returns the result. Note this method expects the query to return multiple results; for queries that return only one
* element use {@link #findUnique(String, Object...)}.
* <p/>
* As oppose, to the {@link #query(String)} method, this method allows for more generic queries (against multiple regions even) to be executed.
*
* <p/>Note that the local query service is used if the region is configured as a client without any pool configuration or server connectivity - otherwise the query service on the default pool
* is being used.
*
* @see QueryService#newQuery(String)
* @see Query#execute(Object[])
* @see SelectResults
* @param query GemFire query
* @param params Values that are bound to parameters (such as $1) in this query.
* @return A {@link SelectResults} instance holding the objects matching the query
* @throws InvalidDataAccessApiUsageException in case the query returns a single result (not a {@link SelectResults}).
*/
public abstract <E> SelectResults<E> find(String query, Object... params) throws InvalidDataAccessApiUsageException;
/**
* Executes a GemFire query with the given (optional) parameters and returns the result. Note this method expects the query to return a single result; for queries that return multiple
* elements use {@link #find(String, Object...)}.
* <p/>
* As oppose, to the {@link #query(String)} method, this method allows for more generic queries (against multiple regions even) to be executed.
*
* <p/>Note that the local query service is used if the region is configured as a client without any pool configuration or server connectivity - otherwise the query service on the default pool
* is being used.
*
* @see QueryService#newQuery(String)
* @see Query#execute(Object[])
* @param query GemFire query
* @param params Values that are bound to parameters (such as $1) in this query.
* @return The (single) object that represents the result of the query.
* @throws InvalidDataAccessApiUsageException in case the query returns multiple objects (through {@link SelectResults}).
*/
public abstract <T> T findUnique(String query, Object... params) throws InvalidDataAccessApiUsageException;
public abstract <T> T execute(GemfireCallback<T> action) throws DataAccessException;
/**
* Execute the action specified by the given action object within a
* Region.
* @param action callback object that specifies the Gemfire action
* @param exposeNativeRegion whether to expose the native
* GemFire region to callback code
* @return a result object returned by the action, or <code>null</code>
* @throws org.springframework.dao.DataAccessException in case of GemFire errors
*/
public abstract <T> T execute(GemfireCallback<T> action, boolean exposeNativeRegion) throws DataAccessException;
}

View File

@@ -55,7 +55,7 @@ import com.gemstone.gemfire.internal.cache.LocalRegion;
*
* @author Costin Leau
*/
public class GemfireTemplate extends GemfireAccessor {
public class GemfireTemplate extends GemfireAccessor implements GemfireOperations {
private boolean exposeNativeRegion = false;
@@ -96,6 +96,10 @@ public class GemfireTemplate extends GemfireAccessor {
return this.exposeNativeRegion;
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#containsKey(java.lang.Object)
*/
@Override
public boolean containsKey(final Object key) {
return execute(new GemfireCallback<Boolean>() {
public Boolean doInGemfire(Region<?,?> region) throws GemFireCheckedException, GemFireException {
@@ -104,6 +108,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#containsKeyOnServer(java.lang.Object)
*/
@Override
public boolean containsKeyOnServer(final Object key) {
return execute(new GemfireCallback<Boolean>() {
public Boolean doInGemfire(Region<?,?> region) throws GemFireCheckedException, GemFireException {
@@ -112,6 +120,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#containsValue(java.lang.Object)
*/
@Override
public boolean containsValue(final Object value) {
return execute(new GemfireCallback<Boolean>() {
public Boolean doInGemfire(Region<?,?> region) throws GemFireCheckedException, GemFireException {
@@ -120,6 +132,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#containsValueForKey(java.lang.Object)
*/
@Override
public boolean containsValueForKey(final Object key) {
return execute(new GemfireCallback<Boolean>() {
public Boolean doInGemfire(Region<?,?> region) throws GemFireCheckedException, GemFireException {
@@ -128,6 +144,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#create(K, V)
*/
@Override
public <K, V> void create(final K key, final V value) {
execute(new GemfireCallback<Object>() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -138,6 +158,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#get(K)
*/
@Override
public <K, V> V get(final K key) {
return execute(new GemfireCallback<V>() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -147,6 +171,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#put(K, V)
*/
@Override
public <K, V> V put(final K key, final V value) {
return execute(new GemfireCallback<V>() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -156,6 +184,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#putIfAbsent(K, V)
*/
@Override
public <K, V> V putIfAbsent(final K key, final V value) {
return execute(new GemfireCallback<V>() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -165,6 +197,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#remove(K)
*/
@Override
public <K, V> V remove(final K key) {
return execute(new GemfireCallback<V>() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -174,6 +210,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#replace(K, V)
*/
@Override
public <K, V> V replace(final K key, final V value) {
return execute(new GemfireCallback<V>() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -183,6 +223,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/* (non-Javadoc)
* @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) {
return execute(new GemfireCallback<Boolean>() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -192,6 +236,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#getAll(java.util.Collection)
*/
@Override
public <K, V> Map<K, V> getAll(final Collection<?> keys) {
return execute(new GemfireCallback<Map<K, V>>() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -201,6 +249,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#putAll(java.util.Map)
*/
@Override
public <K, V> void putAll(final Map<? extends K, ? extends V> map) {
execute(new GemfireCallback<Object>() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -211,17 +263,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/**
* Shortcut for {@link Region#query(String)} method. Filters the values of this region using the predicate given as a string with the syntax of the WHERE clause of the query language.
* The predefined variable this may be used inside the predicate to denote the current element being filtered.
* This method evaluates the passed in where clause and returns results. It is supported on servers as well as clients.
* When executed on a client, this method always runs on the server and returns results.
* When invoking this method from the client, applications can pass in a where clause or a complete query.
* @see Region#query(String)
* @param query A query language boolean query predicate.
* @return A SelectResults containing the values of this Region that match the predicate.
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#query(java.lang.String)
*/
@Override
public <E> SelectResults<E> query(final String query) {
return execute(new GemfireCallback<SelectResults<E>>() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -231,23 +276,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/**
* Executes a GemFire query with the given (optional) parameters and returns the result. Note this method expects the query to return multiple results; for queries that return only one
* element use {@link #findUnique(String, Object...)}.
* <p/>
* As oppose, to the {@link #query(String)} method, this method allows for more generic queries (against multiple regions even) to be executed.
*
* <p/>Note that the local query service is used if the region is configured as a client without any pool configuration or server connectivity - otherwise the query service on the default pool
* is being used.
*
* @see QueryService#newQuery(String)
* @see Query#execute(Object[])
* @see SelectResults
* @param query GemFire query
* @param params Values that are bound to parameters (such as $1) in this query.
* @return A {@link SelectResults} instance holding the objects matching the query
* @throws InvalidDataAccessApiUsageException in case the query returns a single result (not a {@link SelectResults}).
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#find(java.lang.String, java.lang.Object)
*/
@Override
public <E> SelectResults<E> find(final String query, final Object... params)
throws InvalidDataAccessApiUsageException {
return execute(new GemfireCallback<SelectResults<E>>() {
@@ -265,22 +297,10 @@ public class GemfireTemplate extends GemfireAccessor {
});
}
/**
* Executes a GemFire query with the given (optional) parameters and returns the result. Note this method expects the query to return a single result; for queries that return multiple
* elements use {@link #find(String, Object...)}.
* <p/>
* As oppose, to the {@link #query(String)} method, this method allows for more generic queries (against multiple regions even) to be executed.
*
* <p/>Note that the local query service is used if the region is configured as a client without any pool configuration or server connectivity - otherwise the query service on the default pool
* is being used.
*
* @see QueryService#newQuery(String)
* @see Query#execute(Object[])
* @param query GemFire query
* @param params Values that are bound to parameters (such as $1) in this query.
* @return The (single) object that represents the result of the query.
* @throws InvalidDataAccessApiUsageException in case the query returns multiple objects (through {@link SelectResults}).
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#findUnique(java.lang.String, java.lang.Object)
*/
@Override
public <T> T findUnique(final String query, final Object... params) throws InvalidDataAccessApiUsageException {
return execute(new GemfireCallback<T>() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -289,8 +309,13 @@ public class GemfireTemplate extends GemfireAccessor {
Query q = queryService.newQuery(query);
Object result = q.execute(params);
if (result instanceof SelectResults) {
throw new InvalidDataAccessApiUsageException(
SelectResults<T> selectResults = (SelectResults<T>)result;
if (selectResults.asList().size() == 1) {
result = selectResults.iterator().next();
} else {
throw new InvalidDataAccessApiUsageException(
"Result object returned from GemfireCallback isn't unique: [" + result + "]");
}
}
return (T) result;
}
@@ -314,19 +339,18 @@ public class GemfireTemplate extends GemfireAccessor {
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#execute(org.springframework.data.gemfire.GemfireCallback)
*/
@Override
public <T> T execute(GemfireCallback<T> action) throws DataAccessException {
return execute(action, isExposeNativeRegion());
}
/**
* Execute the action specified by the given action object within a
* Region.
* @param action callback object that specifies the Gemfire action
* @param exposeNativeRegion whether to expose the native
* GemFire region to callback code
* @return a result object returned by the action, or <code>null</code>
* @throws org.springframework.dao.DataAccessException in case of GemFire errors
/* (non-Javadoc)
* @see org.springframework.data.gemfire.GemfireOperations#execute(org.springframework.data.gemfire.GemfireCallback, boolean)
*/
@Override
public <T> T execute(GemfireCallback<T> action, boolean exposeNativeRegion) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
try {

View File

@@ -17,6 +17,7 @@
package org.springframework.data.gemfire.support;
import org.springframework.dao.support.DaoSupport;
import org.springframework.data.gemfire.GemfireOperations;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.util.Assert;
@@ -37,7 +38,7 @@ import com.gemstone.gemfire.cache.Region;
*/
public class GemfireDaoSupport extends DaoSupport {
private GemfireTemplate gemfireTemplate;
private GemfireOperations gemfireTemplate;
/**
* Sets the GemFire Region to be used by this DAO.
@@ -57,7 +58,7 @@ public class GemfireDaoSupport extends DaoSupport {
* @return the new GemfireTemplate instance
* @see #setRegion
*/
protected GemfireTemplate createGemfireTemplate(Region<?, ?> region) {
protected GemfireOperations createGemfireTemplate(Region<?, ?> region) {
return new GemfireTemplate(region);
}
@@ -66,7 +67,7 @@ public class GemfireDaoSupport extends DaoSupport {
* as an alternative to specifying a GemFire {@link Region}.
* @see #setRegion
*/
public final void setGemfireTemplate(GemfireTemplate gemfireTemplate) {
public final void setGemfireTemplate(GemfireOperations gemfireTemplate) {
this.gemfireTemplate = gemfireTemplate;
}
@@ -74,7 +75,7 @@ public class GemfireDaoSupport extends DaoSupport {
* Return the GemfireTemplate for this DAO, pre-initialized
* with the Region or set explicitly.
*/
public final GemfireTemplate getGemfireTemplate() {
public final GemfireOperations getGemfireTemplate() {
return gemfireTemplate;
}

View File

@@ -26,9 +26,12 @@ import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.util.CollectionUtils;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.query.SelectResults;
import com.gemstone.gemfire.cache.query.internal.ResultsBag;
import com.gemstone.gemfire.pdx.JSONFormatter;
import com.gemstone.gemfire.pdx.PdxInstance;
@@ -84,7 +87,6 @@ public class JSONRegionAdvice {
+ "execution(* com.gemstone.gemfire.cache.Region.putIfAbsent(..)) ||"
+ "execution(* com.gemstone.gemfire.cache.Region.replace(..))")
public Object put(ProceedingJoinPoint pjp) {
System.out.println("intercepted " + pjp.getSignature().getName());
boolean JSONRegion = isIncludedSONRegion(pjp.getTarget());
Object retVal = null;
@@ -94,6 +96,8 @@ public class JSONRegionAdvice {
Object val = newArgs[1];
newArgs[1] = convertArgumentToPdxInstance(val);
retVal = pjp.proceed(newArgs);
log.debug("converting " + retVal + " to JSON string");
retVal = convertPdxInstanceToJSONString(retVal);
} else {
retVal = pjp.proceed();
}
@@ -128,22 +132,22 @@ public class JSONRegionAdvice {
}
@Around("execution(* com.gemstone.gemfire.cache.Region.get(..)) "
+ "|| execution(* com.gemstone.gemfire.cache.Region.selectValue(..))")
+ "|| execution(* com.gemstone.gemfire.cache.Region.selectValue(..))"
+ "|| execution(* com.gemstone.gemfire.cache.Region.remove(..))")
public Object get(ProceedingJoinPoint pjp) {
Object retVal = null;
try {
if (isIncludedSONRegion(pjp.getTarget())) {
retVal = pjp.proceed();
log.debug("converting " + retVal + " to JSON string");
return convertPdxInstanceToJSONString(retVal);
retVal = convertPdxInstanceToJSONString(retVal);
} else {
return pjp.proceed();
retVal = pjp.proceed();
}
} catch (Throwable t) {
handleThrowable(t);
}
return retVal;
}
@SuppressWarnings("unchecked")
@@ -185,6 +189,35 @@ public class JSONRegionAdvice {
}
return result;
}
@Around("execution(* org.springframework.data.gemfire.GemfireOperations.find(..)) " +
"|| execution(* org.springframework.data.gemfire.GemfireOperations.findUnique(..)) " +
"|| execution(* org.springframework.data.gemfire.GemfireOperations.query(..))")
public Object templateQuery(ProceedingJoinPoint pjp) {
GemfireTemplate template = (GemfireTemplate) pjp.getTarget();
boolean jsonRegion = isIncludedSONRegion(template.getRegion());
Object retVal = null;
try {
if (jsonRegion) {
retVal = pjp.proceed();
if (retVal instanceof SelectResults && convertReturnedCollections ) {
ResultsBag resultsBag = new ResultsBag();
for (Object obj: (SelectResults<?>)retVal) {
resultsBag.add(convertPdxInstanceToJSONString(obj));
}
retVal = resultsBag;
} else {
retVal = convertPdxInstanceToJSONString(retVal);
}
} else {
retVal = pjp.proceed();
}
} catch (Throwable t) {
handleThrowable(t);
}
return retVal;
}
private PdxInstance convertArgumentToPdxInstance(Object value) {
PdxInstance val = null;
@@ -220,9 +253,9 @@ public class JSONRegionAdvice {
Object result = retVal;
if (retVal != null && retVal instanceof PdxInstance) {
result = JSONFormatter.toJSON((PdxInstance) retVal);
}
if (!prettyPrint) {
result = flattenString(result);
if (!prettyPrint) {
result = flattenString(result);
}
}
return result;
}