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 md...@apache.org on 2015/12/08 15:36:38 UTC
svn commit: r1718621 - in /jackrabbit/oak/trunk:
oak-core/src/main/java/org/apache/jackrabbit/oak/json/
oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/tooling/
oak-run/ oak-run/src/main/java/org/apache/jackrabbit/oak/run/
Author: mduerig
Date: Tue Dec 8 14:36:38 2015
New Revision: 1718621
URL: http://svn.apache.org/viewvc?rev=1718621&view=rev
Log:
OAK-3749: Implement tooling for tracing a node through the revision history
Initial implementation
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/tooling/RevisionHistory.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/json/JsonSerializer.java
jackrabbit/oak/trunk/oak-run/README.md
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/json/JsonSerializer.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/json/JsonSerializer.java?rev=1718621&r1=1718620&r2=1718621&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/json/JsonSerializer.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/json/JsonSerializer.java Tue Dec 8 14:36:38 2015
@@ -41,9 +41,10 @@ import org.apache.jackrabbit.oak.spi.sta
* Utility class for serializing node and property states to JSON.
*/
public class JsonSerializer {
+ public static final String DEFAULT_FILTER_EXPRESSION =
+ "{\"properties\":[\"*\", \"-:childNodeCount\"]}";
- private static final JsonFilter DEFAULT_FILTER =
- new JsonFilter("{\"properties\":[\"*\", \"-:childNodeCount\"]}");
+ private static final JsonFilter DEFAULT_FILTER = new JsonFilter(DEFAULT_FILTER_EXPRESSION);
private final JsopBuilder json;
Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/tooling/RevisionHistory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/tooling/RevisionHistory.java?rev=1718621&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/tooling/RevisionHistory.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/tooling/RevisionHistory.java Tue Dec 8 14:36:38 2015
@@ -0,0 +1,161 @@
+/*
+ * 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.plugins.segment.file.tooling;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.transform;
+import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
+import static org.apache.jackrabbit.oak.json.JsonSerializer.DEFAULT_FILTER_EXPRESSION;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.google.common.base.Function;
+import org.apache.jackrabbit.oak.json.BlobSerializer;
+import org.apache.jackrabbit.oak.json.JsonSerializer;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeState;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStore.ReadOnlyStore;
+import org.apache.jackrabbit.oak.plugins.segment.file.JournalReader;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * Utility for tracing a node back through the revision history.
+ */
+public class RevisionHistory {
+ private final ReadOnlyStore store;
+
+ /**
+ * Create a new instance for a {@link FileStore} in the given {@code directory}.
+ *
+ * @param directory
+ * @throws IOException
+ */
+ public RevisionHistory(@Nonnull File directory) throws IOException {
+ this.store = new ReadOnlyStore(checkNotNull(directory));
+ }
+
+ private static NodeState getNode(SegmentNodeState root, String path) {
+ NodeState node = root;
+ for (String name : elements(path)) {
+ node = node.getChildNode(name);
+ }
+ return node;
+ }
+
+ /**
+ * Return the history of the node at the given {@code path} according to the passed
+ * {@code journal}.
+ *
+ * @param journal
+ * @param path
+ * @return
+ * @throws IOException
+ */
+ public Iterable<HistoryElement> getHistory(@Nonnull File journal, @Nonnull final String path)
+ throws IOException {
+ checkNotNull(path);
+ return transform(new JournalReader(checkNotNull(journal)),
+ new Function<String, HistoryElement>() {
+ @Nullable @Override
+ public HistoryElement apply(String revision) {
+ store.setRevision(revision);
+ NodeState node = getNode(store.getHead(), path);
+ return new HistoryElement(revision, node);
+ }
+ });
+ }
+
+ /**
+ * Representation of a point in time for a given node.
+ */
+ public static final class HistoryElement {
+ private final String revision;
+ private final NodeState node;
+
+ HistoryElement(String revision, NodeState node) {
+ this.revision = revision;
+ this.node = node;
+ }
+
+ /**
+ * Revision of the node
+ * @return
+ */
+ @Nonnull
+ public String getRevision() {
+ return revision;
+ }
+
+ /**
+ * Node at given revision
+ * @return
+ */
+ @CheckForNull
+ public NodeState getNode() {
+ return node;
+ }
+
+ /**
+ * Serialise this element to JSON up to the given {@code depth}.
+ * @param depth
+ * @return
+ */
+ public String toString(int depth) {
+ JsonSerializer json = new JsonSerializer(depth, 0, Integer.MAX_VALUE,
+ DEFAULT_FILTER_EXPRESSION, new BlobSerializer());
+ json.serialize(node);
+ return revision + "=" + json;
+ }
+
+ /**
+ * @return {@code toString(0)}
+ */
+ @Override
+ public String toString() {
+ return toString(0);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+
+ HistoryElement that = (HistoryElement) other;
+ return revision.equals(that.revision) &&
+ (node == null ? that.node == null : node.equals(that.node));
+
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * revision.hashCode() +
+ (node != null ? node.hashCode() : 0);
+ }
+ }
+}
Modified: jackrabbit/oak/trunk/oak-run/README.md
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/README.md?rev=1718621&r1=1718620&r2=1718621&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/README.md (original)
+++ jackrabbit/oak/trunk/oak-run/README.md Tue Dec 8 14:36:38 2015
@@ -15,6 +15,7 @@ The following runmodes are currently ava
* console : Start an interactive console.
* explore : Starts a GUI browser based on java swing.
* graph : Export the segment graph of a segment store to a file.
+ * history : Trace the history of a node
* check : Check the FileStore for inconsistencies
* primary : Run a TarMK Cold Standby primary instance
* standby : Run a TarMK Cold Standby standby instance
@@ -120,6 +121,23 @@ a negative offset translating all timest
given)
--output <File> Output file (default: segments.gdf)
+History
+-------
+
+Trace the history of a node backward through the revision history.
+
+ $ java -jar oak-run-*.jar history [File] <options>
+
+ [File] -- Path to segment store (required)
+
+ Option Description
+ ------ -----------
+ --depth <Integer> Depth up to which to dump node states
+ (default: 0)
+ --journal journal file (default: journal.log)
+ --path Path for which to trace the history
+ (default: /)
+
Check
-----
Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java?rev=1718621&r1=1718620&r2=1718621&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java Tue Dec 8 14:36:38 2015
@@ -113,6 +113,8 @@ import org.apache.jackrabbit.oak.plugins
import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
import org.apache.jackrabbit.oak.plugins.segment.file.FileStore.ReadOnlyStore;
import org.apache.jackrabbit.oak.plugins.segment.file.JournalReader;
+import org.apache.jackrabbit.oak.plugins.segment.file.tooling.RevisionHistory;
+import org.apache.jackrabbit.oak.plugins.segment.file.tooling.RevisionHistory.HistoryElement;
import org.apache.jackrabbit.oak.plugins.segment.standby.client.StandbyClient;
import org.apache.jackrabbit.oak.plugins.segment.standby.server.StandbyServer;
import org.apache.jackrabbit.oak.plugins.tika.TextExtractorMain;
@@ -175,6 +177,9 @@ public final class Main {
case GRAPH:
graph(args);
break;
+ case HISTORY:
+ history(args);
+ break;
case CHECK:
check(args);
break;
@@ -882,6 +887,43 @@ public final class Main {
checkConsistency(dir, journalFileName, fullTraversal, debugLevel, binLen);
}
+ private static void history(String[] args) throws IOException {
+ OptionParser parser = new OptionParser();
+ OptionSpec<File> directoryArg = parser.nonOptions(
+ "Path to segment store (required)").ofType(File.class);
+ OptionSpec<String> journalArg = parser.accepts(
+ "journal", "journal file").withRequiredArg().ofType(String.class)
+ .defaultsTo("journal.log");
+ OptionSpec<String> pathArg = parser.accepts(
+ "path", "Path for which to trace the history").withRequiredArg().ofType(String.class)
+ .defaultsTo("/");
+ OptionSpec<Integer> depthArg = parser.accepts(
+ "depth", "Depth up to which to dump node states").withRequiredArg().ofType(Integer.class)
+ .defaultsTo(0);
+ OptionSet options = parser.parse(args);
+
+ File directory = directoryArg.value(options);
+ if (directory == null) {
+ System.err.println("Trace the history of a path. Usage: history [File] <options>");
+ parser.printHelpOn(System.err);
+ System.exit(-1);
+ }
+ if (!isValidFileStore(directory.getPath())) {
+ System.err.println("Invalid FileStore directory " + args[0]);
+ System.exit(1);
+ }
+
+ String path = pathArg.value(options);
+ int depth = depthArg.value(options);
+ String journalName = journalArg.value(options);
+ File journal = new File(directory, journalName);
+
+ Iterable<HistoryElement> history = new RevisionHistory(directory).getHistory(journal, path);
+ for (HistoryElement historyElement : history) {
+ System.out.println(historyElement.toString(depth));
+ }
+ }
+
private static void debugTarFile(FileStore store, String[] args) {
File root = new File(args[0]);
for (int i = 1; i < args.length; i++) {
@@ -1259,6 +1301,7 @@ public final class Main {
CONSOLE("console"),
DEBUG("debug"),
GRAPH("graph"),
+ HISTORY("history"),
CHECK("check"),
COMPACT("compact"),
SERVER("server"),