+ add gemfire template

This commit is contained in:
costin
2010-07-15 20:33:21 +03:00
parent 0403b64ce8
commit f11ff67aa5
3 changed files with 303 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2010 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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
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}.
* <p/>
* Not intended to be used directly.
*
* @author Costin Leau
*/
public class GemfireAccessor implements InitializingBean {
/** Logger available to subclasses */
protected final Log log = LogFactory.getLog(getClass());
private Region<?, ?> region;
public void afterPropertiesSet() {
if (getRegion() == null) {
throw new IllegalArgumentException("Property 'region' is required");
}
}
/**
* Converts the given {@link GemFireCheckedException} to an appropriate exception from the
* <code>org.springframework.dao</code> hierarchy.
* May be overridden in subclasses.
* @param ex GemFireCheckedException that occurred
* @return the corresponding DataAccessException instance
*/
public DataAccessException convertGemFireAccessException(GemFireCheckedException ex) {
return GemfireCacheUtils.convertGemfireAccessException(ex);
}
/**
* Converts the given {@link GemFireException} to an appropriate exception from the
* <code>org.springframework.dao</code> hierarchy.
* May be overridden in subclasses.
* @param ex GemFireException that occurred
* @return the corresponding DataAccessException instance
*/
public DataAccessException convertGemFireAccessException(GemFireException ex) {
return GemfireCacheUtils.convertGemfireAccessException(ex);
}
/**
* Returns the template region.
*
* @return the region
*/
public Region<?, ?> getRegion() {
return region;
}
/**
* Sets the template region.
*
* @param region the region to set
*/
public void setRegion(Region<?, ?> region) {
this.region = region;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2010 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 com.gemstone.gemfire.GemFireCheckedException;
import com.gemstone.gemfire.GemFireException;
import com.gemstone.gemfire.cache.Region;
/**
* Callback interface for GemFire code. To be used with {@link GemfireTemplate}'s execution methods, often as anonymous
* classes within a method implementation. A typical implementation will call Region.get/put/query to perform some
* operations on stored objects.
*
* @author Costin Leau
*/
public interface GemfireCallback<T> {
/**
* Gets called by {@link GemfireTemplate#execute(GemfireCallback)}. Does not need to care about handling transactions
* or exceptions.
* <p/>
* Allows for returning a result object created within the callback, i.e. a domain object or a collection of domain
* objects. A thrown custom RuntimeException is treated as an application exception: It gets propagated to the caller
* of the template.
*
* @param region GemFire Region
* @return a result object, or <tt>null</tt> if none
*/
<K, V> T doInGemfire(Region<K, V> region) throws GemFireCheckedException, GemFireException;
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright 2010 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.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.springframework.dao.DataAccessException;
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;
/**
* 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>
* exception hierarchy.
*
* <p/>
* The central method is <tt>execute</tt>, supporting GemFire access code implementing the GemfireCallback interface.
* It provides dedicated handling such that neither the GemfireCallback implementation nor the calling code needs to
* 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.
*
* @author Costin Leau
*/
public class GemfireTemplate extends GemfireAccessor {
private boolean exposeNativeRegion = false;
private Region<?, ?> regionProxy;
public GemfireTemplate() {
}
public GemfireTemplate(Region<?, ?> region) {
setRegion(region);
afterPropertiesSet();
}
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
regionProxy = createRegionProxy(getRegion());
}
/**
* Sets whether to expose the native Gemfire Region to GemfireCallback
* code. Default is "false": a Region proxy will be returned,
* suppressing <code>close</code> calls.
* <p>As there is often a need to cast to a interface, the exposed proxy
* implements all interfaces implemented by the original {@link Region}.
* If this is not sufficient, turn this flag to "true".
* @see GemfireCallback
*/
public void setExposeNativeRegion(boolean exposeNativeRegion) {
this.exposeNativeRegion = exposeNativeRegion;
}
/**
* Returns whether to expose the native GemFire Region to GemfireCallback
* code, or rather a Region proxy.
*/
public boolean isExposeNativeRegion() {
return this.exposeNativeRegion;
}
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
*/
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);
T result = action.doInGemfire(regionToExpose);
return result;
} catch (GemFireCheckedException ex) {
throw convertGemFireAccessException(ex);
} catch (GemFireException ex) {
throw convertGemFireAccessException(ex);
} catch (RuntimeException ex) {
// callback code threw application exception
throw ex;
}
}
/**
* Create a close-suppressing proxy for the given GemFire {@link Region}.
* Called by the <code>execute</code> method.
*
* @param pm the GemFire Region to create a proxy for
* @return the Region proxy, implementing all interfaces
* implemented by the passed-in Region object
* @see Region#close()
* @see #execute(GemfireCallback, boolean)
*/
@SuppressWarnings("unchecked")
protected <K, V> Region<K, V> createRegionProxy(Region<K, V> region) {
Class<?>[] ifcs = ClassUtils.getAllInterfacesForClass(region.getClass(), getClass().getClassLoader());
return (Region<K, V>) Proxy.newProxyInstance(region.getClass().getClassLoader(), ifcs,
new CloseSuppressingInvocationHandler(region));
}
/**
* Invocation handler that suppresses close calls on GemFire Regions.
* @see Region#close()
*/
private class CloseSuppressingInvocationHandler implements InvocationHandler {
private final Region<?, ?> target;
public CloseSuppressingInvocationHandler(Region<?, ?> target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Invocation on Region interface coming in...
if (method.getName().equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (method.getName().equals("hashCode")) {
// Use hashCode of PersistenceManager proxy.
return System.identityHashCode(proxy);
}
else if (method.getName().equals("close")) {
// Handle close method: suppress, not valid.
return null;
}
// Invoke method on target Region
try {
Object retVal = method.invoke(this.target, args);
return retVal;
} catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
}