SGF-516 - Configure Expiration with annotations.
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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.EnableExpiration.ExpirationType.IDLE_TIMEOUT;
|
||||
|
||||
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 com.gemstone.gemfire.cache.Region;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.gemfire.ExpirationActionType;
|
||||
import org.springframework.data.gemfire.support.Expiration;
|
||||
import org.springframework.data.gemfire.support.IdleTimeoutExpiration;
|
||||
import org.springframework.data.gemfire.support.TimeToLiveExpiration;
|
||||
|
||||
/**
|
||||
* The {@link EnableExpiration} annotation marks a Spring {@link org.springframework.context.annotation.Configuration @Configuration}
|
||||
* annotated class to enable {@link Region} entry expiration for individual entries. Note, this annotation does not
|
||||
* cover {@link Region} expiration; {@link Region} expiration must be configure on the {@link Region} definition itself.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.context.annotation.Import
|
||||
* @see org.springframework.data.gemfire.config.annotation.ExpirationConfiguration
|
||||
* @see org.springframework.data.gemfire.support.Expiration
|
||||
* @see org.springframework.data.gemfire.support.IdleTimeoutExpiration
|
||||
* @see org.springframework.data.gemfire.support.TimeToLiveExpiration
|
||||
* @see com.gemstone.gemfire.cache.Region
|
||||
* @see <a href="http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/#bootstrap:region:expiration:annotation">Annotation-based Data Expiration</a>
|
||||
* @see <a href="http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/expiration/chapter_overview.html">GemFire Expiration</a>
|
||||
* @see <a href="http://geode.incubator.apache.org/docs/guide/developing/expiration/chapter_overview.html">Geode Expiration</a>
|
||||
* @since 1.9.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Import(ExpirationConfiguration.class)
|
||||
@SuppressWarnings({ "unused" })
|
||||
public @interface EnableExpiration {
|
||||
|
||||
/**
|
||||
* Defines individual {@link Region} Expiration policies or customizes the default Expiration policy
|
||||
* for all {@link Region Regions}.
|
||||
*
|
||||
* Defaults to empty.
|
||||
*/
|
||||
ExpirationPolicy[] policies() default {};
|
||||
|
||||
/**
|
||||
* Definition for a specific Expiration policy that can be applied to 1 or more {@link Region Regions}.
|
||||
*
|
||||
* An Expiration policy defines the expiration timeout and expiration action to take when
|
||||
* an {@link Region} entry times out.
|
||||
*
|
||||
* Additionally, the Expiration policy defines the algorithm to use (e.g. Idle Timeout (TTI) or Time-To-Live (TTL),
|
||||
* or both) to determine if and when an {@link Region} entry has timed out.
|
||||
*/
|
||||
@interface ExpirationPolicy {
|
||||
|
||||
/**
|
||||
* Specifies the timeout used to determine when a {@link Region} entry expires.
|
||||
*
|
||||
* This value of this attribute determines the "default" timeout used if no specific timeout was specified.
|
||||
* A specific timeout is determined by {@link Expiration#timeout()}, {@link IdleTimeoutExpiration#timeout()}
|
||||
* or {@link TimeToLiveExpiration#timeout()} attribute on the application domain object.
|
||||
*
|
||||
* See the SDG Reference Guide for more details...
|
||||
*
|
||||
* @see <a href="http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/#bootstrap:region:expiration:annotation">Annotation-based Data Expiration</a>
|
||||
*/
|
||||
int timeout();
|
||||
|
||||
/**
|
||||
* Specifies the action taken when a {@link Region} entry expires.
|
||||
*
|
||||
* This value of this attribute determines the "default" action taken if no specific action was specified.
|
||||
* The specific action is determined by {@link Expiration#action()}, {@link IdleTimeoutExpiration#action()}
|
||||
* or {@link TimeToLiveExpiration#action()} attribute on the application domain object.
|
||||
*
|
||||
* See the SDG Reference Guide for more details...
|
||||
*
|
||||
* @see <a href="http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/#bootstrap:region:expiration:annotation">Annotation-based Data Expiration</a>
|
||||
*/
|
||||
ExpirationActionType action();
|
||||
|
||||
/**
|
||||
* Names of specific {@link Region Regions} on which this Expiration policy is applied.
|
||||
*
|
||||
* If no {@link Region} names are specified then this Expiration policy will apply to
|
||||
* all {@link Region Regions} declared in the Spring context.
|
||||
*
|
||||
* Defaults to all {@link Region Regions}.
|
||||
*/
|
||||
String[] regionNames() default {};
|
||||
|
||||
/**
|
||||
* Types of Expiration algorithms (Idle Timeout (TTI) or Time to Live (TTL)) configured and used by
|
||||
* {@link Region Region(s)} to expire entries.
|
||||
*
|
||||
* Defaults to both Idle Timeout (TTI).
|
||||
*
|
||||
* @see org.springframework.data.gemfire.config.annotation.EnableExpiration.ExpirationType
|
||||
*/
|
||||
ExpirationType[] types() default { IDLE_TIMEOUT };
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ExpirationType} defines different types of GemFire/Geode Expiration policies such as
|
||||
* (Entry) Idle Timeout (TTI) and (Entry) Time to Live (TTL).
|
||||
*
|
||||
* @see <a href="http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/expiration/chapter_overview.html">GemFire Expiration</a>
|
||||
* @see <a href="http://geode.incubator.apache.org/docs/guide/developing/expiration/chapter_overview.html">Geode Expiration</a>
|
||||
*/
|
||||
enum ExpirationType {
|
||||
IDLE_TIMEOUT("TTI"),
|
||||
TIME_TO_LIVE("TTL");
|
||||
|
||||
private final String abbreviation;
|
||||
|
||||
/**
|
||||
* Factory method to lookup an appropriate {@link ExpirationType} based on an abbreviation.
|
||||
*
|
||||
* @param abbreviation abbreviation used to lookup the appropriate {@link ExpirationType}.
|
||||
* @return an {@link ExpirationType} matching the abbreviation or {@literal null} if the abbreviation
|
||||
* does not match an {@link ExpirationType}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.EnableExpiration.ExpirationType
|
||||
* @see #values()
|
||||
* @see #abbreviation()
|
||||
*/
|
||||
static ExpirationType valueOfAbbreviation(String abbreviation) {
|
||||
for (ExpirationType expirationType : values()) {
|
||||
if (expirationType.abbreviation().equalsIgnoreCase(abbreviation)) {
|
||||
return expirationType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link ExpirationType} initialized with the given abbreviation.
|
||||
*
|
||||
* @param abbreviation {@link String} indicating the {@link ExpirationType} abbreviation.
|
||||
*/
|
||||
ExpirationType(String abbreviation) {
|
||||
this.abbreviation = abbreviation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the abbreviation for this {@link ExpirationType}.
|
||||
*
|
||||
* @return a {@link String} with the {@link ExpirationType} abbreviation.
|
||||
*/
|
||||
protected String abbreviation() {
|
||||
return this.abbreviation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%1$s (%2$s)", name(), abbreviation());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,530 @@
|
||||
/*
|
||||
* 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.EnableExpiration.ExpirationPolicy;
|
||||
import static org.springframework.data.gemfire.config.annotation.EnableExpiration.ExpirationType;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.gemstone.gemfire.cache.AttributesMutator;
|
||||
import com.gemstone.gemfire.cache.ExpirationAction;
|
||||
import com.gemstone.gemfire.cache.ExpirationAttributes;
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
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.ExpirationActionType;
|
||||
import org.springframework.data.gemfire.support.AnnotationBasedExpiration;
|
||||
import org.springframework.data.gemfire.util.ArrayUtils;
|
||||
import org.springframework.data.gemfire.util.CollectionUtils;
|
||||
import org.springframework.data.gemfire.util.SpringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ExpirationConfiguration} is a Spring {@link Configuration} class used to configure expiration policies
|
||||
* for GemFire/Geode {@link Region Regions}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.context.annotation.Configuration
|
||||
* @see org.springframework.context.annotation.ImportAware
|
||||
* @see org.springframework.data.gemfire.config.annotation.EnableExpiration
|
||||
* @see com.gemstone.gemfire.cache.ExpirationAttributes
|
||||
* @see com.gemstone.gemfire.cache.Region
|
||||
* @since 1.9.0
|
||||
*/
|
||||
@Configuration
|
||||
public class ExpirationConfiguration implements ImportAware {
|
||||
|
||||
protected static final int DEFAULT_TIMEOUT = 0;
|
||||
|
||||
protected static final ExpirationActionType DEFAULT_ACTION = ExpirationActionType.DEFAULT;
|
||||
|
||||
protected static final ExpirationType[] DEFAULT_EXPIRATION_TYPES = { ExpirationType.IDLE_TIMEOUT };
|
||||
|
||||
private ExpirationPolicyConfigurer expirationPolicyConfigurer;
|
||||
|
||||
/**
|
||||
* Returns the {@link Annotation} {@link Class type} that enables and configures Expiration.
|
||||
*
|
||||
* @return {@link Annotation} {@link Class type} that enables and configures Expiration.
|
||||
* @see java.lang.annotation.Annotation
|
||||
* @see java.lang.Class
|
||||
*/
|
||||
protected Class<? extends Annotation> getAnnotationType() {
|
||||
return EnableExpiration.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the {@link Annotation} type that enables and configures Expiration.
|
||||
*
|
||||
* @return the name of the {@link Annotation} type that enables and configures Expiration.
|
||||
* @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 Expiration.
|
||||
*
|
||||
* @return the simple name of the {@link Annotation} type that enables and configures Expiration.
|
||||
* @see java.lang.Class#getSimpleName()
|
||||
* @see #getAnnotationType()
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
protected String getAnnotationTypeSimpleName() {
|
||||
return getAnnotationType().getSimpleName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
if (importMetadata.hasAnnotation(getAnnotationTypeName())) {
|
||||
Map<String, Object> enableExpirationAttributes =
|
||||
importMetadata.getAnnotationAttributes(getAnnotationTypeName());
|
||||
|
||||
AnnotationAttributes[] policies =
|
||||
(AnnotationAttributes[]) enableExpirationAttributes.get("policies");
|
||||
|
||||
for (AnnotationAttributes expirationPolicyAttributes :
|
||||
ArrayUtils.nullSafeArray(policies, AnnotationAttributes.class)) {
|
||||
|
||||
this.expirationPolicyConfigurer = ComposableExpirationPolicyConfigurer.compose(
|
||||
this.expirationPolicyConfigurer, ExpirationPolicyMetaData.from(expirationPolicyAttributes));
|
||||
}
|
||||
|
||||
this.expirationPolicyConfigurer = (this.expirationPolicyConfigurer != null ? this.expirationPolicyConfigurer
|
||||
: ExpirationPolicyMetaData.fromDefaults());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given bean is a {@link Region}.
|
||||
*
|
||||
* @param bean {@link Object} to evaluate.
|
||||
* @return a boolean value indicating whether the given bean is a {@link Region}.
|
||||
* @see com.gemstone.gemfire.cache.Region
|
||||
*/
|
||||
protected boolean isRegion(Object bean) {
|
||||
return (bean instanceof Region);
|
||||
}
|
||||
|
||||
protected ExpirationPolicyConfigurer getExpirationPolicyConfigurer() {
|
||||
Assert.state(this.expirationPolicyConfigurer != null,
|
||||
"ExpirationPolicyConfigurer was not properly configured and initialized");
|
||||
|
||||
return expirationPolicyConfigurer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("unused")
|
||||
public BeanPostProcessor expirationBeanPostProcessor() {
|
||||
return new BeanPostProcessor() {
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
return (isRegion(bean) ? getExpirationPolicyConfigurer().configure((Region<Object, Object>) bean)
|
||||
: bean);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface defining a contract for implementations that configure a {@link Region Region's} expiration policy.
|
||||
*/
|
||||
protected interface ExpirationPolicyConfigurer {
|
||||
|
||||
/**
|
||||
* Configures the expiration policy for the given {@link Region}.
|
||||
*
|
||||
* @param <K> {@link Class type} of the {@link Region} keys.
|
||||
* @param <V> {@link Class type} of the {@link Region} values.
|
||||
* @param region {@link Region} who's expiration policy will be configured.
|
||||
* @return the given {@link Region}.
|
||||
* @see com.gemstone.gemfire.cache.Region
|
||||
*/
|
||||
<K, V> Region<K, V> configure(Region<K, V> region);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ComposableExpirationPolicyConfigurer} is a {@link ExpirationPolicyConfigurer} implementation
|
||||
* that additionally implements the Composition Software Design Pattern to treat a collection of
|
||||
* {@link ExpirationPolicyConfigurer} objects as a single instace of the {@link ExpirationPolicyConfigurer}.
|
||||
*
|
||||
* @see org.springframework.data.gemfire.config.annotation.ExpirationConfiguration.ExpirationPolicyConfigurer
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Composite_pattern">Composition Software Design Pattern</a>
|
||||
*/
|
||||
protected static class ComposableExpirationPolicyConfigurer implements ExpirationPolicyConfigurer {
|
||||
|
||||
private final ExpirationPolicyConfigurer one;
|
||||
private final ExpirationPolicyConfigurer two;
|
||||
|
||||
/**
|
||||
* Factory method to compose an array of {@link ExpirationPolicyConfigurer} objects.
|
||||
*
|
||||
* @param array array of {@link ComposableExpirationPolicyConfigurer} objects to compose.
|
||||
* @return a composition containing all the {@link ExpirationPolicyConfigurer} objects in the array.
|
||||
* @see org.springframework.data.gemfire.config.annotation.ExpirationConfiguration.ExpirationPolicyConfigurer
|
||||
* @see #compose(Iterable)
|
||||
*/
|
||||
protected static ExpirationPolicyConfigurer compose(ExpirationPolicyConfigurer[] array) {
|
||||
return compose(Arrays.asList(ArrayUtils.nullSafeArray(array, ExpirationPolicyConfigurer.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to compose an {@link Iterable} of {@link ExpirationPolicyConfigurer} objects.
|
||||
*
|
||||
* @param iterable {@link Iterable} of {@link ComposableExpirationPolicyConfigurer} objects to compose.
|
||||
* @return a composition containing all the {@link ExpirationPolicyConfigurer} objects in the {@link Iterable}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.ExpirationConfiguration.ExpirationPolicyConfigurer
|
||||
* @see #compose(ExpirationPolicyConfigurer, ExpirationPolicyConfigurer)
|
||||
*/
|
||||
protected static ExpirationPolicyConfigurer compose(Iterable<ExpirationPolicyConfigurer> iterable) {
|
||||
ExpirationPolicyConfigurer current = null;
|
||||
|
||||
for (ExpirationPolicyConfigurer configurer : CollectionUtils.nullSafeIterable(iterable)) {
|
||||
current = compose(current, configurer);
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to compose 2 {@link ExpirationPolicyConfigurer} objects.
|
||||
*
|
||||
* @param one first {@link ComposableExpirationPolicyConfigurer} to compose.
|
||||
* @param two second {@link ComposableExpirationPolicyConfigurer} to compose.
|
||||
* @return a composition of the 2 {@link ExpirationPolicyConfigurer} objects.
|
||||
* Returns {@code one} if {@code two} is {@literal null} or {@code two} if {@code one} is {@literal null}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.ExpirationConfiguration.ExpirationPolicyConfigurer
|
||||
*/
|
||||
protected static ExpirationPolicyConfigurer compose(ExpirationPolicyConfigurer one,
|
||||
ExpirationPolicyConfigurer two) {
|
||||
|
||||
return (one == null ? two : (two == null ? one : new ComposableExpirationPolicyConfigurer(one, two)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of the {@link ComposableExpirationPolicyConfigurer} initialized with
|
||||
* 2 {@link ExpirationPolicyConfigurer} objects.
|
||||
*
|
||||
* @param one first {@link ComposableExpirationPolicyConfigurer} to compose.
|
||||
* @param two second {@link ComposableExpirationPolicyConfigurer} to compose.
|
||||
* @see org.springframework.data.gemfire.config.annotation.ExpirationConfiguration.ExpirationPolicyConfigurer
|
||||
*/
|
||||
private ComposableExpirationPolicyConfigurer(ExpirationPolicyConfigurer one, ExpirationPolicyConfigurer two) {
|
||||
this.one = one;
|
||||
this.two = two;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public <K, V> Region<K, V> configure(Region<K, V> region) {
|
||||
return this.two.configure(this.one.configure(region));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ExpirationPolicyMetaData} is a {@link ExpirationPolicyConfigurer} implementation that encapsulates
|
||||
* the expiration configuration meta-data (e.g. expiration timeout and action) necessary to configure
|
||||
* a {@link Region Regions's} expiration policy and behavior.
|
||||
*
|
||||
* This class is meant to capture the expiration configuration meta-data specified in the {@link ExpirationPolicy}
|
||||
* nested annotation in the application-level {@link EnableExpiration} annotation.
|
||||
|
||||
* @see org.springframework.data.gemfire.config.annotation.ExpirationConfiguration.ExpirationPolicyConfigurer
|
||||
*/
|
||||
protected static class ExpirationPolicyMetaData implements ExpirationPolicyConfigurer {
|
||||
|
||||
protected static final String[] ALL_REGIONS = new String[0];
|
||||
|
||||
private final ExpirationAttributes defaultExpirationAttributes;
|
||||
|
||||
private final Set<String> regionNames = new HashSet<String>();
|
||||
|
||||
private final Set<ExpirationType> types = new HashSet<ExpirationType>();
|
||||
|
||||
/**
|
||||
* Factory method to construct an instance of {@link ExpirationPolicyMetaData} initialized with
|
||||
* the given {@link AnnotationAttributes} from the nested {@link ExpirationPolicy} annotation
|
||||
* specified in an application-level {@link EnableExpiration} annotation.
|
||||
*
|
||||
* @param expirationPolicyAttributes {@link AnnotationAttributes} from a {@link ExpirationPolicy} annotation.
|
||||
* @return an instance of the {@link ExpirationPolicyMetaData} initialized from
|
||||
* {@link ExpirationPolicy} {@link AnnotationAttributes}.
|
||||
* @throws IllegalArgumentException if {@link AnnotationAttributes#annotationType()} is not assignable to
|
||||
* {@link ExpirationPolicy}.
|
||||
* @see #newExpirationPolicyMetaData(int, ExpirationActionType, String[], ExpirationType[])
|
||||
* @see org.springframework.core.annotation.AnnotationAttributes
|
||||
*/
|
||||
protected static ExpirationPolicyMetaData from(AnnotationAttributes expirationPolicyAttributes) {
|
||||
Assert.isAssignable(ExpirationPolicy.class, expirationPolicyAttributes.annotationType());
|
||||
|
||||
return newExpirationPolicyMetaData((Integer) expirationPolicyAttributes.get("timeout"),
|
||||
expirationPolicyAttributes.<ExpirationActionType>getEnum("action"),
|
||||
expirationPolicyAttributes.getStringArray("regionNames"),
|
||||
(ExpirationType[]) expirationPolicyAttributes.get("types"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to construct an instance of {@link ExpirationPolicyMetaData} initialized with
|
||||
* the given attribute values from the nested {@link ExpirationPolicy} annotation specified in
|
||||
* an application-level {@link EnableExpiration} annotation.
|
||||
*
|
||||
* @param expirationPolicy {@link ExpirationPolicy} annotation containing the attribute values
|
||||
* used to initialize the {@link ExpirationPolicyMetaData} instance.
|
||||
* @return an instance of the {@link ExpirationPolicyMetaData} initialized from
|
||||
* {@link ExpirationPolicy} attributes values.
|
||||
* @see org.springframework.data.gemfire.config.annotation.EnableExpiration.ExpirationPolicy
|
||||
* @see #newExpirationPolicyMetaData(int, ExpirationActionType, String[], ExpirationType[])
|
||||
*/
|
||||
protected static ExpirationPolicyMetaData from(ExpirationPolicy expirationPolicy) {
|
||||
return newExpirationPolicyMetaData(expirationPolicy.timeout(), expirationPolicy.action(),
|
||||
expirationPolicy.regionNames(), expirationPolicy.types());
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to construct an instance of {@link ExpirationPolicyMetaData} using default expiration policy
|
||||
* settings.
|
||||
*
|
||||
* @see #newExpirationPolicyMetaData(int, ExpirationActionType, String[], ExpirationType[])
|
||||
*/
|
||||
protected static ExpirationPolicyMetaData fromDefaults() {
|
||||
return newExpirationPolicyMetaData(DEFAULT_TIMEOUT, DEFAULT_ACTION, ALL_REGIONS, DEFAULT_EXPIRATION_TYPES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method used to construct a new instance of the {@link ExpirationAttributes} initialized with
|
||||
* the given expiration timeout and action that is taken when an {@link Region} entry times out.
|
||||
*
|
||||
* @param timeout int value indicating the expiration timeout in seconds.
|
||||
* @param action expiration action to take when the {@link Region} entry times out.
|
||||
* @return a new instance of {@link ExpirationAttributes} initialized with the given expiration timeout
|
||||
* and action.
|
||||
* @see com.gemstone.gemfire.cache.ExpirationAttributes
|
||||
* @see #newExpirationAttributes(int, ExpirationAction)
|
||||
*/
|
||||
protected static ExpirationAttributes newExpirationAttributes(int timeout, ExpirationActionType action) {
|
||||
return newExpirationAttributes(timeout, action.getExpirationAction());
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method used to construct a new instance of the {@link ExpirationAttributes} initialized with
|
||||
* the given expiration timeout and action that is taken when an {@link Region} entry times out.
|
||||
*
|
||||
* @param timeout int value indicating the expiration timeout in seconds.
|
||||
* @param action expiration action to take when the {@link Region} entry times out.
|
||||
* @return a new instance of {@link ExpirationAttributes} initialized with the given expiration timeout
|
||||
* and action.
|
||||
* @see com.gemstone.gemfire.cache.ExpirationAttributes
|
||||
*/
|
||||
protected static ExpirationAttributes newExpirationAttributes(int timeout, ExpirationAction action) {
|
||||
return new ExpirationAttributes(timeout, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method used to construct an instance of {@link ExpirationPolicyMetaData} initialized with
|
||||
* the given expiration policy meta-data.
|
||||
*
|
||||
* @param timeout int value indicating the expiration timeout in seconds.
|
||||
* @param action expiration action taken when the {@link Region} entry expires.
|
||||
* @param regionNames names of {@link Region Regions} configured with the expiration policy meta-data.
|
||||
* @param types type of expiration algorithm/behavior (TTI/TTL) configured for the {@link Region}.
|
||||
* @return an instance of {@link ExpirationPolicyMetaData} initialized with the given expiration policy
|
||||
* meta-data.
|
||||
* @throws IllegalArgumentException if the {@link ExpirationType} array is empty.
|
||||
* @see org.springframework.data.gemfire.config.annotation.EnableExpiration.ExpirationType
|
||||
* @see org.springframework.data.gemfire.ExpirationActionType
|
||||
* @see #ExpirationPolicyMetaData(ExpirationAttributes, Set, Set)
|
||||
* @see #newExpirationAttributes(int, ExpirationActionType)
|
||||
*/
|
||||
protected static ExpirationPolicyMetaData newExpirationPolicyMetaData(int timeout, ExpirationActionType action,
|
||||
String[] regionNames, ExpirationType[] types) {
|
||||
|
||||
return new ExpirationPolicyMetaData(newExpirationAttributes(timeout, action),
|
||||
CollectionUtils.asSet(ArrayUtils.nullSafeArray(regionNames, String.class)),
|
||||
CollectionUtils.asSet(ArrayUtils.nullSafeArray(types, ExpirationType.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link ExpirationAction} used in the expiration policy. Defaults to
|
||||
* {@link ExpirationActionType#INVALIDATE} if {@code action} is {@literal null}.
|
||||
*
|
||||
* @param action given {@link ExpirationActionType} to evaluate.
|
||||
* @return the resolved {@link ExpirationActionType} or the default if {@code action} is {@literal null}.
|
||||
* @see org.springframework.data.gemfire.ExpirationActionType
|
||||
*/
|
||||
protected static ExpirationActionType resolveAction(ExpirationActionType action) {
|
||||
return SpringUtils.defaultIfNull(action, DEFAULT_ACTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the expiration timeout used in the expiration policy. Defaults to {@literal 0} if {@code timeout}
|
||||
* is less than {@literal 0}.
|
||||
*
|
||||
* @param timeout int value expressing the expiration timeout in seconds.
|
||||
* @return the resolved expiration policy timeout.
|
||||
*/
|
||||
protected static int resolveTimeout(int timeout) {
|
||||
return Math.max(timeout, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of {@link ExpirationPolicyMetaData} initialized with the given expiration policy
|
||||
* configuraiton meta-data and {@link Region} expiration settings.
|
||||
*
|
||||
* @param timeout int value indicating the expiration timeout in seconds.
|
||||
* @param action expiration action taken when the {@link Region} entry expires.
|
||||
* @param regionNames names of {@link Region Regions} configured with the expiration policy meta-data.
|
||||
* @param types type of expiration algorithm/behavior (TTI/TTL) configured for the {@link Region}.
|
||||
* @throws IllegalArgumentException if the {@link ExpirationType} {@link Set} is empty.
|
||||
* @see org.springframework.data.gemfire.config.annotation.EnableExpiration.ExpirationType
|
||||
* @see org.springframework.data.gemfire.ExpirationActionType
|
||||
* @see #ExpirationPolicyMetaData(ExpirationAttributes, Set, Set)
|
||||
* @see #newExpirationAttributes(int, ExpirationActionType)
|
||||
* @see #resolveAction(ExpirationActionType)
|
||||
* @see #resolveTimeout(int)
|
||||
*/
|
||||
protected ExpirationPolicyMetaData(int timeout, ExpirationActionType action, Set<String> regionNames,
|
||||
Set<ExpirationType> types) {
|
||||
|
||||
this(newExpirationAttributes(resolveTimeout(timeout), resolveAction(action)), regionNames, types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of {@link ExpirationPolicyMetaData} initialized with the given expiration policy
|
||||
* configuraiton meta-data and {@link Region} expiration settings.
|
||||
*
|
||||
* @param expirationAttributes {@link ExpirationAttributes} specifying the expiration timeout in seconds
|
||||
* and expiration action taken when the {@link Region} entry expires.
|
||||
* @param regionNames names of {@link Region Regions} configured with the expiration policy meta-data.
|
||||
* @param types type of expiration algorithm/behaviors (TTI/TTL) configured for the {@link Region}.
|
||||
* @throws IllegalArgumentException if the {@link ExpirationType} {@link Set} is empty.
|
||||
* @see org.springframework.data.gemfire.config.annotation.EnableExpiration.ExpirationType
|
||||
* @see com.gemstone.gemfire.cache.ExpirationAttributes
|
||||
*/
|
||||
protected ExpirationPolicyMetaData(ExpirationAttributes expirationAttributes, Set<String> regionNames,
|
||||
Set<ExpirationType> types) {
|
||||
|
||||
Assert.notEmpty(types, "At least one ExpirationPolicy type [TTI, TTL] must be specified");
|
||||
|
||||
this.defaultExpirationAttributes = expirationAttributes;
|
||||
this.regionNames.addAll(CollectionUtils.nullSafeSet(regionNames));
|
||||
this.types.addAll(CollectionUtils.nullSafeSet(types));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to apply this expiration policy to the given {@link Region}.
|
||||
*
|
||||
* @param region {@link Region} to evaluate.
|
||||
* @return a boolean value indicating whether the expiration policy applies to the given {@link Region}.
|
||||
* @see com.gemstone.gemfire.cache.Region
|
||||
* @see #accepts(String)
|
||||
*/
|
||||
protected boolean accepts(Region region) {
|
||||
return (region != null && accepts(region.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to apply this expiration policy to the given {@link Region} identified by name.
|
||||
*
|
||||
* @param regionName name of the {@link Region} to evaluate.
|
||||
* @return a boolean value indicating whether the expiration policy applies to the given {@link Region}
|
||||
* identified by name.
|
||||
*/
|
||||
protected boolean accepts(String regionName) {
|
||||
return (this.regionNames.isEmpty() || this.regionNames.contains(regionName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether Idle Timeout Expiration (TTI) was configured for this expiration policy.
|
||||
*
|
||||
* @return a boolean value indicating whether Idle Timeout Expiration (TTI) was configuration for
|
||||
* this expiration policy.
|
||||
*/
|
||||
protected boolean isIdleTimeout() {
|
||||
return this.types.contains(ExpirationType.IDLE_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether Time-To-Live Expiration (TTL) was configured for this expiration policy.
|
||||
*
|
||||
* @return a boolean value indicating whether Time-To-Live Expiration (TTL) was configuration for
|
||||
* this expiration policy.
|
||||
*/
|
||||
protected boolean isTimeToLive() {
|
||||
return this.types.contains(ExpirationType.TIME_TO_LIVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public <K, V> Region<K, V> configure(Region<K, V> region) {
|
||||
if (accepts(region)) {
|
||||
AttributesMutator<K, V> regionAttributesMutator = region.getAttributesMutator();
|
||||
|
||||
ExpirationAttributes defaultExpirationAttributes = defaultExpirationAttributes();
|
||||
|
||||
if (isIdleTimeout()) {
|
||||
regionAttributesMutator.setCustomEntryIdleTimeout(
|
||||
AnnotationBasedExpiration.<K, V>forIdleTimeout(defaultExpirationAttributes));
|
||||
}
|
||||
|
||||
if (isTimeToLive()) {
|
||||
regionAttributesMutator.setCustomEntryTimeToLive(
|
||||
AnnotationBasedExpiration.<K, V>forTimeToLive(defaultExpirationAttributes));
|
||||
}
|
||||
}
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default, fallback {@link ExpirationAttributes}.
|
||||
*
|
||||
* @return an {@link ExpirationAttributes} containing the defaults.
|
||||
* @see com.gemstone.gemfire.cache.ExpirationAttributes
|
||||
*/
|
||||
protected ExpirationAttributes defaultExpirationAttributes() {
|
||||
return this.defaultExpirationAttributes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,11 @@ package org.springframework.data.gemfire.support;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import com.gemstone.gemfire.cache.CustomExpiry;
|
||||
import com.gemstone.gemfire.cache.ExpirationAction;
|
||||
import com.gemstone.gemfire.cache.ExpirationAttributes;
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
@@ -42,16 +47,11 @@ import org.springframework.expression.spel.support.StandardTypeLocator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.gemstone.gemfire.cache.CustomExpiry;
|
||||
import com.gemstone.gemfire.cache.ExpirationAction;
|
||||
import com.gemstone.gemfire.cache.ExpirationAttributes;
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
|
||||
/**
|
||||
* The AnnotationBasedExpiration class is an implementation of GemFire's CustomExpiry interface that determines
|
||||
* the Time-To-Live (TTL) or Idle-Timeout (TTI) expiration policy of a Region entry by introspecting the Region
|
||||
* entry's class type and reflecting on any Region entries annotated with Spring Data GemFire's Expiration-based
|
||||
* Annotations.
|
||||
* The {@link AnnotationBasedExpiration} class is an implementation of the {@link CustomExpiry} interface
|
||||
* that determines the Time-To-Live (TTL) or Idle-Timeout (TTI) expiration policy of a {@link Region} entry
|
||||
* by introspecting the {@link Region} entry's class type and reflecting on any {@link Region} entries annotated
|
||||
* with SDG's Expiration-based Annotations.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.lang.annotation.Annotation
|
||||
@@ -65,12 +65,14 @@ import com.gemstone.gemfire.cache.Region;
|
||||
* @see com.gemstone.gemfire.cache.ExpirationAction
|
||||
* @see com.gemstone.gemfire.cache.ExpirationAttributes
|
||||
* @see com.gemstone.gemfire.cache.Region
|
||||
* @see <a href="http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/#bootstrap:region:expiration:annotation">Annotation-based Data Expiration</a>
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, CustomExpiry<K, V> {
|
||||
|
||||
protected static final AtomicReference<BeanFactory> BEAN_FACTORY_REFERENCE = new AtomicReference<BeanFactory>(null);
|
||||
protected static final AtomicReference<BeanFactory> BEAN_FACTORY_REFERENCE =
|
||||
new AtomicReference<BeanFactory>(null);
|
||||
|
||||
protected static final AtomicReference<StandardEvaluationContext> EVALUATION_CONTEXT_REFERENCE
|
||||
= new AtomicReference<StandardEvaluationContext>(null);
|
||||
@@ -86,7 +88,7 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the AnnotationBasedExpiration class initialized with a specific, default
|
||||
* Constructs a new instance of {@link AnnotationBasedExpiration} initialized with a specific, default
|
||||
* expiration policy.
|
||||
*
|
||||
* @param defaultExpirationAttributes expiration settings used as the default expiration policy.
|
||||
@@ -97,12 +99,14 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an AnnotationBasedExpiration instance with no default ExpirationAttributes to process
|
||||
* Idle Timeout (TTI) Expiration annotated Region Entries.
|
||||
* Factory method used to construct an instance of {@link AnnotationBasedExpiration} having no default
|
||||
* {@link ExpirationAttributes} to process expired annotated {@link Region} entries
|
||||
* using Idle Timeout (TTI) Expiration.
|
||||
*
|
||||
* @param <K> the class type of the Region Entry Key.
|
||||
* @param <V> the class type of the Region Entry Value.
|
||||
* @return an AnnotationBasedExpiration instance to process Idle Timeout Expiration annotated Region Entries.
|
||||
* @param <K> {@link Class} type of the {@link Region} entry key.
|
||||
* @param <V> {@link Class} type of the {@link Region} entry value.
|
||||
* @return an {@link AnnotationBasedExpiration} instance to process expired annotated {@link Region} entries
|
||||
* using Idle Timeout expiration.
|
||||
* @see org.springframework.data.gemfire.support.AnnotationBasedExpiration
|
||||
* @see org.springframework.data.gemfire.support.IdleTimeoutExpiration
|
||||
* @see #forIdleTimeout(com.gemstone.gemfire.cache.ExpirationAttributes)
|
||||
@@ -112,20 +116,23 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an AnnotationBasedExpiration instance with default ExpirationAttributes to process
|
||||
* Idle Timeout (TTI) Expiration annotated Region Entries.
|
||||
* Factory method used to construct an instance of {@link AnnotationBasedExpiration} initialized with
|
||||
* default {@link ExpirationAttributes} to process expired annotated {@link Region} entries
|
||||
* using Idle Timeout (TTI) expiration.
|
||||
*
|
||||
* @param <K> the class type of the Region Entry Key.
|
||||
* @param <V> the class type of the Region Entry Value.
|
||||
* @param defaultExpirationAttributes ExpirationAttributes used by default if no Expiration policy was specified
|
||||
* on the Region Entry.
|
||||
* @return an AnnotationBasedExpiration instance to process Idle Timeout Expiration annotated Region Entries.
|
||||
* @param <K> {@link Class} type of the {@link Region} entry key.
|
||||
* @param <V> {@link Class} type of the {@link Region} entry value.
|
||||
* @param defaultExpirationAttributes {@link ExpirationAttributes} used by default if no expiration policy
|
||||
* was specified on the {@link Region}.
|
||||
* @return an {@link AnnotationBasedExpiration} instance to process expired annotated {@link Region} entries
|
||||
* using Idle Timeout expiration.
|
||||
* @see org.springframework.data.gemfire.support.AnnotationBasedExpiration
|
||||
* @see org.springframework.data.gemfire.support.IdleTimeoutExpiration
|
||||
* @see #AnnotationBasedExpiration(ExpirationAttributes)
|
||||
*/
|
||||
public static <K, V> AnnotationBasedExpiration<K, V> forIdleTimeout(ExpirationAttributes defaultExpirationAttributes) {
|
||||
return new AnnotationBasedExpiration<K, V>(defaultExpirationAttributes) {
|
||||
@Override protected ExpirationMetaData getExpirationMetaData(final Region.Entry<K, V> entry) {
|
||||
@Override protected ExpirationMetaData getExpirationMetaData(Region.Entry<K, V> entry) {
|
||||
return (isIdleTimeoutConfigured(entry) ? ExpirationMetaData.from(getIdleTimeout(entry))
|
||||
: super.getExpirationMetaData(entry));
|
||||
}
|
||||
@@ -133,42 +140,50 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an AnnotationBasedExpiration instance with no default ExpirationAttributes to process
|
||||
* Time-To-Live (TTL) Expiration annotated Region Entries.
|
||||
* Factory method used to construct an instance of {@link AnnotationBasedExpiration} having no default
|
||||
* {@link ExpirationAttributes} to process expired annotated {@link Region} entries
|
||||
* using Time-To-Live (TTL) Expiration.
|
||||
*
|
||||
* @param <K> the class type of the Region Entry Key.
|
||||
* @param <V> the class type of the Region Entry Value.
|
||||
* @return an AnnotationBasedExpiration instance to process Time-To-Live Expiration annotated Region Entries.
|
||||
* @param <K> {@link Class} type of the {@link Region} entry key.
|
||||
* @param <V> {@link Class} type of the {@link Region} entry value.
|
||||
* @return an {@link AnnotationBasedExpiration} instance to process expired annotated {@link Region} entries
|
||||
* using Time-To-Live expiration.
|
||||
* @see org.springframework.data.gemfire.support.AnnotationBasedExpiration
|
||||
* @see org.springframework.data.gemfire.support.TimeToLiveExpiration
|
||||
* @see #forTimeToLive(com.gemstone.gemfire.cache.ExpirationAttributes)
|
||||
* @see #forTimeToLive(ExpirationAttributes)
|
||||
*/
|
||||
public static <K, V> AnnotationBasedExpiration<K, V> forTimeToLive() {
|
||||
return forTimeToLive(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an AnnotationBasedExpiration instance with default ExpirationAttributes to process
|
||||
* Time-To-Live (TTL) Expiration annotated Region Entries.
|
||||
* Factory method used to construct an instance of {@link AnnotationBasedExpiration} initialized with
|
||||
* default {@link ExpirationAttributes} to process expired annotated {@link Region} entries
|
||||
* using Time-To-Live (TTL) expiration.
|
||||
*
|
||||
* @param <K> the class type of the Region Entry Key.
|
||||
* @param <V> the class type of the Region Entry Value.
|
||||
* @param defaultExpirationAttributes ExpirationAttributes used by default if no Expiration policy was specified
|
||||
* on the Region Entry.
|
||||
* @return an AnnotationBasedExpiration instance to process Time-To-Live Expiration annotated Region Entries.
|
||||
* @param <K> {@link Class} type of the {@link Region} entry key.
|
||||
* @param <V> {@link Class} type of the {@link Region} entry value.
|
||||
* @param defaultExpirationAttributes {@link ExpirationAttributes} used by default if no expiration policy
|
||||
* was specified on the {@link Region}.
|
||||
* @return an {@link AnnotationBasedExpiration} instance to process expired annotated {@link Region} entries
|
||||
* using Time-To-Live expiration.
|
||||
* @see org.springframework.data.gemfire.support.AnnotationBasedExpiration
|
||||
* @see org.springframework.data.gemfire.support.TimeToLiveExpiration
|
||||
* @see #AnnotationBasedExpiration(ExpirationAttributes)
|
||||
*/
|
||||
public static <K, V> AnnotationBasedExpiration<K, V> forTimeToLive(ExpirationAttributes defaultExpirationAttributes) {
|
||||
return new AnnotationBasedExpiration<K, V>(defaultExpirationAttributes) {
|
||||
@Override protected ExpirationMetaData getExpirationMetaData(final Region.Entry<K, V> entry) {
|
||||
@Override protected ExpirationMetaData getExpirationMetaData(Region.Entry<K, V> entry) {
|
||||
return (isTimeToLiveConfigured(entry) ? ExpirationMetaData.from(getTimeToLive(entry))
|
||||
: super.getExpirationMetaData(entry));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
/**
|
||||
* Initializes the Spring Expression Language (SpEL) {@link EvaluationContext} used to parse property placeholder
|
||||
* and SpEL expressions in the Expiration annotation attribute values.
|
||||
*/
|
||||
protected void initEvaluationContext() {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
|
||||
@@ -182,9 +197,11 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
if (beanFactory instanceof ConfigurableBeanFactory) {
|
||||
ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
|
||||
ConversionService conversionService = configurableBeanFactory.getConversionService();
|
||||
|
||||
if (conversionService != null) {
|
||||
evaluationContext.setTypeConverter(new StandardTypeConverter(conversionService));
|
||||
}
|
||||
|
||||
evaluationContext.setTypeLocator(new StandardTypeLocator(configurableBeanFactory.getBeanClassLoader()));
|
||||
}
|
||||
}
|
||||
@@ -198,10 +215,10 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the BeanFactory managing this AnnotationBasedExpiration bean in the Spring context.
|
||||
* Sets the {@link BeanFactory} managing this {@link AnnotationBasedExpiration} bean in the Spring context.
|
||||
*
|
||||
* @param beanFactory the Spring BeanFactory to which this bean belongs.
|
||||
* @throws BeansException if the BeanFactory reference cannot be initialized.
|
||||
* @param beanFactory the Spring {@link BeanFactory} to which this bean belongs.
|
||||
* @throws BeansException if the {@link BeanFactory} reference cannot be initialized.
|
||||
* @see org.springframework.beans.factory.BeanFactory
|
||||
* @see #initEvaluationContext()
|
||||
*/
|
||||
@@ -212,10 +229,11 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the BeanFactory in which this AnnotationBasedExpiration Bean belongs.
|
||||
* Gets a reference to the Spring {@link BeanFactory} in which this {@link AnnotationBasedExpiration} bean
|
||||
* is managed.
|
||||
*
|
||||
* @return a reference to the Spring BeanFactory.
|
||||
* @throws java.lang.IllegalStateException if the BeanFactory reference was not properly initialized.
|
||||
* @return a reference to the Spring {@link BeanFactory}.
|
||||
* @throws java.lang.IllegalStateException if the {@link BeanFactory} reference was not properly initialized.
|
||||
* @see org.springframework.beans.factory.BeanFactory
|
||||
*/
|
||||
protected BeanFactory getBeanFactory() {
|
||||
@@ -250,12 +268,16 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the expiration for a given entry. Returning null indicates that the default for the Region
|
||||
* should be used. The entry parameter should not be used after this method invocation completes.
|
||||
* Calculate the expiration for a given entry. Returning {@literal null} indicates that the default
|
||||
* for the {@link Region} should be used. The entry parameter should not be used after this method
|
||||
* invocation completes.
|
||||
*
|
||||
* @param entry the entry to calculate the expiration for.
|
||||
* @return the expiration to be used, null if the Region's defaults should be used.
|
||||
* @param entry the entry used to determine the appropriate expiration policy.
|
||||
* @return the expiration configuration to be used or {@literal null} if the Region's defaults should be used.
|
||||
* @see com.gemstone.gemfire.cache.ExpirationAttributes
|
||||
* @see com.gemstone.gemfire.cache.Region
|
||||
* @see #getExpirationMetaData(Region.Entry)
|
||||
* @see #newExpirationAttributes(ExpirationMetaData)
|
||||
*/
|
||||
@Override
|
||||
public ExpirationAttributes getExpiry(Region.Entry<K, V> entry) {
|
||||
@@ -263,11 +285,11 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets custom expiration (Annotation-based) meta-data for the given Region entry.
|
||||
* Gets custom expiration (Annotation-based) policy meta-data for the given {@link Region} entry.
|
||||
*
|
||||
* @param entry the Region entry used as the source of the expiration meta-data.
|
||||
* @return ExpirationMetaData extracted from the Region entry.
|
||||
* @throws NullPointerException if the Region.Entry, Region or the Region's attributes are null.
|
||||
* @param entry {@link Region} entry used as the source of the expiration policy meta-data.
|
||||
* @return {@link ExpirationMetaData} extracted from the {@link Region} entry or {@literal null}
|
||||
* if the expiration policy meta-data could not be determined from the {@link Region} entry.
|
||||
* @see org.springframework.data.gemfire.support.AnnotationBasedExpiration.ExpirationMetaData
|
||||
*/
|
||||
protected ExpirationMetaData getExpirationMetaData(Region.Entry<K, V> entry) {
|
||||
@@ -275,15 +297,15 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of ExpirationAttributes configured with the application domain object specific
|
||||
* expiration policy. If the application domain object type has not been annotated with custom expiration
|
||||
* meta-data, then the default expiration settings are used.
|
||||
* Constructs a new instance of {@link ExpirationAttributes} configured with the application domain object
|
||||
* specific expiration policy. If the application domain object type has not been annotated with
|
||||
* custom expiration meta-data, then the default expiration settings are used.
|
||||
*
|
||||
* @param expirationMetaData application domain object specific expiration policy meta-data used to construct
|
||||
* the ExpirationAttributes.
|
||||
* @return a custom ExpirationAttributes configured with the application domain object specific expiration policy
|
||||
* or the default expiration settings if the application domain object has not been annotated with custom
|
||||
* expiration meta-data.
|
||||
* the {@link ExpirationAttributes}.
|
||||
* @return custom {@link ExpirationAttributes} configured from the application domain object specific
|
||||
* expiration policy or the default expiration settings if the application domain object has not been
|
||||
* annotated with custom expiration meta-data.
|
||||
* @see org.springframework.data.gemfire.support.AnnotationBasedExpiration.ExpirationMetaData
|
||||
* @see com.gemstone.gemfire.cache.ExpirationAttributes
|
||||
* @see #getDefaultExpirationAttributes()
|
||||
@@ -302,7 +324,12 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
* @see #isAnnotationPresent(Object, Class)
|
||||
*/
|
||||
protected boolean isExpirationConfigured(Region.Entry<K, V> entry) {
|
||||
return isAnnotationPresent(entry.getValue(), Expiration.class);
|
||||
return (entry != null && isExpirationConfigured(entry.getValue()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private boolean isExpirationConfigured(Object obj) {
|
||||
return isAnnotationPresent(obj, Expiration.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,7 +342,12 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
* @see #getAnnotation(Object, Class)
|
||||
*/
|
||||
protected Expiration getExpiration(Region.Entry<K, V> entry) {
|
||||
return getAnnotation(entry.getValue(), Expiration.class);
|
||||
return getExpiration(entry.getValue());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private Expiration getExpiration(Object obj) {
|
||||
return getAnnotation(obj, Expiration.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -327,7 +359,12 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
* @see #isAnnotationPresent(Object, Class)
|
||||
*/
|
||||
protected boolean isIdleTimeoutConfigured(Region.Entry<K, V> entry) {
|
||||
return isAnnotationPresent(entry.getValue(), IdleTimeoutExpiration.class);
|
||||
return (entry != null && isIdleTimeoutConfigured(entry.getValue()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private boolean isIdleTimeoutConfigured(Object obj) {
|
||||
return isAnnotationPresent(obj, IdleTimeoutExpiration.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,7 +377,12 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
* @see #getAnnotation(Object, Class)
|
||||
*/
|
||||
protected IdleTimeoutExpiration getIdleTimeout(Region.Entry<K, V> entry) {
|
||||
return getAnnotation(entry.getValue(), IdleTimeoutExpiration.class);
|
||||
return getIdleTimeout(entry.getValue());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private IdleTimeoutExpiration getIdleTimeout(Object obj) {
|
||||
return getAnnotation(obj, IdleTimeoutExpiration.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -352,7 +394,12 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
* @see #isAnnotationPresent(Object, Class)
|
||||
*/
|
||||
protected boolean isTimeToLiveConfigured(Region.Entry<K, V> entry) {
|
||||
return isAnnotationPresent(entry.getValue(), TimeToLiveExpiration.class);
|
||||
return (entry != null && isTimeToLiveConfigured(entry.getValue()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private boolean isTimeToLiveConfigured(Object value) {
|
||||
return isAnnotationPresent(value, TimeToLiveExpiration.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -365,7 +412,12 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
* @see #getAnnotation(Object, Class)
|
||||
*/
|
||||
protected TimeToLiveExpiration getTimeToLive(Region.Entry<K, V> entry) {
|
||||
return getAnnotation(entry.getValue(), TimeToLiveExpiration.class);
|
||||
return getTimeToLive(entry.getValue());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private TimeToLiveExpiration getTimeToLive(Object obj) {
|
||||
return getAnnotation(obj, TimeToLiveExpiration.class);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@@ -400,32 +452,39 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
|
||||
private final ExpirationActionType action;
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected ExpirationMetaData(int timeout, ExpirationActionType action) {
|
||||
this.timeout = timeout;
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static ExpirationMetaData from(ExpirationAttributes expirationAttributes) {
|
||||
return new ExpirationMetaData(expirationAttributes.getTimeout(), ExpirationActionType.valueOf(
|
||||
expirationAttributes.getAction()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static ExpirationMetaData from(Expiration expiration) {
|
||||
return new ExpirationMetaData(parseTimeout(expiration.timeout()), parseAction(expiration.action()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static ExpirationMetaData from(IdleTimeoutExpiration expiration) {
|
||||
return new ExpirationMetaData(parseTimeout(expiration.timeout()), parseAction(expiration.action()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static ExpirationMetaData from(TimeToLiveExpiration expiration) {
|
||||
return new ExpirationMetaData(parseTimeout(expiration.timeout()), parseAction(expiration.action()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public ExpirationAttributes toExpirationAttributes() {
|
||||
return new ExpirationAttributes(timeout(), expirationAction());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static int parseTimeout(String timeout) {
|
||||
try {
|
||||
return Integer.parseInt(timeout);
|
||||
@@ -448,6 +507,7 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static ExpirationActionType parseAction(String action) {
|
||||
try {
|
||||
return ExpirationActionType.valueOf(EXPIRATION_ACTION_CONVERTER.convert(action));
|
||||
@@ -455,7 +515,7 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
catch (IllegalArgumentException cause) {
|
||||
// Next, try to parse the 'action' as a Spring Expression using SpEL.
|
||||
EvaluationException evaluationException = new EvaluationException(String.format(
|
||||
"'%1$s' is not resolvable as a valid ExpirationAction(Type)", action), cause);
|
||||
"[%s] is not resolvable as an ExpirationAction(Type)", action), cause);
|
||||
|
||||
EvaluationContext evaluationContext = EVALUATION_CONTEXT_REFERENCE.get();
|
||||
|
||||
@@ -494,18 +554,24 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public ExpirationActionType action() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public ExpirationAction expirationAction() {
|
||||
return action().getExpirationAction();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int timeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj == this) {
|
||||
@@ -522,6 +588,9 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
&& ObjectUtils.nullSafeEquals(this.action(), that.action()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashValue = 17;
|
||||
@@ -530,11 +599,13 @@ public class AnnotationBasedExpiration<K, V> implements BeanFactoryAware, Custom
|
||||
return hashValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{ @type = %1$s, timeout = %2$d, action = %3$s }", getClass().getName(),
|
||||
timeout(), action());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* 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.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.data.gemfire.config.annotation.EnableExpiration.ExpirationPolicy;
|
||||
import static org.springframework.data.gemfire.config.annotation.EnableExpiration.ExpirationType;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import com.gemstone.gemfire.cache.AttributesMutator;
|
||||
import com.gemstone.gemfire.cache.CustomExpiry;
|
||||
import com.gemstone.gemfire.cache.ExpirationAction;
|
||||
import com.gemstone.gemfire.cache.ExpirationAttributes;
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
import com.gemstone.gemfire.cache.RegionAttributes;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.gemfire.ExpirationActionType;
|
||||
import org.springframework.data.gemfire.util.ArrayUtils;
|
||||
|
||||
/**
|
||||
* Unit tests for the {@link EnableExpiration} annotation and {@link ExpirationConfiguration} class.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.springframework.context.ConfigurableApplicationContext
|
||||
* @see org.springframework.data.gemfire.config.annotation.EnableExpiration
|
||||
* @see org.springframework.data.gemfire.config.annotation.ExpirationConfiguration
|
||||
* @see com.gemstone.gemfire.cache.CustomExpiry
|
||||
* @see com.gemstone.gemfire.cache.ExpirationAttributes
|
||||
* @see com.gemstone.gemfire.cache.Region
|
||||
* @since 1.9.0
|
||||
*/
|
||||
public class EnableExpirationConfigurationUnitTests {
|
||||
|
||||
private ConfigurableApplicationContext applicationContext;
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (applicationContext != null) {
|
||||
applicationContext.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected <K, V> void assertRegionExpiration(ExpirationAttributes expectedExpirationAttributes,
|
||||
Region<K, V> region, V... applicationDomainObjects) {
|
||||
|
||||
assertIdleTimeoutExpiration(expectedExpirationAttributes, region, applicationDomainObjects);
|
||||
assertTimeToLiveExpiration(expectedExpirationAttributes, region, applicationDomainObjects);
|
||||
}
|
||||
|
||||
protected <K, V> void assertIdleTimeoutExpiration(ExpirationAttributes expectedExpirationAttributes,
|
||||
Region<K, V> region, V... applicationDomainObjects) {
|
||||
|
||||
assertExpiration(expectedExpirationAttributes, region.getAttributes().getCustomEntryIdleTimeout(),
|
||||
applicationDomainObjects);
|
||||
}
|
||||
|
||||
protected <K, V> void assertNoIdleTimeoutExpiration(Region<K, V> region) {
|
||||
assertThat(region.getAttributes().getCustomEntryIdleTimeout()).isNull();
|
||||
}
|
||||
|
||||
protected <K, V> void assertTimeToLiveExpiration(ExpirationAttributes expectedExpirationAttributes,
|
||||
Region<K, V> region, V... applicationDomainObjects) {
|
||||
|
||||
assertExpiration(expectedExpirationAttributes, region.getAttributes().getCustomEntryTimeToLive(),
|
||||
applicationDomainObjects);
|
||||
}
|
||||
|
||||
protected <K, V> void assertNoTimeToLiveExpiration(Region<K, V> region) {
|
||||
assertThat(region.getAttributes().getCustomEntryTimeToLive()).isNull();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <K, V> void assertExpiration(ExpirationAttributes expectedExpirationAttributes,
|
||||
CustomExpiry<K, V> customExpiry, V... applicationDomainObjects) {
|
||||
|
||||
Region.Entry<K, V> regionEntry = mockRegionEntry(ArrayUtils.getFirst(applicationDomainObjects));
|
||||
|
||||
assertExpiration(customExpiry.getExpiry(regionEntry), expectedExpirationAttributes);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "unused" })
|
||||
protected <K, V> void assertExpiration(ExpirationAttributes actualExpirationAttributes,
|
||||
ExpirationAttributes expectedExpirationAttributes) {
|
||||
|
||||
assertThat(actualExpirationAttributes).isEqualTo(expectedExpirationAttributes);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <K, V> Region<K, V> getRegion(String beanName) {
|
||||
return applicationContext.getBean(beanName, Region.class);
|
||||
}
|
||||
|
||||
protected AnnotationConfigApplicationContext newApplicationContext(Class<?>... annotatedClasses) {
|
||||
return new AnnotationConfigApplicationContext(annotatedClasses);
|
||||
}
|
||||
|
||||
protected ExpirationAttributes newExpirationAttributes(int timeout, ExpirationActionType action) {
|
||||
return newExpirationAttributes(timeout, action.getExpirationAction());
|
||||
}
|
||||
|
||||
protected ExpirationAttributes newExpirationAttributes(int timeout, ExpirationAction action) {
|
||||
return new ExpirationAttributes(timeout, action);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesDefaultExpirationPolicyConfiguration() {
|
||||
applicationContext = newApplicationContext(DefaultExpirationPolicyConfiguration.class);
|
||||
|
||||
ExpirationAttributes expectedExpiration = newExpirationAttributes(0, ExpirationActionType.INVALIDATE);
|
||||
|
||||
Region one = getRegion("One");
|
||||
Region two = getRegion("Two");
|
||||
|
||||
assertIdleTimeoutExpiration(expectedExpiration, one);
|
||||
assertIdleTimeoutExpiration(expectedExpiration, two);
|
||||
|
||||
assertNoTimeToLiveExpiration(one);
|
||||
assertNoTimeToLiveExpiration(two);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesCustomExpirationPolicyConfiguration() {
|
||||
applicationContext = newApplicationContext(CustomExpirationPolicyConfiguration.class);
|
||||
|
||||
ExpirationAttributes expectedExpiration = newExpirationAttributes(300, ExpirationActionType.LOCAL_DESTROY);
|
||||
|
||||
Region one = getRegion("One");
|
||||
Region two = getRegion("Two");
|
||||
|
||||
assertIdleTimeoutExpiration(expectedExpiration, one);
|
||||
assertIdleTimeoutExpiration(expectedExpiration, two);
|
||||
|
||||
assertNoTimeToLiveExpiration(one);
|
||||
assertNoTimeToLiveExpiration(two);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesRegionSpecificExpirationPolicyConfiguration() {
|
||||
applicationContext = newApplicationContext(RegionSpecificExpirationPolicyConfiguration.class);
|
||||
|
||||
ExpirationAttributes expectedIdleTimeout = newExpirationAttributes(180, ExpirationActionType.INVALIDATE);
|
||||
ExpirationAttributes expectedTimeToLive = newExpirationAttributes(360, ExpirationActionType.DESTROY);
|
||||
|
||||
Region one = getRegion("One");
|
||||
Region two = getRegion("Two");
|
||||
|
||||
assertIdleTimeoutExpiration(expectedIdleTimeout, one);
|
||||
assertNoIdleTimeoutExpiration(two);
|
||||
|
||||
assertNoTimeToLiveExpiration(one);
|
||||
assertTimeToLiveExpiration(expectedTimeToLive, two);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesMixedExpirationPolicyConfiguration() {
|
||||
applicationContext = newApplicationContext(MixedExpirationPolicyConfiguration.class);
|
||||
|
||||
ExpirationAttributes expectedIdleTimeout = newExpirationAttributes(120, ExpirationActionType.LOCAL_INVALIDATE);
|
||||
ExpirationAttributes expectedTimeToLive = newExpirationAttributes(600, ExpirationActionType.DESTROY);
|
||||
|
||||
Region one = getRegion("One");
|
||||
Region two = getRegion("Two");
|
||||
|
||||
assertIdleTimeoutExpiration(expectedIdleTimeout, one);
|
||||
assertNoIdleTimeoutExpiration(two);
|
||||
|
||||
assertTimeToLiveExpiration(expectedTimeToLive, one);
|
||||
assertTimeToLiveExpiration(expectedTimeToLive, two);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <K, V> Region<K, V> mockRegion(String name) {
|
||||
Region<K, V> mockRegion = mock(Region.class);
|
||||
|
||||
when(mockRegion.getName()).thenReturn(name);
|
||||
when(mockRegion.getFullPath()).thenReturn(String.format("%1$s%2$s", Region.SEPARATOR, name));
|
||||
|
||||
final AtomicReference<CustomExpiry<K, V>> entryIdleTimeout = new AtomicReference<CustomExpiry<K, V>>(null);
|
||||
final AtomicReference<CustomExpiry<K, V>> entryTimeToLive = new AtomicReference<CustomExpiry<K, V>>(null);
|
||||
|
||||
AttributesMutator<K, V> mockAttributesMutator = mock(AttributesMutator.class);
|
||||
|
||||
when(mockRegion.getAttributesMutator()).thenReturn(mockAttributesMutator);
|
||||
|
||||
doAnswer(new Answer<CustomExpiry<K, V>>() {
|
||||
@Override
|
||||
public CustomExpiry<K, V> answer(InvocationOnMock invocation) throws Throwable {
|
||||
CustomExpiry<K, V> customExpiry = invocation.getArgumentAt(0, CustomExpiry.class);
|
||||
entryIdleTimeout.set(customExpiry);
|
||||
return customExpiry;
|
||||
}
|
||||
}).when(mockAttributesMutator).setCustomEntryIdleTimeout(any(CustomExpiry.class));
|
||||
|
||||
doAnswer(new Answer<CustomExpiry<K, V>>() {
|
||||
@Override
|
||||
public CustomExpiry<K, V> answer(InvocationOnMock invocation) throws Throwable {
|
||||
CustomExpiry<K, V> customExpiry = invocation.getArgumentAt(0, CustomExpiry.class);
|
||||
entryTimeToLive.set(customExpiry);
|
||||
return customExpiry;
|
||||
}
|
||||
}).when(mockAttributesMutator).setCustomEntryTimeToLive(any(CustomExpiry.class));
|
||||
|
||||
RegionAttributes<K, V> mockRegionAttributes = mock(RegionAttributes.class);
|
||||
|
||||
when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes);
|
||||
|
||||
when(mockRegionAttributes.getCustomEntryIdleTimeout()).thenAnswer(new Answer<CustomExpiry<K, V>>() {
|
||||
@Override public CustomExpiry<K, V> answer(InvocationOnMock invocation) throws Throwable {
|
||||
return entryIdleTimeout.get();
|
||||
}
|
||||
});
|
||||
|
||||
when(mockRegionAttributes.getCustomEntryTimeToLive()).thenAnswer(new Answer<CustomExpiry<K, V>>() {
|
||||
@Override public CustomExpiry<K, V> answer(InvocationOnMock invocation) throws Throwable {
|
||||
return entryTimeToLive.get();
|
||||
}
|
||||
});
|
||||
|
||||
return mockRegion;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <K, V> Region.Entry<K, V> mockRegionEntry(V applicationDomainObject) {
|
||||
Region.Entry<K, V> mockRegionEntry = mock(Region.Entry.class);
|
||||
when(mockRegionEntry.getValue()).thenReturn(applicationDomainObject);
|
||||
return mockRegionEntry;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@SuppressWarnings("unused")
|
||||
static class RegionConfiguration {
|
||||
|
||||
@Bean("One")
|
||||
Region<Object, Object> regionOne() {
|
||||
return mockRegion("One");
|
||||
}
|
||||
|
||||
@Bean("Two")
|
||||
Region<Object, Object> regionTwo() {
|
||||
return mockRegion("Two");
|
||||
}
|
||||
}
|
||||
|
||||
@EnableExpiration
|
||||
static class DefaultExpirationPolicyConfiguration extends RegionConfiguration {
|
||||
}
|
||||
|
||||
@EnableExpiration(policies = { @ExpirationPolicy(timeout = 300, action = ExpirationActionType.LOCAL_DESTROY) })
|
||||
static class CustomExpirationPolicyConfiguration extends RegionConfiguration {
|
||||
}
|
||||
|
||||
@EnableExpiration(policies = {
|
||||
@ExpirationPolicy(timeout = 180, action = ExpirationActionType.INVALIDATE, regionNames = "One"),
|
||||
@ExpirationPolicy(timeout = 360, action = ExpirationActionType.DESTROY, regionNames = "Two", types = ExpirationType.TIME_TO_LIVE)
|
||||
})
|
||||
static class RegionSpecificExpirationPolicyConfiguration extends RegionConfiguration {
|
||||
}
|
||||
|
||||
@EnableExpiration(policies = {
|
||||
@ExpirationPolicy(timeout = 120, action = ExpirationActionType.LOCAL_INVALIDATE, regionNames = "One", types = ExpirationType.IDLE_TIMEOUT),
|
||||
@ExpirationPolicy(timeout = 600, action = ExpirationActionType.DESTROY, types = ExpirationType.TIME_TO_LIVE)
|
||||
})
|
||||
static class MixedExpirationPolicyConfiguration extends RegionConfiguration {
|
||||
}
|
||||
}
|
||||
@@ -185,7 +185,7 @@ public class AnnotationBasedExpirationConfigurationIntegrationTest {
|
||||
public void invalidExpirationAction() {
|
||||
expectedException.expect(EvaluationException.class);
|
||||
expectedException.expectCause(isA(IllegalArgumentException.class));
|
||||
expectedException.expectMessage(String.format("'%1$s' is not resolvable as a valid ExpirationAction(Type)",
|
||||
expectedException.expectMessage(String.format("[%s] is not resolvable as an ExpirationAction(Type)",
|
||||
"@expirationProperties['gemfire.region.entry.expiration.invalid.action.string']"));
|
||||
genericExpiration.getExpiry(mockRegionEntry(new RegionEntryWithInvalidExpirationAction()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user