Apply expiration to caching-defined Regions using @EnableExpiration annotation configuration.
Resolves gh-518.
This commit is contained in:
@@ -18,7 +18,6 @@ package org.springframework.data.gemfire.config.annotation;
|
|||||||
|
|
||||||
import static org.springframework.data.gemfire.config.annotation.EnableExpiration.ExpirationPolicy;
|
import static org.springframework.data.gemfire.config.annotation.EnableExpiration.ExpirationPolicy;
|
||||||
import static org.springframework.data.gemfire.config.annotation.EnableExpiration.ExpirationType;
|
import static org.springframework.data.gemfire.config.annotation.EnableExpiration.ExpirationType;
|
||||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
|
||||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
|
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
|
||||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
|
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
|
||||||
|
|
||||||
@@ -29,15 +28,21 @@ import java.util.Optional;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.apache.geode.cache.AttributesMutator;
|
||||||
|
import org.apache.geode.cache.CustomExpiry;
|
||||||
import org.apache.geode.cache.ExpirationAction;
|
import org.apache.geode.cache.ExpirationAction;
|
||||||
import org.apache.geode.cache.ExpirationAttributes;
|
import org.apache.geode.cache.ExpirationAttributes;
|
||||||
import org.apache.geode.cache.Region;
|
import org.apache.geode.cache.Region;
|
||||||
|
import org.apache.geode.cache.RegionAttributes;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.ImportAware;
|
import org.springframework.context.annotation.ImportAware;
|
||||||
|
import org.springframework.context.event.ContextRefreshedEvent;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
import org.springframework.core.annotation.AnnotationAttributes;
|
import org.springframework.core.annotation.AnnotationAttributes;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.data.gemfire.PeerRegionFactoryBean;
|
import org.springframework.data.gemfire.PeerRegionFactoryBean;
|
||||||
@@ -49,6 +54,7 @@ import org.springframework.data.gemfire.expiration.ExpirationActionType;
|
|||||||
import org.springframework.data.gemfire.expiration.ExpiringRegionFactoryBean;
|
import org.springframework.data.gemfire.expiration.ExpiringRegionFactoryBean;
|
||||||
import org.springframework.data.gemfire.util.ArrayUtils;
|
import org.springframework.data.gemfire.util.ArrayUtils;
|
||||||
import org.springframework.data.gemfire.util.CollectionUtils;
|
import org.springframework.data.gemfire.util.CollectionUtils;
|
||||||
|
import org.springframework.data.gemfire.util.SpringUtils;
|
||||||
import org.springframework.lang.NonNull;
|
import org.springframework.lang.NonNull;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@@ -102,7 +108,7 @@ public class ExpirationConfiguration extends AbstractAnnotationConfigSupport imp
|
|||||||
AnnotationAttributes[] policies = enableExpirationAttributes.getAnnotationArray("policies");
|
AnnotationAttributes[] policies = enableExpirationAttributes.getAnnotationArray("policies");
|
||||||
|
|
||||||
for (AnnotationAttributes expirationPolicyAttributes :
|
for (AnnotationAttributes expirationPolicyAttributes :
|
||||||
nullSafeArray(policies, AnnotationAttributes.class)) {
|
ArrayUtils.nullSafeArray(policies, AnnotationAttributes.class)) {
|
||||||
|
|
||||||
this.expirationPolicyConfigurer =
|
this.expirationPolicyConfigurer =
|
||||||
ComposableExpirationPolicyConfigurer.compose(this.expirationPolicyConfigurer,
|
ComposableExpirationPolicyConfigurer.compose(this.expirationPolicyConfigurer,
|
||||||
@@ -146,20 +152,44 @@ public class ExpirationConfiguration extends AbstractAnnotationConfigSupport imp
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@EventListener(ContextRefreshedEvent.class)
|
||||||
|
public void expirationContextRefreshedListener(@NonNull ContextRefreshedEvent event) {
|
||||||
|
|
||||||
|
ApplicationContext applicationContext = event.getApplicationContext();
|
||||||
|
|
||||||
|
for (Region<?, ?> region : applicationContext.getBeansOfType(Region.class).values()) {
|
||||||
|
getExpirationPolicyConfigurer().configure(region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface defining a contract for implementations that configure a {@link Region Region's} expiration policy.
|
* Interface defining a contract for implementations that configure a {@link Region Region's} expiration policy.
|
||||||
|
*
|
||||||
|
* @see java.lang.FunctionalInterface
|
||||||
*/
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
protected interface ExpirationPolicyConfigurer {
|
protected interface ExpirationPolicyConfigurer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the expiration policy for the given {@link Region}.
|
* Configures the expiration policy for the given {@link Region}.
|
||||||
*
|
*
|
||||||
* @param regionFactoryBean {@link Region} object who's expiration policy will be configured.
|
* @param regionBean {@link Region} object who's expiration policy will be configured.
|
||||||
* @return the given {@link Region} object.
|
* @return the given {@link Region} object.
|
||||||
* @see org.apache.geode.cache.Region
|
* @see org.apache.geode.cache.Region
|
||||||
*/
|
*/
|
||||||
Object configure(Object regionFactoryBean);
|
Object configure(Object regionBean);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the expiration policy for the given {@link Region}.
|
||||||
|
*
|
||||||
|
* @param region {@link Region} who's expiration policy will be configured.
|
||||||
|
* @return the given {@link Region}.
|
||||||
|
* @see org.apache.geode.cache.Region
|
||||||
|
*/
|
||||||
|
default Region<?, ?> configure(Region<?, ?> region) {
|
||||||
|
return region;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -242,8 +272,16 @@ public class ExpirationConfiguration extends AbstractAnnotationConfigSupport imp
|
|||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Object configure(Object regionFactoryBean) {
|
public Object configure(Object regionBean) {
|
||||||
return this.two.configure(this.one.configure(regionFactoryBean));
|
return this.two.configure(this.one.configure(regionBean));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Region<?, ?> configure(Region<?, ?> region) {
|
||||||
|
return this.two.configure(this.one.configure(region));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,12 +299,6 @@ public class ExpirationConfiguration extends AbstractAnnotationConfigSupport imp
|
|||||||
|
|
||||||
protected static final String[] ALL_REGIONS = new String[0];
|
protected static final String[] ALL_REGIONS = new String[0];
|
||||||
|
|
||||||
private final ExpirationAttributes defaultExpirationAttributes;
|
|
||||||
|
|
||||||
private final Set<String> regionNames = new HashSet<>();
|
|
||||||
|
|
||||||
private final Set<ExpirationType> types = new HashSet<>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory method to construct an instance of {@link ExpirationPolicyMetaData} initialized with
|
* Factory method to construct an instance of {@link ExpirationPolicyMetaData} initialized with
|
||||||
* the given {@link AnnotationAttributes} from the nested {@link ExpirationPolicy} annotation
|
* the given {@link AnnotationAttributes} from the nested {@link ExpirationPolicy} annotation
|
||||||
@@ -367,8 +399,8 @@ public class ExpirationConfiguration extends AbstractAnnotationConfigSupport imp
|
|||||||
String[] regionNames, ExpirationType[] types) {
|
String[] regionNames, ExpirationType[] types) {
|
||||||
|
|
||||||
return new ExpirationPolicyMetaData(newExpirationAttributes(timeout, action),
|
return new ExpirationPolicyMetaData(newExpirationAttributes(timeout, action),
|
||||||
CollectionUtils.asSet(nullSafeArray(regionNames, String.class)),
|
CollectionUtils.asSet(ArrayUtils.nullSafeArray(regionNames, String.class)),
|
||||||
CollectionUtils.asSet(nullSafeArray(types, ExpirationType.class)));
|
CollectionUtils.asSet(ArrayUtils.nullSafeArray(types, ExpirationType.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -380,7 +412,7 @@ public class ExpirationConfiguration extends AbstractAnnotationConfigSupport imp
|
|||||||
* @see ExpirationActionType
|
* @see ExpirationActionType
|
||||||
*/
|
*/
|
||||||
protected static ExpirationActionType resolveAction(ExpirationActionType action) {
|
protected static ExpirationActionType resolveAction(ExpirationActionType action) {
|
||||||
return Optional.ofNullable(action).orElse(DEFAULT_ACTION);
|
return action != null ? action : DEFAULT_ACTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -394,6 +426,12 @@ public class ExpirationConfiguration extends AbstractAnnotationConfigSupport imp
|
|||||||
return Math.max(timeout, DEFAULT_TIMEOUT);
|
return Math.max(timeout, DEFAULT_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final ExpirationAttributes defaultExpirationAttributes;
|
||||||
|
|
||||||
|
private final Set<String> regionNames = new HashSet<>();
|
||||||
|
|
||||||
|
private final Set<ExpirationType> types = new HashSet<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an instance of {@link ExpirationPolicyMetaData} initialized with the given expiration policy
|
* Constructs an instance of {@link ExpirationPolicyMetaData} initialized with the given expiration policy
|
||||||
* configuraiton meta-data and {@link Region} expiration settings.
|
* configuraiton meta-data and {@link Region} expiration settings.
|
||||||
@@ -442,14 +480,27 @@ public class ExpirationConfiguration extends AbstractAnnotationConfigSupport imp
|
|||||||
/**
|
/**
|
||||||
* Determines whether the given {@link Object} (e.g. Spring bean) is accepted for Eviction policy configuration.
|
* 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.
|
* @param regionBean {@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.
|
* @return a boolean value indicating whether the {@link Object} is accepted for Eviction policy configuration.
|
||||||
* @see #isRegionFactoryBean(Object)
|
* @see #isRegionFactoryBean(Object)
|
||||||
* @see #resolveRegionName(Object)
|
* @see #resolveRegionName(Object)
|
||||||
* @see #accepts(Supplier)
|
* @see #accepts(Supplier)
|
||||||
*/
|
*/
|
||||||
protected boolean accepts(Object regionFactoryBean) {
|
protected boolean accepts(Object regionBean) {
|
||||||
return isRegionFactoryBean(regionFactoryBean) && accepts(() -> resolveRegionName(regionFactoryBean));
|
return isRegionFactoryBean(regionBean) && accepts(() -> resolveRegionName(regionBean));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the given {@link Region} is accepted for Eviction policy configuration.
|
||||||
|
*
|
||||||
|
* @param region {@link Region} being evaluated as a Eviction policy configuration candidate.
|
||||||
|
* @return a boolean value indicated whether the given {@link Region} is accepted as an Expiration policy
|
||||||
|
* configuration candidate.
|
||||||
|
* @see org.apache.geode.cache.Region
|
||||||
|
* @see #accepts(Supplier)
|
||||||
|
*/
|
||||||
|
protected boolean accepts(Region<?, ?> region) {
|
||||||
|
return region != null && accepts(() -> region.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -530,15 +581,49 @@ public class ExpirationConfiguration extends AbstractAnnotationConfigSupport imp
|
|||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Object configure(Object regionFactoryBean) {
|
public Object configure(Object regionBean) {
|
||||||
|
|
||||||
return accepts(regionFactoryBean)
|
return accepts(regionBean)
|
||||||
? setExpirationAttributes((ExpiringRegionFactoryBean<?, ?>) regionFactoryBean)
|
? setExpirationAttributes((ExpiringRegionFactoryBean<?, ?>) regionBean)
|
||||||
: regionFactoryBean;
|
: regionBean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the default, fallback {@link ExpirationAttributes}.
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Region<?, ?> configure(Region<?, ?> region) {
|
||||||
|
|
||||||
|
if (accepts(region)) {
|
||||||
|
|
||||||
|
RegionAttributes<?, ?> regionAttributes = region.getAttributes();
|
||||||
|
|
||||||
|
ExpirationAttributes expirationAttributes = defaultExpirationAttributes();
|
||||||
|
|
||||||
|
AttributesMutator<?, ?> regionAttributesMutator = region.getAttributesMutator();
|
||||||
|
|
||||||
|
if (SpringUtils.areNotNull(regionAttributes, regionAttributesMutator)) {
|
||||||
|
|
||||||
|
CustomExpiry<?, ?> customEntryIdleTimeout = regionAttributes.getCustomEntryIdleTimeout();
|
||||||
|
CustomExpiry<?, ?> customEntryTimeToLive = regionAttributes.getCustomEntryTimeToLive();
|
||||||
|
|
||||||
|
if (isIdleTimeout() && customEntryIdleTimeout == null) {
|
||||||
|
regionAttributesMutator.setCustomEntryIdleTimeout(
|
||||||
|
AnnotationBasedExpiration.forIdleTimeout(expirationAttributes));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTimeToLive() && customEntryTimeToLive == null) {
|
||||||
|
regionAttributesMutator.setCustomEntryTimeToLive(
|
||||||
|
AnnotationBasedExpiration.forTimeToLive(expirationAttributes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default {@link ExpirationAttributes}.
|
||||||
*
|
*
|
||||||
* @return an {@link ExpirationAttributes} containing the defaults.
|
* @return an {@link ExpirationAttributes} containing the defaults.
|
||||||
* @see org.apache.geode.cache.ExpirationAttributes
|
* @see org.apache.geode.cache.ExpirationAttributes
|
||||||
|
|||||||
@@ -13,32 +13,40 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.data.gemfire.config.annotation;
|
package org.springframework.data.gemfire.config.annotation;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.apache.geode.cache.DataPolicy;
|
import org.apache.geode.cache.DataPolicy;
|
||||||
import org.apache.geode.cache.Region;
|
import org.apache.geode.cache.Region;
|
||||||
import org.apache.geode.cache.RegionShortcut;
|
import org.apache.geode.cache.RegionShortcut;
|
||||||
import org.apache.geode.cache.client.ClientRegionShortcut;
|
import org.apache.geode.cache.client.ClientRegionShortcut;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.data.gemfire.expiration.AnnotationBasedExpiration;
|
import org.springframework.data.gemfire.expiration.AnnotationBasedExpiration;
|
||||||
|
import org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMockObjects;
|
||||||
import org.springframework.data.gemfire.test.model.Person;
|
import org.springframework.data.gemfire.test.model.Person;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The EnableExpirationConfigurationIntegrationTests class...
|
* Integration Tests for {@link EnableExpiration} and {@link ExpirationConfiguration}.
|
||||||
*
|
*
|
||||||
* @author John Blum
|
* @author John Blum
|
||||||
* @since 1.0.0
|
* @see org.junit.Test
|
||||||
|
* @see org.apache.geode.cache.Region
|
||||||
|
* @see org.springframework.data.gemfire.config.annotation.EnableExpiration
|
||||||
|
* @see org.springframework.data.gemfire.config.annotation.ExpirationConfiguration
|
||||||
|
* @since 1.9.0
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class EnableExpirationConfigurationIntegrationTests {
|
public class EnableExpirationConfigurationIntegrationTests {
|
||||||
|
|
||||||
private static final String GEMFIRE_LOG_LEVEL = "error";
|
private static final String GEMFIRE_LOG_LEVEL = "error";
|
||||||
@@ -76,6 +84,16 @@ public class EnableExpirationConfigurationIntegrationTests {
|
|||||||
.isInstanceOf(AnnotationBasedExpiration.class);
|
.isInstanceOf(AnnotationBasedExpiration.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void assertApplicationCachingDefinedRegionsExpirationPoliciesAreCorrect() {
|
||||||
|
|
||||||
|
ConfigurableApplicationContext applicationContext = newApplicationContext(ApplicationConfiguration.class);
|
||||||
|
|
||||||
|
assertThat(applicationContext).isNotNull();
|
||||||
|
assertRegionExpirationConfiguration(applicationContext, "CacheOne");
|
||||||
|
assertRegionExpirationConfiguration(applicationContext, "CacheTwo");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void assertClientCacheRegionExpirationPoliciesAreCorrect() {
|
public void assertClientCacheRegionExpirationPoliciesAreCorrect() {
|
||||||
assertRegionExpirationConfiguration(newApplicationContext(ClientCacheRegionExpirationConfiguration.class),
|
assertRegionExpirationConfiguration(newApplicationContext(ClientCacheRegionExpirationConfiguration.class),
|
||||||
@@ -88,14 +106,42 @@ public class EnableExpirationConfigurationIntegrationTests {
|
|||||||
"People");
|
"People");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ClientCacheApplication(name = "EnableExpirationConfigurationIntegrationTests", logLevel = GEMFIRE_LOG_LEVEL)
|
||||||
|
@EnableCachingDefinedRegions(clientRegionShortcut = ClientRegionShortcut.LOCAL)
|
||||||
|
@EnableExpiration
|
||||||
|
@EnableGemFireMockObjects
|
||||||
|
static class ApplicationConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ApplicationService applicationService() {
|
||||||
|
return new ApplicationService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Service
|
||||||
|
static class ApplicationService {
|
||||||
|
|
||||||
|
@Cacheable("CacheOne")
|
||||||
|
public Object someMethod(Object key) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cacheable("CacheTwo")
|
||||||
|
public Object someOtherMethod(Object key) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ClientCacheApplication(name = "EnableExpirationConfigurationIntegrationTests", logLevel = GEMFIRE_LOG_LEVEL)
|
@ClientCacheApplication(name = "EnableExpirationConfigurationIntegrationTests", logLevel = GEMFIRE_LOG_LEVEL)
|
||||||
@EnableEntityDefinedRegions(basePackageClasses = Person.class, clientRegionShortcut = ClientRegionShortcut.LOCAL)
|
@EnableEntityDefinedRegions(basePackageClasses = Person.class, clientRegionShortcut = ClientRegionShortcut.LOCAL)
|
||||||
@EnableExpiration
|
@EnableExpiration
|
||||||
|
@EnableGemFireMockObjects
|
||||||
static class ClientCacheRegionExpirationConfiguration { }
|
static class ClientCacheRegionExpirationConfiguration { }
|
||||||
|
|
||||||
@PeerCacheApplication(name = "EnableExpirationConfigurationIntegrationTests", logLevel = GEMFIRE_LOG_LEVEL)
|
@PeerCacheApplication(name = "EnableExpirationConfigurationIntegrationTests", logLevel = GEMFIRE_LOG_LEVEL)
|
||||||
@EnableEntityDefinedRegions(basePackageClasses = Person.class, serverRegionShortcut = RegionShortcut.LOCAL)
|
@EnableEntityDefinedRegions(basePackageClasses = Person.class, serverRegionShortcut = RegionShortcut.LOCAL)
|
||||||
@EnableExpiration
|
@EnableExpiration
|
||||||
|
@EnableGemFireMockObjects
|
||||||
static class PeerCacheRegionExpirationConfiguration { }
|
static class PeerCacheRegionExpirationConfiguration { }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user