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 ad...@apache.org on 2020/09/28 14:35:50 UTC

svn commit: r1882084 - in /jackrabbit/oak/trunk/oak-run: ./ src/main/java/org/apache/jackrabbit/oak/explorer/

Author: adulceanu
Date: Mon Sep 28 14:35:50 2020
New Revision: 1882084

URL: http://svn.apache.org/viewvc?rev=1882084&view=rev
Log:
OAK-9238 - oak-run explore should support Azure Segment Store
Contribution by Aravindo Wingeier

Added:
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/AbstractSegmentTarExplorerBackend.java
      - copied, changed from r1882018, jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/SegmentTarExplorerBackend.java
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/AzureSegmentStoreExplorerBackend.java
Modified:
    jackrabbit/oak/trunk/oak-run/README.md
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/Explorer.java
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/SegmentTarExplorerBackend.java

Modified: jackrabbit/oak/trunk/oak-run/README.md
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/README.md?rev=1882084&r1=1882083&r2=1882084&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/README.md (original)
+++ jackrabbit/oak/trunk/oak-run/README.md Mon Sep 28 14:35:50 2020
@@ -136,7 +136,11 @@ Explore
 The 'explore' mode starts a desktop browser GUI based on java swing which allows for read-only
 browsing of an existing oak repository.
 
-    $ java -jar oak-run-*.jar explore /path/to/oak/repository [skip-size-check]
+    $ java -jar oak-run-*.jar explore /path/to/oak/repository [--skip-size-check]
+
+Microsoft Azure node stores are also supported using the following command.  The secret key must be supplied as an environment variable `AZURE_SECRET_KEY`.  
+
+    $ java -jar oak-run-*.jar explore az:https://myaccount.blob.core.windows.net/container/repository [--skip-size-check]
 
 frozennoderefsbyscanning
 ------------------------

