Author: frm
Date: Tue Aug 23 14:33:49 2016
New Revision: 1757382

URL: http://svn.apache.org/viewvc?rev=1757382&view=rev
Log:
OAK-4690 - Create utility backends

Added:
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Backup.java
   (with props)
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java
   (with props)
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Compact.java
   (with props)
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugSegments.java
   (with props)
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugStore.java
   (with props)
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugTars.java
   (with props)
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Diff.java
   (with props)
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/GenerationGraph.java
   (with props)
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/History.java
   (with props)
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/PrintingDiff.java
      - copied, changed from r1757147, 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PrintingDiff.java
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Restore.java
   (with props)
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Revisions.java
   (with props)
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/SegmentGraph.java
   (with props)
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Utils.java
   (with props)

Added: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Backup.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Backup.java?rev=1757382&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Backup.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Backup.java
 Tue Aug 23 14:33:49 2016
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.tool;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static 
org.apache.jackrabbit.oak.segment.tool.Utils.newBasicReadOnlyBlobStore;
+import static 
org.apache.jackrabbit.oak.segment.tool.Utils.openReadOnlyFileStore;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.jackrabbit.oak.backup.FileStoreBackup;
+import org.apache.jackrabbit.oak.segment.file.FileStore;
+import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
+
+/**
+ * Perform a backup of a segment store into a specified folder.
+ */
+public class Backup implements Runnable {
+
+    /**
+     * Create a builder for the {@link Backup} command.
+     *
+     * @return an instance of {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Collect options for the {@link Backup} command.
+     */
+    public static class Builder {
+
+        private File source;
+
+        private File target;
+
+        private boolean fakeBlobStore = FileStoreBackup.USE_FAKE_BLOBSTORE;
+
+        private Builder() {
+            // Prevent external instantiation.
+        }
+
+        /**
+         * The source folder of the backup. This parameter is required. The 
path
+         * should point to a valid segment store.
+         *
+         * @param source the path of the source folder of the backup.
+         * @return this builder.
+         */
+        public Builder withSource(File source) {
+            this.source = checkNotNull(source);
+            return this;
+        }
+
+        /**
+         * The target folder of the backup. This parameter is required. The 
path
+         * should point to an existing segment store or to an empty folder. If
+         * the folder doesn't exist, it will be created.
+         *
+         * @param target the path of the target folder of the backup.
+         * @return this builder.
+         */
+        public Builder withTarget(File target) {
+            this.target = checkNotNull(target);
+            return this;
+        }
+
+        /**
+         * Simulate the existence of a file-based blob store. This parameter is
+         * not required and defaults to {@code false}.
+         *
+         * @param fakeBlobStore {@code true} if a file-based blob store should
+         *                      be simulated, {@code false} otherwise.
+         * @return this builder.
+         */
+        public Builder withFakeBlobStore(boolean fakeBlobStore) {
+            this.fakeBlobStore = fakeBlobStore;
+            return this;
+        }
+
+        /**
+         * Create an executable version of the {@link Backup} command.
+         *
+         * @return an instance of {@link Runnable}.
+         */
+        public Runnable build() {
+            checkNotNull(source);
+            checkNotNull(target);
+            return new Backup(this);
+        }
+
+    }
+
+    private final File source;
+
+    private final File target;
+
+    private final boolean fakeBlobStore;
+
+    private Backup(Builder builder) {
+        this.source = builder.source;
+        this.target = builder.target;
+        this.fakeBlobStore = builder.fakeBlobStore;
+    }
+
+    @Override
+    public void run() {
+        try (FileStore fs = newFileStore()) {
+            FileStoreBackup.backup(fs.getReader(), fs.getRevisions(), target);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private FileStore newFileStore() throws IOException, 
InvalidFileStoreVersionException {
+        if (fakeBlobStore) {
+            return openReadOnlyFileStore(source, newBasicReadOnlyBlobStore());
+        }
+
+        return openReadOnlyFileStore(source);
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Backup.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java?rev=1757382&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java
 Tue Aug 23 14:33:49 2016
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.tool;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.File;
+
+import org.apache.jackrabbit.oak.segment.file.tooling.ConsistencyChecker;
+
+/**
+ * Perform a consistency check on an existing segment store.
+ */
+public class Check implements Runnable {
+
+    /**
+     * Create a builder for the {@link Check} command.
+     *
+     * @return an instance of {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Collect options for the {@link Check} command.
+     */
+    public static class Builder {
+
+        private File path;
+
+        private String journal;
+
+        private boolean fullTraversal;
+
+        private long debugInterval = Long.MAX_VALUE;
+
+        private long minimumBinaryLength;
+
+        private Builder() {
+            // Prevent external instantiation.
+        }
+
+        /**
+         * The path to an existing segment store. This parameter is required.
+         *
+         * @param path the path to an existing segment store.
+         * @return this builder.
+         */
+        public Builder withPath(File path) {
+            this.path = checkNotNull(path);
+            return this;
+        }
+
+        /**
+         * The path to the journal of the segment store. This parameter is
+         * required.
+         *
+         * @param journal the path to the journal of the segment store.
+         * @return this builder.
+         */
+        public Builder withJournal(String journal) {
+            this.journal = checkNotNull(journal);
+            return this;
+        }
+
+        /**
+         * Should a full traversal of the segment store be performed? This
+         * parameter is not required and defaults to {@code false}.
+         *
+         * @param fullTraversal {@code true} if a full traversal should be
+         *                      performed, {@code false} otherwise.
+         * @return this builder.
+         */
+        public Builder withFullTraversal(boolean fullTraversal) {
+            this.fullTraversal = fullTraversal;
+            return this;
+        }
+
+        /**
+         * Number of seconds between successive debug print statements. This
+         * parameter is not required and defaults to an arbitrary large number.
+         *
+         * @param debugInterval number of seconds between successive debug 
print
+         *                      statements. It must be strictly positive.
+         * @return this builder.
+         */
+        public Builder withDebugInterval(long debugInterval) {
+            checkArgument(debugInterval > 0);
+            this.debugInterval = debugInterval;
+            return this;
+        }
+
+        /**
+         * Minimum amount of bytes to read from binary properties. This
+         * parameter is not required and defaults to zero.
+         *
+         * @param minimumBinaryLength minimum amount of bytes to read from
+         *                            binary properties. If this parameter is
+         *                            set to {@code -1}, every binary property
+         *                            is read in its entirety.
+         * @return
+         */
+        public Builder withMinimumBinaryLength(long minimumBinaryLength) {
+            this.minimumBinaryLength = minimumBinaryLength;
+            return this;
+        }
+
+        /**
+         * Create an executable version of the {@link Check} command.
+         *
+         * @return an instance of {@link Runnable}.
+         */
+        public Runnable build() {
+            checkNotNull(path);
+            checkNotNull(journal);
+            return new Check(this);
+        }
+
+    }
+
+    private final File path;
+
+    private final String journal;
+
+    private final boolean fullTraversal;
+
+    private final long debugInterval;
+
+    private final long minimumBinaryLength;
+
+    private Check(Builder builder) {
+        this.path = builder.path;
+        this.journal = builder.journal;
+        this.fullTraversal = builder.fullTraversal;
+        this.debugInterval = builder.debugInterval;
+        this.minimumBinaryLength = builder.minimumBinaryLength;
+    }
+
+    @Override
+    public void run() {
+        try {
+            ConsistencyChecker.checkConsistency(path, journal, fullTraversal, 
debugInterval, minimumBinaryLength);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Compact.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Compact.java?rev=1757382&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Compact.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Compact.java
 Tue Aug 23 14:33:49 2016
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.tool;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static 
org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.defaultGCOptions;
+import static 
org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.fileStoreBuilder;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions;
+import org.apache.jackrabbit.oak.segment.file.FileStore;
+import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
+import org.apache.jackrabbit.oak.segment.file.JournalReader;
+
+/**
+ * Perform an offline compaction of an existing segment store.
+ */
+public class Compact implements Runnable {
+
+    /**
+     * Create a builder for the {@link Compact} command.
+     *
+     * @return an instance of {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Collect options for the {@link Compact} command.
+     */
+    public static class Builder {
+
+        private File path;
+
+        private boolean force;
+
+        private Builder() {
+            // Prevent external instantiation.
+        }
+
+        /**
+         * The path to an existing segment store. This parameter is required.
+         *
+         * @param path the path to an existing segment store.
+         * @return this builder.
+         */
+        public Builder withPath(File path) {
+            this.path = checkNotNull(path);
+            return this;
+        }
+
+        /**
+         * Set whether or not to force compact concurrent commits on top of
+         * already compacted commits after the maximum number of retries has
+         * been reached. Force committing tries to exclusively write lock the
+         * node store.
+         *
+         * @param force {@code true} to force an exclusive commit of the
+         *              compacted state, {@code false} otherwise.
+         * @return this builder.
+         */
+        public Builder withForce(boolean force) {
+            this.force = force;
+            return this;
+        }
+
+        /**
+         * Create an executable version of the {@link Compact} command.
+         *
+         * @return an instance of {@link Runnable}.
+         */
+        public Runnable build() {
+            checkNotNull(path);
+            return new Compact(this);
+        }
+
+    }
+
+    private final File path;
+
+    private final boolean force;
+
+    private Compact(Builder builder) {
+        this.path = builder.path;
+        this.force = builder.force;
+    }
+
+    @Override
+    public void run() {
+        try {
+            compact();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void compact() throws IOException, 
InvalidFileStoreVersionException {
+        try (FileStore store = newFileStore()) {
+            store.compact();
+        }
+
+        System.out.println("    -> cleaning up");
+        try (FileStore store = newFileStore()) {
+            for (File file : store.cleanup()) {
+                if (!file.exists() || file.delete()) {
+                    System.out.println("    -> removed old file " + 
file.getName());
+                } else {
+                    System.out.println("    -> failed to remove old file " + 
file.getName());
+                }
+            }
+
+            String head;
+
+            File journal = new File(path, "journal.log");
+
+            try (JournalReader journalReader = new JournalReader(journal)) {
+                head = journalReader.next() + " root " + 
System.currentTimeMillis() + "\n";
+            }
+
+            try (RandomAccessFile journalFile = new RandomAccessFile(journal, 
"rw")) {
+                System.out.println("    -> writing new " + journal.getName() + 
": " + head);
+                journalFile.setLength(0);
+                journalFile.writeBytes(head);
+                journalFile.getChannel().force(false);
+            }
+        }
+    }
+
+    private FileStore newFileStore() throws IOException, 
InvalidFileStoreVersionException {
+        return 
fileStoreBuilder(path.getAbsoluteFile()).withGCOptions(newGCOptions()).build();
+    }
+
+    private SegmentGCOptions newGCOptions() {
+        return defaultGCOptions().setForceAfterFail(force).setOffline();
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Compact.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugSegments.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugSegments.java?rev=1757382&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugSegments.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugSegments.java
 Tue Aug 23 14:33:49 2016
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.tool;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.oak.segment.RecordId.fromString;
+import static 
org.apache.jackrabbit.oak.segment.tool.Utils.openReadOnlyFileStore;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.commons.json.JsopBuilder;
+import org.apache.jackrabbit.oak.json.JsopDiff;
+import org.apache.jackrabbit.oak.segment.RecordId;
+import org.apache.jackrabbit.oak.segment.SegmentId;
+import org.apache.jackrabbit.oak.segment.SegmentNodeState;
+import org.apache.jackrabbit.oak.segment.file.FileStore;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * Print debugging information about segments, node records and node record
+ * ranges.
+ */
+public class DebugSegments implements Runnable {
+
+    private static final Pattern SEGMENT_REGEX = 
Pattern.compile("([0-9a-f-]+)|(([0-9a-f-]+:[0-9a-f]+)(-([0-9a-f-]+:[0-9a-f]+))?)?(/.*)?");
+
+    /**
+     * Create a builder for the {@link DebugSegments} command.
+     *
+     * @return an instance of {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Collect options for the {@link DebugSegments} command.
+     */
+    public static class Builder {
+
+        private File path;
+
+        private List<String> segments = new ArrayList<>();
+
+        private Builder() {
+            // Prevent external instantiation.
+        }
+
+        /**
+         * The path to an existing segment store. This parameter is required.
+         *
+         * @param path the path to an existing segment store.
+         * @return this builder.
+         */
+        public Builder withPath(File path) {
+            this.path = checkNotNull(path);
+            return this;
+        }
+
+        /**
+         * Add a segment, node record or node record range. It is mandatory to
+         * add at least one of a segment, node record or node record range.
+         * <p>
+         * A segment is specified by its ID, which is specified as a sequence 
of
+         * hexadecimal digits and dashes. In example, {@code
+         * 333dc24d-438f-4cca-8b21-3ebf67c05856}.
+         * <p>
+         * A node record is specified by its identifier, with an optional path.
+         * In example, {@code 
333dc24d-438f-4cca-8b21-3ebf67c05856:12345/path/to/child}.
+         * If a path is not specified, it is take to be {@code /}. The command
+         * will print information about the node provided by record ID and 
about
+         * every child identified by the path.
+         * <p>
+         * A node range record is specified by two node identifiers separated 
by
+         * a dash. In example, {@code 
333dc24d-438f-4cca-8b21-3ebf67c05856:12345-46116fda-7a72-4dbc-af88-a09322a7753a:67890}.
+         * The command will perform a diff between the two records and print 
the
+         * result in the JSOP format.
+         *
+         * @param segment The specification for a segment, a node record or a
+         *                node record range.
+         * @return this builder.
+         */
+        public Builder withSegment(String segment) {
+            this.segments.add(checkNotNull(segment));
+            return this;
+        }
+
+        /**
+         * Create an executable version of the {@link DebugSegments} command.
+         *
+         * @return an instance of {@link Runnable}.
+         */
+        public Runnable build() {
+            checkNotNull(path);
+            checkArgument(segments.size() > 0);
+            return new DebugSegments(this);
+        }
+
+    }
+
+    private final File path;
+
+    private final List<String> segments;
+
+    private DebugSegments(Builder builder) {
+        this.path = builder.path;
+        this.segments = new ArrayList<>(builder.segments);
+    }
+
+    @Override
+    public void run() {
+        try (FileStore store = openReadOnlyFileStore(path)) {
+            debugSegments(store);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void debugSegments(FileStore store) {
+        for (String segment : segments) {
+            debugSegment(store, segment);
+        }
+    }
+
+    private void debugSegment(FileStore store, String segment) {
+        Matcher matcher = SEGMENT_REGEX.matcher(segment);
+
+        if (!matcher.matches()) {
+            System.err.println("Unknown argument: " + segment);
+            return;
+        }
+
+        if (matcher.group(1) != null) {
+            UUID uuid = UUID.fromString(matcher.group(1));
+            SegmentId id = store.newSegmentId(uuid.getMostSignificantBits(), 
uuid.getLeastSignificantBits());
+            System.out.println(id.getSegment());
+            return;
+        }
+
+        RecordId id1 = store.getRevisions().getHead();
+        RecordId id2 = null;
+
+        if (matcher.group(2) != null) {
+            id1 = fromString(store, matcher.group(3));
+            if (matcher.group(4) != null) {
+                id2 = fromString(store, matcher.group(5));
+            }
+        }
+
+        String path = "/";
+
+        if (matcher.group(6) != null) {
+            path = matcher.group(6);
+        }
+
+        if (id2 == null) {
+            NodeState node = store.getReader().readNode(id1);
+            System.out.println("/ (" + id1 + ") -> " + node);
+            for (String name : PathUtils.elements(path)) {
+                node = node.getChildNode(name);
+                RecordId nid = null;
+                if (node instanceof SegmentNodeState) {
+                    nid = ((SegmentNodeState) node).getRecordId();
+                }
+                System.out.println("  " + name + " (" + nid + ") -> " + node);
+            }
+            return;
+        }
+
+        NodeState node1 = store.getReader().readNode(id1);
+        NodeState node2 = store.getReader().readNode(id2);
+        for (String name : PathUtils.elements(path)) {
+            node1 = node1.getChildNode(name);
+            node2 = node2.getChildNode(name);
+        }
+        System.out.println(JsopBuilder.prettyPrint(JsopDiff.diffToJsop(node1, 
node2)));
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugSegments.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugStore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugStore.java?rev=1757382&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugStore.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugStore.java
 Tue Aug 23 14:33:49 2016
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.tool;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Sets.newHashSet;
+import static org.apache.commons.io.FileUtils.byteCountToDisplaySize;
+import static org.apache.jackrabbit.oak.segment.RecordType.NODE;
+import static 
org.apache.jackrabbit.oak.segment.tool.Utils.openReadOnlyFileStore;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Queues;
+import org.apache.jackrabbit.oak.segment.RecordId;
+import org.apache.jackrabbit.oak.segment.RecordUsageAnalyser;
+import org.apache.jackrabbit.oak.segment.Segment;
+import org.apache.jackrabbit.oak.segment.SegmentId;
+import org.apache.jackrabbit.oak.segment.file.FileStore;
+
+/**
+ * Print debugging information about a segment store.
+ */
+public class DebugStore implements Runnable {
+
+    /**
+     * Create a builder for the {@link DebugStore} command.
+     *
+     * @return an instance of {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Collect options for the {@link DebugStore} command.
+     */
+    public static class Builder {
+
+        private File path;
+
+        private Builder() {
+            // Prevent external instantiation
+        }
+
+        /**
+         * The path to an existing segment store. This parameter is required.
+         *
+         * @param path the path to an existing segment store.
+         * @return this builder.
+         */
+        public Builder withPath(File path) {
+            this.path = checkNotNull(path);
+            return this;
+        }
+
+        /**
+         * Create an executable version of the {@link DebugStore} command.
+         *
+         * @return an instance of {@link Runnable}.
+         */
+        public Runnable build() {
+            checkNotNull(path);
+            return new DebugStore(this);
+        }
+
+    }
+
+    private final File path;
+
+    private DebugStore(Builder builder) {
+        this.path = builder.path;
+    }
+
+    @Override
+    public void run() {
+        try (FileStore store = openReadOnlyFileStore(path)) {
+            debugFileStore(store);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static void debugFileStore(FileStore store) {
+        Map<SegmentId, List<SegmentId>> idmap = Maps.newHashMap();
+        int dataCount = 0;
+        long dataSize = 0;
+        int bulkCount = 0;
+        long bulkSize = 0;
+
+        RecordUsageAnalyser analyser = new 
RecordUsageAnalyser(store.getReader());
+
+        for (SegmentId id : store.getSegmentIds()) {
+            if (id.isDataSegmentId()) {
+                Segment segment = id.getSegment();
+                dataCount++;
+                dataSize += segment.size();
+                idmap.put(id, segment.getReferencedIds());
+                analyseSegment(segment, analyser);
+            } else if (id.isBulkSegmentId()) {
+                bulkCount++;
+                bulkSize += id.getSegment().size();
+                idmap.put(id, Collections.<SegmentId>emptyList());
+            }
+        }
+        System.out.println("Total size:");
+        System.out.format("%s in %6d data segments%n", 
byteCountToDisplaySize(dataSize), dataCount);
+        System.out.format("%s in %6d bulk segments%n", 
byteCountToDisplaySize(bulkSize), bulkCount);
+        System.out.println(analyser.toString());
+
+        Set<SegmentId> garbage = newHashSet(idmap.keySet());
+        Queue<SegmentId> queue = Queues.newArrayDeque();
+        queue.add(store.getRevisions().getHead().getSegmentId());
+        while (!queue.isEmpty()) {
+            SegmentId id = queue.remove();
+            if (garbage.remove(id)) {
+                queue.addAll(idmap.get(id));
+            }
+        }
+        dataCount = 0;
+        dataSize = 0;
+        bulkCount = 0;
+        bulkSize = 0;
+        for (SegmentId id : garbage) {
+            if (id.isDataSegmentId()) {
+                dataCount++;
+                dataSize += id.getSegment().size();
+            } else if (id.isBulkSegmentId()) {
+                bulkCount++;
+                bulkSize += id.getSegment().size();
+            }
+        }
+        System.out.format("%nAvailable for garbage collection:%n");
+        System.out.format("%s in %6d data segments%n", 
byteCountToDisplaySize(dataSize), dataCount);
+        System.out.format("%s in %6d bulk segments%n", 
byteCountToDisplaySize(bulkSize), bulkCount);
+    }
+
+    private static void analyseSegment(Segment segment, RecordUsageAnalyser 
analyser) {
+        for (int k = 0; k < segment.getRootCount(); k++) {
+            if (segment.getRootType(k) == NODE) {
+                RecordId nodeId = new RecordId(segment.getSegmentId(), 
segment.getRootOffset(k));
+                try {
+                    analyser.analyseNode(nodeId);
+                } catch (Exception e) {
+                    System.err.format("Error while processing node at %s", 
nodeId);
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugStore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugTars.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugTars.java?rev=1757382&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugTars.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugTars.java
 Tue Aug 23 14:33:49 2016
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.tool;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Sets.newTreeSet;
+import static 
org.apache.jackrabbit.oak.segment.SegmentNodeStateHelper.getTemplateId;
+import static 
org.apache.jackrabbit.oak.segment.tool.Utils.openReadOnlyFileStore;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.jcr.PropertyType;
+
+import com.google.common.escape.Escapers;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.segment.RecordId;
+import org.apache.jackrabbit.oak.segment.SegmentBlob;
+import org.apache.jackrabbit.oak.segment.SegmentId;
+import org.apache.jackrabbit.oak.segment.SegmentNodeState;
+import org.apache.jackrabbit.oak.segment.SegmentPropertyState;
+import org.apache.jackrabbit.oak.segment.file.FileStore;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * Print information about one or more TAR files from an existing segment 
store.
+ */
+public class DebugTars implements Runnable {
+
+    /**
+     * Create a builder for the {@link DebugTars} command.
+     *
+     * @return an instance of {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Collect options for the {@link DebugTars} command.
+     */
+    public static class Builder {
+
+        private File path;
+
+        private List<String> tars = new ArrayList<>();
+
+        private int maxCharDisplay = Integer.getInteger("max.char.display", 
60);
+
+        private Builder() {
+            // Prevent external instantiation.
+        }
+
+        /**
+         * The path to an existing segment store. This parameter is required.
+         *
+         * @param path the path to an existing segment store.
+         * @return this builder.
+         */
+        public Builder withPath(File path) {
+            this.path = checkNotNull(path);
+            return this;
+        }
+
+        /**
+         * Add a TAR file. The command will print information about every TAR
+         * file added via this method. It is mandatory to add at least one TAR
+         * file.
+         *
+         * @param tar the name of a TAR file.
+         * @return this builder.
+         */
+        public Builder withTar(String tar) {
+            checkArgument(tar.endsWith(".tar"));
+            this.tars.add(tar);
+            return this;
+        }
+
+        /**
+         * Create an executable version of the {@link DebugTars} command.
+         *
+         * @return an instance of {@link Runnable}.
+         */
+        public Runnable build() {
+            checkNotNull(path);
+            checkArgument(tars.size() > 0);
+            return new DebugTars(this);
+        }
+
+    }
+
+    private final File path;
+
+    private final List<String> tars;
+
+    private final int maxCharDisplay;
+
+    private DebugTars(Builder builder) {
+        this.path = builder.path;
+        this.tars = new ArrayList<>(builder.tars);
+        this.maxCharDisplay = builder.maxCharDisplay;
+    }
+
+    @Override
+    public void run() {
+        try (FileStore store = openReadOnlyFileStore(path)) {
+            debugTarFiles(store);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void debugTarFiles(FileStore store) {
+        for (String tar : tars) {
+            debugTarFile(store, tar);
+        }
+    }
+
+    private void debugTarFile(FileStore store, String t) {
+        File tar = new File(path, t);
+
+        if (!tar.exists()) {
+            System.out.println("file doesn't exist, skipping " + t);
+            return;
+        }
+
+        System.out.println("Debug file " + tar + "(" + tar.length() + ")");
+        Set<UUID> uuids = new HashSet<UUID>();
+        boolean hasrefs = false;
+
+        for (Map.Entry<String, Set<UUID>> e : 
store.getTarReaderIndex().entrySet()) {
+            if (e.getKey().endsWith(t)) {
+                hasrefs = true;
+                uuids = e.getValue();
+            }
+        }
+
+        if (hasrefs) {
+            System.out.println("SegmentNodeState references to " + t);
+            List<String> paths = new ArrayList<String>();
+            filterNodeStates(uuids, paths, store.getHead(), "/");
+            for (String p : paths) {
+                System.out.println("  " + p);
+            }
+        } else {
+            System.out.println("No references to " + t);
+        }
+
+        try {
+            Map<UUID, List<UUID>> graph = store.getTarGraph(t);
+            System.out.println();
+            System.out.println("Tar graph:");
+            for (Map.Entry<UUID, List<UUID>> entry : graph.entrySet()) {
+                System.out.println("" + entry.getKey() + '=' + 
entry.getValue());
+            }
+        } catch (IOException e) {
+            System.out.println("Error getting tar graph:");
+            e.printStackTrace();
+        }
+    }
+
+    private void filterNodeStates(Set<UUID> uuids, List<String> paths, 
SegmentNodeState state, String path) {
+        Set<String> localPaths = newTreeSet();
+        for (PropertyState ps : state.getProperties()) {
+            if (ps instanceof SegmentPropertyState) {
+                SegmentPropertyState sps = (SegmentPropertyState) ps;
+                RecordId recordId = sps.getRecordId();
+                UUID id = recordId.getSegmentId().asUUID();
+                if (uuids.contains(id)) {
+                    if (ps.getType().tag() == PropertyType.STRING) {
+                        String val = "";
+                        if (ps.count() > 0) {
+                            // only shows the first value, do we need more?
+                            val = displayString(ps.getValue(Type.STRING, 0));
+                        }
+                        localPaths.add(getLocalPath(path, ps, val, recordId));
+                    } else {
+                        localPaths.add(getLocalPath(path, ps, recordId));
+                    }
+
+                }
+                if (ps.getType().tag() == PropertyType.BINARY) {
+                    // look for extra segment references
+                    for (int i = 0; i < ps.count(); i++) {
+                        Blob b = ps.getValue(Type.BINARY, i);
+                        for (SegmentId sbid : 
SegmentBlob.getBulkSegmentIds(b)) {
+                            UUID bid = sbid.asUUID();
+                            if (!bid.equals(id) && uuids.contains(bid)) {
+                                localPaths.add(getLocalPath(path, ps, 
recordId));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        RecordId stateId = state.getRecordId();
+        if (uuids.contains(stateId.getSegmentId().asUUID())) {
+            localPaths.add(path + " [SegmentNodeState@" + stateId + "]");
+        }
+
+        RecordId templateId = getTemplateId(state);
+        if (uuids.contains(templateId.getSegmentId().asUUID())) {
+            localPaths.add(path + "[Template@" + templateId + "]");
+        }
+        paths.addAll(localPaths);
+        for (ChildNodeEntry ce : state.getChildNodeEntries()) {
+            NodeState c = ce.getNodeState();
+            if (c instanceof SegmentNodeState) {
+                filterNodeStates(uuids, paths, (SegmentNodeState) c,
+                        path + ce.getName() + "/");
+            }
+        }
+    }
+
+    private String getLocalPath(String path, PropertyState ps, String value, 
RecordId id) {
+        return path + ps.getName() + " = " + value + " [SegmentPropertyState<" 
+ ps.getType() + ">@" + id + "]";
+    }
+
+    private String getLocalPath(String path, PropertyState ps, RecordId id) {
+        return path + ps + " [SegmentPropertyState<" + ps.getType() + ">@" + 
id + "]";
+    }
+
+    private String displayString(String value) {
+        if (maxCharDisplay > 0 && value.length() > maxCharDisplay) {
+            value = value.substring(0, maxCharDisplay) + "... (" + 
value.length() + " chars)";
+        }
+
+        String escaped = Escapers.builder()
+                .setSafeRange(' ', '~')
+                .addEscape('"', "\\\"")
+                .addEscape('\\', "\\\\")
+                .build()
+                .escape(value);
+
+        return '"' + escaped + '"';
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/DebugTars.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Diff.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Diff.java?rev=1757382&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Diff.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Diff.java
 Tue Aug 23 14:33:49 2016
@@ -0,0 +1,302 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.tool;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Lists.reverse;
+import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
+import static org.apache.jackrabbit.oak.segment.RecordId.fromString;
+import static 
org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.fileStoreBuilder;
+import static 
org.apache.jackrabbit.oak.segment.tool.Utils.newBasicReadOnlyBlobStore;
+import static org.apache.jackrabbit.oak.segment.tool.Utils.readRevisions;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.jackrabbit.oak.segment.RecordId;
+import org.apache.jackrabbit.oak.segment.SegmentNotFoundException;
+import org.apache.jackrabbit.oak.segment.file.FileStore.ReadOnlyStore;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * Shows the differences between two head states.
+ */
+public class Diff implements Runnable {
+
+    /**
+     * Create a builder for the {@link Diff} command.
+     *
+     * @return an instance of {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Collect options for the {@link Diff} command.
+     */
+    public static class Builder {
+
+        private File path;
+
+        private String interval;
+
+        private boolean incremental;
+
+        private File out;
+
+        private String filter;
+
+        private boolean ignoreMissingSegments;
+
+        private Builder() {
+            // Prevent external instantiation.
+        }
+
+        /**
+         * The path to an existing segment store. This parameter is required.
+         *
+         * @param path the path to an existing segment store.
+         * @return this builder.
+         */
+        public Builder withPath(File path) {
+            this.path = checkNotNull(path);
+            return this;
+        }
+
+        /**
+         * The two node records to diff specified as a record ID interval. This
+         * parameter is required.
+         * <p>
+         * The interval is specified as two record IDs separated by two full
+         * stops ({@code ..}). In example, {@code 
333dc24d-438f-4cca-8b21-3ebf67c05856:12345..46116fda-7a72-4dbc-af88-a09322a7753a:67890}.
+         * Instead of using a full record ID, it is possible to use the special
+         * placeholder {@code head}. This placeholder is translated to the
+         * record ID of the most recent head state.
+         *
+         * @param interval an interval between two node record IDs.
+         * @return this builder.
+         */
+        public Builder withInterval(String interval) {
+            this.interval = checkNotNull(interval);
+            return this;
+        }
+
+        /**
+         * Set whether or not to perform an incremental diff of the specified
+         * interval. An incremental diff shows every change between the two
+         * records at every revision available to the segment store. This
+         * parameter is not mandatory and defaults to {@code false}.
+         *
+         * @param incremental {@code true} to perform an incremental diff,
+         *                    {@code false} otherwise.
+         * @return this builder.
+         */
+        public Builder withIncremental(boolean incremental) {
+            this.incremental = incremental;
+            return this;
+        }
+
+        /**
+         * The file where the output of this command is stored. this parameter
+         * is mandatory.
+         *
+         * @param file the output file.
+         * @return this builder.
+         */
+        public Builder withOutput(File file) {
+            this.out = checkNotNull(file);
+            return this;
+        }
+
+        /**
+         * The path to a subtree. If specified, this parameter allows to
+         * restrict the diff to the specified subtree. This parameter is not
+         * mandatory and defaults to the entire tree.
+         *
+         * @param filter a path used as as filter for the resulting diff.
+         * @return this builder.
+         */
+        public Builder withFilter(String filter) {
+            this.filter = checkNotNull(filter);
+            return this;
+        }
+
+        /**
+         * Whether to ignore exceptions caused by missing segments in the
+         * segment store. This paramter is not mandatory and defaults to {@code
+         * false}.
+         *
+         * @param ignoreMissingSegments {@code true} to ignore exceptions 
caused
+         *                              by missing segments, {@code false}
+         *                              otherwise.
+         * @return this builder.
+         */
+        public Builder withIgnoreMissingSegments(boolean 
ignoreMissingSegments) {
+            this.ignoreMissingSegments = ignoreMissingSegments;
+            return this;
+        }
+
+        /**
+         * Create an executable version of the {@link Diff} command.
+         *
+         * @return an instance of {@link Runnable}.
+         */
+        public Runnable build() {
+            checkNotNull(path);
+            checkNotNull(interval);
+            checkNotNull(out);
+            checkNotNull(filter);
+            return new Diff(this);
+        }
+
+    }
+
+    private final File path;
+
+    private final String interval;
+
+    private final boolean incremental;
+
+    private final File out;
+
+    private final String filter;
+
+    private final boolean ignoreMissingSegments;
+
+    private Diff(Builder builder) {
+        this.path = builder.path;
+        this.interval = builder.interval;
+        this.incremental = builder.incremental;
+        this.out = builder.out;
+        this.filter = builder.filter;
+        this.ignoreMissingSegments = builder.ignoreMissingSegments;
+    }
+
+    @Override
+    public void run() {
+        try {
+            diff();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void diff() throws Exception {
+        System.out.println("Store " + path);
+        System.out.println("Writing diff to " + out);
+
+        String[] tokens = interval.trim().split("\\.\\.");
+
+        if (tokens.length != 2) {
+            System.out.println("Error parsing revision interval '" + interval 
+ "'.");
+            return;
+        }
+
+        try (ReadOnlyStore store = 
fileStoreBuilder(path).withBlobStore(newBasicReadOnlyBlobStore()).buildReadOnly())
 {
+            RecordId idL;
+
+            try {
+                if (tokens[0].equalsIgnoreCase("head")) {
+                    idL = store.getRevisions().getHead();
+                } else {
+                    idL = fromString(store, tokens[0]);
+                }
+            } catch (IllegalArgumentException e) {
+                System.out.println("Invalid left endpoint for interval " + 
interval);
+                return;
+            }
+
+            RecordId idR;
+
+            try {
+                if (tokens[1].equalsIgnoreCase("head")) {
+                    idR = store.getRevisions().getHead();
+                } else {
+                    idR = fromString(store, tokens[1]);
+                }
+            } catch (IllegalArgumentException e) {
+                System.out.println("Invalid left endpoint for interval " + 
interval);
+                return;
+            }
+
+            long start = System.currentTimeMillis();
+
+            try (PrintWriter pw = new PrintWriter(out)) {
+                if (incremental) {
+                    List<String> revs = readRevisions(path);
+                    System.out.println("Generating diff between " + idL + " 
and " + idR + " incrementally. Found " + revs.size() + " revisions.");
+
+                    int s = revs.indexOf(idL.toString10());
+                    int e = revs.indexOf(idR.toString10());
+                    if (s == -1 || e == -1) {
+                        System.out.println("Unable to match input revisions 
with FileStore.");
+                        return;
+                    }
+                    List<String> revDiffs = revs.subList(Math.min(s, e), 
Math.max(s, e) + 1);
+                    if (s > e) {
+                        // reverse list
+                        revDiffs = reverse(revDiffs);
+                    }
+                    if (revDiffs.size() < 2) {
+                        System.out.println("Nothing to diff: " + revDiffs);
+                        return;
+                    }
+                    Iterator<String> revDiffsIt = revDiffs.iterator();
+                    RecordId idLt = fromString(store, revDiffsIt.next());
+                    while (revDiffsIt.hasNext()) {
+                        RecordId idRt = fromString(store, revDiffsIt.next());
+                        boolean good = diff(store, idLt, idRt, pw);
+                        idLt = idRt;
+                        if (!good && !ignoreMissingSegments) {
+                            break;
+                        }
+                    }
+                } else {
+                    System.out.println("Generating diff between " + idL + " 
and " + idR);
+                    diff(store, idL, idR, pw);
+                }
+            }
+
+            long dur = System.currentTimeMillis() - start;
+            System.out.println("Finished in " + dur + " ms.");
+        }
+    }
+
+    private boolean diff(ReadOnlyStore store, RecordId idL, RecordId idR, 
PrintWriter pw) throws IOException {
+        pw.println("rev " + idL + ".." + idR);
+        try {
+            NodeState before = 
store.getReader().readNode(idL).getChildNode("root");
+            NodeState after = 
store.getReader().readNode(idR).getChildNode("root");
+            for (String name : elements(filter)) {
+                before = before.getChildNode(name);
+                after = after.getChildNode(name);
+            }
+            after.compareAgainstBaseState(before, new PrintingDiff(pw, 
filter));
+            return true;
+        } catch (SegmentNotFoundException ex) {
+            System.out.println(ex.getMessage());
+            pw.println("#SNFE " + ex.getSegmentId());
+            return false;
+        }
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Diff.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/GenerationGraph.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/GenerationGraph.java?rev=1757382&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/GenerationGraph.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/GenerationGraph.java
 Tue Aug 23 14:33:49 2016
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.tool;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.oak.segment.SegmentGraph.writeGCGraph;
+import static 
org.apache.jackrabbit.oak.segment.tool.Utils.openReadOnlyFileStore;
+
+import java.io.File;
+import java.io.OutputStream;
+
+import org.apache.jackrabbit.oak.segment.file.FileStore.ReadOnlyStore;
+
+/**
+ * Generates a garbage collection generation graph. The graph is written in <a
+ * href="https://gephi.github.io/users/supported-graph-formats/gdf-format/";>the
+ * Guess GDF format</a>, which is easily imported into <a
+ * href="https://gephi.github.io/";>Gephi</a>.
+ */
+public class GenerationGraph implements Runnable {
+
+    /**
+     * Create a builder for the {@link GenerationGraph} command.
+     *
+     * @return an instance of {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Collect options for the {@link GenerationGraph} command.
+     */
+    public static class Builder {
+
+        private File path;
+
+        private OutputStream out;
+
+        private Builder() {
+            // Prevent external instantiation.
+        }
+
+        /**
+         * The path to an existing segment store. This parameter is required.
+         *
+         * @param path the path to an existing segment store.
+         * @return this builder.
+         */
+        public Builder withPath(File path) {
+            this.path = checkNotNull(path);
+            return this;
+        }
+
+        /**
+         * The destination of the output of the command. This parameter is
+         * mandatory.
+         *
+         * @param out the destination of the output of the command.
+         * @return this builder.
+         */
+        public Builder withOutput(OutputStream out) {
+            this.out = checkNotNull(out);
+            return this;
+        }
+
+        /**
+         * Create an executable version of the {@link GenerationGraph} command.
+         *
+         * @return an instance of {@link Runnable}.
+         */
+        public Runnable build() {
+            checkNotNull(path);
+            checkNotNull(out);
+            return new GenerationGraph(this);
+        }
+
+    }
+
+    private final File path;
+
+    private final OutputStream out;
+
+    private GenerationGraph(Builder builder) {
+        this.path = builder.path;
+        this.out = builder.out;
+    }
+
+    @Override
+    public void run() {
+        try (ReadOnlyStore store = openReadOnlyFileStore(path)) {
+            writeGCGraph(store, out);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/GenerationGraph.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/History.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/History.java?rev=1757382&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/History.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/History.java
 Tue Aug 23 14:33:49 2016
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.tool;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.File;
+import java.util.Iterator;
+
+import org.apache.jackrabbit.oak.segment.file.tooling.RevisionHistory;
+import 
org.apache.jackrabbit.oak.segment.file.tooling.RevisionHistory.HistoryElement;
+
+/**
+ * Prints the revision history of an existing segment store. Optionally, it can
+ * narrow to the output to the history of a single node.
+ */
+public class History implements Runnable {
+
+    /**
+     * Create a builder for the {@link History} command.
+     *
+     * @return an instance of {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Collect options for the {@link History} command.
+     */
+    public static class Builder {
+
+        private File path;
+
+        private File journal;
+
+        private String node;
+
+        private int depth;
+
+        private Builder() {
+            // Prevent external instantiation.
+        }
+
+        /**
+         * The path to an existing segment store. This parameter is required.
+         *
+         * @param path the path to an existing segment store.
+         * @return this builder.
+         */
+        public Builder withPath(File path) {
+            this.path = checkNotNull(path);
+            return this;
+        }
+
+        /**
+         * The path to the journal. This parameter is required.
+         *
+         * @param journal the path to the journal.
+         * @return this builder.
+         */
+        public Builder withJournal(File journal) {
+            this.journal = checkNotNull(journal);
+            return this;
+        }
+
+        /**
+         * A path to a node. If specified, the history will be restricted to 
the
+         * subtree pointed to by this node. This parameter is not mandatory and
+         * defaults to the entire tree.
+         *
+         * @param node a path to a node.
+         * @return this builder.
+         */
+        public Builder withNode(String node) {
+            this.node = checkNotNull(node);
+            return this;
+        }
+
+        /**
+         * Maximum depth of the history. If specified, this command will print
+         * information about the history of every node at or below the provided
+         * depth. This parameter is not mandatory and defaults to zero.
+         *
+         * @param depth the depth of the subtree.
+         * @return this builder.
+         */
+        public Builder withDepth(int depth) {
+            checkArgument(depth >= 0);
+            this.depth = depth;
+            return this;
+        }
+
+        /**
+         * Create an executable version of the {@link History} command.
+         *
+         * @return an instance of {@link Runnable}.
+         */
+        public Runnable build() {
+            checkNotNull(path);
+            checkNotNull(journal);
+            checkNotNull(node);
+            return new History(this);
+        }
+
+    }
+
+    private final File path;
+
+    private final File journal;
+
+    private final String node;
+
+    private final int depth;
+
+    private History(Builder builder) {
+        this.path = builder.path;
+        this.journal = builder.journal;
+        this.node = builder.node;
+        this.depth = builder.depth;
+    }
+
+    @Override
+    public void run() {
+        try {
+            run(new RevisionHistory(path).getHistory(journal, node));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void run(Iterator<HistoryElement> history) {
+        while (history.hasNext()) {
+            System.out.println(history.next().toString(depth));
+        }
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/History.java
------------------------------------------------------------------------------
    svn:eol-style = native

Copied: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/PrintingDiff.java
 (from r1757147, 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PrintingDiff.java)
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/PrintingDiff.java?p2=jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/PrintingDiff.java&p1=jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PrintingDiff.java&r1=1757147&r2=1757382&rev=1757382&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PrintingDiff.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/PrintingDiff.java
 Tue Aug 23 14:33:49 2016
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.jackrabbit.oak.run;
+package org.apache.jackrabbit.oak.segment.tool;
 
 import static com.google.common.collect.Iterables.transform;
 import static org.apache.commons.io.FileUtils.byteCountToDisplaySize;
@@ -136,4 +136,5 @@ final class PrintingDiff implements Node
         }
         return ps.getName() + "<" + ps.getType() + ">" + val.toString();
     }
+
 }

Added: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Restore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Restore.java?rev=1757382&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Restore.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Restore.java
 Tue Aug 23 14:33:49 2016
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.tool;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.File;
+
+import org.apache.jackrabbit.oak.backup.FileStoreRestore;
+
+/**
+ * Restore a backup of a segment store into an existing segment store.
+ */
+public class Restore implements Runnable {
+
+    /**
+     * Create a builder for the {@link Restore} command.
+     *
+     * @return an instance of {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Collect options for the {@link Restore} command.
+     */
+    public static class Builder {
+
+        private File source;
+
+        private File target;
+
+        private Builder() {
+            // Prevent external instantiation.
+        }
+
+        /**
+         * The source path of the restore. This parameter is mandatory.
+         *
+         * @param source the source path of the restore.
+         * @return this builder.
+         */
+        public Builder withSource(File source) {
+            this.source = checkNotNull(source);
+            return this;
+        }
+
+        /**
+         * The target of the restore. This parameter is mandatory.
+         *
+         * @param target the target of the restore.
+         * @return this builder.
+         */
+        public Builder withTarget(File target) {
+            this.target = checkNotNull(target);
+            return this;
+        }
+
+        /**
+         * Create an executable version of the {@link Restore} command.
+         *
+         * @return an instance of {@link Runnable}.
+         */
+        public Runnable build() {
+            checkNotNull(source);
+            checkNotNull(target);
+            return new Restore(this);
+        }
+
+    }
+
+    private final File source;
+
+    private final File target;
+
+    private Restore(Builder builder) {
+        this.source = builder.source;
+        this.target = builder.target;
+    }
+
+    @Override
+    public void run() {
+        try {
+            FileStoreRestore.restore(source, target);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Restore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Revisions.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Revisions.java?rev=1757382&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Revisions.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Revisions.java
 Tue Aug 23 14:33:49 2016
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.tool;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.oak.segment.tool.Utils.readRevisions;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Collect and print the revisions of a segment store.
+ */
+public class Revisions implements Runnable {
+
+    /**
+     * Create a builder for the {@link Revisions} command.
+     *
+     * @return an instance of {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Collect options for the {@link Revisions} command.
+     */
+    public static class Builder {
+
+        private File path;
+
+        private File out;
+
+        private Builder() {
+            // Prevent external instantiation.
+        }
+
+        /**
+         * The path to an existing segment store. This parameter is required.
+         *
+         * @param path the path to an existing segment store.
+         * @return this builder.
+         */
+        public Builder withPath(File path) {
+            this.path = checkNotNull(path);
+            return this;
+        }
+
+        /**
+         * The file where the output of this command is stored. this parameter
+         * is mandatory.
+         *
+         * @param out the output file.
+         * @return this builder.
+         */
+        public Builder withOutput(File out) {
+            this.out = checkNotNull(out);
+            return this;
+        }
+
+        /**
+         * Create an executable version of the {@link Revisions} command.
+         *
+         * @return an instance of {@link Runnable}.
+         */
+        public Runnable build() {
+            checkNotNull(path);
+            checkNotNull(out);
+            return new Revisions(this);
+        }
+
+    }
+
+    private final File path;
+
+    private final File out;
+
+    private Revisions(Builder builder) {
+        this.path = builder.path;
+        this.out = builder.out;
+    }
+
+    @Override
+    public void run() {
+        try {
+            listRevisions();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void listRevisions() throws IOException {
+        System.out.println("Store " + path);
+        System.out.println("Writing revisions to " + out);
+
+        List<String> revs = readRevisions(path);
+
+        if (revs.isEmpty()) {
+            System.out.println("No revisions found.");
+            return;
+        }
+
+        try (PrintWriter pw = new PrintWriter(out)) {
+            for (String r : revs) {
+                pw.println(r);
+            }
+        }
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Revisions.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/SegmentGraph.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/SegmentGraph.java?rev=1757382&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/SegmentGraph.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/SegmentGraph.java
 Tue Aug 23 14:33:49 2016
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.tool;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.oak.segment.SegmentGraph.writeSegmentGraph;
+import static 
org.apache.jackrabbit.oak.segment.tool.Utils.openReadOnlyFileStore;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.util.Date;
+
+import org.apache.jackrabbit.oak.segment.file.FileStore.ReadOnlyStore;
+
+/**
+ * Generates a segment collection generation graph. The graph is written in <a
+ * href="https://gephi.github.io/users/supported-graph-formats/gdf-format/";>the
+ * Guess GDF format</a>, which is easily imported into <a
+ * href="https://gephi.github.io/";>Gephi</a>.
+ */
+public class SegmentGraph implements Runnable {
+
+    /**
+     * Create a builder for the {@link SegmentGraph} command.
+     *
+     * @return an instance of {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Collect options for the {@link SegmentGraph} command.
+     */
+    public static class Builder {
+
+        private File path;
+
+        private Date epoch;
+
+        private String filter;
+
+        private OutputStream out;
+
+        private Builder() {
+            // Prevent external instantiation.
+        }
+
+        /**
+         * The path to an existing segment store. This parameter is required.
+         *
+         * @param path the path to an existing segment store.
+         * @return this builder.
+         */
+        public Builder withPath(File path) {
+            this.path = checkNotNull(path);
+            return this;
+        }
+
+        /**
+         * Filter out segments that were created before the specified epoch.
+         * This parameter is not mandatory and by default is not specified, 
thus
+         * including every segment in the output.
+         *
+         * @param epoch the minimum creation time of the reported segments.
+         * @return this builder.
+         */
+        public Builder withEpoch(Date epoch) {
+            this.epoch = checkNotNull(epoch);
+            return this;
+        }
+
+        /**
+         * A regular expression that can be used to select a specific subset of
+         * the segments.
+         *
+         * @param filter a regular expression.
+         * @return this builder.
+         */
+        public Builder withFilter(String filter) {
+            this.filter = checkNotNull(filter);
+            return this;
+        }
+
+        /**
+         * The destination of the output of the command. This parameter is
+         * mandatory.
+         *
+         * @param out the destination of the output of the command.
+         * @return this builder.
+         */
+        public Builder withOutput(OutputStream out) {
+            this.out = checkNotNull(out);
+            return this;
+        }
+
+        public Runnable build() {
+            checkNotNull(path);
+            checkNotNull(out);
+            return new SegmentGraph(this);
+        }
+
+    }
+
+    private final File path;
+
+    private final OutputStream out;
+
+    private final Date epoch;
+
+    private final String filter;
+
+    private SegmentGraph(Builder builder) {
+        this.path = builder.path;
+        this.out = builder.out;
+        this.epoch = builder.epoch;
+        this.filter = builder.filter;
+    }
+
+    @Override
+    public void run() {
+        try (ReadOnlyStore store = openReadOnlyFileStore(path)) {
+            writeSegmentGraph(store, out, epoch, filter);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/SegmentGraph.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Utils.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Utils.java?rev=1757382&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Utils.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Utils.java
 Tue Aug 23 14:33:49 2016
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.segment.tool;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Lists.newArrayList;
+import static 
org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.fileStoreBuilder;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.jackrabbit.oak.segment.file.FileStore;
+import org.apache.jackrabbit.oak.segment.file.FileStore.ReadOnlyStore;
+import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
+import org.apache.jackrabbit.oak.segment.file.JournalReader;
+import org.apache.jackrabbit.oak.segment.file.tooling.BasicReadOnlyBlobStore;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+
+class Utils {
+
+    private static final boolean TAR_STORAGE_MEMORY_MAPPED = 
Boolean.getBoolean("tar.memoryMapped");
+
+    private static final int TAR_SEGMENT_CACHE_SIZE = 
Integer.getInteger("cache", 256);
+
+    static FileStore openReadOnlyFileStore(File path, BlobStore blobStore) 
throws IOException, InvalidFileStoreVersionException {
+        return fileStoreBuilder(isValidFileStoreOrFail(path))
+                .withSegmentCacheSize(TAR_SEGMENT_CACHE_SIZE)
+                .withMemoryMapping(TAR_STORAGE_MEMORY_MAPPED)
+                .withBlobStore(blobStore)
+                .buildReadOnly();
+    }
+
+    static ReadOnlyStore openReadOnlyFileStore(File path) throws IOException, 
InvalidFileStoreVersionException {
+        return fileStoreBuilder(isValidFileStoreOrFail(path))
+                .withSegmentCacheSize(TAR_SEGMENT_CACHE_SIZE)
+                .withMemoryMapping(TAR_STORAGE_MEMORY_MAPPED)
+                .buildReadOnly();
+    }
+
+    static BlobStore newBasicReadOnlyBlobStore() {
+        return new BasicReadOnlyBlobStore();
+    }
+
+    static List<String> readRevisions(File store) {
+        File journal = new File(store, "journal.log");
+
+        if (journal.exists()) {
+            try (JournalReader journalReader = new JournalReader(journal)) {
+                return newArrayList(journalReader);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        return newArrayList();
+    }
+
+    private static File isValidFileStoreOrFail(File store) {
+        checkArgument(isValidFileStore(store), "Invalid FileStore directory " 
+ store);
+        return store;
+    }
+
+    private static boolean isValidFileStore(File store) {
+        if (!store.exists()) {
+            return false;
+        }
+
+        if (!store.isDirectory()) {
+            return false;
+        }
+
+        for (String f : store.list()) {
+            if ("journal.log".equals(f)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Utils.java
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to