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

Reply via email to