diff --git oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/SegmentTarExplorerBackend.java oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/SegmentTarExplorerBackend.java index 3c825c2..0c38720 100644 --- oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/SegmentTarExplorerBackend.java +++ oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/SegmentTarExplorerBackend.java @@ -28,12 +28,18 @@ import java.io.IOException; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayDeque; import java.util.Deque; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; + +import javax.annotation.Nullable; + 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; @@ -44,6 +50,7 @@ import org.apache.jackrabbit.oak.segment.SegmentNodeState; import org.apache.jackrabbit.oak.segment.SegmentNodeStateHelper; 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.spi.state.NodeState; @@ -90,9 +97,17 @@ class SegmentTarExplorerBackend implements ExplorerBackend { try { journalReader = new JournalReader(journal); - + Iterator revisionIterator = Iterators.transform(journalReader, + new Function() { + @Nullable + @Override + public String apply(JournalEntry entry) { + return entry.getRevision(); + } + }); + try { - revs = newArrayList(journalReader); + revs = newArrayList(revisionIterator); } finally { journalReader.close(); } diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreUtil.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreUtil.java index d2b8d26..1e33a07 100644 --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreUtil.java +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreUtil.java @@ -49,9 +49,9 @@ class FileStoreUtil { static RecordId findPersistedRecordId(SegmentStore store, File journal) throws IOException { try (JournalReader journalReader = new JournalReader(journal)) { while (journalReader.hasNext()) { - String entry = journalReader.next(); + JournalEntry entry = journalReader.next(); try { - RecordId id = RecordId.fromString(store, entry); + RecordId id = RecordId.fromString(store, entry.getRevision()); if (store.containsSegment(id.getSegmentId())) { return id; } diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/JournalEntry.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/JournalEntry.java new file mode 100644 index 0000000..bf2260f --- /dev/null +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/JournalEntry.java @@ -0,0 +1,67 @@ +/* + * 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.file; + +import javax.annotation.Nonnull; + +/** + * A value class representing an entry in the revisions journal. + */ +public class JournalEntry { + @Nonnull + private final String revision; + + private final long timestamp; + + JournalEntry(@Nonnull String revision, long timestamp) { + this.revision = revision; + this.timestamp = timestamp; + } + + @Nonnull + public String getRevision() { + return revision; + } + + public long getTimestamp() { + return timestamp; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + revision.hashCode(); + result = prime * result + (int) (timestamp ^ (timestamp >>> 32)); + return result; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } else if (object instanceof JournalEntry) { + JournalEntry that = (JournalEntry) object; + return revision.equals(that.revision) && timestamp == that.timestamp; + } else { + return false; + } + } +} diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/JournalReader.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/JournalReader.java index b4d936d..9764420 100644 --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/JournalReader.java +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/JournalReader.java @@ -24,17 +24,20 @@ import static java.nio.charset.Charset.defaultCharset; import java.io.Closeable; import java.io.File; import java.io.IOException; +import java.util.List; -import com.google.common.collect.AbstractIterator; import org.apache.commons.io.input.ReversedLinesFileReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.base.Splitter; +import com.google.common.collect.AbstractIterator; + /** * Iterator over the revisions in the journal in reverse order * (end of the file to beginning). */ -public final class JournalReader extends AbstractIterator implements Closeable { +public final class JournalReader extends AbstractIterator implements Closeable { private static final Logger LOG = LoggerFactory.getLogger(JournalReader.class); private final ReversedLinesFileReader journal; @@ -48,15 +51,29 @@ public final class JournalReader extends AbstractIterator implements Clo * the journal file. */ @Override - protected String computeNext() { + protected JournalEntry computeNext() { try { - String line = journal.readLine(); - while (line != null) { - int space = line.indexOf(' '); - if (space != -1) { - return line.substring(0, space); + String line = null; + while ((line = journal.readLine()) != null) { + if (line.indexOf(' ') != -1) { + List splits = Splitter.on(' ').splitToList(line); + String revision = splits.get(0); + long timestamp = -1L; + + if (splits.size() > 2) { + try { + timestamp = Long.parseLong(splits.get(2)); + } catch (NumberFormatException e) { + // ignore + } + } else { + LOG.warn("Timestamp information is missing for revision {}", revision); + } + + return new JournalEntry(revision, timestamp); + } else { + LOG.warn("Skipping invalid journal entry: {}", line); } - line = journal.readLine(); } } catch (IOException e) { LOG.error("Error reading journal file", e); diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/ConsistencyChecker.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/ConsistencyChecker.java index 1bbed7e..b6686d1 100644 --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/ConsistencyChecker.java +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/ConsistencyChecker.java @@ -132,7 +132,7 @@ public class ConsistencyChecker implements Closeable { int revisionCount = 0; while (journal.hasNext() && count < filterPaths.size()) { - String revision = journal.next(); + String revision = journal.next().getRevision(); checker.print("Checking revision {0}", revision); try { diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/RevisionHistory.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/RevisionHistory.java index f94b960..3dcb662 100644 --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/RevisionHistory.java +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/RevisionHistory.java @@ -32,16 +32,18 @@ import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import com.google.common.base.Function; -import com.google.common.collect.Iterators; import org.apache.jackrabbit.oak.json.BlobSerializer; import org.apache.jackrabbit.oak.json.JsonSerializer; import org.apache.jackrabbit.oak.segment.SegmentNodeState; -import org.apache.jackrabbit.oak.segment.file.ReadOnlyFileStore; 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.spi.state.NodeState; +import com.google.common.base.Function; +import com.google.common.collect.Iterators; + /** * Utility for tracing a node back through the revision history. */ @@ -78,15 +80,18 @@ public class RevisionHistory { public Iterator getHistory(@Nonnull File journal, @Nonnull final String path) throws IOException { checkNotNull(path); - return Iterators.transform(new JournalReader(checkNotNull(journal)), - new Function() { - @Nullable @Override - public HistoryElement apply(String revision) { - store.setRevision(revision); - NodeState node = getNode(store.getHead(), path); - return new HistoryElement(revision, node); - } - }); + + try (JournalReader journalReader = new JournalReader(checkNotNull(journal))) { + return Iterators.transform(journalReader, + new Function() { + @Nullable @Override + public HistoryElement apply(JournalEntry entry) { + store.setRevision(entry.getRevision()); + NodeState node = getNode(store.getHead(), path); + return new HistoryElement(entry.getRevision(), node); + } + }); + } } /** diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Utils.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Utils.java index 04502f0..11df393 100644 --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Utils.java +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Utils.java @@ -23,14 +23,21 @@ import static org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.fileStoreB import java.io.File; import java.io.IOException; +import java.util.Iterator; import java.util.List; +import javax.annotation.Nullable; + 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.tooling.BasicReadOnlyBlobStore; import org.apache.jackrabbit.oak.spi.blob.BlobStore; +import com.google.common.base.Function; +import com.google.common.collect.Iterators; + final class Utils { private static final boolean TAR_STORAGE_MEMORY_MAPPED = Boolean.getBoolean("tar.memoryMapped"); @@ -63,7 +70,14 @@ final class Utils { if (journal.exists()) { try (JournalReader journalReader = new JournalReader(journal)) { - return newArrayList(journalReader); + Iterator revisionIterator = Iterators.transform(journalReader, new Function() { + @Nullable + @Override + public String apply(JournalEntry entry) { + return entry.getRevision(); + } + }); + return newArrayList(revisionIterator); } catch (Exception e) { e.printStackTrace(); } diff --git oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/JournalEntryTest.java oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/JournalEntryTest.java index 36f5144..000efc1 100644 --- oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/JournalEntryTest.java +++ oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/JournalEntryTest.java @@ -73,14 +73,16 @@ public class JournalEntryTest { assertFalse(lines.isEmpty()); String line = lines.get(0); - List journalEntry = journalParts(line); - assertEquals(3, journalEntry.size()); + List parts = journalParts(line); + assertEquals(3, parts.size()); - long entryTime = Long.valueOf(journalEntry.get(2)); + long entryTime = Long.valueOf(parts.get(2)); assertTrue(entryTime >= startTime); JournalReader jr = new JournalReader(journal); - assertEquals(journalParts(lines.get(lines.size() - 1)).get(0), jr.next()); + JournalEntry journalEntry = jr.next(); + assertEquals(journalParts(lines.get(lines.size() - 1)).get(0), journalEntry.getRevision()); + assertEquals(journalParts(lines.get(lines.size() - 1)).get(2), String.valueOf(journalEntry.getTimestamp())); jr.close(); } diff --git oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/JournalReaderTest.java oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/JournalReaderTest.java index c805978..f1e48db 100644 --- oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/JournalReaderTest.java +++ oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/JournalReaderTest.java @@ -46,9 +46,11 @@ public class JournalReaderTest { @Test public void testSingleton() throws IOException { - try (JournalReader journalReader = createJournalReader("one 1")) { + try (JournalReader journalReader = createJournalReader("one 1 123")) { assertTrue(journalReader.hasNext()); - assertEquals("one", journalReader.next()); + JournalEntry entry = journalReader.next(); + assertEquals("one", entry.getRevision()); + assertEquals("123", String.valueOf(entry.getTimestamp())); assertFalse(journalReader.hasNext()); } } @@ -57,11 +59,20 @@ public class JournalReaderTest { public void testMultiple() throws IOException { try (JournalReader journalReader = createJournalReader("one 1\ntwo 2\nthree 3 456")) { assertTrue(journalReader.hasNext()); - assertEquals("three", journalReader.next()); + + JournalEntry entry = journalReader.next(); + assertEquals("three", entry.getRevision()); + assertEquals("456", String.valueOf(entry.getTimestamp())); + assertTrue(journalReader.hasNext()); - assertEquals("two", journalReader.next()); + entry = journalReader.next(); + assertEquals("two", entry.getRevision()); + assertEquals("-1", String.valueOf(entry.getTimestamp())); + assertTrue(journalReader.hasNext()); - assertEquals("one", journalReader.next()); + entry = journalReader.next(); + assertEquals("one", entry.getRevision()); + assertEquals("-1", String.valueOf(entry.getTimestamp())); assertFalse(journalReader.hasNext()); } } @@ -70,34 +81,52 @@ public class JournalReaderTest { public void testSpaces() throws IOException { try (JournalReader journalReader = createJournalReader("\n \n \n ")) { assertTrue(journalReader.hasNext()); - assertEquals("", journalReader.next()); + + JournalEntry entry = journalReader.next(); + assertEquals("", entry.getRevision()); + assertEquals("-1", String.valueOf(entry.getTimestamp())); + assertTrue(journalReader.hasNext()); - assertEquals("", journalReader.next()); + entry = journalReader.next(); + assertEquals("", entry.getRevision()); + assertEquals("-1", String.valueOf(entry.getTimestamp())); + assertTrue(journalReader.hasNext()); - assertEquals("", journalReader.next()); + entry = journalReader.next(); + assertEquals("", entry.getRevision()); + assertEquals("-1", String.valueOf(entry.getTimestamp())); assertFalse(journalReader.hasNext()); } } @Test public void testIgnoreInvalid() throws IOException { - try (JournalReader journalReader = createJournalReader("one 1\ntwo 2\ninvalid\nthree 3")) { + try (JournalReader journalReader = createJournalReader("one 1\ntwo 2\ninvalid\nthree 3 123")) { assertTrue(journalReader.hasNext()); - assertEquals("three", journalReader.next()); + + JournalEntry entry = journalReader.next(); + assertEquals("three", entry.getRevision()); + assertEquals("123", String.valueOf(entry.getTimestamp())); + assertTrue(journalReader.hasNext()); - assertEquals("two", journalReader.next()); + entry = journalReader.next(); + assertEquals("two", entry.getRevision()); + assertEquals("-1", String.valueOf(entry.getTimestamp())); + assertTrue(journalReader.hasNext()); - assertEquals("one", journalReader.next()); + entry = journalReader.next(); + assertEquals("one", entry.getRevision()); + assertEquals("-1", String.valueOf(entry.getTimestamp())); assertFalse(journalReader.hasNext()); } } @Test public void testIterable() throws IOException { - try (JournalReader journalReader = createJournalReader("one 1\ntwo 2\ninvalid\nthree 3")) { - assertTrue(Iterators.contains(journalReader, "three")); - assertTrue(Iterators.contains(journalReader, "two")); - assertTrue(Iterators.contains(journalReader, "one")); + try (JournalReader journalReader = createJournalReader("one 1\ntwo 2\ninvalid\nthree 3 123")) { + assertTrue(Iterators.contains(journalReader, new JournalEntry("three", 123L))); + assertTrue(Iterators.contains(journalReader, new JournalEntry("two", -1L))); + assertTrue(Iterators.contains(journalReader, new JournalEntry("one", -1L))); } } diff --git oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/TarRevisionsTest.java oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/TarRevisionsTest.java index b6f660e..493d1a8 100644 --- oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/TarRevisionsTest.java +++ oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/TarRevisionsTest.java @@ -92,7 +92,7 @@ public class TarRevisionsTest { public void getHead() throws IOException { try (JournalReader reader = createJournalReader()) { assertTrue(reader.hasNext()); - assertEquals(revisions.getHead().toString10(), reader.next()); + assertEquals(revisions.getHead().toString10(), reader.next().getRevision()); } } @@ -112,7 +112,7 @@ public class TarRevisionsTest { try (JournalReader reader = createJournalReader()) { assertTrue(reader.hasNext()); - assertEquals(newRoot.getRecordId().toString10(), reader.next()); + assertEquals(newRoot.getRecordId().toString10(), reader.next().getRevision()); } } @@ -133,7 +133,7 @@ public class TarRevisionsTest { try (JournalReader reader = createJournalReader()) { assertTrue(reader.hasNext()); - assertEquals(newRoot.getRecordId().toString10(), reader.next()); + assertEquals(newRoot.getRecordId().toString10(), reader.next().getRevision()); } }