+ initial commit
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.gemstone.gemfire.cache.Cache;
|
||||
import com.gemstone.gemfire.cache.CacheClosedException;
|
||||
import com.gemstone.gemfire.cache.CacheFactory;
|
||||
import com.gemstone.gemfire.distributed.DistributedMember;
|
||||
import com.gemstone.gemfire.distributed.DistributedSystem;
|
||||
|
||||
/**
|
||||
* Factory used for configuring a Gemfire Cache manager. Allows either retrieval of an existing, opened cache
|
||||
* or the creation of a new one.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanClassLoaderAware, DisposableBean,
|
||||
InitializingBean, FactoryBean<Cache> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(CacheFactoryBean.class);
|
||||
|
||||
private Cache cache;
|
||||
private String name;
|
||||
private Resource cacheXml;
|
||||
private Properties properties;
|
||||
private DistributedSystem system;
|
||||
private ClassLoader beanClassLoader;
|
||||
private GemfireBeanFactoryLocator factoryLocator = new GemfireBeanFactoryLocator();
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
private String beanName;
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// initialize locator
|
||||
factoryLocator.setBeanFactory(beanFactory);
|
||||
factoryLocator.setBeanName(beanName);
|
||||
factoryLocator.afterPropertiesSet();
|
||||
|
||||
Properties cfgProps = mergeProperties();
|
||||
system = DistributedSystem.connect(cfgProps);
|
||||
|
||||
DistributedMember member = system.getDistributedMember();
|
||||
log.info("Connected to Distributed System ['" + system.getName() + "'=" + member.getId() + "@"
|
||||
+ member.getHost() + "]");
|
||||
|
||||
// use the bean class loader to load Declarable classes
|
||||
Thread th = Thread.currentThread();
|
||||
ClassLoader oldTCCL = th.getContextClassLoader();
|
||||
|
||||
try {
|
||||
th.setContextClassLoader(beanClassLoader);
|
||||
// first look for open caches
|
||||
String msg = null;
|
||||
try {
|
||||
cache = CacheFactory.getInstance(system);
|
||||
msg = "Retrieved existing";
|
||||
} catch (CacheClosedException ex) {
|
||||
// fall back to cache creation
|
||||
cache = CacheFactory.create(system);
|
||||
msg = "Created";
|
||||
}
|
||||
|
||||
log.info(msg + " Gemfire Cache ['" + cache.getName() + "'] v. " + CacheFactory.getVersion());
|
||||
|
||||
// load/init cache.xml
|
||||
if (cacheXml != null) {
|
||||
cache.loadCacheXml(cacheXml.getInputStream());
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("Initialized cache from " + cacheXml);
|
||||
} finally {
|
||||
th.setContextClassLoader(oldTCCL);
|
||||
}
|
||||
}
|
||||
|
||||
private Properties mergeProperties() {
|
||||
Properties cfgProps = new Properties(properties);
|
||||
if (StringUtils.hasText(name)) {
|
||||
cfgProps.setProperty("name", name.trim());
|
||||
}
|
||||
return cfgProps;
|
||||
}
|
||||
|
||||
public void destroy() throws Exception {
|
||||
if (cache != null && !cache.isClosed()) {
|
||||
cache.close();
|
||||
}
|
||||
cache = null;
|
||||
|
||||
if (system != null && system.isConnected()) {
|
||||
system.releaseThreadsSockets();
|
||||
system.disconnect();
|
||||
}
|
||||
system = null;
|
||||
|
||||
factoryLocator.destroy();
|
||||
}
|
||||
|
||||
public Cache getObject() throws Exception {
|
||||
return cache;
|
||||
}
|
||||
|
||||
public Class<? extends Cache> getObjectType() {
|
||||
return (cache != null ? cache.getClass() : Cache.class);
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
public void setBeanName(String name) {
|
||||
this.beanName = name;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache properties.
|
||||
*
|
||||
* @param properties the properties to set
|
||||
*/
|
||||
public void setProperties(Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the cache.
|
||||
*
|
||||
* @param name the name to set
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache configuration.
|
||||
*
|
||||
* @param cacheXml the cacheXml to set
|
||||
*/
|
||||
public void setCacheXml(Resource cacheXml) {
|
||||
this.cacheXml = cacheXml;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.springframework.util.Assert;
|
||||
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
|
||||
/**
|
||||
* Client extension for Gemfire regions.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class ClientRegionFactoryBean<K, V> extends RegionFactoryBean<K, V> {
|
||||
|
||||
private Interest<K>[] interests;
|
||||
|
||||
@Override
|
||||
protected void postProcess(Region<K, V> region) {
|
||||
Assert.notEmpty(interests);
|
||||
|
||||
for (Interest<K> interest : interests) {
|
||||
if (interest instanceof RegexInterest) {
|
||||
// do the cast since it's safe
|
||||
region.registerInterestRegex((String) interest.getKey(), interest.getPolicy(), interest.isDurable());
|
||||
}
|
||||
else {
|
||||
region.registerInterest(interest.getKey(), interest.getPolicy(), interest.isDurable());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
Region<K, V> region = getObject();
|
||||
// unregister interests
|
||||
try {
|
||||
if (region != null) {
|
||||
for (Interest<K> interest : interests) {
|
||||
if (interest instanceof RegexInterest) {
|
||||
region.unregisterInterestRegex((String) interest.getKey());
|
||||
}
|
||||
else {
|
||||
region.unregisterInterest(interest.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
// should not really happen since interests are validated at start/registration
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
log.warn("Cannot unregister cache interests", ex);
|
||||
}
|
||||
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the interests for this client region. Both key and regex interest are supported.
|
||||
*
|
||||
* @param interests the interests to set
|
||||
*/
|
||||
public void setInterests(Interest<K>[] interests) {
|
||||
this.interests = interests;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the interests
|
||||
*/
|
||||
Interest<K>[] getInterests() {
|
||||
return interests;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
|
||||
import com.gemstone.gemfire.cache.CacheCallback;
|
||||
import com.gemstone.gemfire.cache.Declarable;
|
||||
|
||||
/**
|
||||
* Convenience class for Spring-aware GemFire Declarable components.
|
||||
* Provides a reference to the current Spring application context, e.g. for bean lookup or resource loading.
|
||||
*
|
||||
* Note that in most cases, one can just declare the same components as Spring beans, through
|
||||
* {@link RegionFactoryBean} which gives access to the full container capabilities and does not enforce the
|
||||
* {@link Declarable} interface to be implemented.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public abstract class DeclarableSupport implements CacheCallback, Declarable {
|
||||
|
||||
private String factoryKey = null;
|
||||
private BeanFactoryReference bfReference = null;
|
||||
|
||||
public DeclarableSupport() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation uses this method as a lifecycle hook to initialize
|
||||
* the bean factory locator.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see #setFactoryKey(String)
|
||||
*/
|
||||
public final void init(Properties props) {
|
||||
bfReference = new GemfireBeanFactoryLocator().useBeanFactory(factoryKey);
|
||||
initInstance(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the current instance based on the given properties.
|
||||
*
|
||||
* @param props
|
||||
*/
|
||||
protected void initInstance(Properties props) {
|
||||
}
|
||||
|
||||
protected BeanFactory getBeanFactory() {
|
||||
return bfReference.getFactory();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
bfReference.release();
|
||||
bfReference = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key under which the enclosing beanFactory can be found.
|
||||
* Needed only if multiple beanFactories are used with GemFire inside
|
||||
* the same class loader / class space.
|
||||
*
|
||||
* @see GemfireBeanFactoryLocator
|
||||
* @param key
|
||||
*/
|
||||
public void setFactoryKey(String key) {
|
||||
this.factoryKey = key;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link BeanFactoryLocator} used for storing Spring application context/bean factory for Gemfire
|
||||
* user components (or {@link com.gemstone.gemfire.cache.Declarable}. As opposed to the "traditional"
|
||||
* {@link org.springframework.beans.factory.access.SingletonBeanFactoryLocator} this implementation does
|
||||
* not require any configuration file; it rather assume declaration inside an application context
|
||||
* (usually through {@link com.gemstone.gemfire.cache.CacheFactory} which it will store under the name
|
||||
* and aliases of the bean (so the same "registry" can be used for storing multiple BeanFactories).
|
||||
* If there is only one BeanFactory registered then a null value can be used with {@link #setBeanName(String)}.
|
||||
*
|
||||
* <p/> In most cases, one does not need to use this class directly as it is used internally
|
||||
* by {@link com.gemstone.gemfire.cache.CacheFactory}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class GemfireBeanFactoryLocator implements BeanFactoryLocator, BeanFactoryAware, BeanNameAware, DisposableBean,
|
||||
InitializingBean {
|
||||
|
||||
private static final Log log = LogFactory.getLog(GemfireBeanFactoryLocator.class);
|
||||
|
||||
// alias/bean name <-> BeanFactory lookup
|
||||
private static final ConcurrentMap<String, BeanFactory> beanFactories = new ConcurrentHashMap<String, BeanFactory>();
|
||||
|
||||
// default factory to return
|
||||
private static volatile boolean canUseDefaultBeanFactory = true;
|
||||
private static volatile BeanFactory defaultFactory = null;
|
||||
|
||||
private static class SimpleBeanFactoryReference implements BeanFactoryReference {
|
||||
|
||||
private BeanFactory bf;
|
||||
|
||||
SimpleBeanFactoryReference(BeanFactory bf) {
|
||||
this.bf = bf;
|
||||
}
|
||||
|
||||
public BeanFactory getFactory() {
|
||||
Assert.notNull(bf, "beanFactory already released or closed");
|
||||
return bf;
|
||||
}
|
||||
|
||||
public void release() throws FatalBeanException {
|
||||
bf = null;
|
||||
}
|
||||
}
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
private String[] names;
|
||||
|
||||
// default factory name
|
||||
private String factoryName = GemfireBeanFactoryLocator.class.getName();
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
// add the factory as default if possible (if it's the only one)
|
||||
synchronized (GemfireBeanFactoryLocator.class) {
|
||||
canUseDefaultBeanFactory = beanFactories.isEmpty();
|
||||
if (canUseDefaultBeanFactory) {
|
||||
if (defaultFactory == null) {
|
||||
defaultFactory = beanFactory;
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("default beanFactoryReference=" + defaultFactory);
|
||||
}
|
||||
else {
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("more then one beanFactory - default not possible to determine");
|
||||
canUseDefaultBeanFactory = false;
|
||||
defaultFactory = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add aliases
|
||||
if (StringUtils.hasText(factoryName)) {
|
||||
String[] aliases = beanFactory.getAliases(factoryName);
|
||||
names = (String[]) ObjectUtils.addObjectToArray(aliases, factoryName);
|
||||
|
||||
for (String name : names) {
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("adding key=" + name + " w/ reference=" + beanFactory);
|
||||
|
||||
if (beanFactories.containsKey(name) || beanFactories.putIfAbsent(name, beanFactory) != null) {
|
||||
throw new IllegalArgumentException("a beanFactoryReference already exists for key " + factoryName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
for (String name : names) {
|
||||
beanFactories.remove(name);
|
||||
}
|
||||
|
||||
if (beanFactory == defaultFactory) {
|
||||
defaultFactory = null;
|
||||
}
|
||||
}
|
||||
|
||||
public BeanFactoryReference useBeanFactory(final String factoryKey) throws BeansException {
|
||||
// see if there is a default FactoryBean
|
||||
BeanFactory factory;
|
||||
|
||||
if (!StringUtils.hasText(factoryKey)) {
|
||||
if (!canUseDefaultBeanFactory)
|
||||
throw new IllegalArgumentException(
|
||||
"a non-null factoryKey needs to be specified as there are more then one factoryKeys available ");
|
||||
factory = defaultFactory;
|
||||
}
|
||||
else {
|
||||
factory = beanFactories.get(factoryKey);
|
||||
if (factory == null)
|
||||
throw new IllegalArgumentException("there is no beanFactory under key " + factoryKey);
|
||||
}
|
||||
|
||||
return new SimpleBeanFactoryReference(factory);
|
||||
}
|
||||
|
||||
public void setBeanName(String name) {
|
||||
factoryName = name;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.dao.PermissionDeniedDataAccessException;
|
||||
import org.springframework.dao.PessimisticLockingFailureException;
|
||||
import org.springframework.dao.TypeMismatchDataAccessException;
|
||||
|
||||
import com.gemstone.gemfire.CancelException;
|
||||
import com.gemstone.gemfire.CopyException;
|
||||
import com.gemstone.gemfire.GemFireCacheException;
|
||||
import com.gemstone.gemfire.GemFireCheckedException;
|
||||
import com.gemstone.gemfire.GemFireConfigException;
|
||||
import com.gemstone.gemfire.GemFireException;
|
||||
import com.gemstone.gemfire.GemFireIOException;
|
||||
import com.gemstone.gemfire.IncompatibleSystemException;
|
||||
import com.gemstone.gemfire.InternalGemFireException;
|
||||
import com.gemstone.gemfire.InvalidValueException;
|
||||
import com.gemstone.gemfire.LicenseException;
|
||||
import com.gemstone.gemfire.NoSystemException;
|
||||
import com.gemstone.gemfire.SystemConnectException;
|
||||
import com.gemstone.gemfire.SystemIsRunningException;
|
||||
import com.gemstone.gemfire.UnmodifiableException;
|
||||
import com.gemstone.gemfire.admin.AdminException;
|
||||
import com.gemstone.gemfire.admin.RegionNotFoundException;
|
||||
import com.gemstone.gemfire.admin.RuntimeAdminException;
|
||||
import com.gemstone.gemfire.cache.CacheException;
|
||||
import com.gemstone.gemfire.cache.CacheExistsException;
|
||||
import com.gemstone.gemfire.cache.CacheLoaderException;
|
||||
import com.gemstone.gemfire.cache.CacheRuntimeException;
|
||||
import com.gemstone.gemfire.cache.CacheWriterException;
|
||||
import com.gemstone.gemfire.cache.CacheXmlException;
|
||||
import com.gemstone.gemfire.cache.CommitConflictException;
|
||||
import com.gemstone.gemfire.cache.CommitIncompleteException;
|
||||
import com.gemstone.gemfire.cache.DiskAccessException;
|
||||
import com.gemstone.gemfire.cache.EntryDestroyedException;
|
||||
import com.gemstone.gemfire.cache.EntryExistsException;
|
||||
import com.gemstone.gemfire.cache.EntryNotFoundException;
|
||||
import com.gemstone.gemfire.cache.EntryNotFoundInRegion;
|
||||
import com.gemstone.gemfire.cache.FailedSynchronizationException;
|
||||
import com.gemstone.gemfire.cache.OperationAbortedException;
|
||||
import com.gemstone.gemfire.cache.PartitionedRegionDistributionException;
|
||||
import com.gemstone.gemfire.cache.PartitionedRegionStorageException;
|
||||
import com.gemstone.gemfire.cache.RegionDestroyedException;
|
||||
import com.gemstone.gemfire.cache.RegionExistsException;
|
||||
import com.gemstone.gemfire.cache.ResourceException;
|
||||
import com.gemstone.gemfire.cache.RoleException;
|
||||
import com.gemstone.gemfire.cache.StatisticsDisabledException;
|
||||
import com.gemstone.gemfire.cache.SynchronizationCommitConflictException;
|
||||
import com.gemstone.gemfire.cache.VersionException;
|
||||
import com.gemstone.gemfire.cache.client.ServerConnectivityException;
|
||||
import com.gemstone.gemfire.cache.execute.FunctionException;
|
||||
import com.gemstone.gemfire.cache.query.CqClosedException;
|
||||
import com.gemstone.gemfire.cache.query.IndexMaintenanceException;
|
||||
import com.gemstone.gemfire.cache.query.QueryException;
|
||||
import com.gemstone.gemfire.cache.query.QueryExecutionTimeoutException;
|
||||
import com.gemstone.gemfire.distributed.LeaseExpiredException;
|
||||
import com.gemstone.gemfire.security.GemFireSecurityException;
|
||||
|
||||
/**
|
||||
* Helper class featuring methods for GemFire Cache or Region handling.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public abstract class GemfireCacheUtils {
|
||||
|
||||
/**
|
||||
* Converts the given (unchecked) Gemfire exception to an appropriate one from the
|
||||
* <code>org.springframework.dao</code> hierarchy.
|
||||
*
|
||||
* @param ex Gemfire unchecked exception
|
||||
* @return new the corresponding DataAccessException instance
|
||||
*/
|
||||
public static DataAccessException convertGemfireAccessException(GemFireException ex) {
|
||||
if (ex instanceof CacheException) {
|
||||
if (ex instanceof CacheExistsException) {
|
||||
return new DataIntegrityViolationException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof CommitConflictException) {
|
||||
return new DataIntegrityViolationException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof CommitIncompleteException) {
|
||||
return new DataIntegrityViolationException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof EntryExistsException) {
|
||||
return new DuplicateKeyException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof EntryNotFoundException) {
|
||||
return new DataRetrievalFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof RegionExistsException) {
|
||||
return new DataIntegrityViolationException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
if (ex instanceof CacheRuntimeException) {
|
||||
if (ex instanceof CacheXmlException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
if (ex instanceof CancelException) {
|
||||
// all cancellations go wrapped by this exception
|
||||
return new GemfireCancellationException((CancelException) ex);
|
||||
}
|
||||
if (ex instanceof CqClosedException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof DiskAccessException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof EntryDestroyedException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof FailedSynchronizationException) {
|
||||
return new PessimisticLockingFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof IndexMaintenanceException) {
|
||||
return new GemfireIndexException((IndexMaintenanceException) ex);
|
||||
}
|
||||
if (ex instanceof OperationAbortedException) {
|
||||
// treat user exceptions first
|
||||
if (ex instanceof CacheLoaderException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
if (ex instanceof CacheWriterException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
// the rest are treated as resource failures
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof PartitionedRegionDistributionException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof PartitionedRegionStorageException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof QueryExecutionTimeoutException) {
|
||||
return new GemfireQueryException((QueryExecutionTimeoutException) ex);
|
||||
}
|
||||
if (ex instanceof RegionDestroyedException) {
|
||||
return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof RegionNotFoundException) {
|
||||
return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof ResourceException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof RoleException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
if (ex instanceof StatisticsDisabledException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
if (ex instanceof SynchronizationCommitConflictException) {
|
||||
return new PessimisticLockingFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
if (ex instanceof CopyException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
if (ex instanceof EntryNotFoundInRegion) {
|
||||
return new DataRetrievalFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof FunctionException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof GemFireCacheException) {
|
||||
return convertGemfireAccessException(((GemFireCacheException) ex).getCacheException());
|
||||
}
|
||||
if (ex instanceof GemFireConfigException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
if (ex instanceof GemFireIOException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof GemFireSecurityException) {
|
||||
return new PermissionDeniedDataAccessException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof IncompatibleSystemException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
if (ex instanceof InternalGemFireException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
if (ex instanceof InvalidValueException) {
|
||||
return new TypeMismatchDataAccessException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof LeaseExpiredException) {
|
||||
return new PessimisticLockingFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof LicenseException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
if (ex instanceof NoSystemException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
if (ex instanceof RuntimeAdminException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
if (ex instanceof ServerConnectivityException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof SystemConnectException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof SystemIsRunningException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
if (ex instanceof UnmodifiableException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
// fall back
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given (checked) Gemfire exception to an appropriate one from the
|
||||
* <code>org.springframework.dao</code> hierarchy.
|
||||
*
|
||||
* @param ex Gemfire unchecked exception
|
||||
* @return new the corresponding DataAccessException instance
|
||||
*/
|
||||
public static DataAccessException convertGemfireAccessException(GemFireCheckedException ex) {
|
||||
// query exceptions
|
||||
if (ex instanceof QueryException) {
|
||||
return new GemfireQueryException((QueryException) ex);
|
||||
}
|
||||
// version exception
|
||||
if (ex instanceof VersionException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
// util.version exception (seems quite similar to the exception above)
|
||||
if (ex instanceof com.gemstone.gemfire.cache.util.VersionException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
// admin exception
|
||||
if (ex instanceof AdminException) {
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
// fall back
|
||||
return new GemfireSystemException(ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
|
||||
import com.gemstone.gemfire.CancelException;
|
||||
|
||||
/**
|
||||
* GemFire-specific class for exceptions caused by system cancellations.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class GemfireCancellationException extends InvalidDataAccessResourceUsageException {
|
||||
|
||||
public GemfireCancellationException(CancelException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.springframework.dao.DataIntegrityViolationException;
|
||||
|
||||
import com.gemstone.gemfire.cache.query.IndexCreationException;
|
||||
import com.gemstone.gemfire.cache.query.IndexExistsException;
|
||||
import com.gemstone.gemfire.cache.query.IndexMaintenanceException;
|
||||
import com.gemstone.gemfire.cache.query.IndexNameConflictException;
|
||||
|
||||
/**
|
||||
* Gemfire-specific subclass thrown on index creation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class GemfireIndexException extends DataIntegrityViolationException {
|
||||
|
||||
public GemfireIndexException(IndexCreationException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
public GemfireIndexException(IndexExistsException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
public GemfireIndexException(IndexNameConflictException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
public GemfireIndexException(IndexMaintenanceException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
|
||||
import com.gemstone.gemfire.cache.query.QueryException;
|
||||
import com.gemstone.gemfire.cache.query.QueryExecutionTimeoutException;
|
||||
|
||||
/**
|
||||
* GemFire-specific subclass of {@link InvalidDataAccessResourceUsageException} thrown on invalid
|
||||
* OQL query syntax.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class GemfireQueryException extends InvalidDataAccessResourceUsageException {
|
||||
|
||||
public GemfireQueryException(QueryException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
public GemfireQueryException(QueryExecutionTimeoutException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.springframework.dao.UncategorizedDataAccessException;
|
||||
|
||||
import com.gemstone.gemfire.GemFireCheckedException;
|
||||
import com.gemstone.gemfire.GemFireException;
|
||||
|
||||
/**
|
||||
* GemFire-specific subclass of UncategorizedDataAccessException, for GemFire system errors that do not match any concrete <code>org.springframework.dao</code> exceptions.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class GemfireSystemException extends UncategorizedDataAccessException {
|
||||
|
||||
public GemfireSystemException(GemFireCheckedException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
public GemfireSystemException(GemFireException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* 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.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.transaction.CannotCreateTransactionException;
|
||||
import org.springframework.transaction.NoTransactionException;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionSystemException;
|
||||
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
|
||||
import org.springframework.transaction.support.DefaultTransactionStatus;
|
||||
import org.springframework.transaction.support.ResourceTransactionManager;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.gemstone.gemfire.cache.Cache;
|
||||
import com.gemstone.gemfire.cache.CacheTransactionManager;
|
||||
import com.gemstone.gemfire.cache.CommitConflictException;
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
|
||||
/**
|
||||
* Local transaction manager for Gemfire Enterprise Fabric (GEF). Provides a {@link PlatformTransactionManager}
|
||||
* implementation for a single Gemfire {@link CacheTransactionManager}.
|
||||
*
|
||||
* Binds one or multiple Gemfire regions for the specified {@link Cache} to the thread, potentially allowing for one
|
||||
* region per cache model.
|
||||
*
|
||||
* <p>
|
||||
* This local strategy is an alternative to executing cache operations within JTA transactions. Its advantage is that
|
||||
* is able to work in any environment, for example a standalone application or a test suite. It is <i>not</i> able to
|
||||
* provide XA transactions, for example to share transactions with data access.
|
||||
*
|
||||
* <p>
|
||||
* To prevent dirty reads, by default, the cache is configured to return copies rather then direct references for
|
||||
* <code>get</code> operations. As a workaround, one could use explicitly deep copy objects before making changes
|
||||
* to them to avoid unnecessary copying on every fetch.
|
||||
*
|
||||
* @see com.gemstone.gemfire.cache.CacheTransactionManager
|
||||
* @see com.gemstone.gemfire.cache.Cache#setCopyOnRead(boolean)
|
||||
* @see com.gemstone.gemfire.cache.Region#get(Object)
|
||||
* @see com.gemstone.gemfire.CopyHelper#copy(Object)
|
||||
* @see #setCopyOnRead(boolean)
|
||||
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
// TODO add lenient behavior if a transaction is already started on the current thread (what should happen then)
|
||||
public class GemfireTransactionManager extends AbstractPlatformTransactionManager implements InitializingBean,
|
||||
ResourceTransactionManager {
|
||||
|
||||
private Cache cache;
|
||||
private boolean copyOnRead = true;
|
||||
|
||||
/**
|
||||
* Creates a new GemfireTransactionManager instance.
|
||||
*/
|
||||
public GemfireTransactionManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GemfireTransactionManager instance.
|
||||
*
|
||||
* @param cache
|
||||
*/
|
||||
public GemfireTransactionManager(Cache cache) {
|
||||
this.cache = cache;
|
||||
afterPropertiesSet();
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(cache, "Cache property is required");
|
||||
cache.setCopyOnRead(copyOnRead);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doGetTransaction() throws TransactionException {
|
||||
CacheTransactionObject txObject = new CacheTransactionObject();
|
||||
CacheHolder cacheHolder = (CacheHolder) TransactionSynchronizationManager.getResource(getCache());
|
||||
txObject.setHolder(cacheHolder);
|
||||
return txObject;
|
||||
}
|
||||
|
||||
protected boolean isExistingTransaction(Object transaction) throws TransactionException {
|
||||
CacheTransactionObject txObject = (CacheTransactionObject) transaction;
|
||||
// Consider a pre-bound cache as transaction.
|
||||
return (txObject.getHolder() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
|
||||
CacheTransactionObject txObject = (CacheTransactionObject) transaction;
|
||||
|
||||
Cache cache = null;
|
||||
|
||||
try {
|
||||
cache = getCache();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Acquired Cache [" + cache + "] for local Cache transaction");
|
||||
}
|
||||
|
||||
txObject.setHolder(new CacheHolder());
|
||||
cache.getCacheTransactionManager().begin();
|
||||
TransactionSynchronizationManager.bindResource(cache, txObject.getHolder());
|
||||
}
|
||||
|
||||
catch (IllegalStateException ex) {
|
||||
throw new CannotCreateTransactionException(
|
||||
"An ongoing transaction already is already associated with the current thread; are there multiple transaction managers ?",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
|
||||
CacheTransactionObject txObject = (CacheTransactionObject) status.getTransaction();
|
||||
if (status.isDebug()) {
|
||||
logger.debug("Committing Gemfire local transaction on Cache [" + cache + "]");
|
||||
}
|
||||
try {
|
||||
cache.getCacheTransactionManager().commit();
|
||||
} catch (IllegalStateException ex) {
|
||||
throw new NoTransactionException(
|
||||
"No transaction associated with the current thread; are there multiple transaction managers ?", ex);
|
||||
} catch (CommitConflictException ex) {
|
||||
// TODO: can this be replaced with HeuristicCompletionException ?
|
||||
throw new TransactionSystemException("Unexpected failure on commit of Cache local transaction", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRollback(DefaultTransactionStatus status) throws TransactionException {
|
||||
CacheTransactionObject txObject = (CacheTransactionObject) status.getTransaction();
|
||||
if (status.isDebug()) {
|
||||
logger.debug("Rolling back Cache local transaction for [" + cache + "]");
|
||||
}
|
||||
try {
|
||||
cache.getCacheTransactionManager().rollback();
|
||||
} catch (IllegalStateException ex) {
|
||||
throw new NoTransactionException(
|
||||
"No transaction associated with the current thread; are there multiple transaction managers ?", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
|
||||
CacheTransactionObject txObject = (CacheTransactionObject) status.getTransaction();
|
||||
if (status.isDebug()) {
|
||||
logger.debug("Setting Gemfire local transaction [" + txObject.getHolder() + "] rollback-only");
|
||||
}
|
||||
txObject.getHolder().setRollbackOnly();
|
||||
}
|
||||
|
||||
protected void doCleanupAfterCompletion(Object transaction) {
|
||||
// Remove the cache holder from the thread.
|
||||
TransactionSynchronizationManager.unbindResource(cache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean useSavepointForNestedTransaction() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Cache that this instance manages local transactions for.
|
||||
*
|
||||
* @return Gemfire cache
|
||||
*/
|
||||
public Cache getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Cache that this instance manages local transactions for.
|
||||
*
|
||||
* @param cache Gemfire cache
|
||||
*/
|
||||
public void setCache(Cache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public Object getResourceFactory() {
|
||||
return getCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Gemfire {@link Region} (as an alternative in setting in the cache directly).
|
||||
*
|
||||
* @param region Gemfire region
|
||||
*/
|
||||
public <K, V> void setRegion(Region<K, V> region) {
|
||||
Assert.notNull(region, "non-null arguments are required");
|
||||
this.cache = region.getCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the cache returns direct references or copies of the objects (default) it manages.
|
||||
* While copies imply additional work for every fetch operation, direct references can cause dirty reads
|
||||
* across concurrent threads in the same VM, whether or not transactions are used.
|
||||
* <p/>
|
||||
* One could explicitly deep copy objects before making changes (for example by using {@link com.gemstone.gemfire.CopyHelper#copy(Object)}
|
||||
* in which case this setting can be set to <code>false</code>. However, unless there is a measurable
|
||||
* performance penalty, the recommendation is to keep this setting to <code>true</code>
|
||||
*
|
||||
* @param copyOnRead whether copies (default) rather then direct references will be returned on
|
||||
* fetch operations
|
||||
*/
|
||||
public void setCopyOnRead(boolean copyOnRead) {
|
||||
this.copyOnRead = copyOnRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* GemfireTM local transaction object.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
private static class CacheTransactionObject {
|
||||
private CacheHolder cacheHolder;
|
||||
|
||||
public CacheHolder getHolder() {
|
||||
return cacheHolder;
|
||||
}
|
||||
|
||||
public void setHolder(CacheHolder cacheHolder) {
|
||||
this.cacheHolder = cacheHolder;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CacheHolder {
|
||||
|
||||
private boolean rollbackOnly = false;
|
||||
|
||||
|
||||
public boolean isRollbackOnly() {
|
||||
return rollbackOnly;
|
||||
}
|
||||
|
||||
public void setRollbackOnly() {
|
||||
rollbackOnly = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
138
src/main/java/org/springframework/data/gemfire/Interest.java
Normal file
138
src/main/java/org/springframework/data/gemfire/Interest.java
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Constants;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.gemstone.gemfire.cache.InterestResultPolicy;
|
||||
|
||||
/**
|
||||
* Basic holder class for registering an interest. Useful for configuring Gemfire caches through XML
|
||||
* and or JavaBeans means.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class Interest<K> implements InitializingBean {
|
||||
|
||||
private static final Constants constants = new Constants(InterestResultPolicy.class);
|
||||
|
||||
private K key;
|
||||
private InterestResultPolicy policy = InterestResultPolicy.DEFAULT;
|
||||
private boolean durable = false;
|
||||
|
||||
public Interest() {
|
||||
}
|
||||
|
||||
public Interest(K key) {
|
||||
this(key, InterestResultPolicy.DEFAULT, false);
|
||||
}
|
||||
|
||||
public Interest(K key, InterestResultPolicy policy) {
|
||||
this(key, policy, false);
|
||||
}
|
||||
|
||||
public Interest(K key, String policy) {
|
||||
this(key, policy, false);
|
||||
}
|
||||
|
||||
public Interest(K key, String policy, boolean durable) {
|
||||
this.key = key;
|
||||
this.policy = (InterestResultPolicy) constants.asObject(policy);
|
||||
this.durable = durable;
|
||||
afterPropertiesSet();
|
||||
}
|
||||
|
||||
public Interest(K key, InterestResultPolicy policy, boolean durable) {
|
||||
this.key = key;
|
||||
this.policy = policy;
|
||||
this.durable = durable;
|
||||
afterPropertiesSet();
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(key, "a non-null key is required");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key of interest.
|
||||
*
|
||||
* @return the key
|
||||
*/
|
||||
protected K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key of interest.
|
||||
*
|
||||
* @param key the key to set
|
||||
*/
|
||||
public void setKey(K key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interest policy.
|
||||
*
|
||||
* @return the policy
|
||||
*/
|
||||
protected InterestResultPolicy getPolicy() {
|
||||
return policy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the interest policy. The argument is set as an Object
|
||||
* to be able to accept both InterestResultType instances but also
|
||||
* Strings (for XML configurations).
|
||||
*
|
||||
* @param policy the policy to set
|
||||
*/
|
||||
public void setPolicy(Object policy) {
|
||||
if (policy instanceof InterestResultPolicy) {
|
||||
this.policy = (InterestResultPolicy) policy;
|
||||
}
|
||||
else {
|
||||
if (policy instanceof String) {
|
||||
this.policy = (InterestResultPolicy) constants.asObject((String) policy);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unknown argument type for property 'policy'" + policy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interest durability.
|
||||
*
|
||||
* @return the durable
|
||||
*/
|
||||
protected boolean isDurable() {
|
||||
return durable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the interest durability.
|
||||
*
|
||||
* @param durable the durable to set
|
||||
*/
|
||||
public void setDurable(boolean durable) {
|
||||
this.durable = durable;
|
||||
}
|
||||
}
|
||||
@@ -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 org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Cache interest based on regular expression rather then individual key types.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RegexInterest extends Interest<String> {
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
Assert.hasText(getKey(), "A non-empty regex is required");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the regex backing this interest.
|
||||
* Similar to {@link #getKey()}.
|
||||
*
|
||||
* @return interest regex
|
||||
*/
|
||||
//TODO: is this really required
|
||||
public String getRegex() {
|
||||
return getKey();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.gemstone.gemfire.cache.AttributesFactory;
|
||||
import com.gemstone.gemfire.cache.Cache;
|
||||
import com.gemstone.gemfire.cache.CacheListener;
|
||||
import com.gemstone.gemfire.cache.CacheLoader;
|
||||
import com.gemstone.gemfire.cache.CacheWriter;
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
import com.gemstone.gemfire.cache.RegionAttributes;
|
||||
|
||||
/**
|
||||
* FactoryBean for creating generic Gemfire {@link Region}s.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RegionFactoryBean<K, V> implements DisposableBean, FactoryBean<Region<K, V>>, InitializingBean {
|
||||
|
||||
protected final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
private Cache cache;
|
||||
private String name;
|
||||
private boolean destroy = false;
|
||||
private Resource snapshot;
|
||||
|
||||
private CacheListener<K, V> cacheListeners[];
|
||||
private CacheLoader<K, V> cacheLoader;
|
||||
private CacheWriter<K, V> cacheWriter;
|
||||
|
||||
private RegionAttributes<K, V> attributes;
|
||||
|
||||
private Region<K, V> region;
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(cache, "Cache property must be set");
|
||||
Assert.hasText(name, "Name property must be set");
|
||||
|
||||
// first get cache
|
||||
region = cache.getRegion(name);
|
||||
if (region != null) {
|
||||
log.info("Retrieved region [" + name + "] from cache");
|
||||
}
|
||||
// fall back to cache creation if one is not found
|
||||
else {
|
||||
if (attributes != null)
|
||||
AttributesFactory.validateAttributes(attributes);
|
||||
|
||||
AttributesFactory<K, V> attrFactory = (attributes != null ? new AttributesFactory<K, V>(attributes)
|
||||
: new AttributesFactory<K, V>());
|
||||
if (!ObjectUtils.isEmpty(cacheListeners)) {
|
||||
for (CacheListener<K, V> listener : cacheListeners) {
|
||||
attrFactory.addCacheListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheLoader != null) {
|
||||
attrFactory.setCacheLoader(cacheLoader);
|
||||
}
|
||||
|
||||
if (cacheWriter != null) {
|
||||
attrFactory.setCacheWriter(cacheWriter);
|
||||
}
|
||||
|
||||
region = cache.createRegion(name, attrFactory.create());
|
||||
log.info("Created new cache region [" + name + "]");
|
||||
if (snapshot != null) {
|
||||
region.loadSnapshot(snapshot.getInputStream());
|
||||
}
|
||||
}
|
||||
|
||||
postProcess(region);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the region object for this factory bean during the initialization process.
|
||||
* The object is already initialized and configured by the factory bean before this method
|
||||
* is invoked.
|
||||
*
|
||||
* @param region
|
||||
*/
|
||||
protected void postProcess(Region<K, V> region) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public void destroy() throws Exception {
|
||||
if (region != null && destroy) {
|
||||
region.destroyRegion();
|
||||
}
|
||||
region = null;
|
||||
}
|
||||
|
||||
public Region<K, V> getObject() throws Exception {
|
||||
return region;
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
return (region != null ? region.getClass() : Region.class);
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache used for creating the region.
|
||||
*
|
||||
* @see org.springframework.data.gemfire.CacheFactoryBean
|
||||
* @param cache the cache to set
|
||||
*/
|
||||
public void setCache(Cache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the cache region. If no cache is found under
|
||||
* the given name, a new one will be created.
|
||||
*
|
||||
* @see com.gemstone.gemfire.cache.Region#getFullPath()
|
||||
*
|
||||
* @param name the region name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the region referred by this factory bean,
|
||||
* will be destroyed on shutdown (default false).
|
||||
*
|
||||
* @param destroy the destroy to set
|
||||
*/
|
||||
public void setDestroy(boolean destroy) {
|
||||
this.destroy = destroy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the snapshots used for loading a newly <i>created</i> region.
|
||||
* That is, the snapshot will be used <i>only</i> when a new region is created - if the region
|
||||
* already exists, no loading will be performed.
|
||||
*
|
||||
* @see #setName(String)
|
||||
* @param snapshot the snapshot to set
|
||||
*/
|
||||
public void setSnapshot(Resource snapshot) {
|
||||
this.snapshot = snapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache listeners used for the region used by this factory.
|
||||
* Used only when a new region is created.Overrides the settings
|
||||
* specified through {@link #setAttributes(RegionAttributes)}.
|
||||
*
|
||||
* @param cacheListeners the cacheListeners to set on a newly created region
|
||||
*/
|
||||
public void setCacheListeners(CacheListener<K, V>[] cacheListeners) {
|
||||
this.cacheListeners = cacheListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache loader used for the region used by this factory.
|
||||
* Used only when a new region is created.Overrides the settings
|
||||
* specified through {@link #setAttributes(RegionAttributes)}.
|
||||
*
|
||||
* @param cacheLoader the cacheLoader to set on a newly created region
|
||||
*/
|
||||
public void setCacheLoader(CacheLoader<K, V> cacheLoader) {
|
||||
this.cacheLoader = cacheLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache loader used for the region used by this factory.
|
||||
* Used only when a new region is created. Overrides the settings
|
||||
* specified through {@link #setAttributes(RegionAttributes)}.
|
||||
*
|
||||
* @param cacheWriter the cacheWriter to set on a newly created region
|
||||
*/
|
||||
public void setCacheWriter(CacheWriter<K, V> cacheWriter) {
|
||||
this.cacheWriter = cacheWriter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache loader used for the region used by this factory.
|
||||
* Used only when a new region is created.
|
||||
*
|
||||
* @param attributes the attributes to set on a newly created region
|
||||
*/
|
||||
public void setAttributes(RegionAttributes<K, V> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.wiring.BeanConfigurerSupport;
|
||||
import org.springframework.beans.factory.wiring.BeanWiringInfo;
|
||||
import org.springframework.beans.factory.wiring.BeanWiringInfoResolver;
|
||||
|
||||
import com.gemstone.gemfire.cache.Declarable;
|
||||
|
||||
/**
|
||||
* Dedicated {@link Declarable} support class for wiring the declaring instance through
|
||||
* the Spring container.
|
||||
* This implementation will first look for a 'bean-name' property which will be used to
|
||||
* locate a 'template' bean definition. In case the property is not given, a bean named
|
||||
* after the class will be searched and if none is found, autowiring will be performed,
|
||||
* based on the settings defined in the Spring container.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class WiringDeclarableSupport extends DeclarableSupport {
|
||||
|
||||
private final String BEAN_NAME_PROP = "bean-name";
|
||||
|
||||
@Override
|
||||
protected void initInstance(Properties props) {
|
||||
BeanFactory bf = getBeanFactory();
|
||||
BeanConfigurerSupport configurer = new BeanConfigurerSupport();
|
||||
configurer.setBeanFactory(bf);
|
||||
|
||||
final String beanName = props.getProperty(BEAN_NAME_PROP);
|
||||
// key specified, search for a bean
|
||||
if (beanName != null) {
|
||||
if (!bf.containsBean(beanName)) {
|
||||
throw new IllegalArgumentException("Cannot find bean named '" + beanName + "'");
|
||||
}
|
||||
configurer.setBeanWiringInfoResolver(new BeanWiringInfoResolver() {
|
||||
|
||||
public BeanWiringInfo resolveWiringInfo(Object beanInstance) {
|
||||
return new BeanWiringInfo(beanName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
configurer.afterPropertiesSet();
|
||||
configurer.configureBean(this);
|
||||
configurer.destroy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* 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.serialization;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.springframework.asm.ClassWriter;
|
||||
import org.springframework.asm.FieldVisitor;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.asm.Opcodes;
|
||||
import org.springframework.asm.Type;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.gemstone.gemfire.DataSerializable;
|
||||
import com.gemstone.gemfire.Instantiator;
|
||||
|
||||
/**
|
||||
* ASM based {@link InstantiatorFactory} implementation. This class relies on ASM 2.x package
|
||||
* repacked by Spring framework to minimize the number of dependencies and avoid any versioning
|
||||
* confusion.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class AsmInstantiatorGenerator implements InstantiatorGenerator, Opcodes {
|
||||
|
||||
private static final String PKG = "org/springextensions/gef/serialization";
|
||||
private static final String CLASS_LABEL = "Instantiator$Synthetic";
|
||||
private static final String INSTANTIATOR_NAME = Type.getInternalName(Instantiator.class);
|
||||
private static final String SERIALIZABLE_NAME = Type.getInternalName(Serializable.class);
|
||||
private static final String CLASS_DESCRIPTOR = Type.getDescriptor(Class.class);
|
||||
private static final String CLASS_FIELD_NAME = "clazz";
|
||||
private static final String ID_FIELD_NAME = "classId";
|
||||
|
||||
private static final String INIT = "<init>";
|
||||
private static final String CINIT = "<clinit>";
|
||||
private static final String NEW_INSTANCE = "newInstance";
|
||||
private static final String NEW_INSTANCE_DESC = Type.getMethodDescriptor(Type.getType(DataSerializable.class),
|
||||
new Type[] {});
|
||||
|
||||
// generated class counter
|
||||
private static final AtomicLong counter = new AtomicLong(1);
|
||||
|
||||
// class cache
|
||||
private final ConcurrentMap<Class<? extends DataSerializable>, Instantiator> cache = new ConcurrentHashMap<Class<? extends DataSerializable>, Instantiator>();
|
||||
|
||||
|
||||
private static final class BytecodeClassLoader extends ClassLoader {
|
||||
|
||||
public BytecodeClassLoader(ClassLoader loader) {
|
||||
super(loader);
|
||||
}
|
||||
|
||||
public Class<?> loadClass(String name, byte[] bytecode) {
|
||||
return defineClass(name, bytecode, 0, bytecode.length);
|
||||
}
|
||||
}
|
||||
|
||||
private final BytecodeClassLoader classLoader;
|
||||
|
||||
public AsmInstantiatorGenerator() {
|
||||
this(AsmInstantiatorGenerator.class.getClassLoader());
|
||||
}
|
||||
|
||||
public AsmInstantiatorGenerator(ClassLoader classLoader) {
|
||||
Assert.notNull(classLoader);
|
||||
this.classLoader = new BytecodeClassLoader(classLoader);
|
||||
}
|
||||
|
||||
public Instantiator getInstantiator(Class<? extends DataSerializable> clazz, int classId) {
|
||||
Instantiator instantiator = cache.get(clazz);
|
||||
if (instantiator == null) {
|
||||
synchronized (cache) {
|
||||
instantiator = cache.get(clazz);
|
||||
if (instantiator == null) {
|
||||
// create Instantiator
|
||||
instantiator = createInstantiator(clazz, classId);
|
||||
cache.putIfAbsent(clazz, instantiator);
|
||||
}
|
||||
}
|
||||
}
|
||||
return instantiator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the custom instantiator created for the given class.
|
||||
*
|
||||
* @param clazz
|
||||
* @param classId
|
||||
* @return
|
||||
*/
|
||||
private Instantiator createInstantiator(Class<? extends DataSerializable> clazz, int classId) {
|
||||
validateClass(clazz);
|
||||
Class<?> clz = createCustomInstantiatorClass(clazz, classId);
|
||||
return (Instantiator) BeanUtils.instantiate(clz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does basic sanity checks to make sure the constructor can be properly invoked by our generated
|
||||
* class.
|
||||
*
|
||||
* @param clazz
|
||||
*/
|
||||
private void validateClass(Class<? extends DataSerializable> clazz) {
|
||||
Assert.isTrue(!Modifier.isAbstract(clazz.getModifiers()), "Cannot instantiate abstract classes");
|
||||
Assert.isTrue(Modifier.isPublic(clazz.getModifiers()), "Only public classes are supported");
|
||||
try {
|
||||
Constructor<? extends DataSerializable> ctor = clazz.getConstructor();
|
||||
Assert.isTrue(Modifier.isPublic(ctor.getModifiers()), "Default constructor is not public");
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalArgumentException("Class " + clazz + " unsuitable for instantiation", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new Instantiator class for the given custom class.
|
||||
*
|
||||
* The generated class has the following definition:
|
||||
*
|
||||
* <pre>
|
||||
* package org.springframework.data.gemfire.serialization;
|
||||
*
|
||||
* public class <<i>T</i>>Instantiator$Synthetic<i>Counter</i> extends Instantiator implements Serializable {
|
||||
*
|
||||
* private static final Class<<i>T</i>> clazz = T.class;
|
||||
* private static final int classId = <i>value</i>;
|
||||
*
|
||||
* public DateInstantiator() {
|
||||
* this(clazz, classId);
|
||||
* }
|
||||
*
|
||||
* public DateInstantiator(Class<? extends DataSerializable> c, int classId) {
|
||||
* super(c, classId);
|
||||
* }
|
||||
*
|
||||
* public <i>T</i> newInstance() {
|
||||
* return new <i>T</i>();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
Class<?> createCustomInstantiatorClass(Class<? extends DataSerializable> clazz, int classId) {
|
||||
String classInternalName = PKG + clazz.getSimpleName() + CLASS_LABEL + counter.getAndIncrement();
|
||||
byte[] bytecode = generateClassBytecode(classInternalName, clazz, classId);
|
||||
// translate internal name to binary form
|
||||
return classLoader.loadClass(classInternalName.replace('/', '.'), bytecode);
|
||||
}
|
||||
|
||||
byte[] generateClassBytecode(String className, Class<? extends DataSerializable> clazz, int classId) {
|
||||
ClassWriter cw = new ClassWriter(false);
|
||||
|
||||
cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, className, null, INSTANTIATOR_NAME, new String[] { SERIALIZABLE_NAME });
|
||||
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, CLASS_FIELD_NAME, CLASS_DESCRIPTOR, null,
|
||||
null);
|
||||
fv.visitEnd();
|
||||
fv = cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, ID_FIELD_NAME, Type.INT_TYPE.getDescriptor(), null,
|
||||
Integer.valueOf(classId));
|
||||
fv.visitEnd();
|
||||
|
||||
String voidNoArgMethodDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {});
|
||||
|
||||
// field class loading
|
||||
MethodVisitor mv = cw.visitMethod(ACC_STATIC, CINIT, voidNoArgMethodDescriptor, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitLdcInsn(Type.getType(clazz));
|
||||
mv.visitFieldInsn(PUTSTATIC, className, CLASS_FIELD_NAME, CLASS_DESCRIPTOR);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
String voidArgClassAndIntDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {
|
||||
Type.getType(Class.class), Type.INT_TYPE });
|
||||
|
||||
// default constructor
|
||||
mv = cw.visitMethod(ACC_PUBLIC, INIT, voidNoArgMethodDescriptor, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETSTATIC, className, CLASS_FIELD_NAME, CLASS_DESCRIPTOR);
|
||||
mv.visitFieldInsn(GETSTATIC, className, ID_FIELD_NAME, Type.INT_TYPE.getDescriptor());
|
||||
mv.visitMethodInsn(INVOKESPECIAL, className, INIT, voidArgClassAndIntDescriptor);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(3, 1);
|
||||
mv.visitEnd();
|
||||
|
||||
// two-arg constructor
|
||||
mv = cw.visitMethod(ACC_PUBLIC, INIT, voidArgClassAndIntDescriptor, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitVarInsn(ILOAD, 2);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, INSTANTIATOR_NAME, INIT, voidArgClassAndIntDescriptor);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(3, 3);
|
||||
mv.visitEnd();
|
||||
|
||||
Type customClassType = Type.getType(clazz);
|
||||
String customTypeNoArgDesc = Type.getMethodDescriptor(customClassType, new Type[] {});
|
||||
|
||||
// newInstance overloaded method
|
||||
mv = cw.visitMethod(ACC_PUBLIC, NEW_INSTANCE, customTypeNoArgDesc, null, null);
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitTypeInsn(NEW, customClassType.getInternalName());
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, customClassType.getInternalName(), INIT, voidNoArgMethodDescriptor);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(2, 1);
|
||||
mv.visitEnd();
|
||||
|
||||
// plus original method signature
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, NEW_INSTANCE, NEW_INSTANCE_DESC, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, className, NEW_INSTANCE, customTypeNoArgDesc);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
|
||||
// end class generation
|
||||
cw.visitEnd();
|
||||
|
||||
return cw.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.serialization;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import com.gemstone.gemfire.DataSerializer;
|
||||
import com.gemstone.gemfire.internal.InternalDataSerializer;
|
||||
|
||||
/**
|
||||
* Generic Serializer for JDK Enums. The class needs to be registered only once - custom enums
|
||||
* will be then understood by the converter by calling {@link #addEnum(Class)}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class EnumSerializer extends DataSerializer implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -7069461993489626976L;
|
||||
|
||||
private static final ConcurrentMap<Class<?>, Enum[]> supportedClasses = new ConcurrentHashMap<Class<?>, Enum[]>();
|
||||
|
||||
private int id = 1024;
|
||||
|
||||
@Override
|
||||
public boolean toData(Object o, DataOutput out) throws IOException {
|
||||
if (o instanceof Enum<?>) {
|
||||
// add enum to the set
|
||||
Enum<?> enm = (Enum<?>) o;
|
||||
Class<?> cls = enm.getDeclaringClass();
|
||||
addEnum(cls);
|
||||
DataSerializer.writeClass(cls, out);
|
||||
out.writeInt(enm.ordinal());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object fromData(DataInput in) throws IOException, ClassNotFoundException {
|
||||
Class cls = DataSerializer.readClass(in);
|
||||
if (cls.isEnum()) {
|
||||
addEnum(cls);
|
||||
int ordinal = in.readInt();
|
||||
return supportedClasses.get(cls)[ordinal];
|
||||
}
|
||||
throw new IOException("Non-enum class read from the stream -" + cls);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addEnum(Class enumClass) {
|
||||
if (!supportedClasses.containsKey(enumClass)) {
|
||||
supportedClasses.put(enumClass, (Enum[]) enumClass.getEnumConstants());
|
||||
}
|
||||
|
||||
// if registered, re-register the serializer to propagate the changes
|
||||
if (InternalDataSerializer.getSerializer(getId()) != null) {
|
||||
if (InternalDataSerializer.getSerializer(enumClass) == null) {
|
||||
InternalDataSerializer.unregister(getId());
|
||||
InternalDataSerializer.register(getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?>[] getSupportedClasses() {
|
||||
return supportedClasses.keySet().toArray(new Class<?>[supportedClasses.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the id for this serializer. Default is 1024;
|
||||
*
|
||||
* @param id the id to set
|
||||
*/
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.serialization;
|
||||
|
||||
import com.gemstone.gemfire.DataSerializable;
|
||||
import com.gemstone.gemfire.Instantiator;
|
||||
|
||||
/**
|
||||
* Factory that generates {@link Instantiator} classes to improve instantiation of
|
||||
* custom types.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface InstantiatorGenerator {
|
||||
|
||||
/**
|
||||
* Returns a (potentially new) Instantiator that optimizes the instantiation of the given types.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Instantiator getInstantiator(Class<? extends DataSerializable> clazz, int classId);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.serialization;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.wiring.BeanConfigurerSupport;
|
||||
|
||||
import com.gemstone.gemfire.DataSerializable;
|
||||
import com.gemstone.gemfire.Instantiator;
|
||||
|
||||
/**
|
||||
* Instantiator that performs instance wiring using the Spring IoC container, allowing common properties
|
||||
* to be injected before the object is hydrated/deserialized. The newly created instances can be configured
|
||||
* either by relying on an existing bean definition (which acts as a template) or by providing an embedded
|
||||
* configuration through annotations.
|
||||
*
|
||||
* <p/>
|
||||
* Can reuse existing instantiators to optimize instance creation. If one is not provided, it will fallback
|
||||
* to reflection invocation.
|
||||
*
|
||||
* @see org.springframework.beans.factory.wiring.BeanConfigurerSupport
|
||||
* @see org.springframework.beans.factory.wiring.BeanWiringInfoResolver
|
||||
* @see org.springframework.beans.factory.annotation.Autowired
|
||||
* @see javax.annotation.Resource
|
||||
* @see javax.inject.Inject
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class WiringInstantiator extends Instantiator implements BeanFactoryAware, InitializingBean,
|
||||
DisposableBean {
|
||||
|
||||
private final Instantiator instantiator;
|
||||
private final Class<? extends DataSerializable> clazz;
|
||||
private BeanConfigurerSupport configurer;
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
public WiringInstantiator(Instantiator instantiator) {
|
||||
super(instantiator.getInstantiatedClass(), instantiator.getId());
|
||||
this.instantiator = instantiator;
|
||||
this.clazz = null;
|
||||
}
|
||||
|
||||
public WiringInstantiator(Class<? extends DataSerializable> c, int classId) {
|
||||
super(c, classId);
|
||||
instantiator = null;
|
||||
clazz = c;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
if (configurer == null) {
|
||||
configurer = new BeanConfigurerSupport();
|
||||
configurer.setBeanFactory(beanFactory);
|
||||
configurer.afterPropertiesSet();
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() throws Exception {
|
||||
configurer.destroy();
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DataSerializable newInstance() {
|
||||
DataSerializable instance = createInstance();
|
||||
configurer.configureBean(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
private DataSerializable createInstance() {
|
||||
if (instantiator != null) {
|
||||
return instantiator.newInstance();
|
||||
}
|
||||
|
||||
return BeanUtils.instantiate(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the manager responsible for configuring the newly created instances.
|
||||
* The given configurer needs to be configured and initialized before-hand.
|
||||
*
|
||||
* @param configurer the configurer to set
|
||||
*/
|
||||
public void setConfigurer(BeanConfigurerSupport configurer) {
|
||||
this.configurer = configurer;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user