From 40e4212ad7f0d4f67e44f9850f2a00751f2adcfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Wis=C5=82owski?= Date: Tue, 23 Feb 2016 14:27:09 +0100 Subject: [PATCH] DATACOUCH-59 - Allow expiry touch on read Configuration is based on Document annotation. When read of document takes place then touch action is executed on document. It is an asynchronous action so it does not block read. It is executed only on single reads (not view-based or N1QL-based ones). --- .../core/CouchbaseTemplateTests.java | 30 +++++++++++++++ .../couchbase/core/CouchbaseTemplate.java | 7 +++- .../BasicCouchbasePersistentEntity.java | 16 +++++--- .../mapping/CouchbasePersistentEntity.java | 8 ++++ .../data/couchbase/core/mapping/Document.java | 6 +++ .../BasicCouchbasePersistentEntityTests.java | 38 +++++++++++++++++++ 6 files changed, 99 insertions(+), 6 deletions(-) diff --git a/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java b/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java index e7348c37..7a0222c5 100644 --- a/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java +++ b/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java @@ -402,6 +402,22 @@ public class CouchbaseTemplateTests { assertEquals(versionedClass.getVersion(), foundClass.getVersion()); } + /** + * @see DATACOUCH-59 + */ + @Test + public void expiryWhenTouchOnReadDocument() throws InterruptedException { + String id = "simple-doc-with-update-expiry-for-read"; + DocumentWithTouchOnRead doc = new DocumentWithTouchOnRead(id); + template.save(doc); + Thread.sleep(1500); + assertNotNull(template.findById(id, DocumentWithTouchOnRead.class)); + Thread.sleep(1500); + assertNotNull(template.findById(id, DocumentWithTouchOnRead.class)); + Thread.sleep(3000); + assertNull(template.findById(id, DocumentWithTouchOnRead.class)); + } + /** * A sample document with just an id and property. */ @@ -433,6 +449,20 @@ public class CouchbaseTemplateTests { } } + /** + * A sample document that expires in 2 seconds and touchOnRead set. + */ + @Document(expiry = 2, touchOnRead = true) + static class DocumentWithTouchOnRead { + + @Id + private final String id; + + public DocumentWithTouchOnRead(String id) { + this.id = id; + } + } + @Document static class ComplexPerson { diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java index 4384fc22..9c8885d4 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java @@ -281,10 +281,15 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP @Override public T findById(final String id, Class entityClass) { + final CouchbasePersistentEntity entity = mappingContext.getPersistentEntity(entityClass); RawJsonDocument result = execute(new BucketCallback() { @Override public RawJsonDocument doInBucket() { - return client.get(id, RawJsonDocument.class); + if (entity.isTouchOnRead()) { + return client.getAndTouch(id, entity.getExpiry(), RawJsonDocument.class); + } else { + return client.get(id, RawJsonDocument.class); + } } }); diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntity.java b/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntity.java index 10c8cb46..a70e07be 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntity.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntity.java @@ -16,12 +16,7 @@ package org.springframework.data.couchbase.core.mapping; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.concurrent.TimeUnit; - import com.couchbase.client.java.repository.annotation.Id; - import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -31,6 +26,10 @@ import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.util.TypeInformation; import org.springframework.expression.spel.support.StandardEvaluationContext; +import java.util.Calendar; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + /** * The representation of a persistent entity. * @@ -123,4 +122,11 @@ public class BasicCouchbasePersistentEntity extends BasicPersistentEntity 0 && annotation.touchOnRead(); + } + } diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentEntity.java b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentEntity.java index d5f09020..e7712bd4 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentEntity.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentEntity.java @@ -42,4 +42,12 @@ public interface CouchbasePersistentEntity extends */ int getExpiry(); + /** + * Flag for using getAndTouch operations for reads, resetting the expiration (if one was set) when the + * entity is directly read (eg. findOne, findById). + * + * @return true if a direct read of the document should trigger a touch, resetting its expiration timer. + */ + boolean isTouchOnRead(); + } diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/Document.java b/src/main/java/org/springframework/data/couchbase/core/mapping/Document.java index a1b6856f..a3f07ee3 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/Document.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/Document.java @@ -46,4 +46,10 @@ public @interface Document { */ TimeUnit expiryUnit() default TimeUnit.SECONDS; + /** + * An optional flag associated with {@link #expiry()} indicating whether the expiry timer should + * be reset whenever the document is directly read (eg. findByOne, findById). + */ + boolean touchOnRead() default false; + } diff --git a/src/test/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntityTests.java b/src/test/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntityTests.java index 38ce03c6..2ee24941 100644 --- a/src/test/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntityTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntityTests.java @@ -17,6 +17,8 @@ package org.springframework.data.couchbase.core.mapping; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.util.Calendar; import java.util.Date; @@ -103,6 +105,42 @@ public class BasicCouchbasePersistentEntityTests { assertEquals(expected.get(Calendar.SECOND), calendar.get(Calendar.SECOND)); } + @Test + public void doesNotUseGetExpiry() throws Exception { + assertEquals(0, getBasicCouchbasePersistentEntity(SimpleDocument.class).getExpiry()); + } + + @Test + public void usesGetExpiry() throws Exception { + assertEquals(10, getBasicCouchbasePersistentEntity(SimpleDocumentWithExpiry.class).getExpiry()); + } + + @Test + public void doesNotUseIsUpdateExpiryForRead() throws Exception { + assertFalse(getBasicCouchbasePersistentEntity(SimpleDocument.class).isTouchOnRead()); + assertFalse(getBasicCouchbasePersistentEntity(SimpleDocumentWithExpiry.class).isTouchOnRead()); + } + + @Test + public void usesTouchOnRead() throws Exception { + assertTrue(getBasicCouchbasePersistentEntity(SimpleDocumentWithTouchOnRead.class).isTouchOnRead()); + } + + private BasicCouchbasePersistentEntity getBasicCouchbasePersistentEntity(Class clazz) { + return new BasicCouchbasePersistentEntity(ClassTypeInformation.from(clazz)); + } + + public static class SimpleDocument { + } + + @Document(expiry = 10) + public static class SimpleDocumentWithExpiry { + } + + @Document(expiry = 10, touchOnRead = true) + public static class SimpleDocumentWithTouchOnRead { + } + /** * Simple POJO to test default expiry. */