From f11ff67aa55367cb74e3f0217c27232def70126b Mon Sep 17 00:00:00 2001 From: costin Date: Thu, 15 Jul 2010 20:33:21 +0300 Subject: [PATCH] + add gemfire template --- .../data/gemfire/GemfireAccessor.java | 87 +++++++++ .../data/gemfire/GemfireCallback.java | 44 +++++ .../data/gemfire/GemfireTemplate.java | 172 ++++++++++++++++++ 3 files changed, 303 insertions(+) create mode 100644 src/main/java/org/springframework/data/gemfire/GemfireAccessor.java create mode 100644 src/main/java/org/springframework/data/gemfire/GemfireCallback.java create mode 100644 src/main/java/org/springframework/data/gemfire/GemfireTemplate.java diff --git a/src/main/java/org/springframework/data/gemfire/GemfireAccessor.java b/src/main/java/org/springframework/data/gemfire/GemfireAccessor.java new file mode 100644 index 00000000..537c02b9 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/GemfireAccessor.java @@ -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}. + *

+ * 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 + * org.springframework.dao 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 + * org.springframework.dao 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; + } +} \ No newline at end of file diff --git a/src/main/java/org/springframework/data/gemfire/GemfireCallback.java b/src/main/java/org/springframework/data/gemfire/GemfireCallback.java new file mode 100644 index 00000000..73f11dfe --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/GemfireCallback.java @@ -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 { + + /** + * Gets called by {@link GemfireTemplate#execute(GemfireCallback)}. Does not need to care about handling transactions + * or exceptions. + *

+ * 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 null if none + */ + T doInGemfire(Region region) throws GemFireCheckedException, GemFireException; +} diff --git a/src/main/java/org/springframework/data/gemfire/GemfireTemplate.java b/src/main/java/org/springframework/data/gemfire/GemfireTemplate.java new file mode 100644 index 00000000..56b33d1b --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/GemfireTemplate.java @@ -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 org.springframework.dao + * exception hierarchy. + * + *

+ * The central method is execute, 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 org.springframework.dao 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 close calls. + *

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 execute(GemfireCallback 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 null + * @throws org.springframework.dao.DataAccessException in case of GemFire errors + */ + public T execute(GemfireCallback 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 execute 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 Region createRegionProxy(Region region) { + Class[] ifcs = ClassUtils.getAllInterfacesForClass(region.getClass(), getClass().getClassLoader()); + return (Region) 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(); + } + } + } +} \ No newline at end of file