This is an automated email from the ASF dual-hosted git repository. jinmeiliao pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push: new 21ae51e GEODE-7193: add entry-idle-time and entry-time-to-live to region attributes 21ae51e is described below commit 21ae51e5788826279cec586681291f1a2fd06b73 Author: Jinmei Liao <jil...@pivotal.io> AuthorDate: Wed Sep 11 22:21:13 2019 -0700 GEODE-7193: add entry-idle-time and entry-time-to-live to region attributes Co-authored-by: Darrel Schneider <dschnei...@pivotal.io> --- .../internal/rest/RegionManagementDunitTest.java | 61 +++++++++++++ .../integrationTest/resources/assembly_content.txt | 3 + .../configuration/converters/RegionConverter.java | 84 +++++++++++++++++ .../validators/RegionConfigValidator.java | 36 ++++++++ .../sanctioned-geode-management-serializables.txt | 7 +- .../converters/RegionConverterTest.java | 101 +++++++++++++++++++++ .../validators/RegionConfigValidatorTest.java | 53 +++++++++++ .../geode/management/configuration/Region.java | 99 ++++++++++++++++---- 8 files changed, 426 insertions(+), 18 deletions(-) diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/RegionManagementDunitTest.java b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/RegionManagementDunitTest.java index f14bd05..769227c 100644 --- a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/RegionManagementDunitTest.java +++ b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/RegionManagementDunitTest.java @@ -27,7 +27,10 @@ import org.junit.ClassRule; import org.junit.Test; import org.apache.geode.cache.Cache; +import org.apache.geode.cache.ExpirationAction; +import org.apache.geode.cache.RegionAttributes; import org.apache.geode.cache.configuration.CacheConfig; +import org.apache.geode.cache.configuration.RegionAttributesType; import org.apache.geode.cache.configuration.RegionConfig; import org.apache.geode.management.api.ClusterManagementRealizationResult; import org.apache.geode.management.api.ClusterManagementResult; @@ -218,4 +221,62 @@ public class RegionManagementDunitTest { assertManagementResult(cms.create(regionConfig)).isSuccessful(); } + + @Test + public void createRegionWithExpiration() throws Exception { + Region region = new Region(); + String regionName = "createRegionWithExpiration"; + region.setName(regionName); + region.setType(RegionType.REPLICATE); + region.addExpiry(Region.ExpirationType.ENTRY_IDLE_TIME, 10000, null); + region.addExpiry(Region.ExpirationType.ENTRY_TIME_TO_LIVE, 20000, + Region.ExpirationAction.INVALIDATE); + + assertManagementResult(cms.create(region)).isSuccessful(); + + locator.invoke(() -> { + CacheConfig cacheConfig = + ClusterStartupRule.getLocator().getConfigurationPersistenceService() + .getCacheConfig("cluster"); + RegionConfig regionConfig = find(cacheConfig.getRegions(), regionName); + RegionAttributesType regionAttributes = regionConfig.getRegionAttributes(); + assertThat(regionAttributes.isStatisticsEnabled()).isTrue(); + assertThat(regionAttributes.getEntryTimeToLive().getTimeout()).isEqualTo("20000"); + assertThat(regionAttributes.getEntryTimeToLive().getAction()).isEqualTo("invalidate"); + assertThat(regionAttributes.getEntryTimeToLive().getCustomExpiry()).isNull(); + + assertThat(regionAttributes.getEntryIdleTime().getTimeout()).isEqualTo("10000"); + assertThat(regionAttributes.getEntryIdleTime().getAction()).isEqualTo("destroy"); + assertThat(regionAttributes.getEntryIdleTime().getCustomExpiry()).isNull(); + + assertThat(regionAttributes.getRegionTimeToLive()).isNull(); + assertThat(regionAttributes.getRegionIdleTime()).isNull(); + }); + + server1.invoke(() -> { + Cache cache = ClusterStartupRule.getCache(); + org.apache.geode.cache.Region actualRegion = cache.getRegion(regionName); + RegionAttributes attributes = actualRegion.getAttributes(); + assertThat(attributes.getStatisticsEnabled()).isTrue(); + assertThat(attributes.getEntryIdleTimeout().getTimeout()).isEqualTo(10000); + assertThat(attributes.getEntryIdleTimeout().getAction()).isEqualTo(ExpirationAction.DESTROY); + assertThat(attributes.getEntryTimeToLive().getTimeout()).isEqualTo(20000); + assertThat(attributes.getEntryTimeToLive().getAction()) + .isEqualTo(ExpirationAction.INVALIDATE); + assertThat(attributes.getRegionIdleTimeout().getTimeout()).isEqualTo(0); + assertThat(attributes.getRegionTimeToLive().getTimeout()).isEqualTo(0); + assertThat(attributes.getCustomEntryIdleTimeout()).isNull(); + assertThat(attributes.getCustomEntryTimeToLive()).isNull(); + }); + + Region regionResult = cms.get(region).getConfigResult().get(0); + List<Region.Expiration> expirations = regionResult.getExpirations(); + assertThat(expirations).hasSize(2); + assertThat(expirations.get(0).getTimeInSeconds()).isEqualTo(10000); + assertThat(expirations.get(0).getAction()).isEqualTo(Region.ExpirationAction.DESTROY); + assertThat(expirations.get(0).getType()).isEqualTo(Region.ExpirationType.ENTRY_IDLE_TIME); + assertThat(expirations.get(1).getTimeInSeconds()).isEqualTo(20000); + assertThat(expirations.get(1).getAction()).isEqualTo(Region.ExpirationAction.INVALIDATE); + assertThat(expirations.get(1).getType()).isEqualTo(Region.ExpirationType.ENTRY_TIME_TO_LIVE); + } } diff --git a/geode-assembly/src/integrationTest/resources/assembly_content.txt b/geode-assembly/src/integrationTest/resources/assembly_content.txt index b717dc0..0990e8e 100644 --- a/geode-assembly/src/integrationTest/resources/assembly_content.txt +++ b/geode-assembly/src/integrationTest/resources/assembly_content.txt @@ -728,6 +728,9 @@ javadoc/org/apache/geode/management/configuration/GroupableConfiguration.html javadoc/org/apache/geode/management/configuration/Index.html javadoc/org/apache/geode/management/configuration/MemberConfig.html javadoc/org/apache/geode/management/configuration/Pdx.html +javadoc/org/apache/geode/management/configuration/Region.Expiration.html +javadoc/org/apache/geode/management/configuration/Region.ExpirationAction.html +javadoc/org/apache/geode/management/configuration/Region.ExpirationType.html javadoc/org/apache/geode/management/configuration/Region.html javadoc/org/apache/geode/management/configuration/RegionType.html javadoc/org/apache/geode/management/configuration/package-frame.html diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/converters/RegionConverter.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/converters/RegionConverter.java index ff7a003..e3dd62b 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/converters/RegionConverter.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/converters/RegionConverter.java @@ -16,8 +16,12 @@ package org.apache.geode.management.internal.configuration.converters; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; +import org.apache.commons.lang3.StringUtils; + import org.apache.geode.cache.configuration.EnumActionDestroyOverflow; import org.apache.geode.cache.configuration.RegionAttributesDataPolicy; import org.apache.geode.cache.configuration.RegionAttributesScope; @@ -42,6 +46,34 @@ public class RegionConverter extends ConfigurationConverter<Region, RegionConfig .flatMap( partitionAttributes -> Optional.ofNullable(partitionAttributes.getRedundantCopies())) .ifPresent(copies -> region.setRedundantCopies(Integer.parseInt(copies))); + + RegionAttributesType.ExpirationAttributesType entryIdleTime = + regionAttributes.getEntryIdleTime(); + List<Region.Expiration> expirations = new ArrayList<>(); + if (entryIdleTime != null) { + expirations.add(convertFrom(Region.ExpirationType.ENTRY_IDLE_TIME, entryIdleTime)); + } + RegionAttributesType.ExpirationAttributesType entryTimeToLive = + regionAttributes.getEntryTimeToLive(); + if (entryTimeToLive != null) { + expirations.add(convertFrom(Region.ExpirationType.ENTRY_TIME_TO_LIVE, entryTimeToLive)); + } + + RegionAttributesType.ExpirationAttributesType regionIdleTime = + regionAttributes.getRegionIdleTime(); + if (regionIdleTime != null) { + expirations.add(convertFrom(Region.ExpirationType.UNSUPPORTED, regionIdleTime)); + } + + RegionAttributesType.ExpirationAttributesType regionTimeToLive = + regionAttributes.getRegionTimeToLive(); + if (regionTimeToLive != null) { + expirations.add(convertFrom(Region.ExpirationType.UNSUPPORTED, regionTimeToLive)); + } + + if (!expirations.isEmpty()) { + region.setExpirations(expirations); + } } return region; } @@ -56,6 +88,7 @@ public class RegionConverter extends ConfigurationConverter<Region, RegionConfig RegionAttributesType attributesType = createRegionAttributesByType(configObject.getType().name()); + attributesType.setStatisticsEnabled(true); attributesType.setDiskStoreName(configObject.getDiskStoreName()); attributesType.setKeyConstraint(configObject.getKeyConstraint()); attributesType.setValueConstraint(configObject.getValueConstraint()); @@ -67,9 +100,60 @@ public class RegionConverter extends ConfigurationConverter<Region, RegionConfig partitionAttributes.setRedundantCopies(configObject.getRedundantCopies().toString()); attributesType.setPartitionAttributes(partitionAttributes); } + + List<Region.Expiration> expirations = configObject.getExpirations(); + if (expirations != null) { + for (Region.Expiration expiration : expirations) { + switch (expiration.getType()) { + case ENTRY_IDLE_TIME: + attributesType.setEntryIdleTime(convertFrom(expiration)); + break; + case ENTRY_TIME_TO_LIVE: + attributesType.setEntryTimeToLive(convertFrom(expiration)); + break; + } + } + } return region; } + RegionAttributesType.ExpirationAttributesType convertFrom(Region.Expiration expiration) { + RegionAttributesType.ExpirationAttributesType xmlExpiration = + new RegionAttributesType.ExpirationAttributesType(); + xmlExpiration.setTimeout(expiration.getTimeInSeconds().toString()); + // when action is null from the management api, the default action is DESTROY + if (expiration.getAction() == null) { + xmlExpiration.setAction(Region.ExpirationAction.DESTROY.name().toLowerCase()); + } else { + xmlExpiration.setAction(expiration.getAction().name().toLowerCase()); + } + return xmlExpiration; + } + + Region.Expiration convertFrom(Region.ExpirationType type, + RegionAttributesType.ExpirationAttributesType xmlExpiration) { + Region.Expiration expiration = new Region.Expiration(); + expiration.setType(type); + if (StringUtils.isBlank(xmlExpiration.getTimeout())) { + expiration.setTimeInSeconds(0); + } else { + expiration.setTimeInSeconds(Integer.parseInt(xmlExpiration.getTimeout())); + } + + // in the xml, the default expiration action is INVALIDATE + if (StringUtils.isBlank(xmlExpiration.getAction())) { + expiration.setAction(Region.ExpirationAction.INVALIDATE); + } else { + try { + expiration.setAction( + Region.ExpirationAction.valueOf(xmlExpiration.getAction().toUpperCase())); + } catch (Exception e) { + expiration.setAction(Region.ExpirationAction.UNSUPPORTED); + } + } + return expiration; + } + /** * Data policy to regionType is almost a 1-to-1 mapping, except in * the case of DataPolicy.PARTITION, we will need to see the local max memory diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidator.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidator.java index 2c6db7b..ccf08b6 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidator.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidator.java @@ -16,6 +16,10 @@ package org.apache.geode.management.internal.configuration.validators; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import org.apache.commons.lang3.StringUtils; import org.apache.geode.internal.cache.InternalCache; @@ -86,5 +90,37 @@ public class RegionConfigValidator implements ConfigurationValidator<Region> { .authorize(ResourcePermission.Resource.CLUSTER, ResourcePermission.Operation.WRITE, ResourcePermission.Target.DISK); } + + // validate expirations + List<Region.Expiration> expirations = config.getExpirations(); + if (expirations != null) { + Set<Region.ExpirationType> existingTypes = new HashSet<>(); + for (Region.Expiration expiration : expirations) { + validate(expiration); + if (existingTypes.contains(expiration.getType())) { + throw new IllegalArgumentException("Can not have multiple " + expiration.getType() + "."); + } + existingTypes.add(expiration.getType()); + } + } + } + + private void validate(Region.Expiration expiration) { + if (expiration.getType() == null) { + throw new IllegalArgumentException("Expiration type must be set."); + } + + if (expiration.getType() == Region.ExpirationType.UNSUPPORTED) { + throw new IllegalArgumentException("Invalid Expiration type."); + } + + if (expiration.getTimeInSeconds() == null || expiration.getTimeInSeconds() < 0) { + throw new IllegalArgumentException( + ("Expiration timeInSeconds must be greater than or equal to 0.")); + } + + if (expiration.getAction() == Region.ExpirationAction.UNSUPPORTED) { + throw new IllegalArgumentException("Invalid Expiration action."); + } } } diff --git a/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-management-serializables.txt b/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-management-serializables.txt index 0398c59..2e1810a 100644 --- a/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-management-serializables.txt +++ b/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-management-serializables.txt @@ -7,10 +7,13 @@ org/apache/geode/management/configuration/GroupableConfiguration,false,group:jav org/apache/geode/management/configuration/Index,false,expression:java/lang/String,keyIndex:java/lang/Boolean,name:java/lang/String,regionPath:java/lang/String org/apache/geode/management/configuration/MemberConfig,false,id:java/lang/String org/apache/geode/management/configuration/Pdx,false,diskStoreName:java/lang/String,ignoreUnreadFields:java/lang/Boolean,pdxSerializer:org/apache/geode/management/configuration/ClassName,persistent:java/lang/Boolean,readSerialized:java/lang/Boolean -org/apache/geode/management/configuration/Region,false,diskStoreName:java/lang/String,keyConstraint:java/lang/String,name:java/lang/String,redundantCopies:java/lang/Integer,type:org/apache/geode/management/configuration/RegionType,valueConstraint:java/lang/String +org/apache/geode/management/configuration/Region,false,diskStoreName:java/lang/String,expirations:java/util/List,keyConstraint:java/lang/String,name:java/lang/String,redundantCopies:java/lang/Integer,type:org/apache/geode/management/configuration/RegionType,valueConstraint:java/lang/String +org/apache/geode/management/configuration/Region$Expiration,false,action:org/apache/geode/management/configuration/Region$ExpirationAction,timeInSeconds:java/lang/Integer,type:org/apache/geode/management/configuration/Region$ExpirationType +org/apache/geode/management/configuration/Region$ExpirationAction,false +org/apache/geode/management/configuration/Region$ExpirationType,false org/apache/geode/management/configuration/RegionType,false org/apache/geode/management/runtime/CacheServerInfo,true,1,bindAddress:java/lang/String,isRunning:boolean,maxConnections:int,maxThreads:int,port:int org/apache/geode/management/runtime/GatewayReceiverInfo,false,bindAddress:java/lang/String,connectedSenders:java/lang/String[],hostnameForSenders:java/lang/String,port:int,running:boolean,senderCount:int org/apache/geode/management/runtime/MemberInformation,true,1,cacheServerList:java/util/List,cacheXmlFilePath:java/lang/String,clientCount:int,cpuUsage:double,groups:java/lang/String,heapUsage:long,host:java/lang/String,hostedRegions:java/util/Set,httpServiceBindAddress:java/lang/String,httpServicePort:int,id:java/lang/String,initHeapSize:long,isCoordinator:boolean,isSecured:boolean,isServer:boolean,locatorPort:int,locators:java/lang/String,logFilePath:java/lang/String,maxHeapSize:long,of [...] org/apache/geode/management/runtime/RuntimeInfo,false,memberName:java/lang/String -org/apache/geode/management/runtime/RuntimeRegionInfo,false,entryCount:long \ No newline at end of file +org/apache/geode/management/runtime/RuntimeRegionInfo,false,entryCount:long diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/converters/RegionConverterTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/converters/RegionConverterTest.java index f67caa5..132b8fc 100644 --- a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/converters/RegionConverterTest.java +++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/converters/RegionConverterTest.java @@ -21,6 +21,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.io.File; import java.net.URL; +import java.util.List; import org.apache.commons.io.FileUtils; import org.junit.Before; @@ -80,6 +81,7 @@ public class RegionConverterTest { assertThat(region.getKeyConstraint()).isEqualTo("bar"); assertThat(region.getDiskStoreName()).isEqualTo("diskstore"); assertThat(region.getRedundantCopies()).isEqualTo(2); + assertThat(region.getExpirations()).isNull(); } @Test @@ -113,6 +115,9 @@ public class RegionConverterTest { assertThat(regionAttributes.getValueConstraint()).isEqualTo("foo"); assertThat(regionAttributes.getDiskStoreName()).isEqualTo("diskstore"); assertThat(regionAttributes.getPartitionAttributes().getRedundantCopies()).isEqualTo("2"); + assertThat(regionAttributes.isStatisticsEnabled()).isTrue(); + assertThat(regionAttributes.getEntryIdleTime()).isNull(); + assertThat(regionAttributes.getEntryTimeToLive()).isNull(); } @Test @@ -185,4 +190,100 @@ public class RegionConverterTest { assertThatThrownBy(() -> converter.createRegionAttributesByType("abc")) .isInstanceOf(IllegalArgumentException.class); } + + @Test + public void convertRegionExpirationFromXml() throws Exception { + config.setType("REPLICATE"); + config.setName("test"); + RegionAttributesType attributes = new RegionAttributesType(); + attributes.setEntryTimeToLive( + new RegionAttributesType.ExpirationAttributesType(10, "destroy", null, null)); + attributes.setEntryIdleTime( + new RegionAttributesType.ExpirationAttributesType(100, "local-destroy", null, null)); + attributes.setRegionIdleTime( + new RegionAttributesType.ExpirationAttributesType(101, "invalidate", null, null)); + attributes.setRegionTimeToLive( + new RegionAttributesType.ExpirationAttributesType(102, "local-invalidate", null, null)); + + config.setRegionAttributes(attributes); + + Region region = converter.fromXmlObject(config); + List<Region.Expiration> expirations = region.getExpirations(); + assertThat(expirations).hasSize(4); + assertThat(expirations.get(0).getTimeInSeconds()).isEqualTo(100); + assertThat(expirations.get(0).getAction()).isEqualTo(Region.ExpirationAction.UNSUPPORTED); + assertThat(expirations.get(0).getType()).isEqualTo(Region.ExpirationType.ENTRY_IDLE_TIME); + assertThat(expirations.get(1).getTimeInSeconds()).isEqualTo(10); + assertThat(expirations.get(1).getAction()).isEqualTo(Region.ExpirationAction.DESTROY); + assertThat(expirations.get(1).getType()).isEqualTo(Region.ExpirationType.ENTRY_TIME_TO_LIVE); + assertThat(expirations.get(2).getTimeInSeconds()).isEqualTo(101); + assertThat(expirations.get(2).getAction()).isEqualTo(Region.ExpirationAction.INVALIDATE); + assertThat(expirations.get(2).getType()).isEqualTo(Region.ExpirationType.UNSUPPORTED); + assertThat(expirations.get(3).getTimeInSeconds()).isEqualTo(102); + assertThat(expirations.get(3).getAction()).isEqualTo(Region.ExpirationAction.UNSUPPORTED); + assertThat(expirations.get(3).getType()).isEqualTo(Region.ExpirationType.UNSUPPORTED); + } + + @Test + public void convertRegionExpirationFromConfig() throws Exception { + region.setName("test"); + region.setType(RegionType.REPLICATE); + region.addExpiry(Region.ExpirationType.ENTRY_IDLE_TIME, 100, null); + region.addExpiry(Region.ExpirationType.ENTRY_TIME_TO_LIVE, 101, + Region.ExpirationAction.INVALIDATE); + + RegionConfig regionConfig = converter.fromConfigObject(region); + RegionAttributesType regionAttributes = regionConfig.getRegionAttributes(); + assertThat(regionAttributes.getEntryIdleTime().getTimeout()).isEqualTo("100"); + assertThat(regionAttributes.getEntryIdleTime().getAction()).isEqualTo("destroy"); + assertThat(regionAttributes.getEntryIdleTime().getCustomExpiry()).isNull(); + assertThat(regionAttributes.getEntryTimeToLive().getTimeout()).isEqualTo("101"); + assertThat(regionAttributes.getEntryTimeToLive().getAction()).isEqualTo("invalidate"); + assertThat(regionAttributes.getEntryTimeToLive().getCustomExpiry()).isNull(); + } + + @Test + public void convertExpirationFromConfig() throws Exception { + Region.Expiration expiration = new Region.Expiration(); + expiration.setTimeInSeconds(2); + RegionAttributesType.ExpirationAttributesType expirationAttributes = + converter.convertFrom(expiration); + assertThat(expirationAttributes.getCustomExpiry()).isNull(); + assertThat(expirationAttributes.getAction()).isEqualTo("destroy"); + assertThat(expirationAttributes.getTimeout()).isEqualTo("2"); + + expiration.setTimeInSeconds(20); + expiration.setAction(Region.ExpirationAction.INVALIDATE); + expirationAttributes = + converter.convertFrom(expiration); + assertThat(expirationAttributes.getCustomExpiry()).isNull(); + assertThat(expirationAttributes.getAction()).isEqualTo("invalidate"); + assertThat(expirationAttributes.getTimeout()).isEqualTo("20"); + } + + @Test + public void convertExpirationFromXml() throws Exception { + RegionAttributesType.ExpirationAttributesType xmlConfig = + new RegionAttributesType.ExpirationAttributesType(); + Region.Expiration expiration = + converter.convertFrom(Region.ExpirationType.ENTRY_IDLE_TIME, xmlConfig); + assertThat(expiration.getType()).isEqualTo(Region.ExpirationType.ENTRY_IDLE_TIME); + assertThat(expiration.getAction()).isEqualTo(Region.ExpirationAction.INVALIDATE); + assertThat(expiration.getTimeInSeconds()).isEqualTo(0); + + xmlConfig.setTimeout("1000"); + xmlConfig.setAction("destroy"); + expiration = + converter.convertFrom(Region.ExpirationType.ENTRY_IDLE_TIME, xmlConfig); + assertThat(expiration.getType()).isEqualTo(Region.ExpirationType.ENTRY_IDLE_TIME); + assertThat(expiration.getAction()).isEqualTo(Region.ExpirationAction.DESTROY); + assertThat(expiration.getTimeInSeconds()).isEqualTo(1000); + + xmlConfig.setAction("local-destroy"); + expiration = + converter.convertFrom(Region.ExpirationType.ENTRY_IDLE_TIME, xmlConfig); + assertThat(expiration.getType()).isEqualTo(Region.ExpirationType.ENTRY_IDLE_TIME); + assertThat(expiration.getAction()).isEqualTo(Region.ExpirationAction.UNSUPPORTED); + assertThat(expiration.getTimeInSeconds()).isEqualTo(1000); + } } diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidatorTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidatorTest.java index b8da0e4..a3b3f31 100644 --- a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidatorTest.java +++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidatorTest.java @@ -167,4 +167,57 @@ public class RegionConfigValidatorTest { .hasMessageContaining( "redundantCopies can only be set with PARTITION regions."); } + + @Test + public void validateExpiration() throws Exception { + config.setName("test"); + config.setType(RegionType.REPLICATE); + config.addExpiry(null, null, null); + assertThatThrownBy(() -> validator.validate(CacheElementOperation.CREATE, config)).isInstanceOf( + IllegalArgumentException.class) + .hasMessageContaining( + "Expiration type must be set."); + config.getExpirations().clear(); + + config.addExpiry(Region.ExpirationType.ENTRY_IDLE_TIME, null, null); + assertThatThrownBy(() -> validator.validate(CacheElementOperation.CREATE, config)).isInstanceOf( + IllegalArgumentException.class) + .hasMessageContaining( + "Expiration timeInSeconds must be greater than or equal to 0."); + config.getExpirations().clear(); + + config.addExpiry(Region.ExpirationType.ENTRY_IDLE_TIME, -1, null); + assertThatThrownBy(() -> validator.validate(CacheElementOperation.CREATE, config)).isInstanceOf( + IllegalArgumentException.class) + .hasMessageContaining( + "Expiration timeInSeconds must be greater than or equal to 0."); + config.getExpirations().clear(); + + config.addExpiry(Region.ExpirationType.UNSUPPORTED, 100, null); + assertThatThrownBy(() -> validator.validate(CacheElementOperation.CREATE, config)).isInstanceOf( + IllegalArgumentException.class) + .hasMessageContaining( + "Invalid Expiration type."); + config.getExpirations().clear(); + + config.addExpiry(Region.ExpirationType.ENTRY_IDLE_TIME, 100, + Region.ExpirationAction.UNSUPPORTED); + assertThatThrownBy(() -> validator.validate(CacheElementOperation.CREATE, config)).isInstanceOf( + IllegalArgumentException.class) + .hasMessageContaining( + "Invalid Expiration action."); + config.getExpirations().clear(); + + config.addExpiry(Region.ExpirationType.ENTRY_IDLE_TIME, 100, null); + config.addExpiry(Region.ExpirationType.ENTRY_IDLE_TIME, 200, null); + assertThatThrownBy(() -> validator.validate(CacheElementOperation.CREATE, config)).isInstanceOf( + IllegalArgumentException.class) + .hasMessageContaining( + "Can not have multiple " + Region.ExpirationType.ENTRY_IDLE_TIME.name()); + config.getExpirations().clear(); + + config.addExpiry(Region.ExpirationType.ENTRY_IDLE_TIME, 100, null); + config.addExpiry(Region.ExpirationType.ENTRY_TIME_TO_LIVE, 200, null); + validator.validate(CacheElementOperation.CREATE, config); + } } diff --git a/geode-management/src/main/java/org/apache/geode/management/configuration/Region.java b/geode-management/src/main/java/org/apache/geode/management/configuration/Region.java index 8faf974..8c21aa6 100644 --- a/geode-management/src/main/java/org/apache/geode/management/configuration/Region.java +++ b/geode-management/src/main/java/org/apache/geode/management/configuration/Region.java @@ -15,7 +15,11 @@ package org.apache.geode.management.configuration; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -38,6 +42,8 @@ public class Region extends GroupableConfiguration<RuntimeRegionInfo> { private String diskStoreName; private Integer redundantCopies; + private List<Expiration> expirations; + public Region() {} @Override @@ -66,6 +72,7 @@ public class Region extends GroupableConfiguration<RuntimeRegionInfo> { public void setName(String value) { if (value == null) { + this.name = null; return; } @@ -127,25 +134,85 @@ public class Region extends GroupableConfiguration<RuntimeRegionInfo> { this.diskStoreName = diskStoreName; } - /** - * two regions are equal if name and type are equal. - */ - @Override - public boolean equals(Object that) { - if (this == that) { - return true; - } - if (that == null || getClass() != that.getClass()) { - return false; + public List<Expiration> getExpirations() { + return expirations; + } + + public void setExpirations(List<Expiration> expirations) { + if (expirations == null) { + this.expirations = null; + return; } - Region config = (Region) that; - return Objects.equals(getName(), config.getName()) && - Objects.equals(getType(), config.getType()); + this.expirations = expirations.stream().filter(Objects::nonNull).collect(Collectors.toList()); } + public void addExpiry(ExpirationType type, Integer timeout, ExpirationAction action) { + if (expirations == null) { + expirations = new ArrayList<>(); + } + expirations.add(new Expiration(type, timeout, action)); + } + + public enum ExpirationType { + ENTRY_TIME_TO_LIVE, + ENTRY_IDLE_TIME, + /** + * for those other types that could exist in cache.xml but not supported by management api + * for example: REGION_IDLE_TIME, REGION_TIME_TO_LIVE, eventually these should be removed + * from the product as well. + */ + UNSUPPORTED + } + + public enum ExpirationAction { + DESTROY, + INVALIDATE, + /** + * for those other actions that could exist in cache.xml but not supported by management api + * for example: LOCAL_DESTROY, LOCAL_INVALIDATE, eventually these should be removed from the + * product as well. + */ + UNSUPPORTED + } + + public static class Expiration implements Serializable { + private ExpirationType type; + private Integer timeInSeconds; + private ExpirationAction action; + + public Expiration() {}; + + public Expiration(ExpirationType type, Integer timeInSeconds, ExpirationAction action) { + setType(type); + this.timeInSeconds = timeInSeconds; + this.action = action; + } - @Override - public int hashCode() { - return Objects.hash(getName()); + public ExpirationType getType() { + return type; + } + + public void setType(ExpirationType type) { + this.type = type; + } + + public Integer getTimeInSeconds() { + return timeInSeconds; + } + + /** + * @param timeInSeconds in seconds + */ + public void setTimeInSeconds(Integer timeInSeconds) { + this.timeInSeconds = timeInSeconds; + } + + public ExpirationAction getAction() { + return action; + } + + public void setAction(ExpirationAction action) { + this.action = action; + } } }