You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by mr...@apache.org on 2014/12/02 11:14:23 UTC

svn commit: r1642837 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/ oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ oak-run/src/...

Author: mreutegg
Date: Tue Dec  2 10:14:22 2014
New Revision: 1642837

URL: http://svn.apache.org/r1642837
Log:
OAK-2291: Associate user defined values with checkpoint

Implementation for DocumentNodeStore

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Checkpoints.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentCheckpointMBean.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/CheckpointTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsTest.java
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/checkpoint/Checkpoints.java
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsHelper.java
    jackrabbit/oak/trunk/oak-run/src/main/js/oak-mongo.js

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Checkpoints.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Checkpoints.java?rev=1642837&r1=1642836&r2=1642837&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Checkpoints.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Checkpoints.java Tue Dec  2 10:14:22 2014
@@ -19,15 +19,23 @@
 
 package org.apache.jackrabbit.oak.plugins.document;
 
+import java.math.BigInteger;
+import java.util.Collections;
 import java.util.Map;
 import java.util.SortedMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.annotation.CheckForNull;
 
+import org.apache.jackrabbit.oak.commons.json.JsopBuilder;
+import org.apache.jackrabbit.oak.commons.json.JsopReader;
+import org.apache.jackrabbit.oak.commons.json.JsopTokenizer;
+import org.apache.jackrabbit.oak.commons.json.JsopWriter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.Maps;
+
 
 /**
  * Checkpoints provide details around which revision are to be kept. These
@@ -37,7 +45,8 @@ class Checkpoints {
     private static final String ID = "checkpoint";
 
     /**
-     * Property name to store all checkpoint data. The data is stored as Revision => expiryTime
+     * Property name to store all checkpoint data. The data is either stored as
+     * Revision => expiryTime or Revision => JSON with expiryTime and info.
      */
     private static final String PROP_CHECKPOINT = "data";
 
@@ -63,13 +72,15 @@ class Checkpoints {
         createIfNotExist();
     }
 