Copied: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/AbstractSegmentTarExplorerBackend.java (from r1882018, jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/SegmentTarExplorerBackend.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/AbstractSegmentTarExplorerBackend.java?p2=jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/AbstractSegmentTarExplorerBackend.java&p1=jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/SegmentTarExplorerBackend.java&r1=1882018&r2=1882084&rev=1882084&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/SegmentTarExplorerBackend.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/AbstractSegmentTarExplorerBackend.java Mon Sep 28 14:35:50 2020
@@ -1,40 +1,5 @@
-/*
- * 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.explorer;
 
-import static com.google.common.collect.Lists.newArrayList;
-import static com.google.common.collect.Sets.newHashSet;
-import static java.util.Collections.reverseOrder;
-import static org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.fileStoreBuilder;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Deque;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.UUID;
-
 import com.google.common.base.Function;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Maps;
@@ -45,48 +10,48 @@ import org.apache.jackrabbit.oak.segment
 import org.apache.jackrabbit.oak.segment.SegmentId;
 import org.apache.jackrabbit.oak.segment.SegmentNodeState;
 import org.apache.jackrabbit.oak.segment.SegmentNodeStateHelper;
-import org.apache.jackrabbit.oak.segment.spi.persistence.JournalFile;
 import org.apache.jackrabbit.oak.segment.SegmentPropertyState;
-import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
 import org.apache.jackrabbit.oak.segment.file.JournalEntry;
 import org.apache.jackrabbit.oak.segment.file.JournalReader;
 import org.apache.jackrabbit.oak.segment.file.ReadOnlyFileStore;
-import org.apache.jackrabbit.oak.segment.file.tar.LocalJournalFile;
+import org.apache.jackrabbit.oak.segment.spi.persistence.JournalFile;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
-import org.jetbrains.annotations.Nullable;
 
-class SegmentTarExplorerBackend implements ExplorerBackend {
-
-    private final File path;
+import java.io.IOException;
+import java.util.AbstractMap;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
 
-    private ReadOnlyFileStore store;
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Sets.newHashSet;
+import static java.util.Collections.reverseOrder;
 
-    private Map<String, Set<UUID>> index;
+/**
+ * Abstraction for Segment-Tar based backends.
+ */
+public abstract class AbstractSegmentTarExplorerBackend implements ExplorerBackend {
+    protected ReadOnlyFileStore store;
+    protected Map<String, Set<UUID>> index;
 
-    SegmentTarExplorerBackend(File path) throws IOException {
-        this.path = path;
-    }
 
-    @Override
-    public void open() throws IOException {
-        try {
-            store = fileStoreBuilder(path).buildReadOnly();
-        } catch (InvalidFileStoreVersionException e) {
-            throw new IllegalStateException(e);
-        }
-        index = store.getTarReaderIndex();
-    }
+    public abstract void open() throws IOException;
 
-    @Override
     public void close() {
         store.close();
         store = null;
         index = null;
     }
 
-    @Override
+    abstract protected JournalFile getJournal();
+
     public List<String> readRevisions() {
-        JournalFile journal = new LocalJournalFile(path, "journal.log");
+        JournalFile journal = getJournal();
 
         if (!journal.exists()) {
             return newArrayList();
@@ -99,13 +64,12 @@ class SegmentTarExplorerBackend implemen
             journalReader = new JournalReader(journal);
             Iterator<String> revisionIterator = Iterators.transform(journalReader,
                     new Function<JournalEntry, String>() {
-                        @Nullable
                         @Override
                         public String apply(JournalEntry entry) {
                             return entry.getRevision();
                         }
                     });
-            
+
             try {
                 revs = newArrayList(revisionIterator);
             } finally {
@@ -126,25 +90,21 @@ class SegmentTarExplorerBackend implemen
         return revs;
     }
 
-    @Override
     public Map<String, Set<UUID>> getTarReaderIndex() {
         return store.getTarReaderIndex();
     }
 
-    @Override
     public Map<UUID, Set<UUID>> getTarGraph(String file) throws IOException {
         return store.getTarGraph(file);
     }
 
-    @Override
     public List<String> getTarFiles() {
         List<String> files = new ArrayList<>(store.getTarReaderIndex().keySet());
         files.sort(reverseOrder());
         return files;
     }
 
-    @Override
-    public void getGcRoots(UUID uuidIn, Map<UUID, Set<Entry<UUID, String>>> links) throws IOException {
+    public void getGcRoots(UUID uuidIn, Map<UUID, Set<Map.Entry<UUID, String>>> links) throws IOException {
         Deque<UUID> todos = new ArrayDeque<UUID>();
         todos.add(uuidIn);
         Set<UUID> visited = newHashSet();
@@ -155,17 +115,17 @@ class SegmentTarExplorerBackend implemen
             }
             for (String f : getTarFiles()) {
                 Map<UUID, Set<UUID>> graph = store.getTarGraph(f);
-                for (Entry<UUID, Set<UUID>> g : graph.entrySet()) {
+                for (Map.Entry<UUID, Set<UUID>> g : graph.entrySet()) {
                     if (g.getValue() != null && g.getValue().contains(uuid)) {
                         UUID uuidP = g.getKey();
                         if (!todos.contains(uuidP)) {
                             todos.add(uuidP);
-                            Set<Entry<UUID, String>> deps = links.get(uuid);
+                            Set<Map.Entry<UUID, String>> deps = links.get(uuid);
                             if (deps == null) {
                                 deps = newHashSet();
                                 links.put(uuid, deps);
                             }
-                            deps.add(new SimpleImmutableEntry<UUID, String>(
+                            deps.add(new AbstractMap.SimpleImmutableEntry<UUID, String>(
                                     uuidP, f));
                         }
                     }
@@ -174,7 +134,6 @@ class SegmentTarExplorerBackend implemen
         }
     }
 
-    @Override
     public Set<UUID> getReferencedSegmentIds() {
         Set<UUID> ids = newHashSet();
 
@@ -185,32 +144,26 @@ class SegmentTarExplorerBackend implemen
         return ids;
     }
 
-    @Override
     public NodeState getHead() {
         return store.getHead();
     }
 
-    @Override
     public NodeState readNodeState(String recordId) {
         return store.getReader().readNode(RecordId.fromString(store.getSegmentIdProvider(), recordId));
     }
 
-    @Override
     public void setRevision(String revision) {
         store.setRevision(revision);
     }
 
-    @Override
     public boolean isPersisted(NodeState state) {
         return state instanceof SegmentNodeState;
     }
 
-    @Override
     public boolean isPersisted(PropertyState state) {
         return state instanceof SegmentPropertyState;
     }
 
-    @Override
     public String getRecordId(NodeState state) {
         if (state instanceof SegmentNodeState) {
             return getRecordId((SegmentNodeState) state);
@@ -219,7 +172,6 @@ class SegmentTarExplorerBackend implemen
         return null;
     }
 
-    @Override
     public UUID getSegmentId(NodeState state) {
         if (state instanceof SegmentNodeState) {
             return getSegmentId((SegmentNodeState) state);
@@ -228,7 +180,6 @@ class SegmentTarExplorerBackend implemen
         return null;
     }
 
-    @Override
     public String getRecordId(PropertyState state) {
         if (state instanceof SegmentPropertyState) {
             return getRecordId((SegmentPropertyState) state);
@@ -237,7 +188,6 @@ class SegmentTarExplorerBackend implemen
         return null;
     }
 
-    @Override
     public UUID getSegmentId(PropertyState state) {
         if (state instanceof SegmentPropertyState) {
             return getSegmentId((SegmentPropertyState) state);
@@ -246,7 +196,6 @@ class SegmentTarExplorerBackend implemen
         return null;
     }
 
-    @Override
     public String getTemplateRecordId(NodeState state) {
         if (state instanceof SegmentNodeState) {
             return getTemplateRecordId((SegmentNodeState) state);
@@ -255,7 +204,6 @@ class SegmentTarExplorerBackend implemen
         return null;
     }
 
-    @Override
     public UUID getTemplateSegmentId(NodeState state) {
         if (state instanceof SegmentNodeState) {
             return getTemplateSegmentId((SegmentNodeState) state);
@@ -264,7 +212,6 @@ class SegmentTarExplorerBackend implemen
         return null;
     }
 
-    @Override
     public String getFile(NodeState state) {
         if (state instanceof SegmentNodeState) {
             return getFile((SegmentNodeState) state);
@@ -273,7 +220,6 @@ class SegmentTarExplorerBackend implemen
         return null;
     }
 
-    @Override
     public String getFile(PropertyState state) {
         if (state instanceof SegmentPropertyState) {
             return getFile((SegmentPropertyState) state);
@@ -282,7 +228,6 @@ class SegmentTarExplorerBackend implemen
         return null;
     }
 
-    @Override
     public String getTemplateFile(NodeState state) {
         if (state instanceof SegmentNodeState) {
             return getTemplateFile((SegmentNodeState) state);
@@ -291,7 +236,6 @@ class SegmentTarExplorerBackend implemen
         return null;
     }
 
-    @Override
     public Map<UUID, String> getBulkSegmentIds(Blob blob) {
         Map<UUID, String> result = Maps.newHashMap();
 
@@ -302,12 +246,10 @@ class SegmentTarExplorerBackend implemen
         return result;
     }
 
-    @Override
     public String getPersistedCompactionMapStats() {
         return "";
     }
 
-    @Override
     public boolean isExternal(Blob blob) {
         if (blob instanceof SegmentBlob) {
             return isExternal((SegmentBlob) blob);
@@ -375,7 +317,7 @@ class SegmentTarExplorerBackend implemen
     }
 
     private String getFile(SegmentId segmentId) {
-        for (Entry<String, Set<UUID>> nameToId : index.entrySet()) {
+        for (Map.Entry<String, Set<UUID>> nameToId : index.entrySet()) {
             for (UUID uuid : nameToId.getValue()) {
                 if (uuid.equals(segmentId.asUUID())) {
                     return nameToId.getKey();
@@ -384,5 +326,4 @@ class SegmentTarExplorerBackend implemen
         }
         return null;
     }
-
 }

Added: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/AzureSegmentStoreExplorerBackend.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/AzureSegmentStoreExplorerBackend.java?rev=1882084&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/AzureSegmentStoreExplorerBackend.java (added)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/AzureSegmentStoreExplorerBackend.java Mon Sep 28 14:35:50 2020
@@ -0,0 +1,45 @@
+package org.apache.jackrabbit.oak.explorer;
+
+import com.google.common.io.Files;
+import org.apache.jackrabbit.oak.segment.azure.tool.ToolUtils;
+import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
+import org.apache.jackrabbit.oak.segment.spi.persistence.JournalFile;
+import org.apache.jackrabbit.oak.segment.spi.persistence.SegmentNodeStorePersistence;
+
+import java.io.IOException;
+
+import static org.apache.jackrabbit.oak.segment.azure.tool.ToolUtils.newSegmentNodeStorePersistence;
+import static org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.fileStoreBuilder;
+
+/**
+ * Backend using a a remote Azure Segment Store.
+ * <p>
+ * The path must be in the form "{@code az:https://myaccount.blob.core.windows.net/container/repository}".
+ * The secret key must be supplied as an environment variable {@codeAZURE_SECRET_KEY}
+ */
+public class AzureSegmentStoreExplorerBackend extends AbstractSegmentTarExplorerBackend {
+    private final String path;
+    private SegmentNodeStorePersistence persistence;
+
+    public AzureSegmentStoreExplorerBackend(String path) {
+        this.path = path;
+    }
+
+    @Override
+    public void open() throws IOException {
+        this.persistence = newSegmentNodeStorePersistence(ToolUtils.SegmentStoreType.AZURE, path);
+
+        try {
+            this.store = fileStoreBuilder(Files.createTempDir())
+                    .withCustomPersistence(persistence)
+                    .buildReadOnly();
+        } catch (InvalidFileStoreVersionException e) {
+            throw new IllegalStateException(e);
+        }
+        this.index = store.getTarReaderIndex();
+    }
+
+    protected JournalFile getJournal() {
+        return persistence.getJournalFile();
+    }
+}

Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/Explorer.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/Explorer.java?rev=1882084&r1=1882083&r2=1882084&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/Explorer.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/Explorer.java Mon Sep 28 14:35:50 2020
@@ -18,28 +18,24 @@
  */
 package org.apache.jackrabbit.oak.explorer;
 
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+import org.apache.commons.io.IOUtils;
+
+import javax.swing.*;
+import javax.swing.UIManager.LookAndFeelInfo;
 import java.awt.*;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.KeyEvent;
-import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.List;
 
-import javax.swing.*;
-import javax.swing.UIManager.LookAndFeelInfo;
-
-import joptsimple.OptionParser;
-import joptsimple.OptionSet;
-import joptsimple.OptionSpec;
-import org.apache.commons.io.IOUtils;
-
 /**
  * NodeStore explorer
- * 
- * GUI based on Swing, for now it is tailored to the TarMK
- * 
+ * <p>
+ * GUI based on Swing, for now it is tailored to the TarMK and Azure Segment Store.
  */
 public class Explorer {
 
@@ -60,7 +56,7 @@ public class Explorer {
     public static void main(String[] args) throws IOException {
         OptionParser parser = new OptionParser();
         OptionSpec skipSizeCheck = parser.accepts("skip-size-check", "Don't compute the size of the records");
-        OptionSpec<File> nonOptions = parser.nonOptions().ofType(File.class);
+        OptionSpec<String> nonOptions = parser.nonOptions("path to repository").ofType(String.class);
         OptionSet options = parser.parse(args);
 
         if (options.valuesOf(nonOptions).isEmpty()) {
@@ -68,16 +64,20 @@ public class Explorer {
             System.exit(1);
         }
 
-        File path = options.valuesOf(nonOptions).get(0);
-
-        ExplorerBackend backend = new SegmentTarExplorerBackend(path);
+        String path = options.valuesOf(nonOptions).get(0);
 
+        ExplorerBackend backend;
+        if (path.startsWith("az:")) {
+            backend = new AzureSegmentStoreExplorerBackend(path);
+        } else {
+            backend = new SegmentTarExplorerBackend(path);
+        }
         new Explorer(path, backend, options.has(skipSizeCheck));
     }
 
     private final ExplorerBackend backend;
 
-    private Explorer(final File path, ExplorerBackend backend, final boolean skipSizeCheck) throws IOException {
+    private Explorer(final String path, ExplorerBackend backend, final boolean skipSizeCheck) throws IOException {
         this.backend = backend;
 
         javax.swing.SwingUtilities.invokeLater(new Runnable() {
@@ -92,7 +92,7 @@ public class Explorer {
         });
     }
 
-    private void createAndShowGUI(final File path, boolean skipSizeCheck) throws IOException {
+    private void createAndShowGUI(final String path, boolean skipSizeCheck) throws IOException {
         JTextArea log = new JTextArea(5, 20);
         log.setMargin(new Insets(5, 5, 5, 5));
         log.setLineWrap(true);
@@ -159,13 +159,7 @@ public class Explorer {
         menuRefs.addActionListener(new ActionListener() {
             @Override
             public void actionPerformed(ActionEvent ev) {
-                List<String> tarFiles = new ArrayList<String>();
-                for (File f : path.listFiles()) {
-                    if (f.getName().endsWith(".tar")) {
-                        tarFiles.add(f.getName());
-                    }
-                }
-
+                List<String> tarFiles = backend.getTarFiles();
                 String s = (String) JOptionPane.showInputDialog(frame,
                         "Choose a tar file", "Tar File Info",
                         JOptionPane.PLAIN_MESSAGE, null, tarFiles.toArray(),

Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/SegmentTarExplorerBackend.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/SegmentTarExplorerBackend.java?rev=1882084&r1=1882083&r2=1882084&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/SegmentTarExplorerBackend.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/SegmentTarExplorerBackend.java Mon Sep 28 14:35:50 2020
@@ -17,54 +17,24 @@
 
 package org.apache.jackrabbit.oak.explorer;
 
-import static com.google.common.collect.Lists.newArrayList;
-import static com.google.common.collect.Sets.newHashSet;
-import static java.util.Collections.reverseOrder;
-import static org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.fileStoreBuilder;
+import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
+import org.apache.jackrabbit.oak.segment.file.tar.LocalJournalFile;
+import org.apache.jackrabbit.oak.segment.spi.persistence.JournalFile;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Deque;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.UUID;
 
-import com.google.common.base.Function;
-import com.google.common.collect.Iterators;
-import com.google.common.collect.Maps;
-import org.apache.jackrabbit.oak.api.Blob;
-import org.apache.jackrabbit.oak.api.PropertyState;
-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.SegmentNodeStateHelper;
-import org.apache.jackrabbit.oak.segment.spi.persistence.JournalFile;
-import org.apache.jackrabbit.oak.segment.SegmentPropertyState;
-import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
-import org.apache.jackrabbit.oak.segment.file.JournalEntry;
-import org.apache.jackrabbit.oak.segment.file.JournalReader;
-import org.apache.jackrabbit.oak.segment.file.ReadOnlyFileStore;
-import org.apache.jackrabbit.oak.segment.file.tar.LocalJournalFile;
-import org.apache.jackrabbit.oak.spi.state.NodeState;
-import org.jetbrains.annotations.Nullable;
+import static org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.fileStoreBuilder;
 
-class SegmentTarExplorerBackend implements ExplorerBackend {
+/**
+ * Backend using a local SegmentTar Node store.
+ */
+class SegmentTarExplorerBackend extends AbstractSegmentTarExplorerBackend  {
 
     private final File path;
 
-    private ReadOnlyFileStore store;
-
-    private Map<String, Set<UUID>> index;
-
-    SegmentTarExplorerBackend(File path) throws IOException {
-        this.path = path;
+    SegmentTarExplorerBackend(String path) throws IOException {
+        this.path = new File(path);
     }
 
     @Override
@@ -78,311 +48,7 @@ class SegmentTarExplorerBackend implemen
     }
 
     @Override
-    public void close() {
-        store.close();
-        store = null;
-        index = null;
-    }
-
-    @Override
-    public List<String> readRevisions() {
-        JournalFile journal = new LocalJournalFile(path, "journal.log");
-
-        if (!journal.exists()) {
-            return newArrayList();
-        }
-
-        List<String> revs = newArrayList();
-        JournalReader journalReader = null;
-
-        try {
-            journalReader = new JournalReader(journal);
-            Iterator<String> revisionIterator = Iterators.transform(journalReader,
-                    new Function<JournalEntry, String>() {
-                        @Nullable
-                        @Override
-                        public String apply(JournalEntry entry) {
-                            return entry.getRevision();
-                        }
-                    });
-            
-            try {
-                revs = newArrayList(revisionIterator);
-            } finally {
-                journalReader.close();
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-        } finally {
-            try {
-                if (journalReader != null) {
-                    journalReader.close();
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-        }
-
-        return revs;
-    }
-
-    @Override
-    public Map<String, Set<UUID>> getTarReaderIndex() {
-        return store.getTarReaderIndex();
-    }
-
-    @Override
-    public Map<UUID, Set<UUID>> getTarGraph(String file) throws IOException {
-        return store.getTarGraph(file);
-    }
-
-    @Override
-    public List<String> getTarFiles() {
-        List<String> files = new ArrayList<>(store.getTarReaderIndex().keySet());
-        files.sort(reverseOrder());
-        return files;
-    }
-
-    @Override
-    public void getGcRoots(UUID uuidIn, Map<UUID, Set<Entry<UUID, String>>> links) throws IOException {
-        Deque<UUID> todos = new ArrayDeque<UUID>();
-        todos.add(uuidIn);
-        Set<UUID> visited = newHashSet();
-        while (!todos.isEmpty()) {
-            UUID uuid = todos.remove();
-            if (!visited.add(uuid)) {
-                continue;
-            }
-            for (String f : getTarFiles()) {
-                Map<UUID, Set<UUID>> graph = store.getTarGraph(f);
-                for (Entry<UUID, Set<UUID>> g : graph.entrySet()) {
-                    if (g.getValue() != null && g.getValue().contains(uuid)) {
-                        UUID uuidP = g.getKey();
-                        if (!todos.contains(uuidP)) {
-                            todos.add(uuidP);
-                            Set<Entry<UUID, String>> deps = links.get(uuid);
-                            if (deps == null) {
-                                deps = newHashSet();
-                                links.put(uuid, deps);
-                            }
-                            deps.add(new SimpleImmutableEntry<UUID, String>(
-                                    uuidP, f));
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
-    public Set<UUID> getReferencedSegmentIds() {
-        Set<UUID> ids = newHashSet();
-
-        for (SegmentId id : store.getReferencedSegmentIds()) {
-            ids.add(id.asUUID());
-        }
-
-        return ids;
-    }
-
-    @Override
-    public NodeState getHead() {
-        return store.getHead();
-    }
-
-    @Override
-    public NodeState readNodeState(String recordId) {
-        return store.getReader().readNode(RecordId.fromString(store.getSegmentIdProvider(), recordId));
-    }
-
-    @Override
-    public void setRevision(String revision) {
-        store.setRevision(revision);
-    }
-
-    @Override
-    public boolean isPersisted(NodeState state) {
-        return state instanceof SegmentNodeState;
-    }
-
-    @Override
-    public boolean isPersisted(PropertyState state) {
-        return state instanceof SegmentPropertyState;
-    }
-
-    @Override
-    public String getRecordId(NodeState state) {
-        if (state instanceof SegmentNodeState) {
-            return getRecordId((SegmentNodeState) state);
-        }
-
-        return null;
-    }
-
-    @Override
-    public UUID getSegmentId(NodeState state) {
-        if (state instanceof SegmentNodeState) {
-            return getSegmentId((SegmentNodeState) state);
-        }
-
-        return null;
-    }
-
-    @Override
-    public String getRecordId(PropertyState state) {
-        if (state instanceof SegmentPropertyState) {
-            return getRecordId((SegmentPropertyState) state);
-        }
-
-        return null;
-    }
-
-    @Override
-    public UUID getSegmentId(PropertyState state) {
-        if (state instanceof SegmentPropertyState) {
-            return getSegmentId((SegmentPropertyState) state);
-        }
-
-        return null;
-    }
-
-    @Override
-    public String getTemplateRecordId(NodeState state) {
-        if (state instanceof SegmentNodeState) {
-            return getTemplateRecordId((SegmentNodeState) state);
-        }
-
-        return null;
+    protected JournalFile getJournal() {
+        return new LocalJournalFile(path, "journal.log");
     }
-
-    @Override
-    public UUID getTemplateSegmentId(NodeState state) {
-        if (state instanceof SegmentNodeState) {
-            return getTemplateSegmentId((SegmentNodeState) state);
-        }
-
-        return null;
-    }
-
-    @Override
-    public String getFile(NodeState state) {
-        if (state instanceof SegmentNodeState) {
-            return getFile((SegmentNodeState) state);
-        }
-
-        return null;
-    }
-
-    @Override
-    public String getFile(PropertyState state) {
-        if (state instanceof SegmentPropertyState) {
-            return getFile((SegmentPropertyState) state);
-        }
-
-        return null;
-    }
-
-    @Override
-    public String getTemplateFile(NodeState state) {
-        if (state instanceof SegmentNodeState) {
-            return getTemplateFile((SegmentNodeState) state);
-        }
-
-        return null;
-    }
-
-    @Override
-    public Map<UUID, String> getBulkSegmentIds(Blob blob) {
-        Map<UUID, String> result = Maps.newHashMap();
-
-        for (SegmentId segmentId : SegmentBlob.getBulkSegmentIds(blob)) {
-            result.put(segmentId.asUUID(), getFile(segmentId));
-        }
-
-        return result;
-    }
-
-    @Override
-    public String getPersistedCompactionMapStats() {
-        return "";
-    }
-
-    @Override
-    public boolean isExternal(Blob blob) {
-        if (blob instanceof SegmentBlob) {
-            return isExternal((SegmentBlob) blob);
-        }
-
-        return false;
-    }
-
-    private boolean isExternal(SegmentBlob blob) {
-        return blob.isExternal();
-    }
-
-    private String getRecordId(SegmentNodeState state) {
-        return state.getRecordId().toString();
-    }
-
-    private UUID getSegmentId(SegmentNodeState state) {
-        return state.getRecordId().getSegmentId().asUUID();
-    }
-
-    private String getRecordId(SegmentPropertyState state) {
-        return state.getRecordId().toString();
-    }
-
-    private UUID getSegmentId(SegmentPropertyState state) {
-        return state.getRecordId().getSegmentId().asUUID();
-    }
-
-    private String getTemplateRecordId(SegmentNodeState state) {
-        RecordId recordId = SegmentNodeStateHelper.getTemplateId(state);
-
-        if (recordId == null) {
-            return null;
-        }
-
-        return recordId.toString();
-    }
-
-    private UUID getTemplateSegmentId(SegmentNodeState state) {
-        RecordId recordId = SegmentNodeStateHelper.getTemplateId(state);
-
-        if (recordId == null) {
-            return null;
-        }
-
-        return recordId.getSegmentId().asUUID();
-    }
-
-    private String getFile(SegmentNodeState state) {
-        return getFile(state.getRecordId().getSegmentId());
-    }
-
-    private String getFile(SegmentPropertyState state) {
-        return getFile(state.getRecordId().getSegmentId());
-    }
-
-    private String getTemplateFile(SegmentNodeState state) {
-        RecordId recordId = SegmentNodeStateHelper.getTemplateId(state);
-
-        if (recordId == null) {
-            return null;
-        }
-
-        return getFile(recordId.getSegmentId());
-    }
-
-    private String getFile(SegmentId segmentId) {
-        for (Entry<String, Set<UUID>> nameToId : index.entrySet()) {
-            for (UUID uuid : nameToId.getValue()) {
-                if (uuid.equals(segmentId.asUUID())) {
-                    return nameToId.getKey();
-                }
-            }
-        }
-        return null;
-    }
-
 }