Implements JIRA feature request SGF-226 enabling Spring Data GemFire to retrieve the shared, persistent cluster configuration from a GemFire 8 Locator Shared Configuration service when the SDG configured peer cache member joins the cluster.

This commit is contained in:
John Blum
2014-07-17 20:27:20 -07:00
parent 76aa819be7
commit f427125669
24 changed files with 1771 additions and 290 deletions

View File

@@ -1,7 +1,7 @@
antlrVersion=2.7.7
aspectjVersion=1.7.4
gemfire.range="[6.5, 8.0)"
gemfireVersion=7.5.BUILD-SNAPSHOT
gemfireVersion=8.0.BUILD-SNAPSHOT
hamcrestVersion=1.3
jacksonVersion=2.4.1
junitVersion=4.11

View File

@@ -58,24 +58,28 @@ import com.gemstone.gemfire.pdx.PdxSerializable;
import com.gemstone.gemfire.pdx.PdxSerializer;
/**
* Factory used for configuring a Gemfire Cache manager. Allows either retrieval
* of an existing, opened cache or the creation of a new one.
*
* FactoryBean used to configure a GemFire peer Cache node. Allows either retrieval of an existing, opened Cache
* or the creation of a new Cache instance.
* <p>
* This class implements the
* {@link org.springframework.dao.support.PersistenceExceptionTranslator}
* This class implements the {@link org.springframework.dao.support.PersistenceExceptionTranslator}
* interface, as auto-detected by Spring's
* {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor}
* , for AOP-based translation of native exceptions to Spring
* DataAccessExceptions. Hence, the presence of this class automatically enables
* a PersistenceExceptionTranslationPostProcessor to translate GemFire
* exceptions.
* {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor}, for AOP-based translation
* of native Exceptions to Spring DataAccessExceptions. Hence, the presence of this class automatically enables
* a PersistenceExceptionTranslationPostProcessor to translate GemFire Exceptions appropriately.
*
* @author Costin Leau
* @author David Turanski
* @author John Blum
* @see org.springframework.beans.factory.BeanClassLoaderAware
* @see org.springframework.beans.factory.BeanFactoryAware
* @see org.springframework.beans.factory.BeanNameAware
* @see org.springframework.beans.factory.FactoryBean
* @see org.springframework.beans.factory.InitializingBean
* @see org.springframework.beans.factory.DisposableBean
* @see org.springframework.dao.support.PersistenceExceptionTranslator
*/
public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanClassLoaderAware, InitializingBean,
DisposableBean, FactoryBean<Cache>, PersistenceExceptionTranslator {
public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, BeanNameAware, FactoryBean<Cache>,
InitializingBean, DisposableBean, PersistenceExceptionTranslator {
protected static final List<String> VALID_JNDI_DATASOURCE_TYPE_NAMES = Collections.unmodifiableList(
Arrays.asList("ManagedDataSource", "PooledDataSource", "SimpleDataSource", "XAPooledDataSource"));
@@ -93,6 +97,7 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
protected Boolean pdxIgnoreUnreadFields;
protected Boolean pdxPersistent;
protected Boolean pdxReadSerialized;
protected Boolean useSharedConfiguration;
protected Cache cache;
@@ -114,7 +119,7 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
protected List<TransactionListener> transactionListeners;
// Defined this way for backward compatibility
// Declared with type 'Object' for backward compatibility
protected Object gatewayConflictResolver;
protected Object pdxSerializer;
@@ -128,20 +133,19 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
protected TransactionWriter transactionWriter;
public static class DynamicRegionSupport {
private String diskDir;
private String poolName;
private Boolean persistent = Boolean.TRUE;
private Boolean registerInterest = Boolean.TRUE;
private String diskDirectory;
private String poolName;
public String getDiskDir() {
return diskDir;
return diskDirectory;
}
public void setDiskDir(String diskDir) {
this.diskDir = diskDir;
public void setDiskDir(String diskDirectory) {
this.diskDirectory = diskDirectory;
}
public Boolean getPersistent() {
@@ -152,14 +156,6 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
this.persistent = persistent;
}
public Boolean getRegisterInterest() {
return registerInterest;
}
public void setRegisterInterest(Boolean registerInterest) {
this.registerInterest = registerInterest;
}
public String getPoolName() {
return poolName;
}
@@ -168,13 +164,20 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
this.poolName = poolName;
}
public Boolean getRegisterInterest() {
return registerInterest;
}
public void setRegisterInterest(Boolean registerInterest) {
this.registerInterest = registerInterest;
}
public void initializeDynamicRegionFactory() {
DynamicRegionFactory.Config config = null;
if (diskDir == null) {
config = new DynamicRegionFactory.Config(null, poolName, persistent, registerInterest);
} else {
config = new DynamicRegionFactory.Config(new File(diskDir), poolName, persistent, registerInterest);
}
File localDiskDirectory = (this.diskDirectory == null ? null : new File(this.diskDirectory));
DynamicRegionFactory.Config config = new DynamicRegionFactory.Config(localDiskDirectory, poolName,
persistent, registerInterest);
DynamicRegionFactory.get().open(config);
}
}
@@ -182,7 +185,6 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
public static class JndiDataSource {
private List<ConfigProperty> props;
private Map<String, String> attributes;
public Map<String, String> getAttributes() {
@@ -294,8 +296,8 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
DistributedMember member = system.getDistributedMember();
log.info(String.format("Connected to Distributed System [%1$s] as Member [%2$s] on Host [%3$s].",
system.getName(), member.getId(), member.getHost()));
log.info(String.format("Connected to Distributed System [%1$s] as Member [%2$s] in Groups [%3$s] with Roles [%4$s] on Host [%5$s] having PID [%6$d].",
system.getName(), member.getId(), member.getGroups(), member.getRoles(), member.getHost(), member.getProcessId()));
log.info(String.format("%1$s GemFire v.%2$s Cache [%3$s].", messagePrefix, CacheFactory.getVersion(),
cache.getName()));
@@ -428,43 +430,30 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
}
@Override
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
if (ex instanceof GemFireException) {
return GemfireCacheUtils.convertGemfireAccessException((GemFireException) ex);
public DataAccessException translateExceptionIfPossible(final RuntimeException e) {
if (e instanceof GemFireException) {
return GemfireCacheUtils.convertGemfireAccessException((GemFireException) e);
}
if (ex instanceof IllegalArgumentException) {
DataAccessException wrapped = GemfireCacheUtils.convertQueryExceptions(ex);
if (e instanceof IllegalArgumentException) {
DataAccessException wrapped = GemfireCacheUtils.convertQueryExceptions(e);
// ignore conversion if the generic exception is returned
if (!(wrapped instanceof GemfireSystemException)) {
return wrapped;
}
}
if (ex.getCause() instanceof GemFireException) {
return GemfireCacheUtils.convertGemfireAccessException((GemFireException) ex.getCause());
if (e.getCause() instanceof GemFireException) {
return GemfireCacheUtils.convertGemfireAccessException((GemFireException) e.getCause());
}
if (ex.getCause() instanceof GemFireCheckedException) {
return GemfireCacheUtils.convertGemfireAccessException((GemFireCheckedException) ex.getCause());
if (e.getCause() instanceof GemFireCheckedException) {
return GemfireCacheUtils.convertGemfireAccessException((GemFireCheckedException) e.getCause());
}
return null;
}
@Override
public Cache getObject() throws Exception {
init();
return cache;
}
@Override
public Class<? extends Cache> getObjectType() {
return (cache != null ? cache.getClass() : Cache.class);
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
@@ -518,7 +507,13 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
this.useBeanFactoryLocator = usage;
}
public void setEnableAutoReconnect(final Boolean enableAutoReconnect) {
/**
* Controls whether auto-reconnect functionality introduced in GemFire 8 is enabled or not.
*
* @param enableAutoReconnect a boolean value to enable/disable auto-reconnect functionality.
* @since GemFire 8.0
*/
public void setEnableAutoReconnect(Boolean enableAutoReconnect) {
this.enableAutoReconnect = enableAutoReconnect;
}
@@ -534,25 +529,24 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
}
/**
* Sets the object preference to PdxInstance type. Applicable on GemFire 6.6
* or higher.
*
* @param pdxPersistent the pdxPersistent to set
*/
public void setPdxPersistent(Boolean pdxPersistent) {
this.pdxPersistent = pdxPersistent;
}
/**
* Controls whether the type metadata for PDX objects is persisted to disk.
* Applicable on GemFire 6.6 or higher.
*
* @param pdxReadSerialized the pdxReadSerialized to set
* Sets the object preference to PdxInstance. Applicable on GemFire 6.6 or higher.
*
* @param pdxReadSerialized a boolean value indicating the PDX instance should be returned from Region.get(key)
* when available.
*/
public void setPdxReadSerialized(Boolean pdxReadSerialized) {
this.pdxReadSerialized = pdxReadSerialized;
}
/**
* Controls whether type metadata for PDX objects is persisted to disk. Applicable on GemFire 6.6 or higher.
*
* @param pdxPersistent a boolean value indicating that PDX type meta-data should be persisted to disk.
*/
public void setPdxPersistent(Boolean pdxPersistent) {
this.pdxPersistent = pdxPersistent;
}
/**
* Controls whether pdx ignores fields that were unread during
* deserialization. Applicable on GemFire 6.6 or higher.
@@ -574,10 +568,12 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
}
/**
* @return the beanFactory
* Set whether the Cache should be closed.
*
* @param close set to false if destroy() should not close the cache
*/
public BeanFactory getBeanFactory() {
return beanFactory;
public void setClose(boolean close) {
this.close = close;
}
/**
@@ -590,12 +586,21 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
}
/**
* Sets the number of seconds in which the implicit object lock request will timeout.
* Set the Cache's critical heap percentage attribute.
*
* @param lockTimeout an integer value specifying the object lock request timeout.
* @param criticalHeapPercentage floating point value indicating the critical heap percentage.
*/
public void setLockTimeout(Integer lockTimeout) {
this.lockTimeout = lockTimeout;
public void setCriticalHeapPercentage(Float criticalHeapPercentage) {
this.criticalHeapPercentage = criticalHeapPercentage;
}
/**
* Set the Cache's eviction heap percentage attribute.
*
* @param evictionHeapPercentage float-point value indicating the Cache's heap use percentage to trigger eviction.
*/
public void setEvictionHeapPercentage(Float evictionHeapPercentage) {
this.evictionHeapPercentage = evictionHeapPercentage;
}
/**
@@ -607,6 +612,15 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
this.lockLease = lockLease;
}
/**
* Sets the number of seconds in which the implicit object lock request will timeout.
*
* @param lockTimeout an integer value specifying the object lock request timeout.
*/
public void setLockTimeout(Integer lockTimeout) {
this.lockTimeout = lockTimeout;
}
/**
* Set for client subscription queue synchronization when this member acts as a server to clients
* and server redundancy is used. Sets the frequency (in seconds) at which the primary server sends messages
@@ -628,33 +642,6 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
this.searchTimeout = searchTimeout;
}
/**
* Set the Cache's eviction heap percentage attribute.
*
* @param evictionHeapPercentage float-point value indicating the Cache's heap use percentage to trigger eviction.
*/
public void setEvictionHeapPercentage(Float evictionHeapPercentage) {
this.evictionHeapPercentage = evictionHeapPercentage;
}
/**
* Set the Cache's critical heap percentage attribute.
*
* @param criticalHeapPercentage floating point value indicating the critical heap percentage.
*/
public void setCriticalHeapPercentage(Float criticalHeapPercentage) {
this.criticalHeapPercentage = criticalHeapPercentage;
}
/**
* Set whether the Cache should be closed.
*
* @param close set to false if destroy() should not close the cache
*/
public void setClose(boolean close) {
this.close = close;
}
/**
* Sets the list of TransactionListeners used to configure the Cache to receive transaction events after
* the transaction is processed (committed, rolled back).
@@ -677,16 +664,6 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
this.transactionWriter = transactionWriter;
}
/**
* Requires GemFire 7.0 or higher
* @param gatewayConflictResolver defined as Object in the signature for backward
* compatibility with Gemfire 6 compatibility. This must be an instance of
* {@link com.gemstone.gemfire.cache.util.GatewayConflictResolver}
*/
public void setGatewayConflictResolver(Object gatewayConflictResolver) {
this.gatewayConflictResolver = gatewayConflictResolver;
}
/**
* Sets an instance of the DynamicRegionSupport to support Dynamic Regions in this GemFire Cache.
*
@@ -696,6 +673,16 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
this.dynamicRegionSupport = dynamicRegionSupport;
}
/**
* Requires GemFire 7.0 or higher
* @param gatewayConflictResolver defined as Object in the signature for backward
* compatibility with Gemfire 6 compatibility. This must be an instance of
* {@link com.gemstone.gemfire.cache.util.GatewayConflictResolver}
*/
public void setGatewayConflictResolver(Object gatewayConflictResolver) {
this.gatewayConflictResolver = gatewayConflictResolver;
}
/**
* @param jndiDataSources the list of configured JndiDataSources to use with this Cache.
*/
@@ -704,10 +691,12 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
}
/**
* @return the list of configured JndiDataSources.
* Sets the state of the use-shared-configuration GemFire distribution config setting.
*
* @param useSharedConfiguration a boolean value to set the use-shared-configuration GemFire distribution property.
*/
public List<JndiDataSource> getJndiDataSources() {
return jndiDataSources;
public void setUseSharedConfiguration(Boolean useSharedConfiguration) {
this.useSharedConfiguration = useSharedConfiguration;
}
/**
@@ -717,6 +706,20 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
return beanClassLoader;
}
/**
* @return the beanFactory
*/
public BeanFactory getBeanFactory() {
return beanFactory;
}
/**
* @return the beanFactoryLocator
*/
public GemfireBeanFactoryLocator getBeanFactoryLocator() {
return factoryLocator;
}
/**
* @return the beanName
*/
@@ -742,10 +745,53 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
return properties;
}
@Override
public Cache getObject() throws Exception {
init();
return cache;
}
@Override
public Class<? extends Cache> getObjectType() {
return (cache != null ? cache.getClass() : Cache.class);
}
@Override
public boolean isSingleton() {
return true;
}
/**
* @return the copyOnRead
*/
public Boolean getCopyOnRead() {
return copyOnRead;
}
/**
* @return the criticalHeapPercentage
*/
public Float getCriticalHeapPercentage() {
return criticalHeapPercentage;
}
/**
* Gets the value for the auto-reconnect setting.
*
* @return a boolean value indicating whether auto-reconnect was specified (non-null) and whether it was enabled
* or not.
*/
public Boolean getEnableAutoReconnect() {
return enableAutoReconnect;
}
/**
* @return the evictionHeapPercentage
*/
public Float getEvictionHeapPercentage() {
return evictionHeapPercentage;
}
/**
* @return the pdxSerializer
*/
@@ -753,13 +799,6 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
return pdxSerializer;
}
/**
* @return the pdxPersistent
*/
public Boolean getPdxPersistent() {
return pdxPersistent;
}
/**
* @return the pdxReadSerialized
*/
@@ -767,6 +806,13 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
return pdxReadSerialized;
}
/**
* @return the pdxPersistent
*/
public Boolean getPdxPersistent() {
return pdxPersistent;
}
/**
* @return the pdxIgnoreUnreadFields
*/
@@ -782,10 +828,10 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
}
/**
* @return the copyOnRead
* @return the lockLease
*/
public Boolean getCopyOnRead() {
return copyOnRead;
public Integer getLockLease() {
return lockLease;
}
/**
@@ -795,13 +841,6 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
return lockTimeout;
}
/**
* @return the lockLease
*/
public Integer getLockLease() {
return lockLease;
}
/**
* @return the messageSyncInterval
*/
@@ -830,21 +869,6 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
return transactionWriter;
}
/**
* @return the evictionHeapPercentage
*/
public Float getEvictionHeapPercentage() {
return evictionHeapPercentage;
}
/**
* @return the criticalHeapPercentage
*/
public Float getCriticalHeapPercentage() {
return criticalHeapPercentage;
}
/**
* @return the dynamicRegionSupport
*/
@@ -860,10 +884,19 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
}
/**
* @return the beanFactoryLocator
* @return the list of configured JndiDataSources.
*/
public GemfireBeanFactoryLocator getBeanFactoryLocator() {
return factoryLocator;
public List<JndiDataSource> getJndiDataSources() {
return jndiDataSources;
}
/**
* Gets the value fo the use-shared-configuration GemFire setting.
*
* @return a boolean value indicating whether shared configuration use has been enabled or not.
*/
public Boolean getUseSharedConfiguration() {
return this.useSharedConfiguration;
}
/**
@@ -878,6 +911,8 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
protected void postProcessPropertiesBeforeInitialization(Properties gemfireProperties) {
gemfireProperties.setProperty("disable-auto-reconnect", String.valueOf(
!Boolean.TRUE.equals(getEnableAutoReconnect())));
gemfireProperties.setProperty("use-shared-configuration", String.valueOf(
Boolean.TRUE.equals(getUseSharedConfiguration())));
}
/* (non-Javadoc)
@@ -891,4 +926,5 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl
init();
}
}
}

View File

@@ -89,14 +89,12 @@ public class ClientCacheFactoryBean extends CacheFactoryBean {
ClientCacheFactory clientCacheFactory = (ClientCacheFactory) factory;
initializePool(clientCacheFactory);
// Now create the cache
GemFireCache cache = clientCacheFactory.create();
// Register for events after pool/regions been created and iff non-durable client
// register for events after Pool and Regions been created and iff non-durable client...
readyForEvents();
// Return the cache
return cache;
}
@@ -110,86 +108,110 @@ public class ClientCacheFactoryBean extends CacheFactoryBean {
return ClientCacheFactory.getAnyInstance();
}
private void initializePool(ClientCacheFactory ccf) {
Pool p = pool;
private void initializePool(ClientCacheFactory clientCacheFactory) {
Pool localPool = pool;
if (p == null) {
if (localPool == null) {
if (StringUtils.hasText(poolName)) {
p = PoolManager.find(poolName);
localPool = PoolManager.find(poolName);
}
// Bind this client cache to a pool that hasn't been created yet.
if (p == null) {
if (localPool == null) {
PoolFactoryBean.connectToTemporaryDs(this.properties);
}
if (StringUtils.hasText(poolName)) {
try {
getBeanFactory().isTypeMatch(poolName, Pool.class);
} catch (Exception e) {
String msg = "No bean found with name " + poolName
+ " of type " + Pool.class.getName();
if (poolName
.equals(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME)) {
msg += ". A client cache requires a pool";
}
throw new BeanInitializationException(msg);
localPool = getBeanFactory().getBean(poolName, Pool.class);
}
p = getBeanFactory().getBean(poolName, Pool.class);
} else {
catch (Exception e) {
String message = String.format("No bean found with name '%1$s' of type '%2$s'.%3$s", poolName,
Pool.class.getName(), (GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME.equals(poolName)
? " A client cache requires a pool." : ""));
throw new BeanInitializationException(message);
}
}
else {
if (log.isDebugEnabled()) {
log.debug("Checking for a unique pool");
log.debug("Checking for a unique pool...");
}
p = getBeanFactory().getBean(Pool.class);
this.poolName = p.getName();
localPool = getBeanFactory().getBean(Pool.class);
this.poolName = localPool.getName();
}
}
if (p != null) {
if (localPool != null) {
// copy the pool settings - this way if the pool is not found, at
// least the cache will have a similar config
ccf.setPoolFreeConnectionTimeout(p.getFreeConnectionTimeout());
ccf.setPoolIdleTimeout(p.getIdleTimeout());
ccf.setPoolLoadConditioningInterval(p.getLoadConditioningInterval());
ccf.setPoolMaxConnections(p.getMaxConnections());
ccf.setPoolMinConnections(p.getMinConnections());
ccf.setPoolMultiuserAuthentication(p.getMultiuserAuthentication());
ccf.setPoolPingInterval(p.getPingInterval());
ccf.setPoolPRSingleHopEnabled(p.getPRSingleHopEnabled());
ccf.setPoolReadTimeout(p.getReadTimeout());
ccf.setPoolRetryAttempts(p.getRetryAttempts());
ccf.setPoolServerGroup(p.getServerGroup());
ccf.setPoolSocketBufferSize(p.getSocketBufferSize());
ccf.setPoolStatisticInterval(p.getStatisticInterval());
ccf.setPoolSubscriptionAckInterval(p.getSubscriptionAckInterval());
ccf.setPoolSubscriptionEnabled(p.getSubscriptionEnabled());
ccf.setPoolSubscriptionMessageTrackingTimeout(p
.getSubscriptionMessageTrackingTimeout());
ccf.setPoolSubscriptionRedundancy(p.getSubscriptionRedundancy());
ccf.setPoolThreadLocalConnections(p.getThreadLocalConnections());
clientCacheFactory.setPoolFreeConnectionTimeout(localPool.getFreeConnectionTimeout());
clientCacheFactory.setPoolIdleTimeout(localPool.getIdleTimeout());
clientCacheFactory.setPoolLoadConditioningInterval(localPool.getLoadConditioningInterval());
clientCacheFactory.setPoolMaxConnections(localPool.getMaxConnections());
clientCacheFactory.setPoolMinConnections(localPool.getMinConnections());
clientCacheFactory.setPoolMultiuserAuthentication(localPool.getMultiuserAuthentication());
clientCacheFactory.setPoolPingInterval(localPool.getPingInterval());
clientCacheFactory.setPoolPRSingleHopEnabled(localPool.getPRSingleHopEnabled());
clientCacheFactory.setPoolReadTimeout(localPool.getReadTimeout());
clientCacheFactory.setPoolRetryAttempts(localPool.getRetryAttempts());
clientCacheFactory.setPoolServerGroup(localPool.getServerGroup());
clientCacheFactory.setPoolSocketBufferSize(localPool.getSocketBufferSize());
clientCacheFactory.setPoolStatisticInterval(localPool.getStatisticInterval());
clientCacheFactory.setPoolSubscriptionAckInterval(localPool.getSubscriptionAckInterval());
clientCacheFactory.setPoolSubscriptionEnabled(localPool.getSubscriptionEnabled());
clientCacheFactory.setPoolSubscriptionMessageTrackingTimeout(localPool.getSubscriptionMessageTrackingTimeout());
clientCacheFactory.setPoolSubscriptionRedundancy(localPool.getSubscriptionRedundancy());
clientCacheFactory.setPoolThreadLocalConnections(localPool.getThreadLocalConnections());
List<InetSocketAddress> locators = localPool.getLocators();
List<InetSocketAddress> locators = p.getLocators();
if (locators != null) {
for (InetSocketAddress inet : locators) {
ccf.addPoolLocator(inet.getHostName(), inet.getPort());
for (InetSocketAddress socketAddress : locators) {
clientCacheFactory.addPoolLocator(socketAddress.getHostName(), socketAddress.getPort());
}
}
List<InetSocketAddress> servers = p.getServers();
List<InetSocketAddress> servers = localPool.getServers();
if (servers != null) {
for (InetSocketAddress inet : servers) {
ccf.addPoolServer(inet.getHostName(), inet.getPort());
for (InetSocketAddress socketAddress : servers) {
clientCacheFactory.addPoolServer(socketAddress.getHostName(), socketAddress.getPort());
}
}
}
}
@Override
protected void applyPdxOptions(Object factory) {
if (factory instanceof ClientCacheFactory) {
new PdxOptions((ClientCacheFactory) factory).run();
}
}
@Override
protected void postProcessPropertiesBeforeInitialization(final Properties gemfireProperties) {
}
/**
* Inform the GemFire cluster that this client cache is ready to receive events.
*/
private void readyForEvents(){
ClientCache clientCache = ClientCacheFactory.getAnyInstance();
if (Boolean.TRUE.equals(readyForEvents) && !clientCache.isClosed()) {
try {
clientCache.readyForEvents();
}
catch (IllegalStateException ignore) {
// cannot be called for a non-durable client so exception is thrown
}
}
}
@Override
public final Boolean getEnableAutoReconnect() {
return Boolean.FALSE;
@@ -231,31 +253,23 @@ public class ClientCacheFactoryBean extends CacheFactoryBean {
this.readyForEvents = readyForEvents;
}
/**
* Gets the value for the readyForEvents property.
*
* @return a boolean value indicating the state of the 'readyForEvents' property.
*/
public Boolean getReadyForEvents(){
return this.readyForEvents;
}
@Override
protected void applyPdxOptions(Object factory) {
if (factory instanceof ClientCacheFactory) {
new PdxOptions((ClientCacheFactory) factory).run();
}
public final Boolean getUseSharedConfiguration() {
return Boolean.FALSE;
}
/**
* Inform the GemFire cluster that this client cache is ready to receive events.
*/
private void readyForEvents(){
ClientCache clientCache = ClientCacheFactory.getAnyInstance();
if (Boolean.TRUE.equals(readyForEvents) && !clientCache.isClosed()) {
try {
clientCache.readyForEvents();
}
catch (IllegalStateException ignore) {
// cannot be called for a non-durable client so exception is thrown
}
}
@Override
public final void setUseSharedConfiguration(Boolean useSharedConfiguration) {
throw new UnsupportedOperationException("Shared, cluster configuration is not applicable to clients.");
}
}

View File

@@ -56,6 +56,8 @@ class CacheParser extends AbstractSimpleBeanDefinitionParser {
ParsingUtils.setPropertyValue(element, builder, "cache-xml-location", "cacheXml");
ParsingUtils.setPropertyReference(element, builder, "properties-ref", "properties");
ParsingUtils.setPropertyValue(element, builder, "lazy-init","lazyInitialize");
ParsingUtils.setPropertyValue(element, builder, "use-bean-factory-locator");
ParsingUtils.setPropertyValue(element, builder, "close");
ParsingUtils.setPropertyValue(element, builder, "copy-on-read");
ParsingUtils.setPropertyValue(element, builder, "critical-heap-percentage");
@@ -70,8 +72,7 @@ class CacheParser extends AbstractSimpleBeanDefinitionParser {
ParsingUtils.setPropertyValue(element, builder, "pdx-persistent");
parsePdxDiskStore(element, parserContext, builder);
ParsingUtils.setPropertyValue(element, builder, "search-timeout");
ParsingUtils.setPropertyValue(element, builder, "lazy-init","lazyInitialize");
ParsingUtils.setPropertyValue(element, builder, "use-bean-factory-locator");
ParsingUtils.setPropertyValue(element, builder, "use-shared-configuration");
List<Element> txListeners = DomUtils.getChildElementsByTagName(element, "transaction-listener");

View File

@@ -274,20 +274,19 @@ Defines a GemFire Cache instance used for creating or retrieving 'regions'.
<xsd:attribute name="enable-auto-reconnect" type="xsd:string" use="optional" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
By default, GemFire 7.5 and later will attempt to reconnect and reinitialize the cache when it has been forced out
of the distributed system by a network-partition event, or has otherwise been shunned by other members.
By default, GemFire 8.0 and later will attempt to reconnect and reinitialize the cache when the peer member has been
forced out of the distributed system by a network-partition event, or has otherwise been shunned by other members.
An auto-reconnect causes all the GemFire component references (e.g. Cache, Regions, Gateways, etc) that may have
been injected into and SDG-based application to become stale. Even when using GemFire's public Java API directly,
GemFire makes no guarantees to automatically refresh any returned references to application objects that now are
stale.
An auto-reconnect causes all GemFire component references (e.g. Cache, Regions, AEQs, Gateways, etc) that may have been
injected into SDG application components to become stale. Even when using GemFire's public Java API directly,
GemFire makes no guarantees to automatically refresh any stale references used by application objects.
Therefore, in Spring Data GemFire, the default behavior will continue to be not to 'auto-reconnect'. This behavior
is not recommended for applications that are 'peer' Caches injecting GemFire components, like the Cache, or Regions
into appliaction components (e.g. @Repository POJOs).
Therefore, in Spring Data GemFire, the default behavior will be to not 'auto-reconnect'. Automatically reconnecting
is not recommended for applications that are also peer member Caches (i.e. GemFire Servers) that inject
GemFire components, such as the Cache or Regions, into application objects (e.g. @Repository POJOs).
Enabling 'auto-reconnect' is only recommended for Spring bootstrapped GemFire Server's that use the Spring Data GemFire
XML namespace to configure GemFire instead of GemFire's cache.xml.
Enabling 'auto-reconnect' is only recommended when bootstrapping a GemFire Server's within a Spring context using
Spring Data GemFire XML namespace and configuration meta-data to configure GemFire instead of GemFire's cache.xml.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -329,6 +328,23 @@ You may want to change this based on your knowledge of the network load or other
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="use-shared-configuration" type="xsd:string" use="optional" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Enables this Spring Data GemFire configured peer member Cache node to participate in cluster-wide configuration
by receiving it's configuration meta-data (in the form of cache.xml) from a Locator, with persistent configuration
enabled, running in the cluster.
The cluster-wide configuration is a shared, persistent and consistent view of the cluster when the member joins
the cluster and requests the cluster configuration from the Locator.
Spring Data GemFire disables this GemFire 8 feature by default assuming that the primary configuration for this
member will be defined in Spring Data GemFire's XML namespace configuration meta-data. However, this feature is useful
in a production setting to set the base configuration of the member and augment that cluster-wide configuration
with Spring configuration meta-data that is specific to the application's needs.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>

View File

@@ -0,0 +1,159 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.gemfire.fork.LocatorProcess;
import org.springframework.data.gemfire.process.ProcessExecutor;
import org.springframework.data.gemfire.process.ProcessWrapper;
import org.springframework.data.gemfire.test.support.ThreadUtils;
import org.springframework.data.gemfire.test.support.ZipUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.FileSystemUtils;
import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.Scope;
/**
* The CacheUsingSharedConfigurationIntegrationTest class is a test suite of test cases testing the integration of
* Spring Data GemFire with GemFire 8's new shared, persistent, cluster configuration service.
*
* @author John Blum
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @since 1.5.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("cacheUsingSharedConfigurationIntegrationTest.xml")
@SuppressWarnings("unused")
public class CacheUsingSharedConfigurationIntegrationTest {
@Resource(name = "SharedConfigRegion")
private Region<Long, String> sharedConfigRegion;
@Resource(name = "NativeLocalRegion")
private Region<Integer, String> nativeLocalRegion;
@Resource(name = "NativePartitionRegion")
private Region<Integer, String> nativePartitionRegion;
@Resource(name = "NativeReplicateRegion")
private Region<Integer, String> nativeReplicateRegion;
@Resource(name = "LocalRegion")
private Region<Integer, String> localRegion;
private static File locatorWorkingDirectory;
private static ProcessWrapper locatorProcess;
@BeforeClass
public static void testSuiteSetup() throws IOException {
String locatorName = "SharedConfigLocator";
locatorWorkingDirectory = new File(System.getProperty("user.dir"), locatorName.toLowerCase());
assertTrue(locatorWorkingDirectory.isDirectory() || locatorWorkingDirectory.mkdirs());
ZipUtils.unzip(new ClassPathResource("/shared_config.zip"), locatorWorkingDirectory);
List<String> arguments = new ArrayList<String>();
arguments.add("-Dgemfire.name=" + locatorName);
arguments.add("-Dspring.gemfire.enable-shared-configuration=true");
arguments.add("-Dspring.gemfire.load-shared-configuration=true");
locatorProcess = ProcessExecutor.launch(locatorWorkingDirectory, LocatorProcess.class,
arguments.toArray(new String[arguments.size()]));
locatorProcess.registerShutdownHook();
waitForLocatorStart(TimeUnit.SECONDS.toMillis(30));
//System.out.println("Shared Configuration Locator should be running!");
}
private static void waitForLocatorStart(final long milliseconds) {
ThreadUtils.timedWait(milliseconds, 500, new ThreadUtils.WaitCondition() {
File pidControlFile = new File(locatorWorkingDirectory, LocatorProcess.getLocatorProcessControlFilename());
@Override public boolean waiting() {
return !pidControlFile.isFile();
}
});
}
@AfterClass
public static void testSuiteTearDown() {
locatorProcess.shutdown();
FileSystemUtils.deleteRecursively(locatorWorkingDirectory);
}
protected Region assertRegion(final Region actualRegion, final String expectedRegionName) {
return assertRegion(actualRegion, expectedRegionName, Region.SEPARATOR+expectedRegionName);
}
protected Region assertRegion(final Region actualRegion, final String expectedRegionName, final String expectedRegionFullPath) {
assertNotNull(String.format("The '%1$s' was not properly configured and initialized!", expectedRegionName), actualRegion);
assertEquals(expectedRegionName, actualRegion.getName());
assertEquals(expectedRegionFullPath, actualRegion.getFullPath());
return actualRegion;
}
protected Region assertRegionAttributes(final Region actualRegion, final DataPolicy expectedDataPolicy, final Scope expectedScope) {
assertNotNull(actualRegion);
assertNotNull(actualRegion.getAttributes());
assertEquals(expectedDataPolicy, actualRegion.getAttributes().getDataPolicy());
assertEquals(expectedScope, actualRegion.getAttributes().getScope());
return actualRegion;
}
@Test
public void testConfiguration() {
assertRegionAttributes(assertRegion(sharedConfigRegion, "SharedConfigRegion"),
DataPolicy.PARTITION, Scope.DISTRIBUTED_NO_ACK);
assertRegionAttributes(assertRegion(nativeLocalRegion, "NativeLocalRegion"), DataPolicy.NORMAL, Scope.LOCAL);
assertRegionAttributes(assertRegion(nativePartitionRegion, "NativePartitionRegion"),
DataPolicy.PARTITION, Scope.DISTRIBUTED_NO_ACK);
assertRegionAttributes(assertRegion(nativeReplicateRegion, "NativeReplicateRegion"),
DataPolicy.REPLICATE, Scope.DISTRIBUTED_ACK);
assertRegionAttributes(assertRegion(localRegion, "LocalRegion"), DataPolicy.NORMAL, Scope.LOCAL);
}
}

View File

@@ -27,6 +27,9 @@ import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.data.gemfire.fork.CacheServerProcess;
import org.springframework.data.gemfire.test.support.IOUtils;
import org.springframework.data.gemfire.test.support.ThreadUtils;
import org.springframework.util.StringUtils;
/**
@@ -34,7 +37,9 @@ import org.springframework.util.StringUtils;
*
* @author Costin Leau
* @author John Blum
* @deprecated ForkUtils has serious design flaws; please use ProcessExecutor instead
*/
@Deprecated
public class ForkUtil {
private static OutputStream processStandardInStream;
@@ -47,13 +52,11 @@ public class ForkUtil {
private static OutputStream cloneJVM(final String arguments) {
String[] args = arguments.split("\\s+");
List<String> command = new ArrayList<String>(args.length + 5);
List<String> command = new ArrayList<String>(args.length + 3);
command.add(JAVA_EXE);
command.add("-classpath");
command.add(JAVA_CLASSPATH);
//command.add("-Xms256m");
//command.add("-Xmx512m");
command.addAll(Arrays.asList(args));
Process javaProcess;
@@ -72,8 +75,8 @@ public class ForkUtil {
final AtomicBoolean runCondition = new AtomicBoolean(true);
final Process p = javaProcess;
new Thread(newProcessStreamReaderRunnable(runCondition, p.getErrorStream(), "[FORK-ERROR]")).start();
new Thread(newProcessStreamReaderRunnable(runCondition, p.getInputStream(), "[FORK]")).start();
startNewThread(newProcessStreamReader(runCondition, p.getErrorStream(), "[FORK-ERROR]"));
startNewThread(newProcessStreamReader(runCondition, p.getInputStream(), "[FORK]"));
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
@@ -98,61 +101,69 @@ public class ForkUtil {
return processStandardInStream;
}
protected static Runnable newProcessStreamReaderRunnable(final AtomicBoolean runCondition, final InputStream processStream, final String label) {
final BufferedReader processStreamReader = new BufferedReader(new InputStreamReader(processStream));
protected static Thread startNewThread(final Runnable runnable) {
Thread runnableThread = new Thread(runnable);
runnableThread.start();
return runnableThread;
}
protected static Runnable newProcessStreamReader(final AtomicBoolean runCondition, final InputStream processStream, final String label) {
return new Runnable() {
public void run() {
BufferedReader processStreamReader = new BufferedReader(new InputStreamReader(processStream));
try {
do {
while (runCondition.get()) {
for (String line = "Reading..."; line != null; line = processStreamReader.readLine()) {
System.out.printf("%1$s %2$s%n ", label, line);
}
Thread.sleep(200);
}
while (runCondition.get());
}
catch (Exception ignore) {
}
finally {
IOUtils.close(processStreamReader);
}
}
};
}
public static OutputStream cacheServer(Class<?> clazz) {
return startCacheServer(clazz.getName());
}
public static OutputStream cacheServer() {
return startCacheServer("org.springframework.data.gemfire.fork.CacheServerProcess");
return cacheServer(CacheServerProcess.class);
}
public static OutputStream cacheServer(Class<?> type) {
return startCacheServer(type.getName());
}
public static OutputStream startCacheServer(String args) {
return startGemFireProcess(args, "cache server");
}
protected static OutputStream startGemFireProcess(final String args, final String processName) {
String className = args.split(" ")[0];
System.out.println("main class:" + className);
System.out.println("main class: " + className);
if (controlFileExists(className)) {
deleteControlFile(className);
}
OutputStream os = cloneJVM(args);
int maxTime = 30000;
int time = 0;
while (!controlFileExists(className) && time < maxTime) {
try {
Thread.sleep(500);
time += 500;
} catch (InterruptedException ex) {
// ignore and move on
}
OutputStream outputStream = cloneJVM(args);
final long timeout = System.currentTimeMillis() + 30000;
while (!controlFileExists(className) && System.currentTimeMillis() < timeout) {
ThreadUtils.sleep(500);
}
if (controlFileExists(className)) {
System.out.println("[FORK] Started cache server");
System.out.printf("[FORK] Started %1$s%n", processName);
}
else {
throw new RuntimeException("could not fork cache server");
throw new RuntimeException(String.format("Failed to fork %1$s", processName));
}
return os;
return outputStream;
}
public static void sendSignal() {
@@ -165,19 +176,16 @@ public class ForkUtil {
}
}
public static boolean deleteControlFile(String name) {
String path = TEMP_DIRECTORY + File.separator + name;
return new File(path).delete();
public static boolean createControlFile(final String name) throws IOException {
return new File(TEMP_DIRECTORY + File.separator + name).createNewFile();
}
public static boolean createControlFile(String name) throws IOException {
String path = TEMP_DIRECTORY + File.separator + name;
return new File(path).createNewFile();
public static boolean controlFileExists(final String name) {
return new File(TEMP_DIRECTORY + File.separator + name).isFile();
}
public static boolean controlFileExists(String name) {
String path = TEMP_DIRECTORY + File.separator + name;
return new File(path).exists();
public static boolean deleteControlFile(final String name) {
return new File(TEMP_DIRECTORY + File.separator + name).delete();
}
}

View File

@@ -74,7 +74,7 @@ public class GemfireV7GatewayNamespaceTest extends RecreatingContextTest {
@Before
@Override
public void createCtx() {
if (GemfireUtils.GEMFIRE_VERSION.startsWith("7")) {
if (GemfireUtils.isGemfireVersion7OrAbove()) {
super.createCtx();
}
}

View File

@@ -38,12 +38,12 @@ import com.gemstone.gemfire.cache.server.CacheServer;
public class CacheServerProcess {
public static void main(final String[] args) throws Exception {
Properties props = new Properties();
props.setProperty("name", "CqServer");
props.setProperty("mcast-port", "0");
props.setProperty("log-level", "warning");
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", "CqServer");
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", "warning");
Cache cache = new CacheFactory(props).create();
Cache cache = new CacheFactory(gemfireProperties).create();
RegionFactory<String, Integer> regionFactory = cache.createRegionFactory();
regionFactory.setDataPolicy(DataPolicy.REPLICATE);
@@ -73,6 +73,8 @@ public class CacheServerProcess {
System.out.println("Waiting for shutdown...");
bufferedReader.readLine();
System.out.println("Shutting down!");
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.fork;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.springframework.data.gemfire.process.support.ProcessUtils;
import org.springframework.data.gemfire.test.support.ThreadUtils;
import com.gemstone.gemfire.distributed.LocatorLauncher;
import com.gemstone.gemfire.distributed.internal.DistributionConfig;
import com.gemstone.gemfire.distributed.internal.InternalLocator;
import com.gemstone.gemfire.distributed.internal.SharedConfiguration;
/**
* The LocatorProcess class is a main Java class that is used fork and launch a GemFire Locator process using the
* LocatorLauncher class.
*
* @author John Blum
* @see com.gemstone.gemfire.distributed.LocatorLauncher
* @since 1.5.0
*/
public class LocatorProcess {
public static final int DEFAULT_LOCATOR_PORT = 20668;
public static final String DEFAULT_GEMFIRE_MEMBER_NAME = "SpringDataGemFire-Locator";
public static final String DEFAULT_HOSTNAME_FOR_CLIENTS = "localhost";
public static final String DEFAULT_HTTP_SERVICE_PORT = "0";
public static final String DEFAULT_LOG_LEVEL = "config";
public static void main(final String... args) throws IOException {
LocatorLauncher locatorLauncher = buildLocatorLauncher();
registerShutdownHook();
// start the GemFire Locator process...
locatorLauncher.start();
waitForLocatorStart(TimeUnit.SECONDS.toMillis(20));
ProcessUtils.writePid(new File(System.getProperty("user.dir"), getLocatorProcessControlFilename()),
ProcessUtils.currentPid());
ProcessUtils.waitForStopSignal();
}
public static String getLocatorProcessControlFilename() {
return LocatorProcess.class.getSimpleName().toLowerCase().concat(".pid");
}
private static LocatorLauncher buildLocatorLauncher() {
return new LocatorLauncher.Builder()
.setMemberName(DEFAULT_GEMFIRE_MEMBER_NAME)
.setHostnameForClients(System.getProperty("spring.gemfire.hostname-for-clients",
DEFAULT_HOSTNAME_FOR_CLIENTS))
.setPort(Integer.getInteger("spring.gemfire.locator-port", DEFAULT_LOCATOR_PORT))
.setRedirectOutput(false)
.set(DistributionConfig.ENABLE_SHARED_CONFIGURATION_NAME, String.valueOf(Boolean.getBoolean(
"spring.gemfire.enable-shared-configuration")))
.set(DistributionConfig.HTTP_SERVICE_PORT_NAME, System.getProperty("spring.gemfire.http-service-port",
DEFAULT_HTTP_SERVICE_PORT))
.set(DistributionConfig.JMX_MANAGER_NAME, String.valueOf(Boolean.TRUE))
.set(DistributionConfig.JMX_MANAGER_START_NAME, String.valueOf(Boolean.FALSE))
.set(DistributionConfig.LOAD_SHARED_CONFIG_FROM_DIR_NAME, String.valueOf(Boolean.getBoolean(
"spring.gemfire.load-shared-configuration")))
.set(DistributionConfig.LOG_LEVEL_NAME, System.getProperty("spring.gemfire.log-level", DEFAULT_LOG_LEVEL))
.build();
}
private static boolean isSharedConfigurationEnabled(final InternalLocator locator) {
return (locator != null && Boolean.valueOf(locator.getDistributedSystem().getProperties().getProperty(
DistributionConfig.ENABLE_SHARED_CONFIGURATION_NAME)));
}
private static void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override public void run() {
stopSharedConfigurationService();
LocatorLauncher.getInstance().stop();
}
private void stopSharedConfigurationService() {
InternalLocator locator = InternalLocator.getLocator();
if (isSharedConfigurationEnabled(locator)) {
SharedConfiguration sharedConfiguration = locator.getSharedConfiguration();
if (sharedConfiguration != null) {
sharedConfiguration.destroySharedConfiguration();
}
}
}
}));
}
private static void waitForLocatorStart(final long milliseconds) {
final InternalLocator locator = InternalLocator.getLocator();
if (isSharedConfigurationEnabled(locator)) {
ThreadUtils.timedWait(milliseconds, 500, new ThreadUtils.WaitCondition() {
@Override public boolean waiting() {
return !locator.isSharedConfigurationRunning();
}
});
}
else {
LocatorLauncher.getInstance().waitOnStatusResponse(milliseconds, Math.min(500, milliseconds),
TimeUnit.MILLISECONDS);
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.process;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The ProcessContext class is a container encapsulating configuration and context meta-data for a running process.
*
* @author John Blum
* @see java.lang.ProcessBuilder
* @since 1.5.0
*/
@SuppressWarnings("unused")
public class ProcessConfiguration {
private final boolean redirectingErrorStream;
private final File workingDirectory;
private final List<String> command;
private final Map<String, String> environment;
public static ProcessConfiguration create(final ProcessBuilder processBuilder) {
Assert.notNull(processBuilder, "The ProcessBuilder used to construct, configure and start the Process must not be null!");
return new ProcessConfiguration(processBuilder.command(), processBuilder.directory(),
processBuilder.environment(), processBuilder.redirectErrorStream());
}
public ProcessConfiguration(final List<String> command, final File workingDirectory,
final Map<String, String> environment, final boolean redirectingErrorStream) {
Assert.notEmpty(command, "The command used to run the process must be specified!");
Assert.isTrue((workingDirectory != null && workingDirectory.isDirectory()), String.format(
"The process working directory (%1$s) is not valid!", workingDirectory));
this.command = new ArrayList<String>(command);
this.workingDirectory = workingDirectory;
this.redirectingErrorStream = redirectingErrorStream;
this.environment = (environment != null
? Collections.unmodifiableMap(new HashMap<String, String>(environment))
: Collections.<String, String>emptyMap());
}
public List<String> getCommand() {
return Collections.unmodifiableList(command);
}
public String getCommandString() {
return StringUtils.arrayToDelimitedString(getCommand().toArray(), " ");
}
public Map<String, String> getEnvironment() {
return environment;
}
public boolean isRedirectingErrorStream() {
return redirectingErrorStream;
}
public File getWorkingDirectory() {
return workingDirectory;
}
@Override
public String toString() {
return "{ command = ".concat(getCommandString())
.concat(", workingDirectory = ".concat(getWorkingDirectory().getAbsolutePath()))
.concat(", redirectingErrorStream = ".concat(String.valueOf(isRedirectingErrorStream())))
.concat(", environment = ".concat(String.valueOf(getEnvironment())))
.concat(" }");
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.process;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The ProcessLauncher class is a utility class for launching Java processes.
*
* @author John Blum
* @see java.lang.Process
* @see java.lang.ProcessBuilder
* @see org.springframework.data.gemfire.process.ProcessConfiguration
* @see org.springframework.data.gemfire.process.ProcessWrapper
* @since 1.5.0
*/
@SuppressWarnings("unused")
public abstract class ProcessExecutor {
protected static final String JAVA_CLASSPATH = System.getProperty("java.class.path");
protected static final String JAVA_HOME = System.getProperty("java.home");
protected static final String JAVA_EXE = JAVA_HOME.concat(File.separator).concat("bin")
.concat(File.separator).concat("java");
protected static final String USER_HOME = System.getProperty("user.home");
protected static final String USER_WORKING_DIRECTORY = System.getProperty("user.dir");
public static ProcessWrapper launch(final Class<?> type, final String... args) throws IOException {
return launch(new File(USER_WORKING_DIRECTORY), type, args);
}
public static ProcessWrapper launch(final File workingDirectory, final Class<?> type, final String... args)
throws IOException
{
ProcessBuilder processBuilder = new ProcessBuilder()
.command(buildCommand(type, args))
.directory(validateDirectory(workingDirectory))
.redirectErrorStream(true);
Process process = processBuilder.start();
ProcessWrapper processWrapper = new ProcessWrapper(process, ProcessConfiguration.create(processBuilder));
processWrapper.register(new ProcessInputStreamListener() {
@Override public void onInput(final String input) {
System.err.printf("[FORK-OUT] - %1$s%n", input);
}
});
return processWrapper;
}
protected static String[] buildCommand(final Class<?> type, final String... args) {
Assert.notNull(type != null, "The main Class to launch must not be null!");
List<String> command = new ArrayList<String>();
List<String> programArgs = Collections.emptyList();
command.add(JAVA_EXE);
command.add("-server");
command.add("-classpath");
command.add(JAVA_CLASSPATH);
if (args != null) {
programArgs = new ArrayList<String>(args.length);
for (String arg : args) {
if (isJvmOption(arg)) {
command.add(arg);
}
else if (!StringUtils.isEmpty(arg)) {
programArgs.add(arg);
}
}
}
command.add(type.getName());
command.addAll(programArgs);
return command.toArray(new String[command.size()]);
}
protected static boolean isJvmOption(final String option) {
return (!StringUtils.isEmpty(option) && (option.startsWith("-D") || option.startsWith("-X")));
}
protected static File validateDirectory(final File workingDirectory) {
Assert.isTrue(workingDirectory != null && (workingDirectory.isDirectory() || workingDirectory.mkdirs()));
return workingDirectory;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.process;
import java.util.EventListener;
/**
* The ProcessStreamListener interface is a callback listener that gets called when input arrives from either a
* process's standard output steam or standard error stream.
*
* @author John Blum
* @see java.util.EventListener
* @since 1.5.0
*/
public interface ProcessInputStreamListener extends EventListener {
void onInput(String input);
}

View File

@@ -0,0 +1,274 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.process;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.data.gemfire.process.support.PidUnavailableException;
import org.springframework.data.gemfire.process.support.ProcessUtils;
import org.springframework.data.gemfire.test.support.IOUtils;
import org.springframework.data.gemfire.test.support.ThreadUtils;
import org.springframework.data.gemfire.test.support.ThrowableUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The ProcessWrapper class is a wrapper for a Process object representing an OS process and the ProcessBuilder used
* to construct and start the process.
*
* @author John Blum
* @see java.lang.Process
* @see java.lang.ProcessBuilder
* @since 1.5.0
*/
@SuppressWarnings("unused")
public class ProcessWrapper {
protected static final boolean DEFAULT_DAEMON_THREAD = true;
protected static final long DEFAULT_WAIT_TIME_MILLISECONDS = TimeUnit.SECONDS.toMillis(15);
private final List<ProcessInputStreamListener> listeners = new CopyOnWriteArrayList<ProcessInputStreamListener>();
protected final Logger log = Logger.getLogger(getClass().getName());
private final Process process;
private final ProcessConfiguration processConfiguration;
public ProcessWrapper(final Process process, final ProcessConfiguration processConfiguration) {
Assert.notNull(process, "The Process object backing this wrapper must not be null!");
Assert.notNull(processConfiguration, "The context and configuration meta-data providing details about"
+ " the environment in which the process is running and how the process was configured must not be null!");
this.process = process;
this.processConfiguration = processConfiguration;
postInit();
}
private void postInit() {
newThread("Process OUT Stream Reader", newProcessInputStreamReader(process.getInputStream())).start();
if (!isRedirectingErrorStream()) {
newThread("Process ERR Stream Reader", newProcessInputStreamReader(process.getErrorStream())).start();
}
}
protected Runnable newProcessInputStreamReader(final InputStream in) {
return new Runnable() {
@Override public void run() {
if (isRunning()) {
BufferedReader inputReader = new BufferedReader(new InputStreamReader(in));
try {
for (String input = inputReader.readLine(); input != null; input = inputReader.readLine()) {
for (ProcessInputStreamListener listener : listeners) {
listener.onInput(input);
}
}
}
catch (IOException ignore) {
// ignore IO error and just stop reading from the process input stream
// IO error occurred most likely because the process was terminated
}
finally {
IOUtils.close(inputReader);
}
}
}
};
}
protected Thread newThread(final String name, final Runnable task) {
Assert.isTrue(!StringUtils.isEmpty(name), "The name of the Thread must be specified!");
Assert.notNull(task, "The Thread task must not be null!");
Thread thread = new Thread(task, name);
thread.setDaemon(DEFAULT_DAEMON_THREAD);
thread.setPriority(Thread.NORM_PRIORITY);
return thread;
}
public List<String> getCommand() {
return processConfiguration.getCommand();
}
public String getCommandString() {
return processConfiguration.getCommandString();
}
public Map<String, String> getEnvironment() {
return processConfiguration.getEnvironment();
}
public int getPid() {
return ProcessUtils.findAndReadPid(getWorkingDirectory());
}
public int safeGetPid() {
try {
return getPid();
}
catch (PidUnavailableException ignore) {
return -1;
}
}
public boolean isRedirectingErrorStream() {
return processConfiguration.isRedirectingErrorStream();
}
public boolean isRunning() {
return ProcessUtils.isRunning(this.process);
}
public File getWorkingDirectory() {
return processConfiguration.getWorkingDirectory();
}
public int exitValue() {
return process.exitValue();
}
public int safeExitValue() {
try {
return exitValue();
}
catch (IllegalThreadStateException ignore) {
return -1;
}
}
public boolean register(final ProcessInputStreamListener listener) {
return (listener != null && listeners.add(listener));
}
public void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override public void run() {
shutdown();
}
}));
}
public void signalStop() {
try {
ProcessUtils.signalStop(this.process);
}
catch (IOException e) {
log.warning("Failed to signal the process to stop!");
if (log.isLoggable(Level.FINE)) {
log.fine(ThrowableUtils.toString(e));
}
}
}
public int stop() {
return stop(DEFAULT_WAIT_TIME_MILLISECONDS);
}
public int stop(final long milliseconds) {
if (isRunning()) {
int exitValue = -1;
final int pid = safeGetPid();
final long timeout = (System.currentTimeMillis() + milliseconds);
final AtomicBoolean exited = new AtomicBoolean(false);
ExecutorService executorService = Executors.newSingleThreadExecutor();
try {
Future<Integer> futureExitValue = executorService.submit(new Callable<Integer>() {
@Override public Integer call() throws Exception {
process.destroy();
int exitValue = process.waitFor();
exited.set(true);
return exitValue;
}
});
while (!exited.get() && System.currentTimeMillis() < timeout) {
try {
exitValue = futureExitValue.get(milliseconds, TimeUnit.MILLISECONDS);
log.info(String.format("Process [%1$s] has been stopped.%n", pid));
}
catch (InterruptedException ignore) {
}
}
}
catch (TimeoutException e) {
exitValue = -1;
log.warning(String.format("Process [%1$d] did not stop within the allotted timeout of %2$d seconds.%n",
pid, TimeUnit.MILLISECONDS.toSeconds(milliseconds)));
}
catch (Exception ignore) {
// handles CancellationException, ExecutionException
}
finally {
executorService.shutdownNow();
}
return exitValue;
}
else {
return exitValue();
}
}
public int shutdown() {
if (isRunning()) {
log.info(String.format("Stopping process [%1$d]...%n", safeGetPid()));
signalStop();
waitFor();
}
return stop();
}
public boolean unregister(final ProcessInputStreamListener listener) {
return listeners.remove(listener);
}
public void waitFor() {
waitFor(DEFAULT_WAIT_TIME_MILLISECONDS);
}
public void waitFor(final long milliseconds) {
ThreadUtils.timedWait(milliseconds, 500, new ThreadUtils.WaitCondition() {
@Override public boolean waiting() {
return isRunning();
}
});
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.process.support;
/**
* The PidUnavailableException class is a RuntimeException indicating that the process ID (PID) is unobtainable for
* the current process.
*
* @author John Blum
* @see java.lang.RuntimeException
* @since 1.5.0
*/
@SuppressWarnings("unused")
public class PidUnavailableException extends RuntimeException {
public PidUnavailableException() {
}
public PidUnavailableException(final String message) {
super(message);
}
public PidUnavailableException(final Throwable cause) {
super(cause);
}
public PidUnavailableException(final String message, final Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,211 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.process.support;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.util.Scanner;
import java.util.logging.Logger;
import org.springframework.data.gemfire.test.support.IOUtils;
import org.springframework.util.StringUtils;
import com.gemstone.gemfire.management.internal.cli.util.spring.Assert;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
/**
* The ProcessUtils class is a utilty class for working with process, or specifically instances
* of the Java Process class.
*
* @author John Blum
* @see java.lang.Process
* @see java.lang.management.RuntimeMXBean
* @see com.sun.tools.attach.VirtualMachine
* @since 1.5.0
*/
@SuppressWarnings("unused")
public abstract class ProcessUtils {
protected static final Logger log = Logger.getLogger(ProcessUtils.class.getName());
protected static final String TERM_TOKEN = "<TERM/>";
public static int currentPid() {
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
String runtimeMXBeanName = runtimeMXBean.getName();
Exception cause = null;
if (StringUtils.hasText(runtimeMXBeanName)) {
int atSignIndex = runtimeMXBeanName.indexOf('@');
if (atSignIndex > 0) {
try {
return Integer.parseInt(runtimeMXBeanName.substring(0, atSignIndex));
}
catch (NumberFormatException e) {
cause = e;
}
}
}
throw new PidUnavailableException(String.format("The process ID (PID) is not available (%1$s)!",
runtimeMXBeanName), cause);
}
public static boolean isRunning(final int processId) {
for (VirtualMachineDescriptor vmDescriptor : VirtualMachine.list()) {
if (String.valueOf(processId).equals(vmDescriptor.id())) {
return true;
}
}
return false;
}
public static boolean isRunning(final Process process) {
try {
process.exitValue();
return false;
}
catch (IllegalThreadStateException ignore) {
return true;
}
}
public static void signalStop(final Process process) throws IOException {
if (isRunning(process)) {
OutputStream processOutputStream = process.getOutputStream();
processOutputStream.write(TERM_TOKEN.concat("\n").getBytes());
processOutputStream.flush();
}
}
public static void waitForStopSignal() {
Scanner in = new Scanner(System.in);
while (!TERM_TOKEN.equals(in.next()));
}
public static int findAndReadPid(final File workingDirectory) {
Assert.isTrue(workingDirectory != null && workingDirectory.isDirectory(), String.format(
"The file system pathname (%1$s) expected to contain a PID file is not a valid directory!",
workingDirectory));
File pidFile = findPidFile(workingDirectory);
if (pidFile == null) {
throw new PidUnavailableException(String.format(
"No PID file was found in working directory (%1$s) or any of it's sub-directories!",
workingDirectory));
}
return readPid(pidFile);
}
protected static File findPidFile(final File workingDirectory) {
Assert.isTrue(workingDirectory != null && workingDirectory.isDirectory(), String.format(
"The file system pathname (%1$s) is not valid directory!", workingDirectory));
for (File file : workingDirectory.listFiles(DirectoryPidFileFilter.INSTANCE)) {
if (file.isDirectory()) {
file = findPidFile(file);
}
if (PidFileFilter.INSTANCE.accept(file)) {
return file;
}
}
return null;
}
public static int readPid(final File pidFile) {
Assert.isTrue(pidFile != null && pidFile.isFile(), String.format(
"The file system pathname (%1$s) is not a valid file!", pidFile));
BufferedReader fileReader = null;
String pidValue = null;
try {
fileReader = new BufferedReader(new FileReader(pidFile));
pidValue = String.valueOf(fileReader.readLine()).trim();
return Integer.parseInt(pidValue);
}
catch (FileNotFoundException e) {
throw new PidUnavailableException(String.format("PID file (%1$s) could not be found!", pidFile), e);
}
catch (IOException e) {
throw new PidUnavailableException(String.format("Unable to read PID from file (%1$s)!", pidFile), e);
}
catch (NumberFormatException e) {
throw new PidUnavailableException(String.format(
"The value (%1$s) from PID file (%2$s) was not a valid numerical PID!", pidValue, pidFile), e);
}
finally {
IOUtils.close(fileReader);
}
}
public static void writePid(final File pidFile, final int pid) throws IOException {
Assert.isTrue(pidFile != null && (pidFile.isFile() || pidFile.createNewFile()), String.format(
"The file system pathname (%1$s) in which the PID will be written is not a valid file!", pidFile));
Assert.isTrue(pid > 0, String.format("The PID value (%1$d) must greater than 0!", pid));
PrintWriter fileWriter = new PrintWriter(new BufferedWriter(new FileWriter(pidFile, false), 16), true);
try {
fileWriter.println(pid);
}
finally {
pidFile.deleteOnExit();
IOUtils.close(fileWriter);
}
}
protected static class DirectoryPidFileFilter extends PidFileFilter {
protected static final DirectoryPidFileFilter INSTANCE = new DirectoryPidFileFilter();
@Override
public boolean accept(final File pathname) {
return (pathname != null && (pathname.isDirectory() || super.accept(pathname)));
}
}
protected static class PidFileFilter implements FileFilter {
protected static final PidFileFilter INSTANCE = new PidFileFilter();
@Override
public boolean accept(final File pathname) {
return (pathname != null && pathname.isFile() && pathname.getName().endsWith(".pid"));
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.test.support;
import java.util.Enumeration;
import java.util.Iterator;
/**
* The CollectionUtils class is a utility class for working with the Java Collections Framework.
*
* @author John Blum
* @see java.util.Collection
* @see java.util.Collections
* @since 1.5.0
*/
@SuppressWarnings("unused")
public abstract class CollectionUtils {
public static <T> Iterable<T> iterable(final Enumeration<T> enumeration) {
return new Iterable<T>() {
@Override public Iterator<T> iterator() {
return org.springframework.util.CollectionUtils.toIterator(enumeration);
}
};
}
public static <T> Iterable<T> iterable(final Iterator<T> iterator) {
return new Iterable<T>() {
@Override public Iterator<T> iterator() {
return iterator;
}
};
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.test.support;
import java.io.Closeable;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The IOUtils class is an utility class working with IO operations.
*
* @author John Blum
* @since 1.5.0
*/
@SuppressWarnings("unused")
public abstract class IOUtils {
private static final Logger log = Logger.getLogger(IOUtils.class.getName());
public static boolean close(final Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
return true;
}
catch (IOException ignore) {
if (log.isLoggable(Level.FINE)) {
log.fine(String.format("Failed to close Closeable object (%1$s) due to I/O error:%n%2$s",
closeable, ThrowableUtils.toString(ignore)));
}
}
}
return false;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.test.support;
import java.util.concurrent.TimeUnit;
/**
* The ThreadUtils class is a utility class for working with Java Threads.
*
* @author John Blum
* @see java.lang.Thread
* @since 1.5.0
*/
@SuppressWarnings("unused")
public abstract class ThreadUtils {
public static boolean sleep(final long milliseconds) {
try {
Thread.sleep(milliseconds);
return true;
}
catch (InterruptedException ignore) {
return false;
}
}
public static void timedWait(final long milliseconds) {
timedWait(milliseconds, milliseconds);
}
public static void timedWait(final long milliseconds, final long interval) {
timedWait(milliseconds, interval, new WaitCondition() {
@Override public boolean waiting() {
return true;
}
});
}
public static void timedWait(final long milliseconds, long interval, final WaitCondition waitCondition) {
final long timeout = (System.currentTimeMillis() + milliseconds);
interval = Math.min(interval, milliseconds);
while (waitCondition.waiting() && (System.currentTimeMillis() < timeout)) {
try {
synchronized (waitCondition) {
TimeUnit.MILLISECONDS.timedWait(waitCondition, interval);
}
}
catch (InterruptedException ignore) {
}
}
}
public static interface WaitCondition {
boolean waiting();
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.test.support;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* The ExceptionUtils class is a utility class for working with Throwable, Exception and Error objects.
*
* @author John Blum
* @see java.lang.Error
* @see java.lang.Exception
* @see java.lang.Throwable
* @since 1.5.0
*/
@SuppressWarnings("unused")
public abstract class ThrowableUtils {
public static String toString(Throwable t) {
StringWriter writer = new StringWriter();
t.printStackTrace(new PrintWriter(writer));
return writer.toString();
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.test.support;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
/**
* The ZipUtils class is an abstract utility class for working with JAR and ZIP archives.
*
* @author John Blum
* @see java.io.File
* @see java.util.zip.ZipFile
* @since 1.5.0
*/
public abstract class ZipUtils {
public static void unzip(final Resource zipResource, final File directory) throws IOException {
Assert.notNull(zipResource, "The ZIP Resource must not be null!");
Assert.isTrue(directory != null && directory.isDirectory(), String.format(
"The file system pathname (%1$s) is not a valid directory!", directory));
ZipFile zipFile = new ZipFile(zipResource.getFile(), ZipFile.OPEN_READ);
for (ZipEntry entry : CollectionUtils.iterable(zipFile.entries())) {
if (entry.isDirectory()) {
new File(directory, entry.getName()).mkdirs();
}
else {
DataInputStream entryInputStream = new DataInputStream(zipFile.getInputStream(entry));
DataOutputStream entryOutputStream = new DataOutputStream(new FileOutputStream(
new File(directory, entry.getName())));
try {
FileCopyUtils.copy(entryInputStream, entryOutputStream);
}
finally {
IOUtils.close(entryInputStream);
IOUtils.close(entryOutputStream);
}
}
}
}
}

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:gfe="http://www.springframework.org/schema/gemfire"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
">
<util:properties id="gemfirePeerCacheConfigurationSettings">
<prop key="name">CacheUsingSharedConfigurationIntegrationTest</prop>
<prop key="mcast-port">0</prop>
<prop key="log-level">config</prop>
<prop key="locators">localhost[20668]</prop>
</util:properties>
<gfe:cache properties-ref="gemfirePeerCacheConfigurationSettings" use-bean-factory-locator="false"
use-shared-configuration="true" cache-xml-location="/sharedconfig-test-cache.xml" lazy-init="false"/>
<gfe:lookup-region id="SharedConfigRegion"/>
<gfe:lookup-region id="NativeLocalRegion"/>
<gfe:lookup-region id="NativePartitionRegion"/>
<gfe:lookup-region id="NativeReplicateRegion"/>
<gfe:local-region id="LocalRegion"/>
</beans>

Binary file not shown.

View File

@@ -0,0 +1,23 @@
<?xml version="1.0"?>
<!DOCTYPE cache PUBLIC "-//GemStone Systems, Inc.//GemFire Declarative Caching 8.0//EN"
"http://www.gemstone.com/dtd/cache8_0.dtd">
<cache>
<region name="NativeLocalRegion" refid="LOCAL">
<region-attributes initial-capacity="1" load-factor="0.95">
<key-constraint>java.lang.Integer</key-constraint>
<value-constraint>java.lang.String</value-constraint>
</region-attributes>
</region>
<region name="NativePartitionRegion">
<region-attributes data-policy="partition" initial-capacity="1" load-factor="0.85">
<key-constraint>java.lang.Long</key-constraint>
<value-constraint>java.lang.String</value-constraint>
</region-attributes>
</region>
<region name="NativeReplicateRegion">
<region-attributes data-policy="replicate" initial-capacity="1" load-factor="0.75" scope="distributed-ack">
<key-constraint>java.lang.Integer</key-constraint>
<value-constraint>java.lang.String</value-constraint>
</region-attributes>
</region>
</cache>