-    public Revision create(long lifetimeInMillis) {
+    public Revision create(long lifetimeInMillis, Map<String, String> info) {
         Revision r = nodeStore.getHeadRevision();
         createCounter.getAndIncrement();
         performCleanupIfRequired();
         UpdateOp op = new UpdateOp(ID, false);
-        long endTime = nodeStore.getClock().getTime() + lifetimeInMillis;
-        op.setMapEntry(PROP_CHECKPOINT, r, Long.toString(endTime));
+        long endTime = BigInteger.valueOf(nodeStore.getClock().getTime())
+                .add(BigInteger.valueOf(lifetimeInMillis))
+                .min(BigInteger.valueOf(Long.MAX_VALUE)).longValue();
+        op.setMapEntry(PROP_CHECKPOINT, r, new Info(endTime, info).toString());
         store.createOrUpdate(Collection.SETTINGS, op);
         return r;
     }
@@ -92,7 +103,7 @@ class Checkpoints {
     @CheckForNull
     public Revision getOldestRevisionToKeep() {
         //Get uncached doc
-        SortedMap<Revision, String> checkpoints = getCheckpoints();
+        SortedMap<Revision, Info> checkpoints = getCheckpoints();
 
         if(checkpoints == null){
             log.debug("No checkpoint registered so far");
@@ -104,8 +115,8 @@ class Checkpoints {
         Revision lastAliveRevision = null;
         long oldestExpiryTime = 0;
 
-        for (Map.Entry<Revision, String> e : checkpoints.entrySet()) {
-            final long expiryTime = Long.parseLong(e.getValue());
+        for (Map.Entry<Revision, Info> e : checkpoints.entrySet()) {
+            final long expiryTime = e.getValue().getExpiryTime();
             if (currentTime > expiryTime) {
                 op.removeMapEntry(PROP_CHECKPOINT, e.getKey());
             } else if (expiryTime > oldestExpiryTime) {
@@ -124,13 +135,22 @@ class Checkpoints {
 
     @SuppressWarnings("unchecked")
     @CheckForNull
-    SortedMap<Revision, String> getCheckpoints() {
+    SortedMap<Revision, Info> getCheckpoints() {
         Document cdoc = store.find(Collection.SETTINGS, ID, 0);
-        return (SortedMap<Revision, String>) cdoc.get(PROP_CHECKPOINT);
+        SortedMap<Revision, String> data =
+                (SortedMap<Revision, String>) cdoc.get(PROP_CHECKPOINT);
+        if (data == null) {
+            return null;
+        }
+        SortedMap<Revision, Info> checkpoints = Maps.newTreeMap(data.comparator());
+        for (Map.Entry<Revision, String> entry : data.entrySet()) {
+            checkpoints.put(entry.getKey(), Info.fromString(entry.getValue()));
+        }
+        return checkpoints;
     }
 
     int size(){
-        SortedMap<Revision, String> checkpoints = getCheckpoints();
+        SortedMap<Revision, Info> checkpoints = getCheckpoints();
         return checkpoints == null ? 0 : checkpoints.size();
     }
 
@@ -154,5 +174,66 @@ class Checkpoints {
         }
     }
 
+    static final class Info {
+
+        private static final String EXPIRES = "expires";
 
+        private final long expiryTime;
+
+        private final Map<String, String> info;
+
+        private Info(long expiryTime, Map<String, String> info) {
+            this.expiryTime = expiryTime;
+            this.info = Collections.unmodifiableMap(info);
+        }
+
+        static Info fromString(String info) {
+            long expiryTime;
+            Map<String, String> map;
+            if (info.startsWith("{")) {
+                map = Maps.newHashMap();
+                JsopReader reader = new JsopTokenizer(info);
+                reader.read('{');
+                String key = reader.readString();
+                if (!EXPIRES.equals(key)) {
+                    throw new IllegalArgumentException("First entry in the " +
+                            "checkpoint info must be the expires date: " + info);
+                }
+                reader.read(':');
+                expiryTime = Long.parseLong(reader.readString());
+                while (reader.matches(',')) {
+                    key = reader.readString();
+                    reader.read(':');
+                    map.put(key, reader.readString());
+                }
+                reader.read('}');
+                reader.read(JsopReader.END);
+            } else {
+                // old format
+                map = Collections.emptyMap();
+                expiryTime = Long.parseLong(info);
+            }
+            return new Info(expiryTime, map);
+        }
+
+        Map<String, String> get() {
+            return info;
+        }
+
+        long getExpiryTime() {
+            return expiryTime;
+        }
+
+        @Override
+        public String toString() {
+            JsopWriter writer = new JsopBuilder();
+            writer.object();
+            writer.key(EXPIRES).value(Long.toString(expiryTime));
+            for (Map.Entry<String, String> entry : info.entrySet()) {
+                writer.key(entry.getKey()).value(entry.getValue());
+            }
+            writer.endObject();
+            return writer.toString();
+        }
+    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentCheckpointMBean.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentCheckpointMBean.java?rev=1642837&r1=1642836&r2=1642837&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentCheckpointMBean.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentCheckpointMBean.java Tue Dec  2 10:14:22 2014
@@ -29,6 +29,8 @@ import javax.management.openmbean.Tabula
 
 import org.apache.jackrabbit.oak.util.AbstractCheckpointMBean;
 
+import static org.apache.jackrabbit.oak.plugins.document.Checkpoints.Info;
+
 /**
  * {@code CheckpointMBean} implementation for the {@code DocumentNodeStore}.
  */
@@ -41,17 +43,18 @@ public class DocumentCheckpointMBean ext
 
     @Override
     protected void collectCheckpoints(TabularDataSupport tab) throws OpenDataException {
-        Map<Revision, String> checkpoints = store.getCheckpoints().getCheckpoints();
+        Map<Revision, Info> checkpoints = store.getCheckpoints().getCheckpoints();
         if (checkpoints == null) {
             checkpoints = Collections.emptyMap();
         }
 
-        for (Entry<Revision, String> checkpoint : checkpoints.entrySet()) {
+        for (Entry<Revision, Info> checkpoint : checkpoints.entrySet()) {
             String id = checkpoint.getKey().toString();
+            Info info = checkpoint.getValue();
             Date created = new Date(checkpoint.getKey().getTimestamp());
-            Date expires = new Date(Long.parseLong(checkpoint.getValue()));
+            Date expires = new Date(info.getExpiryTime());
             tab.put(id, toCompositeData(
-                    id, created.toString(), expires.toString(), store.checkpointInfo(id)));
+                    id, created.toString(), expires.toString(), info.get()));
         }
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java?rev=1642837&r1=1642836&r2=1642837&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java Tue Dec  2 10:14:22 2014
@@ -1365,19 +1365,27 @@ public final class DocumentNodeStore
     @Nonnull
     @Override
     public String checkpoint(long lifetime, @Nonnull Map<String, String> properties) {
-        throw new UnsupportedOperationException();  // FIXME implement. See OAK-2291
+        return checkpoints.create(lifetime, properties).toString();
     }
 
     @Nonnull
     @Override
     public String checkpoint(long lifetime) {
-        return checkpoints.create(lifetime).toString();
+        Map<String, String> empty = Collections.emptyMap();
+        return checkpoint(lifetime, empty);
     }
 
     @Nonnull
     @Override
     public Map<String, String> checkpointInfo(@Nonnull String checkpoint) {
-        throw new UnsupportedOperationException();  // FIXME implement. See OAK-2291
+        Revision r = Revision.fromString(checkpoint);
+        Checkpoints.Info info = checkpoints.getCheckpoints().get(r);
+        if (info == null) {
+            // checkpoint does not exist
+            return Collections.emptyMap();
+        } else {
+            return info.get();
+        }
     }
 
     @CheckForNull

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/CheckpointTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/CheckpointTest.java?rev=1642837&r1=1642836&r2=1642837&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/CheckpointTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/CheckpointTest.java Tue Dec  2 10:14:22 2014
@@ -23,7 +23,6 @@ import static org.junit.Assert.assertEqu
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
 import static org.junit.runners.Parameterized.Parameters;
 
 import java.util.Arrays;
@@ -102,8 +101,6 @@ public class CheckpointTest {
 
     @Test
     public void checkpointInfo() throws CommitFailedException {
-        // FIXME implement. See OAK-2291
-        assumeTrue(fixture != NodeStoreFixture.MONGO_NS);
         ImmutableMap<String, String> props = ImmutableMap.of(
                 "one", "1", "two", "2", "three", "2");
         String cp = store.checkpoint(Long.MAX_VALUE, props);

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsTest.java?rev=1642837&r1=1642836&r2=1642837&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsTest.java Tue Dec  2 10:14:22 2014
@@ -18,6 +18,8 @@
  */
 package org.apache.jackrabbit.oak.plugins.document;
 
+import java.util.Collections;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
@@ -28,9 +30,13 @@ import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 
+import com.google.common.collect.ImmutableMap;
+
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 public class CheckpointsTest {
 
@@ -147,4 +153,52 @@ public class CheckpointsTest {
         assertEquals(1, store.getCheckpoints().size());
         assertEquals(r1, store.getCheckpoints().getOldestRevisionToKeep());
     }
+
+    @Test
+    public void readOldFormat() throws Exception {
+        clock.waitUntil(System.currentTimeMillis());
+        DocumentStore docStore = store.getDocumentStore();
+        Map<String, String> empty = Collections.emptyMap();
+        Revision r = Revision.fromString(
+                store.checkpoint(Integer.MAX_VALUE, empty));
+
+        Document doc = docStore.find(Collection.SETTINGS, "checkpoint");
+        assertNotNull(doc);
+        @SuppressWarnings("unchecked")
+        Map<Revision, String> data = (Map<Revision, String>) doc.get("data");
+        assertNotNull(data);
+        assertEquals(1, data.size());
+        assertTrue(data.containsKey(r));
+
+        // manually update checkpoint data with old format
+        UpdateOp update = new UpdateOp("checkpoint", false);
+        long expires = clock.getTime() + 1000 + Integer.MAX_VALUE;
+        update.setMapEntry("data", r, String.valueOf(expires));
+        assertNotNull(docStore.findAndUpdate(Collection.SETTINGS, update));
+
+        Checkpoints.Info info = store.getCheckpoints().getCheckpoints().get(r);
+        assertNotNull(info);
+        assertEquals(expires, info.getExpiryTime());
+    }
+
+    @Test
+    public void expiryOverflow() throws Exception {
+        clock.waitUntil(System.currentTimeMillis());
+        Map<String, String> empty = Collections.emptyMap();
+        Revision r = Revision.fromString(
+                store.checkpoint(Long.MAX_VALUE, empty));
+        Checkpoints.Info info = store.getCheckpoints().getCheckpoints().get(r);
+        assertNotNull(info);
+        assertEquals(Long.MAX_VALUE, info.getExpiryTime());
+    }
+
+    @Test
+    public void userInfoNamedExpires() throws Exception {
+        Map<String, String> props = ImmutableMap.of("expires", "today");
+        Revision r = Revision.fromString(
+                store.checkpoint(Integer.MAX_VALUE, props));
+        Map<String, String> info = store.checkpointInfo(r.toString());
+        assertNotNull(info);
+        assertEquals(props, info);
+    }
 }

Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/checkpoint/Checkpoints.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/checkpoint/Checkpoints.java?rev=1642837&r1=1642836&r2=1642837&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/checkpoint/Checkpoints.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/checkpoint/Checkpoints.java Tue Dec  2 10:14:22 2014
@@ -175,10 +175,10 @@ public abstract class Checkpoints {
         @Override
         public List<CP> list() {
             List<CP> list = Lists.newArrayList();
-            for (Map.Entry<Revision, String> entry : getCheckpoints(store).entrySet()) {
+            for (Map.Entry<Revision, Long> entry : getCheckpoints(store).entrySet()) {
                 list.add(new CP(entry.getKey().toString(),
                         entry.getKey().getTimestamp(),
-                        Long.parseLong(entry.getValue())));
+                        entry.getValue()));
             }
             return list;
         }

Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsHelper.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsHelper.java?rev=1642837&r1=1642836&r2=1642837&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsHelper.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsHelper.java Tue Dec  2 10:14:22 2014
@@ -18,16 +18,26 @@
  */
 package org.apache.jackrabbit.oak.plugins.document;
 
+import java.util.Map;
 import java.util.SortedMap;
 
+import com.google.common.collect.Maps;
+
+import static org.apache.jackrabbit.oak.plugins.document.Checkpoints.Info;
+
 /**
  * Helper class to access package private functionality.
  */
 public abstract class CheckpointsHelper {
 
-    public static SortedMap<Revision, String> getCheckpoints(
+    public static SortedMap<Revision, Long> getCheckpoints(
             DocumentNodeStore store) {
-        return store.getCheckpoints().getCheckpoints();
+        SortedMap<Revision, Info> checkpoints = store.getCheckpoints().getCheckpoints();
+        SortedMap<Revision, Long> map = Maps.newTreeMap(checkpoints.comparator());
+        for (Map.Entry<Revision, Info> entry : checkpoints.entrySet()) {
+            map.put(entry.getKey(), entry.getValue().getExpiryTime());
+        }
+        return map;
     }
 
     public static long removeAll(DocumentNodeStore store) {

Modified: jackrabbit/oak/trunk/oak-run/src/main/js/oak-mongo.js
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/js/oak-mongo.js?rev=1642837&r1=1642836&r2=1642837&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/js/oak-mongo.js (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/js/oak-mongo.js Tue Dec  2 10:14:22 2014
@@ -222,7 +222,12 @@ var oak = (function(global){
         var r;
         for (r in data) {
             var rev = new Revision(r);
-            var exp = data[r];
+            var exp;
+            if (exp.charAt(0) == '{') {
+                exp = JSON.parse(data[r])["expires"];
+            } else {
+                exp = data[r];
+            }
             result[r] = {created:rev.asDate(), expires:new Date(parseInt(exp, 10))};
         }
         return result;