diff --git a/src/main/java/org/springframework/data/gemfire/support/AnnotationBasedExpiration.java b/src/main/java/org/springframework/data/gemfire/support/AnnotationBasedExpiration.java index 4ab3973d..ad5d7f82 100644 --- a/src/main/java/org/springframework/data/gemfire/support/AnnotationBasedExpiration.java +++ b/src/main/java/org/springframework/data/gemfire/support/AnnotationBasedExpiration.java @@ -17,9 +17,20 @@ package org.springframework.data.gemfire.support; import java.lang.annotation.Annotation; +import java.util.concurrent.atomic.AtomicReference; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.data.gemfire.ExpirationActionConverter; import org.springframework.data.gemfire.ExpirationActionType; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import com.gemstone.gemfire.cache.CustomExpiry; @@ -34,15 +45,22 @@ import com.gemstone.gemfire.cache.Region; * Annotations. * * @author John Blum + * @see org.springframework.beans.factory.BeanFactory + * @see org.springframework.beans.factory.BeanFactoryAware + * @see org.springframework.data.gemfire.ExpirationActionType * @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.CustomExpiry + * @see com.gemstone.gemfire.cache.ExpirationAction * @see com.gemstone.gemfire.cache.ExpirationAttributes + * @see com.gemstone.gemfire.cache.Region * @since 1.7.0 */ @SuppressWarnings("unused") -public class AnnotationBasedExpiration implements CustomExpiry { +public class AnnotationBasedExpiration implements BeanFactoryAware, CustomExpiry { + + private static final AtomicReference beanFactory = new AtomicReference(null); //private ExpirationAttributes defaultExpirationAttributes = ExpirationAttributes.DEFAULT; private ExpirationAttributes defaultExpirationAttributes; @@ -51,6 +69,7 @@ public class AnnotationBasedExpiration implements CustomExpiry { * Constructs a new instance of the AnnotationBasedExpiration class with no default expiration policy. */ public AnnotationBasedExpiration() { + this(null); } /** @@ -136,6 +155,17 @@ public class AnnotationBasedExpiration implements CustomExpiry { }; } + @Override + public void setBeanFactory(final BeanFactory beanFactory) throws BeansException { + AnnotationBasedExpiration.beanFactory.set(beanFactory); + } + + protected BeanFactory getBeanFactory() { + BeanFactory localBeanFactory = beanFactory.get(); + Assert.state(localBeanFactory != null, "beanFactory was not properly initialized"); + return localBeanFactory; + } + /** * Sets the expiration policy to use by default when no application domain object specific expiration meta-data * has been specified. @@ -306,13 +336,15 @@ public class AnnotationBasedExpiration implements CustomExpiry { */ protected static class ExpirationMetaData { + private static final ExpirationActionConverter EXPIRATION_ACTION_CONVERTER = new ExpirationActionConverter(); + private final int timeout; - private final ExpirationActionType expirationAction; + private final ExpirationActionType action; - protected ExpirationMetaData(final int timeout, final ExpirationActionType expirationAction) { + protected ExpirationMetaData(int timeout, ExpirationActionType action) { this.timeout = timeout; - this.expirationAction = expirationAction; + this.action = action; } protected static ExpirationMetaData from(ExpirationAttributes expirationAttributes) { @@ -321,23 +353,59 @@ public class AnnotationBasedExpiration implements CustomExpiry { } protected static ExpirationMetaData from(Expiration expiration) { - return new ExpirationMetaData(expiration.timeout(), expiration.action()); + return new ExpirationMetaData(parseTimeout(expiration.timeout()), parseAction(expiration.action())); } protected static ExpirationMetaData from(IdleTimeoutExpiration expiration) { - return new ExpirationMetaData(expiration.timeout(), expiration.action()); + return new ExpirationMetaData(parseTimeout(expiration.timeout()), parseAction(expiration.action())); } protected static ExpirationMetaData from(TimeToLiveExpiration expiration) { - return new ExpirationMetaData(expiration.timeout(), expiration.action()); + return new ExpirationMetaData(parseTimeout(expiration.timeout()), parseAction(expiration.action())); } public ExpirationAttributes toExpirationAttributes() { return new ExpirationAttributes(timeout(), expirationAction()); } + protected static int parseTimeout(String timeout) { + try { + return Integer.parseInt(timeout); + } + catch (NumberFormatException ignore) { + StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); + + evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory.get())); + + return new SpelExpressionParser().parseExpression(timeout).getValue(evaluationContext, Integer.TYPE); + } + } + + protected static ExpirationActionType parseAction(String action) { + try { + return ExpirationActionType.valueOf(EXPIRATION_ACTION_CONVERTER.convert(action)); + } + catch (RuntimeException e) { + StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); + evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory.get())); + ExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression(action); + Class valueType = expression.getValueType(evaluationContext); + + if (String.class.equals(valueType)) { + return ExpirationActionType.valueOfIgnoreCase(expression.getValue(evaluationContext, String.class)); + } + else if (ExpirationAction.class.equals(valueType)) { + return ExpirationActionType.valueOf(expression.getValue(evaluationContext, ExpirationAction.class)); + } + else { + return expression.getValue(evaluationContext, ExpirationActionType.class); + } + } + } + public ExpirationActionType action() { - return expirationAction; + return action; } public ExpirationAction expirationAction() { diff --git a/src/main/java/org/springframework/data/gemfire/support/Expiration.java b/src/main/java/org/springframework/data/gemfire/support/Expiration.java index 1bf34db3..4b31ad6e 100644 --- a/src/main/java/org/springframework/data/gemfire/support/Expiration.java +++ b/src/main/java/org/springframework/data/gemfire/support/Expiration.java @@ -23,8 +23,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.springframework.data.gemfire.ExpirationActionType; - /** * Expiration is an enumerated type encapsulating custom expiration settings for application domain objects * to express their general expiration policy. @@ -39,8 +37,8 @@ import org.springframework.data.gemfire.ExpirationActionType; @Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE }) public @interface Expiration { - ExpirationActionType action() default ExpirationActionType.INVALIDATE; + String action() default "INVALIDATE"; - int timeout() default 0; + String timeout() default "0"; } diff --git a/src/main/java/org/springframework/data/gemfire/support/IdleTimeoutExpiration.java b/src/main/java/org/springframework/data/gemfire/support/IdleTimeoutExpiration.java index db81522f..f1b6a868 100644 --- a/src/main/java/org/springframework/data/gemfire/support/IdleTimeoutExpiration.java +++ b/src/main/java/org/springframework/data/gemfire/support/IdleTimeoutExpiration.java @@ -23,8 +23,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.springframework.data.gemfire.ExpirationActionType; - /** * IdleTimeoutExpiration is an enumerated type encapsulating custom expiration settings for application domain objects * to express their idle-timeout (TTI) expiration policy. @@ -41,8 +39,8 @@ import org.springframework.data.gemfire.ExpirationActionType; @SuppressWarnings("unused") public @interface IdleTimeoutExpiration { - ExpirationActionType action() default ExpirationActionType.INVALIDATE; + String action() default "INVALIDATE"; - int timeout() default 0; + String timeout() default "0"; } diff --git a/src/main/java/org/springframework/data/gemfire/support/TimeToLiveExpiration.java b/src/main/java/org/springframework/data/gemfire/support/TimeToLiveExpiration.java index 8de7e833..b8e46b01 100644 --- a/src/main/java/org/springframework/data/gemfire/support/TimeToLiveExpiration.java +++ b/src/main/java/org/springframework/data/gemfire/support/TimeToLiveExpiration.java @@ -23,8 +23,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.springframework.data.gemfire.ExpirationActionType; - /** * TimeToLiveExpiration is an enumerated type encapsulating custom expiration settings for application domain objects * to express their time-to-live (TTL) expiration policy. @@ -41,8 +39,8 @@ import org.springframework.data.gemfire.ExpirationActionType; @SuppressWarnings("unused") public @interface TimeToLiveExpiration { - ExpirationActionType action() default ExpirationActionType.INVALIDATE; + String action() default "INVALIDATE"; - int timeout() default 0; + String timeout() default "0"; } diff --git a/src/test/java/org/springframework/data/gemfire/support/AnnotationBasedExpirationConfigurationIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/support/AnnotationBasedExpirationConfigurationIntegrationTest.java index 0d1aeeb0..f1b0ad18 100644 --- a/src/test/java/org/springframework/data/gemfire/support/AnnotationBasedExpirationConfigurationIntegrationTest.java +++ b/src/test/java/org/springframework/data/gemfire/support/AnnotationBasedExpirationConfigurationIntegrationTest.java @@ -33,7 +33,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.data.gemfire.ExpirationActionType; import org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -63,6 +62,10 @@ import com.gemstone.gemfire.cache.Region; @SuppressWarnings("unused") public class AnnotationBasedExpirationConfigurationIntegrationTest { + @Autowired + @Qualifier("genericExpiration") + private AnnotationBasedExpiration genericExpiration; + @Autowired @Qualifier("ttiExpiration") private AnnotationBasedExpiration idleTimeoutExpiration; @@ -86,6 +89,8 @@ public class AnnotationBasedExpirationConfigurationIntegrationTest { assertThat(defaultExpirationAttributes, is(not(nullValue()))); assertThat(defaultExpirationAttributes.getTimeout(), is(equalTo(600))); assertThat(defaultExpirationAttributes.getAction(), is(equalTo(ExpirationAction.DESTROY))); + assertThat(genericExpiration, is(instanceOf(CustomExpiry.class))); + assertThat(genericExpiration.getDefaultExpirationAttributes(), is(nullValue())); assertThat(idleTimeoutExpiration, is(instanceOf(CustomExpiry.class))); assertThat(idleTimeoutExpiration.getDefaultExpirationAttributes(), is(nullValue())); assertThat(timeToLiveExpiration, is(instanceOf(CustomExpiry.class))); @@ -117,44 +122,84 @@ public class AnnotationBasedExpirationConfigurationIntegrationTest { public void exampleRegionIdleTimeoutExpirationPolicy() { CustomExpiry expiration = example.getAttributes().getCustomEntryIdleTimeout(); - assertExpiration(expiration.getExpiry(mockRegionEntry( - new ApplicationDomainObjectWithIdleTimeoutExpirationPolicy())), 120, ExpirationAction.INVALIDATE); - assertExpiration(expiration.getExpiry(mockRegionEntry( - new ApplicationDomainObjectWithTimeToLiveGenericExpirationPolicies())), 60, ExpirationAction.INVALIDATE); - assertExpiration(expiration.getExpiry(mockRegionEntry( - new ApplicationDomainObjectWithGenericExpirationPolicy())), 60, ExpirationAction.DESTROY); - assertThat(expiration.getExpiry(mockRegionEntry(new ApplicationDomainObjectWithNoExpirationPolicy())), - is(nullValue())); + assertExpiration(expiration.getExpiry(mockRegionEntry(new ApplicationDomainObjectWithTimeToLiveGenericExpirationPolicies())), + 60, ExpirationAction.INVALIDATE); + assertExpiration(expiration.getExpiry(mockRegionEntry(new ApplicationDomainObjectWithIdleTimeoutExpirationPolicy())), + 120, ExpirationAction.INVALIDATE); + assertExpiration(expiration.getExpiry(mockRegionEntry(new ApplicationDomainObjectWithGenericExpirationPolicy())), + 60, ExpirationAction.DESTROY); + assertThat(expiration.getExpiry(mockRegionEntry(new ApplicationDomainObjectWithNoExpirationPolicy())), is(nullValue())); + assertThat(expiration.getExpiry(mockRegionEntry(new RegionEntryTimeToLiveExpirationPolicy())), is(nullValue())); + assertExpiration(expiration.getExpiry(mockRegionEntry(new RegionEntryIdleTimeoutExpirationPolicy())), + 60, ExpirationAction.INVALIDATE); + assertExpiration(expiration.getExpiry(mockRegionEntry(new RegionEntryGenericExpirationPolicy())), + 60, ExpirationAction.DESTROY); } @Test public void exampleRegionTimeToLiveExpirationPolicy() { CustomExpiry expiration = example.getAttributes().getCustomEntryTimeToLive(); - assertExpiration(expiration.getExpiry(mockRegionEntry( - new ApplicationDomainObjectWithTimeToLiveGenericExpirationPolicies())), 300, ExpirationAction.DESTROY); - assertExpiration(defaultExpirationAttributes, expiration.getExpiry(mockRegionEntry( - new ApplicationDomainObjectWithIdleTimeoutExpirationPolicy()))); - assertExpiration(expiration.getExpiry(mockRegionEntry( - new ApplicationDomainObjectWithGenericExpirationPolicy())), 60, ExpirationAction.DESTROY); - assertExpiration(defaultExpirationAttributes, expiration.getExpiry(mockRegionEntry( - new ApplicationDomainObjectWithNoExpirationPolicy()))); + assertExpiration(expiration.getExpiry(mockRegionEntry(new ApplicationDomainObjectWithTimeToLiveGenericExpirationPolicies())), + 300, ExpirationAction.DESTROY); + assertExpiration(defaultExpirationAttributes, expiration.getExpiry(mockRegionEntry(new ApplicationDomainObjectWithIdleTimeoutExpirationPolicy()))); + assertExpiration(expiration.getExpiry(mockRegionEntry(new ApplicationDomainObjectWithGenericExpirationPolicy())), + 60, ExpirationAction.DESTROY); + assertExpiration(defaultExpirationAttributes, expiration.getExpiry(mockRegionEntry(new ApplicationDomainObjectWithNoExpirationPolicy()))); + assertExpiration(expiration.getExpiry(mockRegionEntry(new RegionEntryTimeToLiveExpirationPolicy())), + 60, ExpirationAction.LOCAL_INVALIDATE); + assertExpiration(defaultExpirationAttributes, expiration.getExpiry(mockRegionEntry(new RegionEntryIdleTimeoutExpirationPolicy()))); + assertExpiration(expiration.getExpiry(mockRegionEntry(new RegionEntryGenericExpirationPolicy())), + 60, ExpirationAction.DESTROY); } - @Expiration(timeout = 60, action = ExpirationActionType.INVALIDATE) - @TimeToLiveExpiration(timeout = 300, action = ExpirationActionType.DESTROY) + @Test + public void genericExpirationPolicy() { + assertExpiration(genericExpiration.getExpiry(mockRegionEntry( + new ApplicationDomainObjectWithTimeToLiveGenericExpirationPolicies())), 60, ExpirationAction.INVALIDATE); + assertThat(genericExpiration.getExpiry(mockRegionEntry( + new ApplicationDomainObjectWithIdleTimeoutExpirationPolicy())), is(nullValue())); + assertExpiration(genericExpiration.getExpiry(mockRegionEntry( + new ApplicationDomainObjectWithGenericExpirationPolicy())), 60, ExpirationAction.DESTROY); + assertThat(genericExpiration.getExpiry(mockRegionEntry( + new ApplicationDomainObjectWithNoExpirationPolicy())), is(nullValue())); + assertThat(genericExpiration.getExpiry(mockRegionEntry( + new RegionEntryTimeToLiveExpirationPolicy())), is(nullValue())); + assertThat(genericExpiration.getExpiry(mockRegionEntry( + new RegionEntryIdleTimeoutExpirationPolicy())), is(nullValue())); + assertExpiration(genericExpiration.getExpiry(mockRegionEntry( + new RegionEntryGenericExpirationPolicy())), 60, ExpirationAction.DESTROY); + } + + @Expiration(timeout = "60", action = "INVALIDATE") + @TimeToLiveExpiration(timeout = "300", action = "DESTROY") protected static class ApplicationDomainObjectWithTimeToLiveGenericExpirationPolicies { } - @IdleTimeoutExpiration(timeout = 120, action = ExpirationActionType.INVALIDATE) + @IdleTimeoutExpiration(timeout = "120", action = "INVALIDATE") protected static class ApplicationDomainObjectWithIdleTimeoutExpirationPolicy { } - @Expiration(timeout = 60, action = ExpirationActionType.DESTROY) + @Expiration(timeout = "60", action = "DESTROY") protected static class ApplicationDomainObjectWithGenericExpirationPolicy { } protected static class ApplicationDomainObjectWithNoExpirationPolicy { } + @TimeToLiveExpiration(timeout = "@expirationProperties['gemfire.region.entry.expiration.timeout']", + action = "@expirationProperties['gemfire.region.entry.expiration.action.string']") + protected static class RegionEntryTimeToLiveExpirationPolicy { + } + + @IdleTimeoutExpiration(timeout = "@expirationProperties['gemfire.region.entry.expiration.timeout']", + action = "@expirationProperties['gemfire.region.entry.expiration.action.gemfire.type']") + protected static class RegionEntryIdleTimeoutExpirationPolicy { + } + + @Expiration(timeout = "@expirationProperties['gemfire.region.entry.expiration.timeout']", + action = "@expirationProperties['gemfire.region.entry.expiration.action.spring.type']") + protected static class RegionEntryGenericExpirationPolicy { + } + } diff --git a/src/test/java/org/springframework/data/gemfire/support/AnnotationBasedExpirationTest.java b/src/test/java/org/springframework/data/gemfire/support/AnnotationBasedExpirationTest.java index fe4e9dc4..d381863d 100644 --- a/src/test/java/org/springframework/data/gemfire/support/AnnotationBasedExpirationTest.java +++ b/src/test/java/org/springframework/data/gemfire/support/AnnotationBasedExpirationTest.java @@ -350,36 +350,36 @@ public class AnnotationBasedExpirationTest { expirationMetaData.expirationAction()); } - @Expiration(timeout = 60, action = ExpirationActionType.INVALIDATE) - @IdleTimeoutExpiration(timeout = 120, action = ExpirationActionType.LOCAL_INVALIDATE) - @TimeToLiveExpiration(timeout = 300, action = ExpirationActionType.LOCAL_DESTROY) + @Expiration(timeout = "60", action = "INVALIDATE") + @IdleTimeoutExpiration(timeout = "120", action = "LOCAL_INVALIDATE") + @TimeToLiveExpiration(timeout = "300", action = "LOCAL_DESTROY") public static class RegionEntryValueWithTimeToLiveIdleTimeoutGenericExpiration { } - @IdleTimeoutExpiration(timeout = 120, action = ExpirationActionType.LOCAL_INVALIDATE) - @TimeToLiveExpiration(timeout = 300, action = ExpirationActionType.LOCAL_DESTROY) + @IdleTimeoutExpiration(timeout = "120", action = "LOCAL_INVALIDATE") + @TimeToLiveExpiration(timeout = "300", action = "LOCAL_DESTROY") public static class RegionEntryValueWithTimeToLiveIdleTimeoutExpiration { } - @Expiration(timeout = 60, action = ExpirationActionType.INVALIDATE) - @TimeToLiveExpiration(timeout = 300, action = ExpirationActionType.LOCAL_DESTROY) + @Expiration(timeout = "60", action = "INVALIDATE") + @TimeToLiveExpiration(timeout = "300", action = "LOCAL_DESTROY") public static class RegionEntryValueWithTimeToLiveGenericExpiration { } - @TimeToLiveExpiration(timeout = 300, action = ExpirationActionType.LOCAL_DESTROY) + @TimeToLiveExpiration(timeout = "300", action = "LOCAL_DESTROY") public static class RegionEntryValueWithTimeToLiveExpiration { } - @Expiration(timeout = 60, action = ExpirationActionType.INVALIDATE) - @IdleTimeoutExpiration(timeout = 120, action = ExpirationActionType.LOCAL_INVALIDATE) + @Expiration(timeout = "60", action = "INVALIDATE") + @IdleTimeoutExpiration(timeout = "120", action = "LOCAL_INVALIDATE") public static class RegionEntryValueWithIdleTimeoutGenericExpiration { } - @IdleTimeoutExpiration(timeout = 120, action = ExpirationActionType.LOCAL_INVALIDATE) + @IdleTimeoutExpiration(timeout = "120", action = "LOCAL_INVALIDATE") public static class RegionEntryValueWithIdleTimeoutExpiration { } - @Expiration(timeout = 60, action = ExpirationActionType.INVALIDATE) + @Expiration(timeout = "60", action = "INVALIDATE") public static class RegionEntryValueWithGenericExpiration { } diff --git a/src/test/resources/org/springframework/data/gemfire/support/AnnotationBasedExpirationConfigurationIntegrationTest-context.xml b/src/test/resources/org/springframework/data/gemfire/support/AnnotationBasedExpirationConfigurationIntegrationTest-context.xml index 293018d3..e455be48 100644 --- a/src/test/resources/org/springframework/data/gemfire/support/AnnotationBasedExpirationConfigurationIntegrationTest-context.xml +++ b/src/test/resources/org/springframework/data/gemfire/support/AnnotationBasedExpirationConfigurationIntegrationTest-context.xml @@ -11,6 +11,15 @@ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd "> + + 60 + LOCAL_INVALIDATE + #{T(com.gemstone.gemfire.cache.ExpirationAction).INVALIDATE} + #{T(org.springframework.data.gemfire.ExpirationActionType).DESTROY} + + + + AnnotationBasedExpirationConfigurationIntegrationTest 0 @@ -24,6 +33,8 @@ + +