SGF-547 - Configure Eviction with annotations.

(cherry picked from commit fffb2b0de6)
Signed-off-by: John Blum <jblum@pivotal.io>
This commit is contained in:
John Blum
2016-12-05 11:02:32 -08:00
parent 350a7e7a2c
commit 2679d78ac2
13 changed files with 1363 additions and 357 deletions

View File

@@ -31,22 +31,39 @@ import org.springframework.beans.factory.InitializingBean;
* @see org.apache.geode.cache.AttributesFactory
* @see org.apache.geode.cache.RegionAttributes
*/
@SuppressWarnings({ "deprecation", "unused" })
public class RegionAttributesFactoryBean extends AttributesFactory implements FactoryBean<RegionAttributes>,
InitializingBean {
@SuppressWarnings({ "unused" })
public class RegionAttributesFactoryBean extends AttributesFactory
implements FactoryBean<RegionAttributes>, InitializingBean {
private RegionAttributes attributes;
/**
* @inheritDoc
*/
@Override
public void afterPropertiesSet() throws Exception {
attributes = super.create();
}
/**
* @inheritDoc
*/
@Override
public RegionAttributes getObject() throws Exception {
return attributes;
}
/**
* @inheritDoc
*/
@Override
public Class<?> getObjectType() {
return (attributes != null ? attributes.getClass() : RegionAttributes.class);
}
/**
* @inheritDoc
*/
@Override
public boolean isSingleton() {
return true;
@@ -55,10 +72,4 @@ public class RegionAttributesFactoryBean extends AttributesFactory implements Fa
public void setIndexUpdateType(final IndexMaintenancePolicyType indexUpdateType) {
indexUpdateType.setIndexMaintenance(this);
}
@Override
public void afterPropertiesSet() throws Exception {
attributes = super.create();
}
}

View File

@@ -27,6 +27,7 @@ import org.apache.geode.cache.CacheLoader;
import org.apache.geode.cache.CacheWriter;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.EvictionAction;
import org.apache.geode.cache.EvictionAttributes;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.PartitionAttributes;
import org.apache.geode.cache.PartitionAttributesFactory;
@@ -79,6 +80,8 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
private DataPolicy dataPolicy;
private EvictionAttributes evictionAttributes;
private GatewaySender[] gatewaySenders;
private RegionAttributes<K, V> attributes;
@@ -91,15 +94,21 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
private String diskStoreName;
/**
* @inheritDoc
*/
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
postProcess(getRegion());
}
/**
* @inheritDoc
*/
@Override
@SuppressWarnings("all")
protected Region<K, V> lookupFallback(GemFireCache gemfireCache, String regionName) throws Exception {
@SuppressWarnings({ "unchecked" })
protected Region<K, V> lookupRegion(GemFireCache gemfireCache, String regionName) throws Exception {
Assert.isTrue(gemfireCache instanceof Cache, String.format("Unable to create Regions from '%1$s'.",
gemfireCache));
@@ -133,6 +142,10 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
regionFactory.setDiskStoreName(diskStoreName);
}
if (evictionAttributes != null) {
regionFactory.setEvictionAttributes(evictionAttributes);
}
if (scope != null) {
regionFactory.setScope(scope);
}
@@ -179,7 +192,7 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
* @see org.apache.geode.cache.Cache#createRegionFactory(org.apache.geode.cache.RegionShortcut)
* @see org.apache.geode.cache.RegionFactory
*/
protected RegionFactory<K, V> createRegionFactory(final Cache cache) {
protected RegionFactory<K, V> createRegionFactory(Cache cache) {
if (shortcut != null) {
RegionFactory<K, V> regionFactory = mergeRegionAttributes(cache.createRegionFactory(shortcut), attributes);
setDataPolicy(getDataPolicy(regionFactory));
@@ -208,14 +221,14 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
* @see org.apache.geode.cache.DataPolicy
*/
@SuppressWarnings({ "deprecation", "unchecked" })
DataPolicy getDataPolicy(final RegionFactory regionFactory) {
DataPolicy getDataPolicy(RegionFactory regionFactory) {
return ((RegionAttributes) getFieldValue(getFieldValue(regionFactory, "attrsFactory", AttributesFactory.class),
"regionAttributes", null)).getDataPolicy();
}
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
private <T> T getFieldValue(final Object source, final String fieldName, final Class<T> targetType) {
private <T> T getFieldValue(Object source, String fieldName, Class<T> targetType) {
Field field = ReflectionUtils.findField(source.getClass(), fieldName, targetType);
ReflectionUtils.makeAccessible(field);
return (T) ReflectionUtils.getField(field, source);
@@ -240,9 +253,9 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
* @see org.apache.geode.cache.RegionAttributes
* @see org.apache.geode.cache.RegionFactory
*/
@SuppressWarnings({ "deprecation", "unchecked" })
protected <K, V> RegionFactory<K, V> mergeRegionAttributes(final RegionFactory<K, V> regionFactory,
final RegionAttributes<K, V> regionAttributes) {
@SuppressWarnings("unchecked")
protected <K, V> RegionFactory<K, V> mergeRegionAttributes(RegionFactory<K, V> regionFactory,
RegionAttributes<K, V> regionAttributes) {
if (regionAttributes != null) {
// NOTE this validation may not be strictly required depending on how the RegionAttributes were "created",
@@ -286,6 +299,7 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
return regionFactory;
}
/* (non-Javadoc) */
protected <K, V> void mergePartitionAttributes(final RegionFactory<K, V> regionFactory,
RegionAttributes<K, V> regionAttributes) {
@@ -323,7 +337,7 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
* @see org.apache.geode.cache.AttributesFactory#validateAttributes(:RegionAttributes)
*/
@SuppressWarnings("deprecation")
void validateRegionAttributes(final RegionAttributes regionAttributes) {
void validateRegionAttributes(RegionAttributes regionAttributes) {
org.apache.geode.cache.AttributesFactory.validateAttributes(regionAttributes);
}
@@ -419,7 +433,8 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
* @param regionFactory the GemFire RegionFactory used to create the Region for post-processing.
* @see org.apache.geode.cache.RegionFactory
*/
protected void postProcess(RegionFactory<K, V> regionFactory) {
protected RegionFactory<K, V> postProcess(RegionFactory<K, V> regionFactory) {
return regionFactory;
}
/**
@@ -429,7 +444,8 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
* @param region the GemFire Region to post-process.
* @see org.apache.geode.cache.Region
*/
protected void postProcess(Region<K, V> region) {
protected Region<K, V> postProcess(Region<K, V> region) {
return region;
}
/**
@@ -642,6 +658,10 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
this.diskStoreName = diskStoreName;
}
public void setEvictionAttributes(EvictionAttributes evictionAttributes) {
this.evictionAttributes = evictionAttributes;
}
/**
*
* @param gatewaySenders defined as Object for backward compatibility with
@@ -694,13 +714,51 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
this.snapshot = snapshot;
}
/**
* @inheritDoc
*/
@Override
public boolean isAutoStartup() {
return true;
@SuppressWarnings("all")
public void start() {
if (!ObjectUtils.isEmpty(gatewaySenders)) {
synchronized (gatewaySenders) {
for (GatewaySender gatewaySender: gatewaySenders) {
if (!(gatewaySender.isManualStart() || gatewaySender.isRunning())) {
gatewaySender.start();
}
}
}
}
this.running = true;
}
/**
* @inheritDoc
<<<<<<< HEAD
=======
*/
@Override
public void stop(Runnable callback) {
stop();
callback.run();
}
/**
* @inheritDoc
>>>>>>> fffb2b0... SGF-547 - Configure Eviction with annotations.
*/
@Override
public void stop() {
if (!ObjectUtils.isEmpty(gatewaySenders)) {
synchronized (gatewaySenders) {
for (GatewaySender gatewaySender : gatewaySenders) {
gatewaySender.stop();
}
}
}
this.running = false;
}
/**
@@ -723,42 +781,7 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
* @inheritDoc
*/
@Override
public void start() {
if (!ObjectUtils.isEmpty(gatewaySenders)) {
synchronized (gatewaySenders) {
for (GatewaySender gatewaySender: gatewaySenders) {
if (!(gatewaySender.isManualStart() || gatewaySender.isRunning())) {
gatewaySender.start();
}
}
}
}
this.running = true;
}
/**
* @inheritDoc
*/
@Override
public void stop() {
if (!ObjectUtils.isEmpty(gatewaySenders)) {
synchronized (gatewaySenders) {
for (GatewaySender gatewaySender : gatewaySenders) {
gatewaySender.stop();
}
}
}
this.running = false;
}
/**
* @inheritDoc
*/
@Override
public void stop(Runnable callback) {
stop();
callback.run();
public boolean isAutoStartup() {
return true;
}
}

View File

@@ -49,72 +49,100 @@ public abstract class RegionLookupFactoryBean<K, V>
private GemFireCache cache;
private Region<?, ?> parent;
private volatile Region<K, V> region;
private String beanName;
private String name;
private String regionName;
/**
* @inheritDoc
*/
@Override
@SuppressWarnings("all")
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "the 'cache' reference property must be set");
Assert.notNull(this.cache, "A 'Cache' reference must be set");
String regionName = (StringUtils.hasText(this.regionName) ? this.regionName
: (StringUtils.hasText(name) ? name : beanName));
String regionName = resolveRegionName();
Assert.hasText(regionName, "'regionName', 'name' or 'beanName' property must be set");
synchronized (cache) {
//region = (getParent() != null ? getParent().getSubregion(regionName) : cache.getRegion(regionName));
synchronized (this.cache) {
if (isLookupEnabled()) {
if (getParent() != null) {
region = getParent().getSubregion(regionName);
this.region = getParent().getSubregion(regionName);
}
else {
region = cache.getRegion(regionName);
this.region = this.cache.getRegion(regionName);
}
}
if (region != null) {
log.info(String.format("found Region (%1$s) in Cache (%2$s)", regionName, cache.getName()));
log.info(String.format("Found Region [%1$s] in Cache [%2$s]", regionName, cache.getName()));
}
else {
region = lookupFallback(cache, regionName);
region = lookupRegion(cache, regionName);
}
}
}
/**
* Fallback method in case the named Region does not exist. By default, this implementation throws an exception.
* Method to perform a lookup when the named {@link Region} does not exist. By default, this implementation
* throws an exception.
*
* @param cache a reference to the GemFire Cache.
* @param regionName the name of the GemFire Cache Region.
* @return the Region in the GemFire Cache with the given name.
* @throws Exception if the lookup operation fails.
* @param cache reference to the GemFire cache.
* @param regionName name of the GemFire {@link Region}.
* @return the {@link Region} in the GemFire cache with the given name.
* @throws BeanInitializationException if the lookup operation fails.
* @see org.apache.geode.cache.Region
*/
protected Region<K, V> lookupFallback(GemFireCache cache, String regionName) throws Exception {
throw new BeanInitializationException(String.format("Cannot find Region [%1$s] in Cache [%2$s].",
regionName, cache));
protected Region<K, V> lookupRegion(GemFireCache cache, String regionName) throws Exception {
throw new BeanInitializationException(String.format(
"Region [%1$s] in Cache [%2$s] not found", regionName, cache));
}
/**
* @inheritDoc
*/
@Override
public Region<K, V> getObject() throws Exception {
return getRegion();
}
/**
* @inheritDoc
*/
@Override
public Class<?> getObjectType() {
Region localRegion = getRegion();
return (localRegion != null ? localRegion.getClass() : Region.class);
Region region = getRegion();
return (region != null ? region.getClass() : Region.class);
}
/**
* @inheritDoc
*/
@Override
public boolean isSingleton() {
return true;
}
/**
* Sets the name of the Cache Region based on the bean 'id' attribute. If no Region is found for the given name,
* a new one will be created.
* Resolves the name of the GemFire {@link Region}.
*
* @param name the name of this bean (Region) in the application context (bean factory).
* @return a {@link String} indicating the name of the GemFire {@link Region}.
* @see org.apache.geode.cache.Region#getName()
*/
public String resolveRegionName() {
return (StringUtils.hasText(this.regionName) ? this.regionName
: (StringUtils.hasText(this.name) ? this.name : this.beanName));
}
/**
* Sets the name of the {@link Region} based on the bean 'id' attribute. If no {@link Region} is found
* with the given name, a new one will be created.
*
* @param name name of this {@link Region} bean in the Spring {@link org.springframework.context.ApplicationContext}.
* @see org.springframework.beans.factory.BeanNameAware#setBeanName(String)
*/
public void setBeanName(String name) {
@@ -122,9 +150,9 @@ public abstract class RegionLookupFactoryBean<K, V>
}
/**
* Sets a reference to the Cache used to create the Region.
* Sets a reference to the {@link GemFireCache} used to create the {@link Region}.
*
* @param cache a reference to the Cache.
* @param cache reference to the {@link GemFireCache}.
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.apache.geode.cache.GemFireCache
*/
@@ -132,60 +160,6 @@ public abstract class RegionLookupFactoryBean<K, V>
this.cache = cache;
}
/**
* Sets the name of the Cache Region based on the bean 'name' attribute. If no Region is found with the given name,
* a new one will be created. If no name is given, the value of the 'beanName' property will be used.
*
* @param name the region name
* @see #setBeanName(String)
* @see org.apache.geode.cache.Region#getFullPath()
*/
public void setName(String name) {
this.name = name;
}
/**
* Sets a reference to the parent Region if this FactoryBean represents a GemFire Cache Sub-Region.
*
* @param parent a reference to the parent Region if this Region is a Sub-Region.
* @see org.apache.geode.cache.Region
*/
public void setParent(Region<?, ?> parent) {
this.parent = parent;
}
/**
* Gets a reference to the parent Region if this FactoryBean represents a GemFire Cache Sub-Region.
*
* @return a reference to the parent Region or null if this Region is not a Sub-Region.
* @see org.apache.geode.cache.Region
*/
protected Region<?, ?> getParent() {
return parent;
}
/**
* Gets the reference to the GemFire Region obtained by this Spring FactoryBean during the lookup operation.
*
* @return a reference to the GemFire Region found during lookup.
* @see org.apache.geode.cache.Region
*/
protected Region<K, V> getRegion() {
return region;
}
/**
* Sets the name of the Cache Region as expected by GemFire. If no Region is found with the given name, a new one
* will be created. If no name is given, the value of the 'name' property will be used.
*
* @param regionName a String indicating the name of the Region in GemFire.
* @see #setName(String)
* @see org.apache.geode.cache.Region#getName()
*/
public void setRegionName(String regionName) {
this.regionName = regionName;
}
/* (non-Javadoc) */
boolean isLookupEnabled() {
return Boolean.TRUE.equals(getLookupEnabled());
@@ -200,4 +174,61 @@ public abstract class RegionLookupFactoryBean<K, V>
public Boolean getLookupEnabled() {
return lookupEnabled;
}
/**
* Sets the name of the cache {@link Region} based on the bean 'name' attribute. If no {@link Region} is found
* with the given name, a new one will be created. If no name is given, the value of the 'beanName' property
* will be used.
*
* @param name {@link Region} name.
* @see #setBeanName(String)
*/
public void setName(String name) {
this.name = name;
}
/**
* Sets a reference to the parent {@link Region} to indicated this {@link FactoryBean} represents a GemFire cache
* {@link Region Sub-Region}.
*
* @param parent reference to the parent {@link Region}.
* @see org.apache.geode.cache.Region
*/
public void setParent(Region<?, ?> parent) {
this.parent = parent;
}
/**
* Returns a reference to the parent {@link Region} indicating this {@link FactoryBean} represents a GemFire cache
* {@link Region Sub-Region}.
*
* @return a reference to the parent {@link Region} or {@literal null} if this {@link Region}
* is not a {@link Region Sub-Region}.
* @see org.apache.geode.cache.Region
*/
protected Region<?, ?> getParent() {
return this.parent;
}
/**
* Returns a reference to the GemFire {@link Region} resolved by this Spring {@link FactoryBean}
* during the lookup operation; maybe a new {@link Region}.
*
* @return a reference to the GemFire {@link Region} resolved during lookup.
* @see org.apache.geode.cache.Region
*/
protected Region<K, V> getRegion() {
return this.region;
}
/**
* Sets the name of the cache {@link Region}. If no {@link Region} is found with the given name,
* a new one will be created. If no name is given, the value of the 'name' property will be used.
*
* @param regionName name of the {@link Region}.
* @see #setName(String)
*/
public void setRegionName(String regionName) {
this.regionName = regionName;
}
}

View File

@@ -22,6 +22,7 @@ import org.apache.geode.cache.CacheListener;
import org.apache.geode.cache.CacheLoader;
import org.apache.geode.cache.CacheWriter;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.EvictionAttributes;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
@@ -33,17 +34,19 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.core.io.Resource;
import org.springframework.data.gemfire.DataPolicyConverter;
import org.springframework.data.gemfire.GemfireUtils;
import org.springframework.data.gemfire.RegionLookupFactoryBean;
import org.springframework.data.gemfire.config.xml.GemfireConstants;
import org.springframework.data.gemfire.util.ArrayUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Client extension for GemFire Regions.
* Spring {@link FactoryBean} used to create a GemFire client {@link Region}.
*
* @author Costin Leau
* @author David Turanski
@@ -86,6 +89,8 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
private DataPolicy dataPolicy;
private EvictionAttributes evictionAttributes;
private Interest<K>[] interests;
private RegionAttributes<K, V> attributes;
@@ -95,75 +100,61 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
private String diskStoreName;
private String poolName;
/**
* @inheritDoc
*/
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
postProcess(getRegion());
}
/**
* @inheritDoc
*/
@Override
@SuppressWarnings("all")
protected Region<K, V> lookupFallback(GemFireCache cache, String regionName) throws Exception {
protected Region<K, V> lookupRegion(GemFireCache cache, String regionName) throws Exception {
Assert.isTrue(GemfireUtils.isClient(cache), "A ClientCache is required to create a client Region");
ClientRegionFactory<K, V> clientRegionFactory = ((ClientCache) cache).createClientRegionFactory(
resolveClientRegionShortcut());
if (attributes != null) {
clientRegionFactory.setCloningEnabled(attributes.getCloningEnabled());
clientRegionFactory.setCompressor(attributes.getCompressor());
clientRegionFactory.setConcurrencyChecksEnabled(attributes.getConcurrencyChecksEnabled());
clientRegionFactory.setConcurrencyLevel(attributes.getConcurrencyLevel());
clientRegionFactory.setCustomEntryIdleTimeout(attributes.getCustomEntryIdleTimeout());
clientRegionFactory.setCustomEntryTimeToLive(attributes.getCustomEntryTimeToLive());
clientRegionFactory.setDiskStoreName(attributes.getDiskStoreName());
clientRegionFactory.setDiskSynchronous(attributes.isDiskSynchronous());
clientRegionFactory.setEntryIdleTimeout(attributes.getEntryIdleTimeout());
clientRegionFactory.setEntryTimeToLive(attributes.getEntryTimeToLive());
clientRegionFactory.setEvictionAttributes(attributes.getEvictionAttributes());
clientRegionFactory.setInitialCapacity(attributes.getInitialCapacity());
clientRegionFactory.setKeyConstraint(attributes.getKeyConstraint());
clientRegionFactory.setLoadFactor(attributes.getLoadFactor());
clientRegionFactory.setPoolName(attributes.getPoolName());
clientRegionFactory.setRegionIdleTimeout(attributes.getRegionIdleTimeout());
clientRegionFactory.setRegionTimeToLive(attributes.getRegionTimeToLive());
clientRegionFactory.setStatisticsEnabled(attributes.getStatisticsEnabled());
clientRegionFactory.setValueConstraint(attributes.getValueConstraint());
}
String poolName = resolvePoolName();
if (StringUtils.hasText(poolName)) {
clientRegionFactory.setPoolName(eagerlyInitializePool(poolName));
}
ClientRegionFactory<K, V> clientRegionFactory =
((ClientCache) cache).createClientRegionFactory(resolveClientRegionShortcut());
setAttributes(clientRegionFactory);
addCacheListeners(clientRegionFactory);
setDiskStoreName(clientRegionFactory);
setEvictionAttributes(clientRegionFactory);
setPoolName(clientRegionFactory);
if (diskStoreName != null) {
clientRegionFactory.setDiskStoreName(diskStoreName);
}
Region<K, V> clientRegion = (getParent() != null ? clientRegionFactory.createSubregion(getParent(), regionName)
: clientRegionFactory.create(regionName));
if (log.isInfoEnabled()) {
if (getParent() != null) {
log.info(String.format("Created new Client Cache sub-Region [%1$s] under parent Region [%2$s].",
regionName, getParent().getName()));
}
else {
log.info(String.format("Created new Client Cache Region [%1$s].", regionName));
}
}
if (snapshot != null) {
clientRegion.loadSnapshot(snapshot.getInputStream());
}
return clientRegion;
return logCreateRegionEvent(create(clientRegionFactory, regionName));
}
/* (non-Javadoc) */
private Region<K, V> create(ClientRegionFactory<K, V> clientRegionFactory, String regionName) {
return (getParent() != null ? clientRegionFactory.createSubregion(getParent(), regionName)
: clientRegionFactory.create(regionName));
}
/* (non-Javadoc) */
private Region<K, V> logCreateRegionEvent(Region<K, V> region) {
if (log.isInfoEnabled()) {
if (getParent() != null) {
log.info(String.format("Created new client cache Sub-Region [%1$s] under parent Region [%2$s].",
region.getName(), getParent().getName()));
}
else {
log.info(String.format("Created new client cache Region [%s].", region.getName()));
}
}
return region;
}
/**
* Resolves the {@link ClientRegionShortcut} used to configure the data policy of the client {@link Region}.
*
* @return a {@link ClientRegionShortcut} used to configure the data policy of the client {@link Region}.
* @see org.apache.geode.cache.client.ClientRegionShortcut
*/
ClientRegionShortcut resolveClientRegionShortcut() {
ClientRegionShortcut resolvedShortcut = this.shortcut;
@@ -181,10 +172,10 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
resolvedShortcut = ClientRegionShortcut.LOCAL_PERSISTENT;
}
else {
// NOTE the Data Policy validation is based on the ClientRegionShortcut initialization logic
// NOTE the DataPolicy validation is based on the ClientRegionShortcut initialization logic
// in org.apache.geode.internal.cache.GemFireCacheImpl.initializeClientRegionShortcuts
throw new IllegalArgumentException(String.format(
"Data Policy '%1$s' is invalid for Client Regions.", this.dataPolicy));
throw new IllegalArgumentException(String.format("Data Policy '%s' is invalid for Client Regions",
this.dataPolicy));
}
}
else {
@@ -193,43 +184,13 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
}
}
// NOTE the ClientRegionShortcut and Persistent attribute will be compatible if the shortcut was derived from
// the Data Policy.
// NOTE the ClientRegionShortcut and Persistent attribute will be compatible if the shortcut
// was derived from the Data Policy.
assertClientRegionShortcutAndPersistentAttributeAreCompatible(resolvedShortcut);
return resolvedShortcut;
}
/* (non-Javadoc) */
String resolvePoolName() {
String poolName = this.poolName;
if (!StringUtils.hasText(poolName)) {
String defaultPoolName = GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME;
poolName = (beanFactory.containsBean(defaultPoolName) ? defaultPoolName : poolName);
}
return poolName;
}
/* (non-Javadoc) */
String eagerlyInitializePool(String poolName) {
try {
if (beanFactory.isTypeMatch(poolName, Pool.class)) {
if (log.isDebugEnabled()) {
log.debug(String.format("Found bean definition for Pool [%1$s]; Eagerly initializing...", poolName));
}
beanFactory.getBean(poolName, Pool.class);
}
}
catch (BeansException ignore) {
log.warn(ignore.getMessage());
}
return poolName;
}
/**
* Validates that the settings for ClientRegionShortcut and the 'persistent' attribute in &lt;gfe:*-region&gt; elements
* are compatible.
@@ -240,17 +201,17 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
* @see #isNotPersistent()
* @see org.apache.geode.cache.client.ClientRegionShortcut
*/
protected void assertClientRegionShortcutAndPersistentAttributeAreCompatible(final ClientRegionShortcut resolvedShortcut) {
private void assertClientRegionShortcutAndPersistentAttributeAreCompatible(ClientRegionShortcut resolvedShortcut) {
final boolean persistentNotSpecified = (this.persistent == null);
if (ClientRegionShortcut.LOCAL_PERSISTENT.equals(resolvedShortcut)
|| ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW.equals(resolvedShortcut)) {
|| ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW.equals(resolvedShortcut)) {
Assert.isTrue(persistentNotSpecified || isPersistent(), String.format(
"Client Region Shortcut '%1$s' is invalid when persistent is false.", resolvedShortcut));
"Client Region Shortcut '%s' is invalid when persistent is false", resolvedShortcut));
}
else {
Assert.isTrue(persistentNotSpecified || isNotPersistent(), String.format(
"Client Region Shortcut '%1$s' is invalid when persistent is true.", resolvedShortcut));
"Client Region Shortcut '%s' is invalid when persistent is true", resolvedShortcut));
}
}
@@ -264,43 +225,146 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
* @see #isNotPersistent()
* @see org.apache.geode.cache.DataPolicy
*/
protected void assertDataPolicyAndPersistentAttributeAreCompatible(final DataPolicy resolvedDataPolicy) {
private void assertDataPolicyAndPersistentAttributeAreCompatible(DataPolicy resolvedDataPolicy) {
if (resolvedDataPolicy.withPersistence()) {
Assert.isTrue(isPersistentUnspecified() || isPersistent(), String.format(
"Data Policy '%1$s' is invalid when persistent is false.", resolvedDataPolicy));
"Data Policy '%s' is invalid when persistent is false", resolvedDataPolicy));
}
else {
// NOTE otherwise, the Data Policy is without persistence, so...
Assert.isTrue(isPersistentUnspecified() || isNotPersistent(), String.format(
"Data Policy '%1$s' is invalid when persistent is true.", resolvedDataPolicy));
"Data Policy '%s' is invalid when persistent is true", resolvedDataPolicy));
}
}
private void addCacheListeners(ClientRegionFactory<K, V> factory) {
if (attributes != null) {
CacheListener<K, V>[] cacheListeners = attributes.getCacheListeners();
/* (non-Javadoc) */
private ClientRegionFactory<K, V> setAttributes(ClientRegionFactory<K, V> clientRegionFactory) {
RegionAttributes<K, V> localAttributes = this.attributes;
if (!ObjectUtils.isEmpty(cacheListeners)) {
for (CacheListener<K, V> cacheListener : cacheListeners) {
factory.addCacheListener(cacheListener);
if (localAttributes != null) {
clientRegionFactory.setCloningEnabled(localAttributes.getCloningEnabled());
clientRegionFactory.setCompressor(localAttributes.getCompressor());
clientRegionFactory.setConcurrencyChecksEnabled(localAttributes.getConcurrencyChecksEnabled());
clientRegionFactory.setConcurrencyLevel(localAttributes.getConcurrencyLevel());
clientRegionFactory.setCustomEntryIdleTimeout(localAttributes.getCustomEntryIdleTimeout());
clientRegionFactory.setCustomEntryTimeToLive(localAttributes.getCustomEntryTimeToLive());
clientRegionFactory.setDiskStoreName(localAttributes.getDiskStoreName());
clientRegionFactory.setDiskSynchronous(localAttributes.isDiskSynchronous());
clientRegionFactory.setEntryIdleTimeout(localAttributes.getEntryIdleTimeout());
clientRegionFactory.setEntryTimeToLive(localAttributes.getEntryTimeToLive());
clientRegionFactory.setEvictionAttributes(localAttributes.getEvictionAttributes());
clientRegionFactory.setInitialCapacity(localAttributes.getInitialCapacity());
clientRegionFactory.setKeyConstraint(localAttributes.getKeyConstraint());
clientRegionFactory.setLoadFactor(localAttributes.getLoadFactor());
clientRegionFactory.setPoolName(localAttributes.getPoolName());
clientRegionFactory.setRegionIdleTimeout(localAttributes.getRegionIdleTimeout());
clientRegionFactory.setRegionTimeToLive(localAttributes.getRegionTimeToLive());
clientRegionFactory.setStatisticsEnabled(localAttributes.getStatisticsEnabled());
clientRegionFactory.setValueConstraint(localAttributes.getValueConstraint());
}
return clientRegionFactory;
}
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
private ClientRegionFactory<K, V> addCacheListeners(ClientRegionFactory<K, V> clientRegionFactory) {
for (CacheListener<K, V> cacheListener : this.<K, V>attributesCacheListeners()) {
clientRegionFactory.addCacheListener(cacheListener);
}
for (CacheListener<K, V> cacheListener : ArrayUtils.nullSafeArray(this.cacheListeners, CacheListener.class)) {
clientRegionFactory.addCacheListener(cacheListener);
}
return clientRegionFactory;
}
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
private <K, V> CacheListener<K, V>[] attributesCacheListeners() {
CacheListener[] cacheListeners = (this.attributes != null ? this.attributes.getCacheListeners() : null);
return ArrayUtils.nullSafeArray(cacheListeners, CacheListener.class);
}
/* (non-Javadoc) */
private ClientRegionFactory<K, V> setDiskStoreName(ClientRegionFactory<K, V> clientRegionFactory) {
if (StringUtils.hasText(this.diskStoreName)) {
clientRegionFactory.setDiskStoreName(this.diskStoreName);
}
return clientRegionFactory;
}
/* (non-Javadoc) */
private ClientRegionFactory<K, V> setEvictionAttributes(ClientRegionFactory<K, V> clientRegionFactory) {
if (this.evictionAttributes != null) {
clientRegionFactory.setEvictionAttributes(this.evictionAttributes);
}
return clientRegionFactory;
}
/* (non-Javadoc) */
private ClientRegionFactory<K, V> setPoolName(ClientRegionFactory<K, V> clientRegionFactory) {
String poolName = resolvePoolName();
if (StringUtils.hasText(poolName)) {
clientRegionFactory.setPoolName(eagerlyInitializePool(poolName));
}
return clientRegionFactory;
}
/* (non-Javadoc) */
private String resolvePoolName() {
String poolName = this.poolName;
if (!StringUtils.hasText(poolName)) {
String defaultPoolName = GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME;
poolName = (this.beanFactory.containsBean(defaultPoolName) ? defaultPoolName : poolName);
}
return poolName;
}
/* (non-Javadoc) */
private String eagerlyInitializePool(String poolName) {
try {
if (this.beanFactory.isTypeMatch(poolName, Pool.class)) {
if (log.isDebugEnabled()) {
log.debug(String.format("Found bean definition for Pool [%1$s]; Eagerly initializing...", poolName));
}
this.beanFactory.getBean(poolName, Pool.class);
}
}
catch (BeansException ignore) {
log.warn(ignore.getMessage());
}
if (!ObjectUtils.isEmpty(cacheListeners)) {
for (CacheListener<K, V> cacheListener : cacheListeners) {
factory.addCacheListener(cacheListener);
}
}
return poolName;
}
protected void postProcess(final Region<K, V> region) {
/* (non-Javadoc) */
protected void postProcess(Region<K, V> region) throws Exception {
loadSnapshot(region);
registerInterests(region);
setCacheLoader(region);
setCacheWriter(region);
}
private void registerInterests(final Region<K, V> region) {
/* (non-Javadoc) */
private Region<K, V> loadSnapshot(Region<K, V> region) throws Exception {
if (snapshot != null) {
region.loadSnapshot(snapshot.getInputStream());
}
return region;
}
/* (non-Javadoc) */
private Region<K, V> registerInterests(Region<K, V> region) {
if (!ObjectUtils.isEmpty(interests)) {
for (Interest<K> interest : interests) {
if (interest instanceof RegexInterest) {
@@ -313,20 +377,31 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
}
}
}
return region;
}
private void setCacheLoader(final Region<K, V> region) {
/* (non-Javadoc) */
private Region<K, V> setCacheLoader(Region<K, V> region) {
if (cacheLoader != null) {
region.getAttributesMutator().setCacheLoader(this.cacheLoader);
}
return region;
}
private void setCacheWriter(final Region<K, V> region) {
/* (non-Javadoc) */
private Region<K, V> setCacheWriter(Region<K, V> region) {
if (cacheWriter != null) {
region.getAttributesMutator().setCacheWriter(this.cacheWriter);
}
return region;
}
/**
* @inheritDoc
*/
@Override
public void destroy() throws Exception {
Region<K, V> region = getObject();
@@ -367,43 +442,7 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
this.beanFactory = beanFactory;
}
/**
* Set the interests for this client region. Both key and regex interest are
* supported.
*
* @param interests the interests to set
*/
public void setInterests(Interest<K>[] interests) {
this.interests = interests;
}
/**
* @return the interests
*/
Interest<K>[] getInterests() {
return interests;
}
/**
* Sets the pool used by this client.
*
* @param pool the GemFire client pool.
*/
public void setPool(Pool pool) {
Assert.notNull(pool, "pool cannot be null");
setPoolName(pool.getName());
}
/**
* Sets the pool name used by this client.
*
* @param poolName a String specify the name of the GemFire client pool.
*/
public void setPoolName(String poolName) {
Assert.hasText(poolName, "pool name is required");
this.poolName = poolName;
}
/* (non-Javadoc) */
final boolean isClose() {
return close;
}
@@ -421,23 +460,6 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
this.destroy = (this.destroy && !close); // retain previous value iff close is false.
}
final boolean isDestroy() {
return destroy;
}
/**
* Indicates whether the region referred by this factory bean will be
* destroyed on shutdown (default false). Note: destroy and close are
* mutually exclusive. Enabling one will automatically disable the other.
*
* @param destroy whether or not to destroy the region
* @see #setClose(boolean)
*/
public void setDestroy(boolean destroy) {
this.destroy = destroy;
this.close = (this.close && !destroy); // retain previous value iff destroy is false;
}
/**
* Sets the cache listeners used for the region used by this factory. Used
* only when a new region is created.Overrides the settings specified
@@ -494,6 +516,24 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
setDataPolicy(resolvedDataPolicy);
}
/* (non-Javadoc) */
final boolean isDestroy() {
return destroy;
}
/**
* Indicates whether the region referred by this factory bean will be
* destroyed on shutdown (default false). Note: destroy and close are
* mutually exclusive. Enabling one will automatically disable the other.
*
* @param destroy whether or not to destroy the region
* @see #setClose(boolean)
*/
public void setDestroy(boolean destroy) {
this.destroy = destroy;
this.close = (this.close && !destroy); // retain previous value iff destroy is false;
}
/**
* Sets the name of disk store to use for overflow and persistence
*
@@ -503,6 +543,25 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
this.diskStoreName = diskStoreName;
}
public void setEvictionAttributes(EvictionAttributes evictionAttributes) {
this.evictionAttributes = evictionAttributes;
}
/**
* Set the interests for this client region. Both key and regex interest are
* supported.
*
* @param interests the interests to set
*/
public void setInterests(Interest<K>[] interests) {
this.interests = interests;
}
/* (non-Javadoc) */
Interest<K>[] getInterests() {
return this.interests;
}
protected boolean isPersistentUnspecified() {
return (persistent == null);
}
@@ -520,26 +579,44 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
}
/**
* Initializes the client using a GemFire {@link ClientRegionShortcut}. The
* recommended way for creating clients since it covers all the major
* scenarios with minimal configuration.
* Sets the {@link Pool} used by this client {@link Region}.
*
* @param shortcut the ClientRegionShortcut to use.
* @param pool GemFire client {@link Pool}.
* @see org.apache.geode.cache.client.Pool
*/
public void setPool(Pool pool) {
Assert.notNull(pool, "Pool cannot be null");
setPoolName(pool.getName());
}
/**
* Sets the {@link Pool} name used by this client {@link Region}.
*
* @param poolName String specifying the name of the GemFire client {@link Pool}.
*/
public void setPoolName(String poolName) {
Assert.hasText(poolName, "Pool name is required");
this.poolName = poolName;
}
/**
* Initializes the client {@link Region} using a GemFire {@link ClientRegionShortcut}.
*
* @param shortcut {@link ClientRegionShortcut} used to initialize this client {@link Region}.
*/
public void setShortcut(ClientRegionShortcut shortcut) {
this.shortcut = shortcut;
}
/**
* Sets the snapshots used for loading a newly <i>created</i> region. That
* is, the snapshot will be used <i>only</i> when a new region is created -
* if the region already exists, no loading will be performed.
* Specifies the data snapshots used for loading a newly <i>created</i> {@link Region}.
* The snapshot will be used <i>only</i> when a new {@link Region} is created.
* If the {@link Region} already exists, no loading will be performed.
*
* @param snapshot the snapshot to set
* @see #setName(String)
* @param snapshot {@link Resource} referencing the snapshot used to load the {@link Region} with data.
* @see org.springframework.core.io.Resource
*/
public void setSnapshot(Resource snapshot) {
this.snapshot = snapshot;
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2016 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.config.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.apache.geode.cache.EvictionAttributes;
import org.apache.geode.cache.Region;
import org.springframework.context.annotation.Import;
import org.springframework.data.gemfire.eviction.EvictionActionType;
import org.springframework.data.gemfire.eviction.EvictionPolicyType;
/**
* The {@link EnableEviction} annotation marks a Spring {@link org.springframework.context.annotation.Configuration @Configuration}
* annotated class to enable {@link Region} Eviction.
*
* @author John Blum
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.config.annotation.EvictionConfiguration
* @see org.apache.geode.cache.Region
* @since 1.9.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(EvictionConfiguration.class)
@SuppressWarnings({ "unused" })
public @interface EnableEviction {
/**
* Defines individual {@link Region} Eviction policies or customizes the default Eviction policy applied
* to all {@link Region Regions}.
*
* Defaults to empty.
*/
EvictionPolicy[] policies() default {};
/**
* Definition for a specific Eviction policy that can be applied to 1 or more {@link Region Regions}.
*
* An Eviction policy defines the maximum (a.k.a. threshold) along with {@link ObjectSizer} used to size
* {@link Region} entry values and the action applied when {@link Region} entries are to be evicted.
*
* Additionally, the Eviction policy defines the algorithm used (eviction based on entry count, JVM Heap percentage
* or system memory size used) to determine when an Eviction should occur.
*/
@interface EvictionPolicy {
/**
* Action to take on an {@link Region} entry when evicted.
*
* Defaults to {@link EvictionActionType#LOCAL_DESTROY}.
*
* @see org.springframework.data.gemfire.EvictionActionType
*/
EvictionActionType action() default EvictionActionType.LOCAL_DESTROY;
/**
* Threshold applied for entry count Eviction.
*
* Defaults to {@link EvictionAttributes#DEFAULT_ENTRIES_MAXIMUM}
*/
int maximum() default EvictionAttributes.DEFAULT_ENTRIES_MAXIMUM;
/**
* Name of a Spring bean of type {@link ObjectSizer} defined in the Spring context used
* to size {@link Region} entry values.
*
* Defaults to empty.
*
* @see org.apache.geode.cache.util.ObjectSizer
*/
String objectSizerName() default "";
/**
* Names of {@link Region Regions} for which this Eviction policy applies.
*
* Defaults to empty.
*/
String[] regionNames() default {};
/**
* Eviction alorithm used during Eviction.
*
* Defaults to {@link EvictionPolicyType#ENTRY_COUNT}.
*
* @see org.springframework.data.gemfire.eviction.EvictionPolicyType
*/
EvictionPolicyType type() default EvictionPolicyType.ENTRY_COUNT;
}
}

View File

@@ -0,0 +1,465 @@
/*
* Copyright 2016 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.config.annotation;
import static org.springframework.data.gemfire.config.annotation.EnableEviction.EvictionPolicy;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.geode.cache.EvictionAttributes;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.util.ObjectSizer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.RegionFactoryBean;
import org.springframework.data.gemfire.RegionLookupFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.eviction.EvictionActionType;
import org.springframework.data.gemfire.eviction.EvictionAttributesFactoryBean;
import org.springframework.data.gemfire.eviction.EvictionPolicyType;
import org.springframework.data.gemfire.util.ArrayUtils;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The {@link EvictionConfiguration} class is a Spring {@link Configuration @Configuration} annotated class to enable
* Eviction policy configuration on GemFire/Geode {@link Region Regions}.
*
* @author John Blum
* @see org.springframework.beans.factory.config.BeanPostProcessor
* @see org.springframework.context.ApplicationContext
* @see org.springframework.context.ApplicationContextAware
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.ImportAware
* @see org.springframework.data.gemfire.eviction.EvictionActionType
* @see org.springframework.data.gemfire.eviction.EvictionAttributesFactoryBean
* @see org.springframework.data.gemfire.eviction.EvictionPolicyType
* @see org.springframework.data.gemfire.RegionFactoryBean
* @see org.springframework.data.gemfire.client.ClientRegionFactoryBean
* @see org.apache.geode.cache.EvictionAttributes
* @see org.apache.geode.cache.Region
* @since 1.9.0
*/
@Configuration
public class EvictionConfiguration implements ApplicationContextAware, ImportAware {
private ApplicationContext applicationContext;
private EvictionPolicyConfigurer evictionPolicyConfigurer;
/**
* Determines whether the Spring bean is an instance of {@link RegionFactoryBean}
* or {@link ClientRegionFactoryBean}.
*
* @param bean Spring bean to evaluate.
* @return a boolean value indicating whether the Spring bean is an instance of {@link RegionFactoryBean}.
* @see org.springframework.data.gemfire.RegionFactoryBean
* @see org.springframework.data.gemfire.client.ClientRegionFactoryBean
*/
protected static boolean isRegionFactoryBean(Object bean) {
return (bean instanceof RegionFactoryBean || bean instanceof ClientRegionFactoryBean);
}
/**
* Returns the {@link Annotation} {@link Class type} that enables and configures Eviction.
*
* @return the {@link Annotation} {@link Class type} to enable and configure Eviction.
* @see java.lang.annotation.Annotation
* @see java.lang.Class
*/
protected Class<? extends Annotation> getAnnotationType() {
return EnableEviction.class;
}
/**
* Returns the name of the {@link Annotation} type that enables and configures Eviction.
*
* @return the name of the {@link Annotation} type that enables and configures Eviction.
* @see java.lang.Class#getName()
* @see #getAnnotationType()
*/
protected String getAnnotationTypeName() {
return getAnnotationType().getName();
}
/**
* Returns the simple name of the {@link Annotation} type that enables and configures Eviction.
*
* @return the simple name of the {@link Annotation} type that enables and configures Eviction.
* @see java.lang.Class#getSimpleName()
* @see #getAnnotationType()
*/
@SuppressWarnings("unused")
protected String getAnnotationTypeSimpleName() {
return getAnnotationType().getSimpleName();
}
/**
* @inheritDoc
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* @inheritDoc
*/
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
if (importMetadata.hasAnnotation(getAnnotationTypeName())) {
Map<String, Object> enableEvictionAttributes =
importMetadata.getAnnotationAttributes(getAnnotationTypeName());
AnnotationAttributes[] policies = (AnnotationAttributes[]) enableEvictionAttributes.get("policies");
for (AnnotationAttributes evictionPolicyAttributes
: ArrayUtils.nullSafeArray(policies, AnnotationAttributes.class)) {
this.evictionPolicyConfigurer = ComposableEvictionPolicyConfigurer.compose(
this.evictionPolicyConfigurer, EvictionPolicyMetaData.from(evictionPolicyAttributes,
this.applicationContext));
}
this.evictionPolicyConfigurer = (this.evictionPolicyConfigurer != null ? this.evictionPolicyConfigurer
: EvictionPolicyMetaData.fromDefaults());
}
}
/**
* Returns a reference to the configured {@link EvictionPolicyConfigurer} used to configure the Eviction policy
* of a {@link Region}.
*
* @return a reference to the configured {@link EvictionPolicyConfigurer}.
* @see org.springframework.data.gemfire.config.annotation.EvictionConfiguration.EvictionPolicyConfigurer
*/
protected EvictionPolicyConfigurer getEvictionPolicyConfigurer() {
Assert.state(this.evictionPolicyConfigurer != null,
"EvictionPolicyConfigurer was not properly configured and initialized");
return this.evictionPolicyConfigurer;
}
@Bean
@SuppressWarnings("unused")
public BeanPostProcessor evictionBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return (isRegionFactoryBean(bean) ? getEvictionPolicyConfigurer().configure(bean) : bean);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
};
}
/**
* {@link EvictionPolicyConfigurer} configures the Eviction policy of a GemFire {@link Region}.
*/
protected interface EvictionPolicyConfigurer {
/**
* Configure the Eviction policy on the given SDG {@link RegionFactoryBean} or {@link ClientRegionFactoryBean}
* used to create a GemFire {@link Region}.
*
* @param regionFactoryBean {@link RegionFactoryBean} or {@link ClientRegionFactoryBean} used to create
* a GemFire {@link Region}.
* @return the given {@code regionFactoryBean}.
* @see org.springframework.data.gemfire.RegionFactoryBean
* @see org.springframework.data.gemfire.client.ClientRegionFactoryBean
*/
Object configure(Object regionFactoryBean);
}
/**
* {@link ComposableEvictionPolicyConfigurer} is a {@link EvictionPolicyConfigurer} implementation that composes
* multiple {@link EvictionPolicyConfigurer} objects into a composition using the Composite Software Design Pattern
* making the composition appear as a single {@link EvictionPolicyConfigurer}.
*
* @see org.springframework.data.gemfire.config.annotation.EvictionConfiguration.EvictionPolicyConfigurer
*/
protected static class ComposableEvictionPolicyConfigurer implements EvictionPolicyConfigurer {
private final EvictionPolicyConfigurer one;
private final EvictionPolicyConfigurer two;
/**
* Composes the array of {@link EvictionPolicyConfigurer} objects into a single
* {@link EvictionPolicyConfigurer} implementation using the Composite Software Design Pattern.
*
* @param array array of {@link EvictionPolicyConfigurer} objects to compose.
* @return an {@link EvictionPolicyConfigurer} implementation composed from the array
* of {@link EvictionPolicyConfigurer} objects.
* @see org.springframework.data.gemfire.config.annotation.EvictionConfiguration.EvictionPolicyConfigurer
* @see #compose(Iterable)
*/
@SuppressWarnings("unused")
protected static EvictionPolicyConfigurer compose(EvictionPolicyConfigurer[] array) {
return compose(Arrays.asList(ArrayUtils.nullSafeArray(array, EvictionPolicyConfigurer.class)));
}
/**
* Composes the {@link Iterable} of {@link EvictionPolicyConfigurer} objects into a single
* {@link EvictionPolicyConfigurer} implementation using the Composite Software Design Pattern.
*
* @param iterable {@link Iterable} of {@link EvictionPolicyConfigurer} objects to compose.
* @return an {@link EvictionPolicyConfigurer} implementation composed from the {@link Iterable}
* of {@link EvictionPolicyConfigurer} objects.
* @see org.springframework.data.gemfire.config.annotation.EvictionConfiguration.EvictionPolicyConfigurer
* @see #compose(EvictionPolicyConfigurer, EvictionPolicyConfigurer)
*/
protected static EvictionPolicyConfigurer compose(Iterable<EvictionPolicyConfigurer> iterable) {
EvictionPolicyConfigurer current = null;
for (EvictionPolicyConfigurer evictionPolicyConfigurer : CollectionUtils.nullSafeIterable(iterable)) {
current = compose(current, evictionPolicyConfigurer);
}
return current;
}
/**
* Composes two {@link EvictionPolicyConfigurer} objects into a composition object
* implementing the {@link EvictionPolicyConfigurer} interface.
*
* @param one first {@link EvictionPolicyConfigurer} object to compose.
* @param two second {@link EvictionPolicyConfigurer} object to compose.
* @return an {@link EvictionPolicyConfigurer} object implementation composed of
* multiple {@link EvictionPolicyConfigurer} objects using the Composite Software Design Pattern.
*/
protected static EvictionPolicyConfigurer compose(EvictionPolicyConfigurer one, EvictionPolicyConfigurer two) {
return (one == null ? two : (two == null ? one : new ComposableEvictionPolicyConfigurer(one, two)));
}
/**
* Constructs a new instance of the {@link ComposableEvictionPolicyConfigurer} initialized with the two
* {@link EvictionPolicyConfigurer} objects.
*
* @param one first {@link EvictionPolicyConfigurer} object to compose.
* @param two second {@link EvictionPolicyConfigurer} object to compose.
*/
private ComposableEvictionPolicyConfigurer(EvictionPolicyConfigurer one, EvictionPolicyConfigurer two) {
this.one = one;
this.two = two;
}
/**
* @inheritDoc
*/
@Override
public Object configure(Object regionFactoryBean) {
return two.configure(one.configure(regionFactoryBean));
}
}
protected static class EvictionPolicyMetaData implements EvictionPolicyConfigurer {
protected static final String[] ALL_REGIONS = new String[0];
private final EvictionAttributes evictionAttributes;
private final Set<String> regionNames = new HashSet<String>();
protected static EvictionPolicyMetaData from(AnnotationAttributes evictionPolicyAttributes,
ApplicationContext applicationContext) {
return from((Integer) evictionPolicyAttributes.get("maximum"),
evictionPolicyAttributes.<EvictionPolicyType>getEnum("type"),
evictionPolicyAttributes.<EvictionActionType>getEnum("action"),
resolveObjectSizer(evictionPolicyAttributes.getString("objectSizerName"), applicationContext),
evictionPolicyAttributes.getStringArray("regionNames"));
}
protected static EvictionPolicyMetaData from(EvictionPolicy evictionPolicy,
ApplicationContext applicationContext) {
return from(evictionPolicy.maximum(), evictionPolicy.type(), evictionPolicy.action(),
resolveObjectSizer(evictionPolicy.objectSizerName(), applicationContext), evictionPolicy.regionNames());
}
protected static EvictionPolicyMetaData from(int maximum, EvictionPolicyType type, EvictionActionType action,
ObjectSizer objectSizer, String... regionNames) {
EvictionAttributesFactoryBean factoryBean = new EvictionAttributesFactoryBean();
factoryBean.setAction(action.getEvictionAction());
factoryBean.setObjectSizer(objectSizer);
factoryBean.setThreshold(resolveThreshold(maximum, type));
factoryBean.setType(type);
factoryBean.afterPropertiesSet();
return new EvictionPolicyMetaData(factoryBean.getObject(), regionNames);
}
protected static EvictionPolicyMetaData fromDefaults() {
return new EvictionPolicyMetaData(EvictionAttributes.createLRUEntryAttributes());
}
protected static ObjectSizer resolveObjectSizer(String objectSizerName, ApplicationContext applicationContext) {
boolean resolvable = StringUtils.hasText(objectSizerName)
&& applicationContext.containsBean(objectSizerName);
return (resolvable ? applicationContext.getBean(objectSizerName, ObjectSizer.class) : null);
}
/**
* Resolves the Eviction policy threshold (a.k.a. maximum) based on the {@link EvictionPolicyType}.
*
* For instance {@link EvictionPolicyType#HEAP_PERCENTAGE} does not support maximum/threshold since
* the settings are determined by the GemFire/Geode cache critical heap percentage and eviction heap percentage
* System property settings.
*
* @param maximum integer value specifying the configured Eviction threshold.
* @param type {@link EvictionPolicyType} specifying the type of Eviction algorithm.
* @return a resolved value for the Eviction maximum/threshold.
* @see org.springframework.data.gemfire.eviction.EvictionPolicyType
*/
protected static Integer resolveThreshold(int maximum, EvictionPolicyType type) {
return (EvictionPolicyType.HEAP_PERCENTAGE.equals(type) ? null : maximum);
}
/**
* Constructs an instance of {@link EvictionPolicyMetaData} initialized with the given
* {@link EvictionAttributes} applying to all {@link Region Regions}.
*
* @param evictionAttributes {@link EvictionAttributes} specifying the Eviction policy configuration
* for a {@link Region}.
* @see org.apache.geode.cache.EvictionAttributes
* @see #EvictionPolicyMetaData(EvictionAttributes, String[])
*/
protected EvictionPolicyMetaData(EvictionAttributes evictionAttributes) {
this(evictionAttributes, ALL_REGIONS);
}
/**
* Constructs an instance of {@link EvictionPolicyMetaData} initialized with the given
* {@link EvictionAttributes} to apply to the specific {@link Region Regions}.
*
* @param evictionAttributes {@link EvictionAttributes} specifying the Eviction policy configuration
* for a {@link Region}.
* @param regionNames names of {@link Region Regions} on which the Eviction policy is applied.
* @see org.apache.geode.cache.EvictionAttributes
*/
protected EvictionPolicyMetaData(EvictionAttributes evictionAttributes, String[] regionNames) {
Assert.notNull(evictionAttributes, "EvictionAttributes must not be null");
this.evictionAttributes = evictionAttributes;
Collections.addAll(this.regionNames, ArrayUtils.nullSafeArray(regionNames, String.class));
}
/**
* Returns an instance of the {@link EvictionAttributes} specifying the Eviction policy configuration
* captured in this Eviction policy meta-data.
*
* @return an instance of the {@link EvictionAttributes} specifying the {@link Region}
* Eviction policy configuration.
* @throws IllegalStateException if the {@link EvictionAttributes} were not properly initialized.
* @see org.apache.geode.cache.EvictionAttributes
*/
protected EvictionAttributes getEvictionAttributes() {
Assert.state(this.evictionAttributes != null,
"EvictionAttributes was not properly configured and initialized");
return this.evictionAttributes;
}
/**
* Determines whether the given {@link Object} (e.g. Spring bean) is accepted for Eviction policy configuration.
*
* @param regionFactoryBean {@link Object} being evaluated as an Eviction policy configuration candidate.
* @return a boolean value indicating whether the {@link Object} is accepted for Eviction policy configuration.
* @see #isRegionFactoryBean(Object)
* @see #resolveRegionName(Object)
* @see #accepts(String)
*/
protected boolean accepts(Object regionFactoryBean) {
return (isRegionFactoryBean(regionFactoryBean) && accepts(resolveRegionName(regionFactoryBean)));
}
/**
* Determine whether the {@link Region} identified by name is accepted for Eviction policy configuration.
*
* @param regionName name of the {@link Region} targeted for Eviction policy configuration.
* @return a boolean value if the named {@link Region} is accepted for Eviction policy configuration.
*/
protected boolean accepts(String regionName) {
return (this.regionNames.isEmpty() || this.regionNames.contains(regionName));
}
/**
* Resolves the name of a given {@link Region} from the corresponding {@link RegionLookupFactoryBean} object.
*
* @param regionFactoryBean {@link RegionLookupFactoryBean} from which to resolve the {@link Region} name.
* @return the resolved name of the {@link Region} created from the given {@link RegionLookupFactoryBean}.
* @see org.springframework.data.gemfire.RegionLookupFactoryBean#resolveRegionName()
*/
protected String resolveRegionName(Object regionFactoryBean) {
return (regionFactoryBean instanceof RegionLookupFactoryBean
? ((RegionLookupFactoryBean) regionFactoryBean).resolveRegionName() : null);
}
/**
* Sets the {@link EvictionAttributes} on the {@link RegionFactoryBean} or {@link ClientRegionFactoryBean}
* used to create the targeted {@link Region}.
*
* @param regionFactoryBean {@link RegionFactoryBean} or {@link ClientRegionFactoryBean} on which to
* set the {@link EvictionAttributes} encapsulating the Eviction policy for the targeted {@link Region}.
* @return the {@code regionFactoryBean}.
* @see org.springframework.data.gemfire.RegionFactoryBean#setEvictionAttributes(EvictionAttributes)
* @see org.springframework.data.gemfire.client.ClientRegionFactoryBean#setEvictionAttributes(EvictionAttributes)
* @see org.apache.geode.cache.EvictionAttributes
* @see #getEvictionAttributes()
*/
protected Object setEvictionAttributes(Object regionFactoryBean) {
if (regionFactoryBean instanceof RegionFactoryBean) {
((RegionFactoryBean) regionFactoryBean).setEvictionAttributes(getEvictionAttributes());
}
else {
((ClientRegionFactoryBean) regionFactoryBean).setEvictionAttributes(getEvictionAttributes());
}
return regionFactoryBean;
}
/**
* @inheritDoc
*/
@Override
public Object configure(Object regionFactoryBean) {
return (accepts(regionFactoryBean) ? setEvictionAttributes(regionFactoryBean) : regionFactoryBean);
}
}
}

View File

@@ -52,6 +52,9 @@ public class EvictionAttributesFactoryBean implements FactoryBean<EvictionAttrib
private ObjectSizer objectSizer = null;
/**
* @inheritDoc
*/
public void afterPropertiesSet() {
evictionAttributes = createAttributes();
}
@@ -74,17 +77,23 @@ public class EvictionAttributesFactoryBean implements FactoryBean<EvictionAttrib
}
}
/* non-Javadoc */
/**
* @inheritDoc
*/
public EvictionAttributes getObject() {
return evictionAttributes;
}
/* non-Javadoc */
/**
* @inheritDoc
*/
public Class<?> getObjectType() {
return (evictionAttributes != null ? evictionAttributes.getClass() : EvictionAttributes.class);
}
/* non-Javadoc */
/**
* @inheritDoc
*/
public boolean isSingleton() {
return true;
}
@@ -168,5 +177,4 @@ public class EvictionAttributesFactoryBean implements FactoryBean<EvictionAttrib
public EvictionPolicyType getType() {
return type;
}
}