diff --git a/src/asciidoc/reference/region.adoc b/src/asciidoc/reference/region.adoc
index 60d5888c..8491a518 100644
--- a/src/asciidoc/reference/region.adoc
+++ b/src/asciidoc/reference/region.adoc
@@ -627,7 +627,7 @@ settings on a Session-based application domain object like so...
[source,java]
----
-@Expiration(timeout = 1800, action = ExpirationActionType.INVALIDATE)
+@Expiration(timeout = "1800", action = "INVALIDATE")
public static class SessionBasedApplicationDomainObject {
}
----
@@ -637,9 +637,9 @@ and `@TimeToLiveExpiration` for Idle Timeout (TTI) and Time-to-Live (TTL) Expira
[source,java]
----
-@TimeToLiveExpiration(timeout = 3600, action = ExpirationActionType.LOCAL_DESTROY)
-@IdleTimeoutExpiration(timeout = 1800, action = ExpirationActionType.LOCAL_INVALIDATE)
-@Expiration(timeout = 1800, action = ExpirationActionType.INVALIDATE)
+@TimeToLiveExpiration(timeout = "3600", action = "LOCAL_DESTROY")
+@IdleTimeoutExpiration(timeout = "1800", action = "LOCAL_INVALIDATE")
+@Expiration(timeout = "1800", action = "INVALIDATE")
public static class AnotherSessionBasedApplicationDomainObject {
}
----
@@ -647,9 +647,11 @@ public static class AnotherSessionBasedApplicationDomainObject {
Both `@IdleTimeoutExpiration` and `@TimeToLiveExpiration` take precedence over the generic `@Expiration` annotation
when more than one Expiration annotation type is specified, as shown above. Though, neither `@IdleTimeoutExpiration`
nor `@TimeToLiveExpiration` overrides the other; rather they may compliment each other when different Region Entry
-Expiration types, such as TTL and TTI, are both configured.
+Expiration types, such as TTL and TTI, are configured.
-Also, all @Expiration-based annotations apply only to Region Entry values. Expiration for a "Region" is not covered
+[NOTE]
+====
+All @Expiration-based annotations apply only to Region Entry values. Expiration for a "Region" is not covered
by Spring Data GemFire's Expiration annotation support. However, GemFire and Spring Data GemFire do allow you to set
Region Expiration using the SDG XML namespace, like so...
@@ -660,6 +662,7 @@ Region Expiration using the SDG XML namespace, like so...
----
+====
Spring Data GemFire's @Expiration annotation support is implemented with GemFire's http://gemfire.docs.pivotal.io/latest/javadocs/japi/com/gemstone/gemfire/cache/CustomExpiry.html[`CustomExpiry`] interface.
See http://gemfire.docs.pivotal.io/latest/userguide/index.html#developing/expiration/configuring_data_expiration.html[GemFire's User Guide] for more details
@@ -668,13 +671,13 @@ The Spring Data GemFire `AnnotationBasedExpiration` class (and `CustomExpiry` im
for processing the SDG @Expiration annotations and applying the Expiration policy and settings appropriately
for Region Entry Expiration on request.
-To use Spring Data GemFire to configure specifically GemFire Regions to appropriately apply the Expiration policy
+To use Spring Data GemFire to configure specific GemFire Regions to appropriately apply the Expiration policy
and settings applied to your application domain objects annotated with @Expiration-based annotations, you must...
-1. Define a Spring bean in the Spring ApplicationContext of type `AnnotationBasedExpiration` using a constructor
-or one of the factory methods. When configuring Expiration for a specific Expiration type, such as Idle Timeout
-or Time-to-Live, then you should use one of the convenient factory methods of the `AnnotationBasedExpiration` class,
-like so...
+1. Define a Spring bean in the Spring ApplicationContext of type `AnnotationBasedExpiration` using the appropriate
+constructor or one of the convenient factory methods. When configuring Expiration for a specific Expiration type,
+such as Idle Timeout or Time-to-Live, then you should use one of the factory methods of the `AnnotationBasedExpiration`
+class, like so...
+
[source,xml]
----
@@ -693,10 +696,10 @@ along with the `` element to set TTI.
====
2. (optional) Annotate your application domain objects that will be stored in the Region with Expiration policies
-and custom settings with one of the Spring Data GemFire @Expiration annotations: `@Expiration`, `@IdleTimeoutExpiration`
+and custom settings using one of Spring Data GemFire's @Expiration annotations: `@Expiration`, `@IdleTimeoutExpiration`
and/or `@TimeToLiveExpiration`
-3. (optional) In cases where certain application domain objects have not been annotated with Spring Data GemFire's
+3. (optional) In cases where particular application domain objects have not been annotated with Spring Data GemFire's
@Expiration annotations at all, but the GemFire Region is configured to use SDG's custom `AnnotationBasedExpiration` class
to determine the Expiration policy and settings for objects stored in the Region, then it is possible to set "default"
Expiration attributes on the `AnnotationBasedExpiration` bean by doing the following...
@@ -718,6 +721,75 @@ Expiration attributes on the `AnnotationBasedExpiration` bean by doing the follo
----
+You may have noticed that the Spring Data GemFire's @Expiration annotations use String as the attributes type, rather
+than and perhaps more appropriately being strongly typed, i.e. `int` for 'timeout' and SDG'S `ExpirationActionType`
+for 'action'. Why is that?
+
+Well, enter one of Spring Data GemFire's other features, leveraging Spring's core infrastructure
+for configuration convenience: Property Placeholders and Spring Expression Language (SpEL).
+
+For instance, a developer can specify both the Expiration 'timeout' and 'action' using Property Placeholders
+in the @Expiration annotation attributes...
+
+[source,java]
+----
+@TimeToLiveExpiration(timeout = "${gemfire.region.entry.expiration.ttl.timeout}"
+ action = "${gemfire.region.entry.expiration.ttl.action}")
+public class ExampleApplicationDomainObject {
+}
+----
+
+Then, in your Spring context XML or in JavaConfig, you would declare the following beans...
+
+[source,xml]
+----
+
+ 600
+ INVALIDATE
+ ...
+
+
+
+----
+
+This is both convenient when multiple application domain objects might share similar Expiration policies and settings,
+or when you wish to externalize the configuration.
+
+However, a developer may want more dynamic Expiration configuration determined by the state of the running system.
+This is where the power of SpEL comes in and is the recommended approach. Not only can you refer to beans
+in the Spring context and access bean properties, invoke methods, etc, the values for Expiration 'timeout' and 'action'
+can be strongly typed. For example (building on the example above)...
+
+[source,xml]
+----
+
+ 600
+ #{T(org.springframework.data.gemfire.ExpirationActionType).DESTROY}
+ #{T(com.gemstone.gemfire.cache.ExpirationAction).INVALIDATE}
+ ...
+
+
+
+----
+
+Then, on your application domain object...
+
+[source,java]
+----
+@TimeToLiveExpiration(timeout = "@expirationSettings['gemfire.region.entry.expiration.ttl.timeout']"
+ action = "@expirationSetting['gemfire.region.entry.expiration.ttl.action']")
+public class ExampleApplicationDomainObject {
+}
+----
+
+You can imagine that the 'expirationSettings' bean could be a more interesting and useful object rather than a simple
+instance of `java.util.Properties`. In this example, even the Properties ('expirationSettings') using using SpEL
+to based the action value on the actual Expiration action enumerated types leading to more quickly identified failures
+if the types ever change.
+
+All of this has been demonstrated and tested in the Spring Data GemFire test suite, by way of example. See the
+https://github.com/spring-projects/spring-data-gemfire[source] for further details.
+
[[bootstrap:region:local]]
== Local Region
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 24db16ff..9b0346e1 100644
--- a/src/test/java/org/springframework/data/gemfire/support/AnnotationBasedExpirationConfigurationIntegrationTest.java
+++ b/src/test/java/org/springframework/data/gemfire/support/AnnotationBasedExpirationConfigurationIntegrationTest.java
@@ -19,6 +19,7 @@ package org.springframework.data.gemfire.support;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.isA;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
@@ -29,11 +30,14 @@ import static org.mockito.Mockito.when;
import javax.annotation.Resource;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer;
+import org.springframework.expression.EvaluationException;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -65,6 +69,9 @@ import com.gemstone.gemfire.cache.Region;
@SuppressWarnings("unused")
public class AnnotationBasedExpirationConfigurationIntegrationTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
@Autowired
@Qualifier("genericExpiration")
private AnnotationBasedExpiration