+ initial commit

This commit is contained in:
costin
2010-06-29 20:58:20 +03:00
commit 8ddeae76a1
41 changed files with 3241 additions and 0 deletions

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View 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;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire;
import 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();
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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 &lt;<i>T</i>>Instantiator$Synthetic<i>Counter</i> extends Instantiator implements Serializable {
*
* private static final Class&lt;<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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}