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:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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(" }");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
BIN
src/test/resources/shared_config.zip
Normal file
BIN
src/test/resources/shared_config.zip
Normal file
Binary file not shown.
23
src/test/resources/sharedconfig-test-cache.xml
Normal file
23
src/test/resources/sharedconfig-test-cache.xml
Normal 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>
|
||||
Reference in New Issue
Block a user