diff --git a/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/PersistentCacheTest.java b/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/PersistentCacheTest.java index cf47e13c2f..e03996d14e 100644 --- a/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/PersistentCacheTest.java +++ b/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/PersistentCacheTest.java @@ -26,8 +26,10 @@ import org.apache.jackrabbit.oak.plugins.document.DocumentNodeState; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreBuilder; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreHelper; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.PathRev; import org.apache.jackrabbit.oak.plugins.document.Revision; +import org.apache.jackrabbit.oak.plugins.document.RevisionVector; import org.apache.jackrabbit.oak.stats.StatisticsProvider; import javax.jcr.Repository; @@ -73,7 +75,9 @@ public class PersistentCacheTest extends AbstractTest { @Override protected void runTest() throws Exception { for (int i = 0; i < ITEMS_TO_ADD; i++) { - PathRev key = PathRev.fromString("/" + timestamp.getAndIncrement() + "@" + new Revision(timestamp.getAndIncrement(), 0, 0)); + Path p = Path.fromString("/" + timestamp.getAndIncrement()); + Revision r = new Revision(timestamp.getAndIncrement(), 0, 0); + PathRev key = new PathRev(p, new RevisionVector(r)); nodesCache.put(key, dns.getRoot()); nodesCache.getIfPresent(key); // read, so the entry is marked as used } diff --git a/oak-run/src/main/groovy/org/apache/jackrabbit/oak/console/commands/LsdDocumentCommand.groovy b/oak-run/src/main/groovy/org/apache/jackrabbit/oak/console/commands/LsdDocumentCommand.groovy index b813f3c5c8..55ec16eb84 100644 --- a/oak-run/src/main/groovy/org/apache/jackrabbit/oak/console/commands/LsdDocumentCommand.groovy +++ b/oak-run/src/main/groovy/org/apache/jackrabbit/oak/console/commands/LsdDocumentCommand.groovy @@ -22,6 +22,7 @@ import groovy.transform.CompileStatic import org.apache.jackrabbit.oak.console.ConsoleSession import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore import org.apache.jackrabbit.oak.plugins.document.NodeDocument +import org.apache.jackrabbit.oak.plugins.document.Path import org.apache.jackrabbit.oak.plugins.document.util.Utils import org.codehaus.groovy.tools.shell.CommandSupport import org.codehaus.groovy.tools.shell.Groovysh @@ -40,7 +41,7 @@ class LsdDocumentCommand extends CommandSupport{ Object execute(List args) { assert session.store instanceof DocumentNodeStore PrintWriter writer = io.out - String path = session.getWorkingPath(); + Path path = Path.fromString(session.getWorkingPath()); String fromKey = Utils.getKeyLowerLimit(path); String toKey = Utils.getKeyUpperLimit(path); int num = 0; diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/index/indexer/document/NodeStateEntryTraverser.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/index/indexer/document/NodeStateEntryTraverser.java index e898286c21..374c3263ce 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/index/indexer/document/NodeStateEntryTraverser.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/index/indexer/document/NodeStateEntryTraverser.java @@ -31,6 +31,7 @@ import org.apache.jackrabbit.oak.plugins.document.Collection; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeState; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore; import org.apache.jackrabbit.oak.plugins.document.NodeDocument; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.RevisionVector; import org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentStore; import org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentTraverser; @@ -101,7 +102,7 @@ public class NodeStateEntryTraverser implements Iterable, Closea @SuppressWarnings("StaticPseudoFunctionalStyleMethod") private Iterable getEntries(NodeDocument doc) { - String path = doc.getPath(); + Path path = Path.fromString(doc.getPath()); DocumentNodeState nodeState = documentNodeStore.getNode(path, rootRevision); @@ -113,7 +114,7 @@ public class NodeStateEntryTraverser implements Iterable, Closea return transform( concat(singleton(nodeState), nodeState.getAllBundledNodesStates()), - dns -> new NodeStateEntry(dns, dns.getPath()) + dns -> new NodeStateEntry(dns, dns.getPath().toString()) ); } diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/plugins/document/SweepHelper.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/plugins/document/SweepHelper.java index 7c2160a64a..f449b20c0d 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/plugins/document/SweepHelper.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/plugins/document/SweepHelper.java @@ -62,7 +62,7 @@ public final class SweepHelper { sweeper.sweep(seeker.getCandidates(sweepRev.get().getTimestamp()), new NodeDocumentSweepListener() { @Override - public void sweepUpdate(Map updates) + public void sweepUpdate(Map updates) throws DocumentStoreException { // create an invalidate entry JournalEntry inv = JOURNAL.newDocument(store); diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/MapDBMapFactory.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/MapDBMapFactory.java index 07fb41a197..ba760c36bf 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/MapDBMapFactory.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/MapDBMapFactory.java @@ -20,9 +20,13 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.Serializable; +import java.util.Comparator; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.jackrabbit.oak.plugins.document.Path; +import org.apache.jackrabbit.oak.plugins.document.PathComparator; import org.apache.jackrabbit.oak.plugins.document.Revision; +import org.mapdb.BTreeKeySerializer; import org.mapdb.BTreeMap; import org.mapdb.DB; import org.mapdb.DBMaker; @@ -46,11 +50,39 @@ public class MapDBMapFactory extends MapFactory { } @Override - public BTreeMap create() { + public BTreeMap create() { return db.createTreeMap(String.valueOf(counter.incrementAndGet())) + .keySerializer(new PathSerializer()) .valueSerializer(new RevisionSerializer()) .counterEnable() - .makeStringMap(); + .make(); + } + + private static class PathSerializer extends BTreeKeySerializer { + + @Override + public void serialize(DataOutput out, int start, int end, Object[] keys) + throws IOException { + for (int i = start; i < end; i++) { + String p = keys[i].toString(); + out.writeUTF(p); + } + } + + @Override + public Object[] deserialize(DataInput in, int start, int end, int size) + throws IOException { + Object[] keys = new Object[size]; + for (int i = start; i < end; i++) { + keys[i] = Path.fromString(in.readUTF()); + } + return keys; + } + + @Override + public Comparator getComparator() { + return PathComparator.INSTANCE; + } } private static class RevisionSerializer implements Serializer, diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/AbstractDocumentNodeState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/AbstractDocumentNodeState.java index 66868431b7..bfa71c58c0 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/AbstractDocumentNodeState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/AbstractDocumentNodeState.java @@ -36,7 +36,7 @@ public abstract class AbstractDocumentNodeState extends AbstractNodeState { LoggerFactory.getLogger(AbstractDocumentNodeState.class.getName() + ".perf")); - public abstract String getPath(); + public abstract Path getPath(); public abstract RevisionVector getLastRevision(); diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Branch.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Branch.java index aa6511851a..089f3d4dbb 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Branch.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Branch.java @@ -232,7 +232,7 @@ class Branch { * there is none in this branch. */ @Nullable - public Revision getUnsavedLastRevision(String path, + public Revision getUnsavedLastRevision(Path path, Revision readRevision) { readRevision = readRevision.asBranchRevision(); for (Revision r : commits.descendingKeySet()) { @@ -266,22 +266,22 @@ class Branch { * @return modified paths until {@code r}. * @throws IllegalArgumentException if r is not a branch revision. */ - Iterable getModifiedPathsUntil(@NotNull final Revision r) { + Iterable getModifiedPathsUntil(@NotNull final Revision r) { checkArgument(checkNotNull(r).isBranch(), "Not a branch revision: %s", r); if (!commits.containsKey(r)) { return Collections.emptyList(); } - Iterable> paths = transform(filter(commits.entrySet(), + Iterable> paths = transform(filter(commits.entrySet(), new Predicate>() { @Override public boolean apply(Map.Entry input) { return !input.getValue().isRebase() && input.getKey().compareRevisionTime(r) <= 0; } - }), new Function, Iterable>() { + }), new Function, Iterable>() { @Override - public Iterable apply(Map.Entry input) { + public Iterable apply(Map.Entry input) { return input.getValue().getModifiedPaths(); } }); @@ -310,9 +310,9 @@ class Branch { abstract void applyTo(UnsavedModifications trunk, Revision commit); - abstract boolean isModified(String path); + abstract boolean isModified(Path path); - abstract Iterable getModifiedPaths(); + abstract Iterable getModifiedPaths(); protected abstract boolean isRebase(); } @@ -322,7 +322,7 @@ class Branch { */ private static class BranchCommitImpl extends BranchCommit { - private final Set modifications = Sets.newHashSet(); + private final Set modifications = Sets.newHashSet(); BranchCommitImpl(RevisionVector base, Revision commit) { super(base, commit); @@ -330,18 +330,18 @@ class Branch { @Override void applyTo(UnsavedModifications trunk, Revision commit) { - for (String p : modifications) { + for (Path p : modifications) { trunk.put(p, commit); } } @Override - boolean isModified(String path) { // TODO: rather pass NodeDocument? + boolean isModified(Path path) { // TODO: rather pass NodeDocument? return modifications.contains(path); } @Override - Iterable getModifiedPaths() { + Iterable getModifiedPaths() { return modifications; } @@ -353,7 +353,7 @@ class Branch { //------------------< LastRevTracker >---------------------------------- @Override - public void track(String path) { + public void track(Path path) { modifications.add(path); } @@ -381,7 +381,7 @@ class Branch { } @Override - boolean isModified(String path) { + boolean isModified(Path path) { for (BranchCommit c : previous.values()) { if (c.isModified(path)) { return true; @@ -396,11 +396,11 @@ class Branch { } @Override - Iterable getModifiedPaths() { - Iterable> paths = transform(previous.values(), - new Function>() { + Iterable getModifiedPaths() { + Iterable> paths = transform(previous.values(), + new Function>() { @Override - public Iterable apply(BranchCommit branchCommit) { + public Iterable apply(BranchCommit branchCommit) { return branchCommit.getModifiedPaths(); } }); @@ -426,7 +426,7 @@ class Branch { //------------------< LastRevTracker >---------------------------------- @Override - public void track(String path) { + public void track(Path path) { throw new UnsupportedOperationException("RebaseCommit is read-only"); } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Collision.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Collision.java index 237669ebfe..894137116a 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Collision.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Collision.java @@ -164,7 +164,7 @@ class Collision { } } // at this point we have a commitRootPath - UpdateOp op = new UpdateOp(Utils.getIdFromPath(commitRootPath), false); + UpdateOp op = new UpdateOp(Utils.getIdFromPath(Path.fromString(commitRootPath)), false); NodeDocument commitRoot = store.find(Collection.NODES, op.getId()); // check commit status of revision if (isCommitted(context.getCommitValue(revision, commitRoot))) { diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java index 288f50bbe5..9c37dde2d3 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java @@ -62,7 +62,7 @@ public class Commit { protected final DocumentNodeStore nodeStore; private final RevisionVector baseRevision; private final Revision revision; - private final HashMap operations = new LinkedHashMap(); + private final HashMap operations = new LinkedHashMap<>(); private final Set collisions = new LinkedHashSet(); private Branch b; private Rollback rollback = Rollback.NONE; @@ -71,14 +71,14 @@ public class Commit { * List of all node paths which have been modified in this commit. In addition to the nodes * which are actually changed it also contains there parent node paths */ - private final HashSet modifiedNodes = new HashSet(); + private final HashSet modifiedNodes = new HashSet<>(); - private final HashSet addedNodes = new HashSet(); - private final HashSet removedNodes = new HashSet(); + private final HashSet addedNodes = new HashSet<>(); + private final HashSet removedNodes = new HashSet<>(); /** Set of all nodes which have binary properties. **/ - private final HashSet nodesWithBinaries = Sets.newHashSet(); - private final HashMap bundledNodes = Maps.newHashMap(); + private final HashSet nodesWithBinaries = new HashSet<>(); + private final HashMap bundledNodes = new HashMap<>(); /** * Create a new Commit. @@ -99,11 +99,11 @@ public class Commit { Commit(@NotNull DocumentNodeStore nodeStore, @NotNull Revision revision, @Nullable RevisionVector baseRevision, - @NotNull Map operations, - @NotNull Set addedNodes, - @NotNull Set removedNodes, - @NotNull Set nodesWithBinaries, - @NotNull Map bundledNodes) { + @NotNull Map operations, + @NotNull Set addedNodes, + @NotNull Set removedNodes, + @NotNull Set nodesWithBinaries, + @NotNull Map bundledNodes) { this(nodeStore, revision, baseRevision); this.operations.putAll(operations); this.addedNodes.addAll(addedNodes); @@ -112,7 +112,7 @@ public class Commit { this.bundledNodes.putAll(bundledNodes); } - UpdateOp getUpdateOperationForNode(String path) { + UpdateOp getUpdateOperationForNode(Path path) { UpdateOp op = operations.get(path); if (op == null) { op = createUpdateOp(path, revision, isBranchCommit()); @@ -121,7 +121,7 @@ public class Commit { return op; } - static UpdateOp createUpdateOp(String path, + static UpdateOp createUpdateOp(Path path, Revision revision, boolean isBranch) { String id = Utils.getIdFromPath(path); @@ -162,7 +162,7 @@ public class Commit { * modifications. */ @NotNull - Iterable getModifiedPaths() { + Iterable getModifiedPaths() { return modifiedNodes; } @@ -248,7 +248,7 @@ public class Commit { private void updateBinaryStatus() { DocumentStore store = this.nodeStore.getDocumentStore(); - for (String path : this.nodesWithBinaries) { + for (Path path : this.nodesWithBinaries) { NodeDocument nd = store.getIfCached(Collection.NODES, Utils.getIdFromPath(path)); if ((nd == null) || !nd.hasBinary()) { UpdateOp updateParentOp = getUpdateOperationForNode(path); @@ -305,10 +305,10 @@ public class Commit { // the visibility of the commit String commitValue = baseBranchRevision != null ? baseBranchRevision.getBranchRevision().toString() : "c"; DocumentStore store = nodeStore.getDocumentStore(); - String commitRootPath = null; + Path commitRootPath = null; if (baseBranchRevision != null) { // branch commits always use root node as commit root - commitRootPath = "/"; + commitRootPath = Path.ROOT; } ArrayList changedNodes = new ArrayList(); // operations are added to this list before they are executed, @@ -316,23 +316,24 @@ public class Commit { ArrayList opLog = new ArrayList(); // Compute the commit root - for (String p : operations.keySet()) { + for (Path p : operations.keySet()) { markChanged(p); if (commitRootPath == null) { commitRootPath = p; } else { - while (!PathUtils.isAncestor(commitRootPath, p)) { - commitRootPath = PathUtils.getParentPath(commitRootPath); - if (denotesRoot(commitRootPath)) { + while (!commitRootPath.isAncestorOf(p)) { + Path parent = commitRootPath.getParent(); + if (parent == null) { break; } + commitRootPath = parent; } } } rollback = new Rollback(revision, opLog, Utils.getIdFromPath(commitRootPath)); - for (String p : bundledNodes.keySet()){ + for (Path p : bundledNodes.keySet()){ markChanged(p); } @@ -345,7 +346,7 @@ public class Commit { store.create(JOURNAL, singletonList(doc.asUpdateOp(r))); } - int commitRootDepth = PathUtils.getDepth(commitRootPath); + int commitRootDepth = commitRootPath.getDepth(); // check if there are real changes on the commit root boolean commitRootHasChanges = operations.containsKey(commitRootPath); for (UpdateOp op : operations.values()) { @@ -476,14 +477,13 @@ public class Commit { } private void updateParentChildStatus() { - final Set processedParents = Sets.newHashSet(); - for (String path : addedNodes) { - if (denotesRoot(path)) { + final Set processedParents = Sets.newHashSet(); + for (Path path : addedNodes) { + Path parentPath = path.getParent(); + if (parentPath == null) { continue; } - String parentPath = PathUtils.getParentPath(path); - if (processedParents.contains(parentPath)) { continue; } @@ -725,10 +725,10 @@ public class Commit { */ void applyLastRevUpdates(boolean isBranchCommit) { LastRevTracker tracker = nodeStore.createTracker(revision, isBranchCommit); - for (String path : modifiedNodes) { + for (Path path : modifiedNodes) { UpdateOp op = operations.get(path); // track _lastRev only when path is not for a bundled node state - if ((op == null || !hasContentChanges(op) || denotesRoot(path)) + if ((op == null || !hasContentChanges(op) || path.isRoot()) && !isBundled(path)) { // track intermediate node and root tracker.track(path); @@ -743,33 +743,30 @@ public class Commit { * @param isBranchCommit whether this is a commit to a branch */ public void applyToCache(RevisionVector before, boolean isBranchCommit) { - HashMap> nodesWithChangedChildren = new HashMap>(); - for (String p : modifiedNodes) { - if (denotesRoot(p)) { + HashMap> nodesWithChangedChildren = new HashMap<>(); + for (Path p : modifiedNodes) { + if (p.isRoot()) { continue; } - String parent = PathUtils.getParentPath(p); - ArrayList list = nodesWithChangedChildren.get(parent); - if (list == null) { - list = new ArrayList(); - nodesWithChangedChildren.put(parent, list); - } + Path parent = p.getParent(); + ArrayList list = nodesWithChangedChildren + .computeIfAbsent(parent, k -> new ArrayList<>()); list.add(p); } // the commit revision with branch flag if this is a branch commit Revision rev = isBranchCommit ? revision.asBranchRevision() : revision; RevisionVector after = before.update(rev); DiffCache.Entry cacheEntry = nodeStore.getDiffCache().newEntry(before, after, true); - List added = new ArrayList(); - List removed = new ArrayList(); - List changed = new ArrayList(); - for (String path : modifiedNodes) { + List added = new ArrayList<>(); + List removed = new ArrayList<>(); + List changed = new ArrayList<>(); + for (Path path : modifiedNodes) { added.clear(); removed.clear(); changed.clear(); - ArrayList changes = nodesWithChangedChildren.get(path); + ArrayList changes = nodesWithChangedChildren.get(path); if (changes != null) { - for (String s : changes) { + for (Path s : changes) { if (addedNodes.contains(s)) { added.add(s); } else if (removedNodes.contains(s)) { @@ -801,41 +798,38 @@ public class Commit { * @param changed the list of changed child nodes * @param cacheEntry the cache entry changes are added to */ - private void addChangesToDiffCacheEntry(String path, - List added, - List removed, - List changed, + private void addChangesToDiffCacheEntry(Path path, + List added, + List removed, + List changed, DiffCache.Entry cacheEntry) { // update diff cache JsopWriter w = new JsopStream(); - for (String p : added) { - w.tag('+').key(PathUtils.getName(p)).object().endObject(); + for (Path p : added) { + w.tag('+').key(p.getName()).object().endObject(); } - for (String p : removed) { - w.tag('-').value(PathUtils.getName(p)); + for (Path p : removed) { + w.tag('-').value(p.getName()); } - for (String p : changed) { - w.tag('^').key(PathUtils.getName(p)).object().endObject(); + for (Path p : changed) { + w.tag('^').key(p.getName()).object().endObject(); } cacheEntry.append(path, w.toString()); } - private void markChanged(String path) { - if (!denotesRoot(path) && !PathUtils.isAbsolute(path)) { - throw new IllegalArgumentException("path: " + path); - } + private void markChanged(Path path) { while (true) { if (!modifiedNodes.add(path)) { break; } - if (denotesRoot(path)) { + path = path.getParent(); + if (path == null) { break; } - path = PathUtils.getParentPath(path); } } - private boolean isBundled(String path) { + private boolean isBundled(Path path) { return bundledNodes.containsKey(path); } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CommitBuilder.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CommitBuilder.java index ae577393d0..1f4019b826 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CommitBuilder.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CommitBuilder.java @@ -30,6 +30,7 @@ import org.apache.jackrabbit.oak.spi.state.NodeState; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; /** @@ -43,14 +44,14 @@ class CommitBuilder { private final DocumentNodeStore nodeStore; private final Revision revision; private final RevisionVector baseRevision; - private final Map operations = new LinkedHashMap<>(); + private final Map operations = new LinkedHashMap<>(); - private final Set addedNodes = new HashSet<>(); - private final Set removedNodes = new HashSet<>(); + private final Set addedNodes = new HashSet<>(); + private final Set removedNodes = new HashSet<>(); /** Set of all nodes which have binary properties. **/ - private final Set nodesWithBinaries = new HashSet<>(); - private final Map bundledNodes = new HashMap<>(); + private final Set nodesWithBinaries = new HashSet<>(); + private final Map bundledNodes = new HashMap<>(); /** * Creates a new builder with a pseudo commit revision. Building the commit @@ -103,7 +104,7 @@ class CommitBuilder { * @return {@code this} builder. */ @NotNull - CommitBuilder addNode(@NotNull String path) { + CommitBuilder addNode(@NotNull Path path) { addNode(new DocumentNodeState(nodeStore, path, new RevisionVector(revision))); return this; } @@ -121,7 +122,7 @@ class CommitBuilder { throws DocumentStoreException { checkNotNull(node); - String path = node.getPath(); + Path path = node.getPath(); UpdateOp op = node.asOperation(revision); if (operations.containsKey(path)) { String msg = "Node already added: " + path; @@ -144,8 +145,8 @@ class CommitBuilder { * @return {@code this} builder. */ @NotNull - CommitBuilder addBundledNode(@NotNull String path, - @NotNull String bundlingRootPath) { + CommitBuilder addBundledNode(@NotNull Path path, + @NotNull Path bundlingRootPath) { checkNotNull(path); checkNotNull(bundlingRootPath); @@ -163,7 +164,7 @@ class CommitBuilder { * a node at the given {@code path} in this commit builder. */ @NotNull - CommitBuilder removeNode(@NotNull String path, + CommitBuilder removeNode(@NotNull Path path, @NotNull NodeState state) throws DocumentStoreException { checkNotNull(path); @@ -192,7 +193,7 @@ class CommitBuilder { * @return {@code this} builder. */ @NotNull - CommitBuilder updateProperty(@NotNull String path, + CommitBuilder updateProperty(@NotNull Path path, @NotNull String propertyName, @Nullable String value) { checkNotNull(path); @@ -212,7 +213,7 @@ class CommitBuilder { * @return {@code this} builder. */ @NotNull - CommitBuilder markNodeHavingBinary(@NotNull String path) { + CommitBuilder markNodeHavingBinary(@NotNull Path path) { checkNotNull(path); nodesWithBinaries.add(path); @@ -248,7 +249,7 @@ class CommitBuilder { checkNotNull(revision); Revision from = this.revision; - Map operations = Maps.transformValues( + Map operations = Maps.transformValues( this.operations, op -> rewrite(op, from, revision)); return new Commit(nodeStore, revision, baseRevision, operations, addedNodes, removedNodes, nodesWithBinaries, bundledNodes); @@ -266,7 +267,7 @@ class CommitBuilder { //-------------------------< internal >------------------------------------- - private UpdateOp getUpdateOperationForNode(String path) { + private UpdateOp getUpdateOperationForNode(Path path) { UpdateOp op = operations.get(path); if (op == null) { op = createUpdateOp(path, revision, isBranchCommit()); @@ -275,7 +276,7 @@ class CommitBuilder { return op; } - private static UpdateOp createUpdateOp(String path, + private static UpdateOp createUpdateOp(Path path, Revision revision, boolean isBranch) { String id = Utils.getIdFromPath(path); diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DiffCache.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DiffCache.java index 1ff59dd7ad..01afa8de83 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DiffCache.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DiffCache.java @@ -47,12 +47,12 @@ abstract class DiffCache { @Nullable abstract String getChanges(@NotNull RevisionVector from, @NotNull RevisionVector to, - @NotNull String path, + @NotNull Path path, @Nullable Loader loader); /** * Starts a new cache entry for the diff cache. Actual changes are added - * to the entry with the {@link Entry#append(String, String)} method. + * to the entry with the {@link Entry#append(Path, String)} method. * * @param from the from revision. * @param to the to revision. @@ -73,7 +73,7 @@ abstract class DiffCache { /** * Parses the jsop diff returned by - * {@link #getChanges(RevisionVector, RevisionVector, String, Loader)} and reports the + * {@link #getChanges(RevisionVector, RevisionVector, Path, Loader)} and reports the * changes by calling the appropriate methods on {@link Diff}. * * @param jsop the jsop diff to parse. @@ -134,7 +134,7 @@ abstract class DiffCache { * @param path the path of the parent node. * @param changes the child node changes. */ - void append(@NotNull String path, + void append(@NotNull Path path, @NotNull String changes); /** diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentBranchRootNodeState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentBranchRootNodeState.java index 81f2ad315a..e71b3a8ee1 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentBranchRootNodeState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentBranchRootNodeState.java @@ -32,11 +32,12 @@ class DocumentBranchRootNodeState extends DocumentNodeState { DocumentBranchRootNodeState(@NotNull DocumentNodeStore store, @NotNull DocumentNodeStoreBranch branch, - @NotNull String path, + @NotNull Path path, @NotNull RevisionVector rootRevision, @Nullable RevisionVector lastRevision, - @NotNull BundlingContext bundlingContext) { - super(store, path, lastRevision, rootRevision, false, bundlingContext); + @NotNull BundlingContext bundlingContext, + int memory) { + super(store, path, lastRevision, rootRevision, false, bundlingContext, memory); this.store = store; this.branch = checkNotNull(branch); } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java index e3a6ebc0ba..2295a7841d 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java @@ -20,14 +20,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; -import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Set; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; import com.google.common.collect.TreeTraverser; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.cache.CacheValue; @@ -35,7 +32,6 @@ import org.apache.jackrabbit.oak.commons.json.JsopBuilder; import org.apache.jackrabbit.oak.commons.json.JsopReader; import org.apache.jackrabbit.oak.commons.json.JsopTokenizer; import org.apache.jackrabbit.oak.api.PropertyState; -import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.commons.json.JsopWriter; import org.apache.jackrabbit.oak.json.JsonSerializer; import org.apache.jackrabbit.oak.plugins.document.bundlor.BundlorUtils; @@ -82,7 +78,7 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach */ static final int MAX_FETCH_SIZE = INITIAL_FETCH_SIZE << 4; - private final String path; + private final Path path; private final RevisionVector lastRevision; private final RevisionVector rootRevision; private final boolean fromExternalChange; @@ -93,39 +89,42 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach private final BundlingContext bundlingContext; private AbstractDocumentNodeState cachedSecondaryState; + private int memory; DocumentNodeState(@NotNull DocumentNodeStore store, - @NotNull String path, + @NotNull Path path, @NotNull RevisionVector rootRevision) { this(store, path, rootRevision, Collections.emptyList(), false, null); } - DocumentNodeState(@NotNull DocumentNodeStore store, @NotNull String path, + DocumentNodeState(@NotNull DocumentNodeStore store, @NotNull Path path, @NotNull RevisionVector rootRevision, Iterable properties, boolean hasChildren, @Nullable RevisionVector lastRevision) { this(store, path, rootRevision, asMap(properties), - hasChildren, lastRevision, false); + hasChildren, 0, lastRevision, false); } - private DocumentNodeState(@NotNull DocumentNodeStore store, - @NotNull String path, - @NotNull RevisionVector rootRevision, - @NotNull Map properties, - boolean hasChildren, - @Nullable RevisionVector lastRevision, - boolean fromExternalChange) { + public DocumentNodeState(@NotNull DocumentNodeStore store, + @NotNull Path path, + @NotNull RevisionVector rootRevision, + @NotNull Map properties, + boolean hasChildren, + int memory, + @Nullable RevisionVector lastRevision, + boolean fromExternalChange) { this(store, path, lastRevision, rootRevision, - fromExternalChange, createBundlingContext(checkNotNull(properties), hasChildren)); + fromExternalChange, createBundlingContext(checkNotNull(properties), hasChildren), memory); } protected DocumentNodeState(@NotNull DocumentNodeStore store, - @NotNull String path, - @Nullable RevisionVector lastRevision, - @Nullable RevisionVector rootRevision, - boolean fromExternalChange, - BundlingContext bundlingContext) { + @NotNull Path path, + @Nullable RevisionVector lastRevision, + @NotNull RevisionVector rootRevision, + boolean fromExternalChange, + BundlingContext bundlingContext, + int memory) { this.store = checkNotNull(store); this.path = checkNotNull(path); this.rootRevision = checkNotNull(rootRevision); @@ -134,6 +133,7 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach this.properties = bundlingContext.getProperties(); this.bundlingContext = bundlingContext; this.hasChildren = bundlingContext.hasChildren(); + this.memory = memory; } /** @@ -155,7 +155,7 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach if (rootRevision.equals(root) && fromExternalChange == externalChange) { return this; } else { - return new DocumentNodeState(store, path, lastRevision, root, externalChange, bundlingContext); + return new DocumentNodeState(store, path, lastRevision, root, externalChange, bundlingContext, memory); } } @@ -165,7 +165,7 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach */ @NotNull public DocumentNodeState fromExternalChange() { - return new DocumentNodeState(store, path, lastRevision, rootRevision, true, bundlingContext); + return new DocumentNodeState(store, path, lastRevision, rootRevision, true, bundlingContext, memory); } /** @@ -180,9 +180,9 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach */ @NotNull DocumentNodeState asBranchRootState(@NotNull DocumentNodeStoreBranch branch) { - checkState(PathUtils.denotesRoot(path)); + checkState(path.isRoot()); checkState(getRootRevision().isBranch()); - return new DocumentBranchRootNodeState(store, branch, path, rootRevision, lastRevision, bundlingContext); + return new DocumentBranchRootNodeState(store, branch, path, rootRevision, lastRevision, bundlingContext, memory); } /** @@ -210,7 +210,7 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach } @Override - public String getPath() { + public Path getPath() { return path; } @@ -284,7 +284,7 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach return bundledChildCount; } - String name = null; + String name = ""; long count = 0; int fetchSize = INITIAL_FETCH_SIZE; long remaining = Math.max(max, 1); // fetch at least once @@ -347,7 +347,7 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach @NotNull @Override public NodeBuilder builder() { - if ("/".equals(getPath())) { + if (getPath().isRoot()) { if (getRootRevision().isBranch()) { throw new IllegalStateException("Cannot create builder from branched DocumentNodeState"); } else { @@ -416,8 +416,8 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach UpdateOp asOperation(@NotNull Revision revision) { String id = Utils.getIdFromPath(path); UpdateOp op = new UpdateOp(id, true); - if (Utils.isLongPath(path)) { - op.set(NodeDocument.PATH, path); + if (Utils.isIdFromLongPath(id)) { + op.set(NodeDocument.PATH, path.toString()); } NodeDocument.setModified(op, revision); NodeDocument.setDeleted(op, revision, false); @@ -434,36 +434,40 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach @Override public int getMemory() { - long size = 40 // shallow - + (lastRevision != null ? lastRevision.getMemory() : 0) - + rootRevision.getMemory() - + estimateMemoryUsage(path); - // rough approximation for properties - for (Map.Entry entry : bundlingContext.getAllProperties().entrySet()) { - // name - size += estimateMemoryUsage(entry.getKey()); - PropertyState propState = entry.getValue(); - if (propState.getType() != Type.BINARY - && propState.getType() != Type.BINARIES) { - for (int i = 0; i < propState.count(); i++) { - // size() returns length of string - // shallow memory: - // - 8 bytes per reference in values list - // - 48 bytes per string - // double usage per property because of parsed PropertyState - size += (56 + propState.size(i) * 2) * 2; + long size = memory; + if (size == 0) { + size = 40 // shallow + + (lastRevision != null ? lastRevision.getMemory() : 0) + + rootRevision.getMemory() + + path.getMemory(); + // rough approximation for properties + for (Map.Entry entry : bundlingContext.getAllProperties().entrySet()) { + // name + size += estimateMemoryUsage(entry.getKey()); + PropertyState propState = entry.getValue(); + if (propState.getType() != Type.BINARY + && propState.getType() != Type.BINARIES) { + for (int i = 0; i < propState.count(); i++) { + // size() returns length of string + // shallow memory: + // - 8 bytes per reference in values list + // - 48 bytes per string + // double usage per property because of parsed PropertyState + size += (56 + propState.size(i) * 2) * 2; + } + } else { + // calculate size based on blobId value + // referencing the binary in the blob store + // double the size because the parsed PropertyState + // will have a similarly sized blobId as well + size += (long)estimateMemoryUsage(asString(entry.getValue())) * 2; } - } else { - // calculate size based on blobId value - // referencing the binary in the blob store - // double the size because the parsed PropertyState - // will have a similarly sized blobId as well - size += (long)estimateMemoryUsage(asString(entry.getValue())) * 2; } - } - if (size > Integer.MAX_VALUE) { - log.debug("Estimated memory footprint larger than Integer.MAX_VALUE: {}.", size); - size = Integer.MAX_VALUE; + if (size > Integer.MAX_VALUE) { + log.debug("Estimated memory footprint larger than Integer.MAX_VALUE: {}.", size); + size = Integer.MAX_VALUE; + } + memory = (int) size; } return (int) size; } @@ -478,6 +482,19 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach .filter(dns -> !dns.getPath().equals(this.getPath()) ); //Exclude this } + /** + * Returns all properties, including bundled, as Json serialized value. + * + * @return all properties, including bundled. + */ + public Map getAllBundledProperties() { + Map allProps = new HashMap<>(); + for (Map.Entry e : bundlingContext.getAllProperties().entrySet()) { + allProps.put(e.getKey(), asString(e.getValue())); + } + return allProps; + } + //------------------------------< internal >-------------------------------- @Nullable @@ -504,7 +521,7 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach return null; } - return store.getNode(concat(getPath(), childNodeName), lastRevision); + return store.getNode(new Path(getPath(), childNodeName), lastRevision); } @Nullable @@ -521,13 +538,13 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach * {@code name}. * * @param name the name of the lower bound child node entry (exclusive) or - * {@code null}, if the method should start with the first known - * child node. + * the empty {@code String}, if the method should start with the + * first known child node. * @param limit the maximum number of child node entries to return. * @return the child node entries. */ @NotNull - private Iterable getChildNodeEntries(@Nullable String name, + private Iterable getChildNodeEntries(@NotNull String name, int limit) { Iterable children = store.getChildNodes(this, name, limit); return Iterables.transform(children, new Function() { @@ -537,7 +554,7 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach @NotNull @Override public String getName() { - return PathUtils.getName(input.getPath()); + return input.getPath().getName(); } @NotNull @@ -558,72 +575,6 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach return builder.build(); } - public String asString() { - JsopWriter json = new JsopBuilder(); - json.key("path").value(path); - json.key("rev").value(rootRevision.toString()); - if (lastRevision != null) { - json.key("lastRev").value(lastRevision.toString()); - } - if (hasChildren) { - json.key("hasChildren").value(true); - } - if (properties.size() > 0) { - json.key("prop").object(); - for (Map.Entry e : bundlingContext.getAllProperties().entrySet()) { - json.key(e.getKey()).value(asString(e.getValue())); - } - json.endObject(); - } - return json.toString(); - } - - public static DocumentNodeState fromString(DocumentNodeStore store, String s) { - JsopTokenizer json = new JsopTokenizer(s); - String path = null; - RevisionVector rootRev = null; - RevisionVector lastRev = null; - boolean hasChildren = false; - HashMap map = new HashMap(); - while (true) { - String k = json.readString(); - json.read(':'); - if ("path".equals(k)) { - path = json.readString(); - } else if ("rev".equals(k)) { - rootRev = RevisionVector.fromString(json.readString()); - } else if ("lastRev".equals(k)) { - lastRev = RevisionVector.fromString(json.readString()); - } else if ("hasChildren".equals(k)) { - hasChildren = json.read() == JsopReader.TRUE; - } else if ("prop".equals(k)) { - json.read('{'); - while (true) { - if (json.matches('}')) { - break; - } - k = json.readString(); - json.read(':'); - String v = json.readString(); - map.put(k, v); - json.matches(','); - } - } - if (json.matches(JsopReader.END)) { - break; - } - json.read(','); - } - List props = Lists.newArrayListWithCapacity(map.size()); - for (Entry e : map.entrySet()) { - String value = e.getValue(); - if (value != null) { - props.add(store.createPropertyState(e.getKey(), value)); - } - } - return new DocumentNodeState(store, path, rootRev, props, hasChildren, lastRev); - } - /** * A list of children for a node. */ @@ -710,7 +661,7 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach private class ChildNodeEntryIterator implements Iterator { - private String previousName; + private String previousName = ""; private Iterator current; private int fetchSize = INITIAL_FETCH_SIZE; private int currentRemaining = fetchSize; @@ -768,11 +719,12 @@ public class DocumentNodeState extends AbstractDocumentNodeState implements Cach private AbstractDocumentNodeState createBundledState(String childNodeName, Matcher child) { return new DocumentNodeState( store, - concat(path, childNodeName), + new Path(path, childNodeName), lastRevision, rootRevision, fromExternalChange, - bundlingContext.childContext(child)); + bundlingContext.childContext(child), + memory); } private Iterator getBundledChildren(){ diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateCache.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateCache.java index 9ffb4403cd..270d0b239a 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateCache.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateCache.java @@ -25,13 +25,13 @@ import org.jetbrains.annotations.Nullable; public interface DocumentNodeStateCache { DocumentNodeStateCache NOOP = new DocumentNodeStateCache() { @Override - public AbstractDocumentNodeState getDocumentNodeState(String path, @Nullable RevisionVector rootRevision, + public AbstractDocumentNodeState getDocumentNodeState(Path path, @Nullable RevisionVector rootRevision, RevisionVector lastRev) { return null; } @Override - public boolean isCached(String path) { + public boolean isCached(Path path) { return false; } }; @@ -47,14 +47,14 @@ public interface DocumentNodeStateCache { * path is not cached then null would be returned */ @Nullable - AbstractDocumentNodeState getDocumentNodeState(String path, RevisionVector rootRevision, RevisionVector lastRev); + AbstractDocumentNodeState getDocumentNodeState(Path path, RevisionVector rootRevision, RevisionVector lastRev); /** * Determines if given path is cached by this implementation * @param path path to check * @return true if given path is cached */ - boolean isCached(String path); + boolean isCached(Path path); } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java index f457210d33..1bcd5f376d 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java @@ -21,19 +21,18 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.transform; -import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.reverse; import static java.util.Collections.singletonList; import static java.util.concurrent.TimeUnit.MICROSECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static org.apache.jackrabbit.oak.api.CommitFailedException.OAK; -import static org.apache.jackrabbit.oak.commons.PathUtils.ROOT_PATH; -import static org.apache.jackrabbit.oak.commons.PathUtils.concat; import static org.apache.jackrabbit.oak.plugins.document.Collection.JOURNAL; import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES; import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreBuilder.MANY_CHILDREN_THRESHOLD; import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.MODIFIED_IN_SECS_RESOLUTION; +import static org.apache.jackrabbit.oak.plugins.document.Path.NULL; +import static org.apache.jackrabbit.oak.plugins.document.Path.ROOT; import static org.apache.jackrabbit.oak.plugins.document.UpdateOp.Key; import static org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation; import static org.apache.jackrabbit.oak.plugins.document.util.Utils.alignWithExternalRevisions; @@ -100,7 +99,6 @@ import org.apache.jackrabbit.oak.commons.json.JsopWriter; import org.apache.jackrabbit.oak.api.Blob; import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.cache.CacheStats; -import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.json.BlobSerializer; import org.apache.jackrabbit.oak.plugins.document.util.LeaseCheckDocumentStoreWrapper; import org.apache.jackrabbit.oak.plugins.document.util.LoggingDocumentStoreWrapper; @@ -386,7 +384,7 @@ public final class DocumentNodeStore * * Key: PathRev, value: Children */ - private final Cache nodeChildrenCache; + private final Cache nodeChildrenCache; private final CacheStats nodeChildrenCacheStats; /** @@ -515,10 +513,10 @@ public final class DocumentNodeStore */ private final Set inDoubtTrunkCommits = Sets.newConcurrentHashSet(); - private final Predicate nodeCachePredicate; + private final Predicate nodeCachePredicate; public DocumentNodeStore(DocumentNodeStoreBuilder builder) { - this.nodeCachePredicate = builder.getNodeCachePredicate(); + this.nodeCachePredicate = builder.getNodeCachePathPredicate(); this.updateLimit = builder.getUpdateLimit(); this.commitValueResolver = new CachingCommitValueResolver( builder.getCommitValueCacheSize(), this::getSweepRevisions); @@ -591,7 +589,7 @@ public final class DocumentNodeStore this.lastRevRecoveryAgent = new LastRevRecoveryAgent(store, this, lastRevSeeker, clusterId -> this.signalClusterStateChange()); this.disableBranches = builder.isDisableBranches(); - this.missing = new DocumentNodeState(this, "MISSING", + this.missing = new DocumentNodeState(this, NULL, new RevisionVector(new Revision(0, 0, 0))) { @Override public int getMemory() { @@ -612,13 +610,13 @@ public final class DocumentNodeStore diffCache = builder.getDiffCache(this.clusterId); // check if root node exists - NodeDocument rootDoc = store.find(NODES, Utils.getIdFromPath("/")); + NodeDocument rootDoc = store.find(NODES, Utils.getIdFromPath(ROOT)); if (rootDoc == null) { // root node is missing: repository is not initialized Revision commitRev = newRevision(); RevisionVector head = new RevisionVector(commitRev); Commit commit = new CommitBuilder(this, commitRev, null) - .addNode("/") + .addNode(ROOT) .build(); try { commit.applyToDocumentStore(); @@ -626,12 +624,12 @@ public final class DocumentNodeStore commit.rollback(); throw new IllegalStateException("Conflict while creating root document", e); } - unsavedLastRevisions.put("/", commitRev); + unsavedLastRevisions.put(ROOT, commitRev); sweepRevisions = sweepRevisions.pmax(head); setRoot(head); // make sure _lastRev is written back to store backgroundWrite(); - rootDoc = store.find(NODES, Utils.getIdFromPath("/")); + rootDoc = store.find(NODES, Utils.getIdFromPath(ROOT)); // at this point the root document must exist if (rootDoc == null) { throw new IllegalStateException("Root document does not exist"); @@ -648,7 +646,7 @@ public final class DocumentNodeStore "missing revision for clusterId " + clusterId + ": " + rootRev); } - unsavedLastRevisions.put("/", initialRev); + unsavedLastRevisions.put(ROOT, initialRev); // set initial sweep revision sweepRevisions = sweepRevisions.pmax(new RevisionVector(initialRev)); if (!readOnlyMode) { @@ -747,7 +745,7 @@ public final class DocumentNodeStore @Override public void headOfQueue(@NotNull Revision revision) { setRoot(getHeadRevision().update(revision)); - unsavedLastRevisions.put(ROOT_PATH, revision); + unsavedLastRevisions.put(ROOT, revision); } }); } catch (DocumentStoreException e) { @@ -1069,11 +1067,11 @@ public final class DocumentNodeStore return nodeCache; } - public Cache getNodeChildrenCache() { + public Cache getNodeChildrenCache() { return nodeChildrenCache; } - public Predicate getNodeCachePredicate() { + public Predicate getNodeCachePredicate() { return nodeCachePredicate; } @@ -1092,7 +1090,7 @@ public final class DocumentNodeStore } void invalidateNodeCache(String path, RevisionVector revision){ - nodeCache.invalidate(new PathRev(path, revision)); + nodeCache.invalidate(new PathRev(Path.fromString(path), revision)); } public int getPendingWriteCount() { @@ -1113,14 +1111,15 @@ public final class DocumentNodeStore } @Nullable - AbstractDocumentNodeState getSecondaryNodeState(@NotNull final String path, + AbstractDocumentNodeState getSecondaryNodeState(@NotNull final Path path, @NotNull final RevisionVector rootRevision, @NotNull final RevisionVector rev) { //Check secondary cache first return nodeStateCache.getDocumentNodeState(path, rootRevision, rev); } - PropertyState createPropertyState(String name, String value){ + @NotNull + public PropertyState createPropertyState(String name, String value){ return new DocumentPropertyState(this, name, checkNotNull(value)); } @@ -1134,8 +1133,8 @@ public final class DocumentNodeStore * given revision. */ @Nullable - public DocumentNodeState getNode(@NotNull final String path, - @NotNull final RevisionVector rev) { + public DocumentNodeState getNode(@NotNull final Path path, + @NotNull final RevisionVector rev) { checkNotNull(rev); checkNotNull(path); final long start = PERFLOG.start(); @@ -1168,16 +1167,16 @@ public final class DocumentNodeStore @NotNull DocumentNodeState.Children getChildren(@NotNull final AbstractDocumentNodeState parent, - @Nullable final String name, - final int limit) + @NotNull final String name, + final int limit) throws DocumentStoreException { if (checkNotNull(parent).hasNoChildren()) { return DocumentNodeState.NO_CHILDREN; } - final String path = checkNotNull(parent).getPath(); + final Path path = checkNotNull(parent).getPath(); final RevisionVector readRevision = parent.getLastRevision(); try { - PathRev key = childNodeCacheKey(path, readRevision, name); + NamePathRev key = childNodeCacheKey(path, readRevision, name); DocumentNodeState.Children children = nodeChildrenCache.get(key, new Callable() { @Override public DocumentNodeState.Children call() throws Exception { @@ -1211,15 +1210,15 @@ public final class DocumentNodeStore * ascending order. * * @param parent the parent node. - * @param name the name of the lower bound child node (exclusive) or - * {@code null} if no lower bound is given. + * @param name the name of the lower bound child node (exclusive) or the + * empty {@code String} if no lower bound is given. * @param limit the maximum number of child nodes to return. * @return the children of {@code parent}. */ - DocumentNodeState.Children readChildren(AbstractDocumentNodeState parent, - String name, int limit) { + DocumentNodeState.Children readChildren(@NotNull AbstractDocumentNodeState parent, + @NotNull String name, int limit) { String queriedName = name; - String path = parent.getPath(); + Path path = parent.getPath(); RevisionVector rev = parent.getLastRevision(); LOG.trace("Reading children for [{}] at rev [{}]", path, rev); Iterable docs; @@ -1233,10 +1232,10 @@ public final class DocumentNodeStore int numReturned = 0; for (NodeDocument doc : docs) { numReturned++; - String p = doc.getPath(); + Path p = Path.fromString(doc.getPath()); // remember name of last returned document for // potential next round of readChildDocs() - name = PathUtils.getName(p); + name = p.getName(); // filter out deleted children DocumentNodeState child = getNode(p, rev); if (child == null) { @@ -1244,7 +1243,7 @@ public final class DocumentNodeStore } if (c.children.size() < limit) { // add to children until limit is reached - c.children.add(PathUtils.getName(p)); + c.children.add(p.getName()); } else { // enough collected and we know there are more c.hasMore = true; @@ -1256,7 +1255,7 @@ public final class DocumentNodeStore // fewer documents returned than requested // -> no more documents c.hasMore = false; - if (queriedName == null) { + if (queriedName.isEmpty()) { //we've got to the end of list and we started from the top //This list is complete and can be sorted Collections.sort(c.children); @@ -1274,20 +1273,21 @@ public final class DocumentNodeStore * lower exclusive bound. * * @param path the path of the parent document. - * @param name the lower exclusive bound or {@code null}. + * @param name the name of the lower bound child node (exclusive) or the + * empty {@code String} if no lower bound is given. * @param limit the maximum number of child documents to return. * @return the child documents. */ @NotNull - private Iterable readChildDocs(@NotNull final String path, - @Nullable String name, + private Iterable readChildDocs(@NotNull final Path path, + @NotNull String name, final int limit) { final String to = Utils.getKeyUpperLimit(checkNotNull(path)); final String from; - if (name != null) { - from = Utils.getIdFromPath(concat(path, name)); - } else { + if (name.isEmpty()) { from = Utils.getKeyLowerLimit(path); + } else { + from = Utils.getIdFromPath(new Path(path, name)); } return store.query(Collection.NODES, from, to, limit); } @@ -1297,15 +1297,15 @@ public final class DocumentNodeStore * {@code name} (exclusive). * * @param parent the parent node. - * @param name the name of the lower bound child node (exclusive) or - * {@code null}, if the method should start with the first known - * child node. + * @param name the name of the lower bound child node (exclusive) or the + * empty {@code String}, if the method should start with the + * first known child node. * @param limit the maximum number of child nodes to return. * @return the child nodes. */ @NotNull Iterable getChildNodes(@NotNull final DocumentNodeState parent, - @Nullable final String name, + @NotNull final String name, final int limit) { // Preemptive check. If we know there are no children then // return straight away @@ -1317,7 +1317,7 @@ public final class DocumentNodeStore return transform(getChildren(parent, name, limit).children, new Function() { @Override public DocumentNodeState apply(String input) { - String p = concat(parent.getPath(), input); + Path p = new Path(parent.getPath(), input); DocumentNodeState result = getNode(p, readRevision); if (result == null) { // This is very unexpected situation - parent's child list @@ -1361,7 +1361,7 @@ public final class DocumentNodeStore } @Nullable - DocumentNodeState readNode(String path, RevisionVector readRevision) { + private DocumentNodeState readNode(Path path, RevisionVector readRevision) { final long start = PERFLOG.start(); String id = Utils.getIdFromPath(path); Revision lastRevision = getPendingModifications().get(path); @@ -1395,9 +1395,9 @@ public final class DocumentNodeStore * */ void applyChanges(RevisionVector before, RevisionVector after, - Revision rev, String path, - boolean isNew, List added, - List removed, List changed) { + Revision rev, Path path, + boolean isNew, List added, + List removed, List changed) { if (isNew) { // determine the revision for the nodeChildrenCache entry when // the node is new. Fallback to after revision in case document @@ -1412,18 +1412,18 @@ public final class DocumentNodeStore // this is a leaf node. // check if it has the children flag set if (doc != null && doc.hasChildren()) { - PathRev key = childNodeCacheKey(path, afterLastRev, null); + NamePathRev key = childNodeCacheKey(path, afterLastRev, ""); LOG.debug("nodeChildrenCache.put({},{})", key, "NO_CHILDREN"); nodeChildrenCache.put(key, DocumentNodeState.NO_CHILDREN); } } else { DocumentNodeState.Children c = new DocumentNodeState.Children(); Set set = Sets.newTreeSet(); - for (String p : added) { - set.add(PathUtils.getName(p)); + for (Path p : added) { + set.add(p.getName()); } c.children.addAll(set); - PathRev key = childNodeCacheKey(path, afterLastRev, null); + NamePathRev key = childNodeCacheKey(path, afterLastRev, ""); LOG.debug("nodeChildrenCache.put({},{})", key, c); nodeChildrenCache.put(key, c); } @@ -1432,9 +1432,9 @@ public final class DocumentNodeStore DocumentNodeState beforeState = getRoot(before); // do we have a cached before state that can be used // to calculate the new children? - int depth = PathUtils.getDepth(path); + int depth = path.getDepth(); for (int i = 1; i <= depth && beforeState != null; i++) { - String p = PathUtils.getAncestorPath(path, depth - i); + Path p = path.getAncestor(depth - i); RevisionVector lastRev = beforeState.getLastRevision(); PathRev key = new PathRev(p, lastRev); beforeState = nodeCache.getIfPresent(key); @@ -1442,10 +1442,10 @@ public final class DocumentNodeStore // This is unexpected. The before state should exist. // Invalidate the relevant cache entries. (OAK-6294) LOG.warn("Before state is missing {}. Invalidating " + - "affected cache entries.", key.asString()); + "affected cache entries.", key); store.invalidateCache(NODES, Utils.getIdFromPath(p)); nodeCache.invalidate(key); - nodeChildrenCache.invalidate(childNodeCacheKey(path, lastRev, null)); + nodeChildrenCache.invalidate(childNodeCacheKey(path, lastRev, "")); beforeState = null; } } @@ -1454,12 +1454,12 @@ public final class DocumentNodeStore if (beforeState.hasNoChildren()) { children = DocumentNodeState.NO_CHILDREN; } else { - PathRev key = childNodeCacheKey(path, beforeState.getLastRevision(), null); + NamePathRev key = childNodeCacheKey(path, beforeState.getLastRevision(), ""); children = nodeChildrenCache.getIfPresent(key); } } if (children != null) { - PathRev afterKey = new PathRev(path, beforeState.getLastRevision().update(rev)); + NamePathRev afterKey = childNodeCacheKey(path, beforeState.getLastRevision().update(rev), ""); // are there any added or removed children? if (added.isEmpty() && removed.isEmpty()) { // simply use the same list @@ -1468,11 +1468,11 @@ public final class DocumentNodeStore } else if (!children.hasMore){ // list is complete. use before children as basis Set afterChildren = Sets.newTreeSet(children.children); - for (String p : added) { - afterChildren.add(PathUtils.getName(p)); + for (Path p : added) { + afterChildren.add(p.getName()); } - for (String p : removed) { - afterChildren.remove(PathUtils.getName(p)); + for (Path p : removed) { + afterChildren.remove(p.getName()); } DocumentNodeState.Children c = new DocumentNodeState.Children(); c.children.addAll(afterChildren); @@ -1487,8 +1487,8 @@ public final class DocumentNodeStore // incomplete list, but we only removed nodes // use linked hash set to retain order Set afterChildren = Sets.newLinkedHashSet(children.children); - for (String p : removed) { - afterChildren.remove(PathUtils.getName(p)); + for (Path p : removed) { + afterChildren.remove(p.getName()); } DocumentNodeState.Children c = new DocumentNodeState.Children(); c.children.addAll(afterChildren); @@ -1612,7 +1612,7 @@ public final class DocumentNodeStore */ @NotNull DocumentNodeState getRoot(@NotNull RevisionVector revision) { - DocumentNodeState root = getNode("/", revision); + DocumentNodeState root = getNode(ROOT, revision); if (root == null) { throw new IllegalStateException( "root node does not exist at revision " + revision); @@ -1676,9 +1676,9 @@ public final class DocumentNodeStore revs.add(ancestorRev); } revs.addAll(b.getCommits().tailSet(ancestorRev)); - UpdateOp rootOp = new UpdateOp(Utils.getIdFromPath("/"), false); + UpdateOp rootOp = new UpdateOp(Utils.getIdFromPath(ROOT), false); // reset each branch commit in reverse order - Map operations = Maps.newHashMap(); + Map operations = Maps.newHashMap(); AtomicReference currentRev = new AtomicReference<>(); for (Revision r : reverse(revs)) { operations.clear(); @@ -1728,7 +1728,7 @@ public final class DocumentNodeStore MergeCommit commit = newMergeCommit(base, numBranchCommits); try { // make branch commits visible - UpdateOp op = new UpdateOp(Utils.getIdFromPath("/"), false); + UpdateOp op = new UpdateOp(Utils.getIdFromPath(ROOT), false); NodeDocument.setModified(op, commit.getRevision()); if (b != null) { // check the branch age and fail the commit @@ -1812,7 +1812,7 @@ public final class DocumentNodeStore } else { return new LastRevTracker() { @Override - public void track(String path) { + public void track(Path path) { unsavedLastRevisions.put(path, r); } }; @@ -2273,7 +2273,7 @@ public final class DocumentNodeStore // was successful -> apply them to the diff cache try { JournalEntry.applyTo(changedPaths, diffCache, - PathUtils.ROOT_PATH, oldHead, newHead); + ROOT, oldHead, newHead); } catch (Exception e1) { LOG.error("backgroundRead: Exception while processing external changes from journal: " + e1, e1); } @@ -2318,7 +2318,7 @@ public final class DocumentNodeStore while ((b = branches.pollOrphanedBranch()) != null) { LOG.debug("Cleaning up orphaned branch with base revision: {}, " + "commits: {}", b.getBase(), b.getCommits()); - UpdateOp op = new UpdateOp(Utils.getIdFromPath("/"), false); + UpdateOp op = new UpdateOp(Utils.getIdFromPath(ROOT), false); for (Revision r : b.getCommits()) { r = r.asTrunkRevision(); NodeDocument.removeRevision(op, r); @@ -2329,7 +2329,7 @@ public final class DocumentNodeStore } private void cleanRootCollisions() { - String id = Utils.getIdFromPath("/"); + String id = Utils.getIdFromPath(ROOT); NodeDocument root = store.find(NODES, id); if (root != null) { cleanCollisions(root, Integer.MAX_VALUE); @@ -2491,7 +2491,7 @@ public final class DocumentNodeStore Revision newSweepRev = sweeper.sweep(docs, new NodeDocumentSweepListener() { @Override - public void sweepUpdate(final Map updates) + public void sweepUpdate(final Map updates) throws DocumentStoreException { // create a synthetic commit. this commit does not have any // changes, we just use it to create a journal entry for @@ -2518,7 +2518,7 @@ public final class DocumentNodeStore } } - private void writeUpdates(Map updates, + private void writeUpdates(Map updates, Revision revision) throws DocumentStoreException { // create journal entry @@ -2532,7 +2532,7 @@ public final class DocumentNodeStore throw new DocumentStoreException(msg); } changes.invalidate(Collections.singleton(r)); - unsavedLastRevisions.put(ROOT_PATH, revision); + unsavedLastRevisions.put(ROOT, revision); RevisionVector newHead = getHeadRevision().update(revision); setRoot(newHead); commitQueue.headRevisionChanged(); @@ -2769,22 +2769,22 @@ public final class DocumentNodeStore /** * Search for presence of child node as denoted by path in the children cache of parent * - * @param path + * @param path the path of the child node * @param rev revision at which check is performed * @return true if and only if the children cache entry for parent path is complete * and that list does not have the given child node. A false indicates that node might * exist */ - private boolean checkNodeNotExistsFromChildrenCache(String path, + private boolean checkNodeNotExistsFromChildrenCache(Path path, RevisionVector rev) { - if (PathUtils.denotesRoot(path)) { + final Path parentPath = path.getParent(); + if (parentPath == null) { return false; } - final String parentPath = PathUtils.getParentPath(path); - PathRev key = childNodeCacheKey(parentPath, rev, null);//read first child cache entry + NamePathRev key = childNodeCacheKey(parentPath, rev, "");//read first child cache entry DocumentNodeState.Children children = nodeChildrenCache.getIfPresent(key); - String lookupChildName = PathUtils.getName(path); + String lookupChildName = path.getName(); //Does not know about children so cannot say for sure if (children == null) { @@ -2850,8 +2850,8 @@ public final class DocumentNodeStore if (continueDiff) { DocumentNodeState.Children fromChildren, toChildren; - fromChildren = getChildren(from, null, max); - toChildren = getChildren(to, null, max); + fromChildren = getChildren(from, "", max); + toChildren = getChildren(to, "", max); getChildrenDoneIn = debug ? now() : 0; if (!fromChildren.hasMore && !toChildren.hasMore) { @@ -2867,8 +2867,8 @@ public final class DocumentNodeStore } else { diffAlgo = "diffAllChildren"; max = Integer.MAX_VALUE; - fromChildren = getChildren(from, null, max); - toChildren = getChildren(to, null, max); + fromChildren = getChildren(from, "", max); + toChildren = getChildren(to, "", max); diffFewChildren(w, from.getPath(), fromChildren, fromRev, toChildren, toRev); } @@ -2889,7 +2889,7 @@ public final class DocumentNodeStore return diff; } - private void diffManyChildren(JsopWriter w, String path, + private void diffManyChildren(JsopWriter w, Path path, RevisionVector fromRev, RevisionVector toRev) { long minTimestamp = Utils.getMinTimestampForDiff( @@ -2905,13 +2905,13 @@ public final class DocumentNodeStore long minValue = NodeDocument.getModifiedInSecs(minTimestamp); String fromKey = Utils.getKeyLowerLimit(path); String toKey = Utils.getKeyUpperLimit(path); - Set paths = Sets.newHashSet(); + Set paths = Sets.newHashSet(); LOG.debug("diffManyChildren: path: {}, fromRev: {}, toRev: {}", path, fromRev, toRev); for (NodeDocument doc : store.query(Collection.NODES, fromKey, toKey, NodeDocument.MODIFIED_IN_SECS, minValue, Integer.MAX_VALUE)) { - paths.add(doc.getPath()); + paths.add(Path.fromString(doc.getPath())); } LOG.debug("diffManyChildren: Affected paths: {}", paths.size()); @@ -2927,10 +2927,10 @@ public final class DocumentNodeStore } } } - for (String p : paths) { + for (Path p : paths) { DocumentNodeState fromNode = getNode(p, fromRev); DocumentNodeState toNode = getNode(p, toRev); - String name = PathUtils.getName(p); + String name = p.getName(); LOG.trace("diffManyChildren: Changed Path {}", path); @@ -2963,21 +2963,21 @@ public final class DocumentNodeStore } } - private static void addPathsForDiff(String path, - Set paths, - Iterable modified) { - for (String p : modified) { - if (PathUtils.denotesRoot(p)) { + private static void addPathsForDiff(Path path, + Set paths, + Iterable modified) { + for (Path p : modified) { + if (p.isRoot()) { continue; } - String parent = PathUtils.getParentPath(p); + Path parent = p.getParent(); if (path.equals(parent)) { paths.add(p); } } } - private void diffFewChildren(JsopWriter w, String parentPath, + private void diffFewChildren(JsopWriter w, Path parentPath, DocumentNodeState.Children fromChildren, RevisionVector fromRev, DocumentNodeState.Children toChildren, @@ -2987,7 +2987,7 @@ public final class DocumentNodeStore if (!childrenSet.contains(n)) { w.tag('-').value(n); } else { - String path = concat(parentPath, n); + Path path = new Path(parentPath, n); DocumentNodeState n1 = getNode(path, fromRev); DocumentNodeState n2 = getNode(path, toRev); // this is not fully correct: @@ -3009,11 +3009,10 @@ public final class DocumentNodeStore } } - private static PathRev childNodeCacheKey(@NotNull String path, - @NotNull RevisionVector readRevision, - @Nullable String name) { - String p = (name == null ? "" : name) + path; - return new PathRev(p, readRevision); + private static NamePathRev childNodeCacheKey(@NotNull Path path, + @NotNull RevisionVector readRevision, + @NotNull String name) { + return new NamePathRev(name, path, readRevision); } private static DocumentRootBuilder asDocumentRootBuilder(NodeBuilder builder) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java index 0fbd15f3c9..b3abc6d745 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java @@ -16,7 +16,7 @@ */ package org.apache.jackrabbit.oak.plugins.document; -import java.util.EnumMap; +import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; @@ -151,8 +151,7 @@ public class DocumentNodeStoreBuilder> { private CacheStats blobStoreCacheStats; private DocumentStoreStatsCollector documentStoreStatsCollector; private DocumentNodeStoreStatsCollector nodeStoreStatsCollector; - private Map persistentCacheStats = - new EnumMap(CacheType.class); + private Map persistentCacheStats = new HashMap<>(); private boolean bundlingDisabled; private JournalPropertyHandlerFactory journalPropertyHandlerFactory = new JournalPropertyHandlerFactory(); @@ -161,7 +160,7 @@ public class DocumentNodeStoreBuilder> { private long maxRevisionAgeMillis = DEFAULT_JOURNAL_GC_MAX_AGE_MILLIS; private GCMonitor gcMonitor = new LoggingGCMonitor( LoggerFactory.getLogger(VersionGarbageCollector.class)); - private Predicate nodeCachePredicate = Predicates.alwaysTrue(); + private Predicate nodeCachePredicate = Predicates.alwaysTrue(); /** * @return a new {@link DocumentNodeStoreBuilder}. @@ -479,7 +478,7 @@ public class DocumentNodeStoreBuilder> { } @NotNull - public Map getPersistenceCacheStats() { + public Map getPersistenceCacheStats() { return persistentCacheStats; } @@ -591,11 +590,11 @@ public class DocumentNodeStoreBuilder> { return buildCache(CacheType.NODE, getNodeCacheSize(), store, null); } - public Cache buildChildrenCache(DocumentNodeStore store) { + public Cache buildChildrenCache(DocumentNodeStore store) { return buildCache(CacheType.CHILDREN, getChildrenCacheSize(), store, null); } - public Cache buildMemoryDiffCache() { + public Cache buildMemoryDiffCache() { return buildCache(CacheType.DIFF, getMemoryDiffCacheSize(), null, null); } @@ -621,12 +620,29 @@ public class DocumentNodeStoreBuilder> { return new NodeDocumentCache(nodeDocumentsCache, nodeDocumentsCacheStats, prevDocumentsCache, prevDocumentsCacheStats, locks); } + /** + * @deprecated Use {@link #setNodeCachePathPredicate(Predicate)} instead. + */ + @Deprecated public T setNodeCachePredicate(Predicate p){ - this.nodeCachePredicate = p; + this.nodeCachePredicate = input -> input != null && p.apply(input.toString()); return thisBuilder(); } + /** + * @deprecated Use {@link #getNodeCachePathPredicate()} instead. + */ + @Deprecated public Predicate getNodeCachePredicate() { + return input -> input != null && nodeCachePredicate.apply(Path.fromString(input)); + } + + public T setNodeCachePathPredicate(Predicate p){ + this.nodeCachePredicate = p; + return thisBuilder(); + } + + public Predicate getNodeCachePathPredicate() { return nodeCachePredicate; } @@ -654,7 +670,7 @@ public class DocumentNodeStoreBuilder> { } PersistentCacheStats stats = PersistentCache.getPersistentCacheStats(cache); if (stats != null) { - persistentCacheStats.put(cacheType, stats); + persistentCacheStats.put(cacheType.name(), stats); } } return cache; diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java index 84b476248f..139d68dcfc 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java @@ -20,7 +20,6 @@ package org.apache.jackrabbit.oak.plugins.document; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Lists.newArrayList; -import static java.util.Collections.emptyList; import static org.apache.jackrabbit.oak.commons.IOUtils.closeQuietly; import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toLong; import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreBuilder.DEFAULT_MEMORY_CACHE_SIZE; @@ -79,7 +78,6 @@ import org.apache.jackrabbit.oak.plugins.blob.BlobTrackingStore; import org.apache.jackrabbit.oak.plugins.blob.SharedDataStore; import org.apache.jackrabbit.oak.plugins.blob.datastore.BlobIdTracker; import org.apache.jackrabbit.oak.plugins.blob.datastore.SharedDataStoreUtils; -import org.apache.jackrabbit.oak.plugins.document.persistentCache.CacheType; import org.apache.jackrabbit.oak.plugins.document.persistentCache.PersistentCacheStats; import org.apache.jackrabbit.oak.plugins.document.util.MongoConnection; import org.apache.jackrabbit.oak.spi.cluster.ClusterRepositoryInfo; @@ -88,7 +86,6 @@ import org.apache.jackrabbit.oak.spi.blob.BlobStoreWrapper; import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore; import org.apache.jackrabbit.oak.spi.blob.stats.BlobStoreStatsMBean; import org.apache.jackrabbit.oak.spi.commit.BackgroundObserverMBean; -import org.apache.jackrabbit.oak.spi.filter.PathFilter; import org.apache.jackrabbit.oak.spi.gc.DelegatingGCMonitor; import org.apache.jackrabbit.oak.spi.gc.GCMonitor; import org.apache.jackrabbit.oak.spi.gc.GCMonitorTracker; @@ -460,7 +457,7 @@ public class DocumentNodeStoreService { setPrefetchExternalChanges(config.prefetchExternalChanges()). setUpdateLimit(config.updateLimit()). setJournalGCMaxAge(config.journalGCMaxAge()). - setNodeCachePredicate(createCachePredicate()); + setNodeCachePathPredicate(createCachePredicate()); if (!Strings.isNullOrEmpty(persistentCache)) { builder.setPersistentCache(persistentCache); @@ -481,7 +478,7 @@ public class DocumentNodeStoreService { return customBlobStore && blobStore instanceof BlobStoreWrapper; } - private Predicate createCachePredicate() { + private Predicate createCachePredicate() { if (config.persistentCacheIncludes().length == 0) { return Predicates.alwaysTrue(); } @@ -489,16 +486,24 @@ public class DocumentNodeStoreService { return Predicates.alwaysTrue(); } - Set paths = new HashSet<>(); + Set paths = new HashSet<>(); for (String p : config.persistentCacheIncludes()) { p = p != null ? Strings.emptyToNull(p.trim()) : null; if (p != null) { - paths.add(p); + paths.add(Path.fromString(p)); } } - PathFilter pf = new PathFilter(paths, emptyList()); log.info("Configuring persistent cache to only cache nodes under paths {}", paths); - return path -> path != null && pf.filter(path) == PathFilter.Result.INCLUDE; + return input -> { + if (input != null) { + for (Path p : paths) { + if (p.isAncestorOf(input)) { + return true; + } + } + } + return false; + }; } private boolean isNodeStoreProvider() { @@ -731,7 +736,7 @@ public class DocumentNodeStoreService { } // register persistent cache stats - Map persistenceCacheStats = mkBuilder.getPersistenceCacheStats(); + Map persistenceCacheStats = mkBuilder.getPersistenceCacheStats(); for (PersistentCacheStats pcs: persistenceCacheStats.values()) { addRegistration( registerMBean(whiteboard, diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ExternalChange.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ExternalChange.java index 8a24cdbe3a..bce0e4e33c 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ExternalChange.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ExternalChange.java @@ -97,7 +97,7 @@ abstract class ExternalChange { Clock clock = store.getClock(); int clusterId = store.getClusterId(); long time = clock.getTime(); - String id = Utils.getIdFromPath("/"); + String id = Utils.getIdFromPath(Path.ROOT); NodeDocument doc = store.getDocumentStore().find(NODES, id, store.getAsyncDelay()); if (doc == null) { return stats; diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalDiffLoader.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalDiffLoader.java index f2091e516a..22e265f004 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalDiffLoader.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalDiffLoader.java @@ -65,13 +65,13 @@ class JournalDiffLoader implements DiffCache.Loader { @Override public String call() { - String path = node.getPath(); RevisionVector afterRev = node.getRootRevision(); RevisionVector beforeRev = base.getRootRevision(); - stats = new Stats(path, beforeRev, afterRev); + stats = new Stats(node.getPath(), beforeRev, afterRev); StringSort changes = JournalEntry.newSorter(); try { + String path = node.getPath().toString(); readTrunkChanges(path, beforeRev, afterRev, changes); readBranchChanges(path, beforeRev, changes); @@ -80,7 +80,7 @@ class JournalDiffLoader implements DiffCache.Loader { changes.sort(); DiffCache df = ns.getDiffCache(); WrappedDiffCache wrappedCache = new WrappedDiffCache(node.getPath(), df, stats); - JournalEntry.applyTo(changes, wrappedCache, path, beforeRev, afterRev); + JournalEntry.applyTo(changes, wrappedCache, node.getPath(), beforeRev, afterRev); return wrappedCache.changes; } catch (IOException e) { @@ -214,14 +214,14 @@ class JournalDiffLoader implements DiffCache.Loader { private static class Stats { private final Stopwatch sw = Stopwatch.createStarted(); - private final String path; + private final Path path; private final RevisionVector from, to; private long numJournalEntries; private long numDiffEntries; private long keyMemory; private long valueMemory; - Stats(String path, RevisionVector from, RevisionVector to) { + Stats(Path path, RevisionVector from, RevisionVector to) { this.path = path; this.from = from; this.to = to; @@ -241,12 +241,12 @@ class JournalDiffLoader implements DiffCache.Loader { private static class WrappedDiffCache extends DiffCache { - private final String path; + private final Path path; private String changes = ""; private final DiffCache cache; private Stats stats; - WrappedDiffCache(String path, + WrappedDiffCache(Path path, DiffCache cache, Stats stats) { this.path = path; @@ -262,7 +262,7 @@ class JournalDiffLoader implements DiffCache.Loader { @Override String getChanges(@NotNull RevisionVector from, @NotNull RevisionVector to, - @NotNull String path, + @NotNull Path path, @Nullable Loader loader) { return cache.getChanges(from, to, path, loader); } @@ -275,7 +275,7 @@ class JournalDiffLoader implements DiffCache.Loader { final Entry entry = cache.newEntry(from, to, local); return new Entry() { @Override - public void append(@NotNull String path, + public void append(@NotNull Path path, @NotNull String changes) { trackStats(path, from, to, changes); entry.append(path, changes); @@ -291,12 +291,12 @@ class JournalDiffLoader implements DiffCache.Loader { }; } - private void trackStats(String path, + private void trackStats(Path path, RevisionVector from, RevisionVector to, String changes) { stats.numDiffEntries++; - stats.keyMemory += new StringValue(path).getMemory(); + stats.keyMemory += path.getMemory(); stats.keyMemory += from.getMemory(); stats.keyMemory += to.getMemory(); stats.valueMemory += new StringValue(changes).getMemory(); diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalEntry.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalEntry.java index a5949c29cb..896bcf9eeb 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalEntry.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalEntry.java @@ -26,6 +26,7 @@ import java.util.Set; import java.util.function.Consumer; import com.google.common.collect.AbstractIterator; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -128,7 +129,7 @@ public final class JournalEntry extends Document { static void applyTo(@NotNull Iterable changedPaths, @NotNull DiffCache diffCache, - @NotNull String path, + @NotNull Path path, @NotNull RevisionVector from, @NotNull RevisionVector to) throws IOException { LOG.debug("applyTo: starting for {} from {} to {}", path, from, to); @@ -206,12 +207,14 @@ public final class JournalEntry extends Document { LOG.debug("applyTo: done. totalCnt: {}, deDuplicatedCnt: {}", totalCnt, deDuplicatedCnt); } - private static boolean inScope(TreeNode node, String path) { - if (PathUtils.denotesRoot(path)) { + private static boolean inScope(TreeNode node, Path path) { + if (path.isRoot()) { return true; } - String p = node.getPath(); - return p.startsWith(path) && (p.length() == path.length() || p.charAt(path.length()) == '/'); + Path p = node.getPath(); + int depthDiff = p.getDepth() - path.getDepth(); + return depthDiff >= 0 + && Iterables.elementsEqual(path.elements(), p.getAncestor(depthDiff).elements()); } /** @@ -365,9 +368,9 @@ public final class JournalEntry extends Document { return Long.parseLong(parts[1], 16); } - void modified(String path) { + void modified(Path path) { TreeNode node = getChanges(); - for (String name : PathUtils.elements(path)) { + for (String name : path.elements()) { if (node.get(name) == null) { numChangedNodes++; } @@ -375,8 +378,8 @@ public final class JournalEntry extends Document { } } - void modified(Iterable paths) { - for (String p : paths) { + void modified(Iterable paths) { + for (Path p : paths) { modified(p); } } @@ -692,23 +695,15 @@ public final class JournalEntry extends Document { return n; } - private String getPath() { - return buildPath(new StringBuilder()).toString(); - } - - private StringBuilder buildPath(StringBuilder sb) { + private Path getPath() { + Path p; if (parent != null) { - parent.buildPath(sb); - if (parent.parent != null) { - // only add slash if parent is not the root - sb.append("/"); - } + p = new Path(parent.getPath(), name); } else { // this is the root - sb.append("/"); + p = Path.ROOT; } - sb.append(name); - return sb; + return p; } void parse(JsopReader reader) { diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryAgent.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryAgent.java index 3f65fac95d..fe0f7460b1 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryAgent.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryAgent.java @@ -22,7 +22,6 @@ import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.filterKeys; import static java.util.Collections.singletonList; -import static org.apache.jackrabbit.oak.commons.PathUtils.ROOT_PATH; import static org.apache.jackrabbit.oak.plugins.document.Collection.JOURNAL; import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES; import static org.apache.jackrabbit.oak.plugins.document.util.Utils.PROPERTY_OR_DELETED; @@ -40,7 +39,6 @@ import com.google.common.base.Supplier; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; -import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.plugins.document.util.MapFactory; import org.apache.jackrabbit.oak.plugins.document.util.Utils; import org.apache.jackrabbit.oak.stats.Clock; @@ -239,7 +237,7 @@ public class LastRevRecoveryAgent { final NodeDocumentSweeper sweeper = new NodeDocumentSweeper(context, true); sweeper.sweep(suspects, new NodeDocumentSweepListener() { @Override - public void sweepUpdate(Map updates) + public void sweepUpdate(Map updates) throws DocumentStoreException { if (dryRun) { log.info("Dry run of sweeper identified [{}] documents for " + @@ -276,7 +274,7 @@ public class LastRevRecoveryAgent { UnsavedModifications unsavedParents = new UnsavedModifications(); //Map of known last rev of checked paths - Map knownLastRevOrModification = MapFactory.getInstance().create(); + Map knownLastRevOrModification = MapFactory.getInstance().create(); final JournalEntry changes = JOURNAL.newDocument(store); long count = 0; @@ -296,7 +294,7 @@ public class LastRevRecoveryAgent { // most recent revision currently obtained from either a // _lastRev entry or an explicit modification on the document if (lastRevForParents != null) { - knownLastRevOrModification.put(doc.getPath(), lastRevForParents); + knownLastRevOrModification.put(Path.fromString(doc.getPath()), lastRevForParents); } //If both currentLastRev and lostLastRev are null it means @@ -308,19 +306,19 @@ public class LastRevRecoveryAgent { //2. Update lastRev for parent paths aka rollup if (lastRevForParents != null) { - String path = doc.getPath(); + Path path = Path.fromString(doc.getPath()); changes.modified(path); // track all changes while (true) { - if (PathUtils.denotesRoot(path)) { + path = path.getParent(); + if (path == null) { break; } - path = PathUtils.getParentPath(path); unsavedParents.put(path, lastRevForParents); } } } - for (String parentPath : unsavedParents.getPaths()) { + for (Path parentPath : unsavedParents.getPaths()) { Revision calcLastRev = unsavedParents.get(parentPath); Revision knownLastRev = knownLastRevOrModification.get(parentPath); if (knownLastRev == null) { @@ -348,11 +346,11 @@ public class LastRevRecoveryAgent { } if (sweepRev.get() != null) { - unsaved.put(ROOT_PATH, sweepRev.get()); + unsaved.put(Path.ROOT, sweepRev.get()); } // take the root's lastRev - final Revision lastRootRev = unsaved.get(ROOT_PATH); + final Revision lastRootRev = unsaved.get(Path.ROOT); //Note the size before persist as persist operation //would empty the internal state diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevTracker.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevTracker.java index 811ef05de5..1376d81660 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevTracker.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevTracker.java @@ -27,5 +27,5 @@ public interface LastRevTracker { * * @param path the path of the document to update. */ - public void track(String path); + public void track(Path path); } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java index 7b83c6f22a..458ab398bd 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java @@ -58,7 +58,7 @@ public class LocalDiffCache extends DiffCache { @Override public String getChanges(@NotNull RevisionVector from, @NotNull RevisionVector to, - @NotNull String path, + @NotNull Path path, @Nullable Loader loader) { RevisionsKey key = new RevisionsKey(from, to); Diff diff = diffCache.getIfPresent(key); @@ -78,14 +78,14 @@ public class LocalDiffCache extends DiffCache { final @NotNull RevisionVector to, boolean local /*ignored*/) { return new Entry() { - private final Map changesPerPath = Maps.newHashMap(); + private final Map changesPerPath = Maps.newHashMap(); private long size; @Override - public void append(@NotNull String path, @NotNull String changes) { + public void append(@NotNull Path path, @NotNull String changes) { if (exceedsSize()){ return; } - size += size(path) + size(changes); + size += path.getMemory() + size(changes); changesPerPath.put(path, changes); } @@ -117,16 +117,16 @@ public class LocalDiffCache extends DiffCache { public static final class Diff implements CacheValue { - private final Map changes; + private final Map changes; private long memory; - public Diff(Map changes, long memory) { + public Diff(Map changes, long memory) { this.changes = changes; this.memory = memory; } public static Diff fromString(String value) { - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); JsopReader reader = new JsopTokenizer(value); while (true) { if (reader.matches(JsopReader.END)) { @@ -135,7 +135,7 @@ public class LocalDiffCache extends DiffCache { String k = reader.readString(); reader.read(':'); String v = reader.readString(); - map.put(k, v); + map.put(Path.fromString(k), v); if (reader.matches(JsopReader.END)) { break; } @@ -146,14 +146,14 @@ public class LocalDiffCache extends DiffCache { public String asString(){ JsopBuilder builder = new JsopBuilder(); - for (Map.Entry entry : changes.entrySet()) { - builder.key(entry.getKey()); + for (Map.Entry entry : changes.entrySet()) { + builder.key(entry.getKey().toString()); builder.value(entry.getValue()); } return builder.toString(); } - public Map getChanges() { + public Map getChanges() { return Collections.unmodifiableMap(changes); } @@ -161,8 +161,8 @@ public class LocalDiffCache extends DiffCache { public int getMemory() { if (memory == 0) { long m = 0; - for (Map.Entry e : changes.entrySet()){ - m += size(e.getKey()) + size(e.getValue()); + for (Map.Entry e : changes.entrySet()){ + m += e.getKey().getMemory() + size(e.getValue()); } memory = m; } @@ -174,7 +174,7 @@ public class LocalDiffCache extends DiffCache { } } - String get(String path) { + String get(Path path) { return changes.get(path); } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java index 38946e433a..53fd65ff4d 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java @@ -21,6 +21,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import org.apache.jackrabbit.oak.cache.CacheStats; +import org.apache.jackrabbit.oak.cache.CacheValue; import org.apache.jackrabbit.oak.plugins.document.util.StringValue; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,9 +31,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static com.google.common.base.Preconditions.checkNotNull; -import static org.apache.jackrabbit.oak.commons.PathUtils.denotesRoot; -import static org.apache.jackrabbit.oak.commons.PathUtils.getName; -import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath; /** * An in-memory diff cache implementation. @@ -53,7 +51,7 @@ public class MemoryDiffCache extends DiffCache { * * Key: PathRev, value: StringValue */ - protected final Cache diffCache; + protected final Cache diffCache; protected final CacheStats diffCacheStats; @@ -67,9 +65,9 @@ public class MemoryDiffCache extends DiffCache { @Override public String getChanges(@NotNull final RevisionVector from, @NotNull final RevisionVector to, - @NotNull final String path, + @NotNull final Path path, @Nullable final Loader loader) { - PathRev key = diffCacheKey(path, from, to); + Key key = new Key(path, from, to); StringValue diff; if (loader == null) { diff = diffCache.getIfPresent(key); @@ -121,8 +119,8 @@ public class MemoryDiffCache extends DiffCache { } @Override - public void append(@NotNull String path, @NotNull String changes) { - PathRev key = diffCacheKey(path, from, to); + public void append(@NotNull Path path, @NotNull String changes) { + Key key = new Key(path, from, to); if (changes.length() > CACHE_VALUE_LIMIT) { LOG.warn("Not caching entry for {} from {} to {}. Length of changes is {}.", path, from, to, changes.length()); @@ -138,12 +136,6 @@ public class MemoryDiffCache extends DiffCache { } } - private static PathRev diffCacheKey(@NotNull String path, - @NotNull RevisionVector from, - @NotNull RevisionVector to) { - return new PathRev(from + path, to); - } - /** * Returns {@code true} if it can be inferred from cache entries on * ancestors of the given {@code path} that the node was not changed between @@ -159,27 +151,28 @@ public class MemoryDiffCache extends DiffCache { */ private boolean isUnchanged(@NotNull final RevisionVector from, @NotNull final RevisionVector to, - @NotNull final String path) { - return !denotesRoot(path) - && isChildUnchanged(from, to, getParentPath(path), getName(path)); + @NotNull final Path path) { + Path parent = path.getParent(); + return parent != null + && isChildUnchanged(from, to, parent, path.getName()); } private boolean isChildUnchanged(@NotNull final RevisionVector from, @NotNull final RevisionVector to, - @NotNull final String parent, + @NotNull final Path parent, @NotNull final String name) { - PathRev parentKey = diffCacheKey(parent, from, to); + Key parentKey = new Key(parent, from, to); StringValue parentCachedEntry = diffCache.getIfPresent(parentKey); boolean unchanged; if (parentCachedEntry == null) { - if (denotesRoot(parent)) { + if (parent.getParent() == null) { // reached root and we don't know whether name // changed between from and to unchanged = false; } else { // recurse down unchanged = isChildUnchanged(from, to, - getParentPath(parent), getName(parent)); + parent.getParent(), parent.getName()); } } else { unchanged = parseJsopDiff(parentCachedEntry.asString(), new Diff() { @@ -201,4 +194,106 @@ public class MemoryDiffCache extends DiffCache { } return unchanged; } + + public static final class Key implements CacheValue, Comparable { + + private final Path path; + + private final RevisionVector from; + + private final RevisionVector to; + + public Key(@NotNull Path path, + @NotNull RevisionVector from, + @NotNull RevisionVector to) { + this.path = checkNotNull(path); + this.from = checkNotNull(from); + this.to = checkNotNull(to); + if (path == Path.NULL) { + throw new IllegalArgumentException("NULL Path not allowed"); + } + } + + @NotNull + public Path getPath() { + return path; + } + + @NotNull + public RevisionVector getFromRevision() { + return from; + } + + @NotNull + public RevisionVector getToRevision() { + return to; + } + + public String asString() { + return toString(); + } + + public static Key fromString(@NotNull String s) { + int idx1 = s.indexOf('/'); + int idx2 = s.lastIndexOf('@'); + if (idx1 == -1 || idx2 == -1) { + throw new IllegalArgumentException("Malformed " + + MemoryDiffCache.Key.class.getSimpleName() + ": " + s); + } + return new Key( + Path.fromString(s.substring(idx1, idx2)), + RevisionVector.fromString(s.substring(0, idx1)), + RevisionVector.fromString(s.substring(idx2 + 1)) + ); + } + + @Override + public int getMemory() { + return 32 + path.getMemory() + from.getMemory() + to.getMemory(); + } + + @Override + public int compareTo(@NotNull MemoryDiffCache.Key other) { + if (this == other) { + return 0; + } + int compare = this.from.compareTo(other.from); + if (compare != 0) { + return compare; + } + compare = this.path.compareTo(other.path); + if (compare != 0) { + return compare; + } + return this.to.compareTo(other.to); + } + + @Override + public String toString() { + int dim = from.getDimensions() + to.getDimensions(); + StringBuilder sb = new StringBuilder(path.length() + (Revision.REV_STRING_APPROX_SIZE + 1) * dim); + from.toStringBuilder(sb); + path.toStringBuilder(sb).append('@'); + to.toStringBuilder(sb); + return sb.toString(); + } + + @Override + public int hashCode() { + return path.hashCode() ^ from.hashCode() ^ to.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj instanceof Key) { + Key other = (Key) obj; + return from.equals(other.from) + && to.equals(other.to) + && path.equals(other.path); + } + return false; + } + } } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MissingLastRevSeeker.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MissingLastRevSeeker.java index 32cad8c983..f12bd56ecf 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MissingLastRevSeeker.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MissingLastRevSeeker.java @@ -39,8 +39,6 @@ import static org.apache.jackrabbit.oak.plugins.document.util.Utils.getSelectedD */ public class MissingLastRevSeeker { - protected final String ROOT_PATH = "/"; - private final DocumentStore store; protected final Clock clock; @@ -125,7 +123,7 @@ public class MissingLastRevSeeker { } public NodeDocument getRoot() { - return store.find(Collection.NODES, Utils.getIdFromPath(ROOT_PATH)); + return store.find(Collection.NODES, Utils.getIdFromPath(Path.ROOT)); } /** diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NamePathRev.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NamePathRev.java new file mode 100644 index 0000000000..4125af254c --- /dev/null +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NamePathRev.java @@ -0,0 +1,126 @@ +/* + * 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.document; + +import org.apache.jackrabbit.oak.cache.CacheValue; +import org.apache.jackrabbit.oak.commons.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A cache key implementation, which is a combination of a name, path and a + * revision vector. + */ +public final class NamePathRev implements CacheValue, Comparable { + + private static final Logger LOG = LoggerFactory.getLogger(NamePathRev.class); + + @NotNull + private final String name; + + @NotNull + private final Path path; + + @NotNull + private final RevisionVector revision; + + public NamePathRev(@NotNull String name, + @NotNull Path path, + @NotNull RevisionVector revision) { + this.name = checkNotNull(name); + this.path = checkNotNull(path); + this.revision = checkNotNull(revision); + } + + @NotNull + public Path getPath() { + return path; + } + + @NotNull + public String getName() { + return name; + } + + @NotNull + public RevisionVector getRevision() { + return revision; + } + + @Override + public int getMemory() { + long size = 28L // shallow size + + path.getMemory() + + StringUtils.estimateMemoryUsage(name) + + revision.getMemory(); + if (size > Integer.MAX_VALUE) { + LOG.debug("Estimated memory footprint larger than Integer.MAX_VALUE: {}.", size); + size = Integer.MAX_VALUE; + } + return (int) size; + } + + //---------------------------< Comparable >--------------------------------- + + public int compareTo(@NotNull NamePathRev b) { + if (this == b) { + return 0; + } + int compare = name.compareTo(b.name); + if (compare == 0) { + compare = path.compareTo(b.path); + } + if (compare == 0) { + compare = revision.compareTo(b.revision); + } + return compare; + } + + //----------------------------< Object >------------------------------------ + + @Override + public int hashCode() { + return name.hashCode() ^ path.hashCode() ^ revision.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof NamePathRev) { + NamePathRev other = (NamePathRev) obj; + return revision.equals(other.revision) + && name.equals(other.name) + && path.equals(other.path); + } + return false; + } + + @Override + public String toString() { + int dim = revision.getDimensions(); + int len = path.length() + name.length(); + StringBuilder sb = new StringBuilder(len + (Revision.REV_STRING_APPROX_SIZE + 1) * dim); + sb.append(name); + path.toStringBuilder(sb).append('@'); + revision.toStringBuilder(sb); + return sb.toString(); + } +} diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java index 4fac646518..a65015039d 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java @@ -917,7 +917,7 @@ public final class NodeDocument extends Document { // deleted return null; } - String path = getPath(); + Path path = Path.fromString(getPath()); List props = Lists.newArrayList(); for (String key : keySet()) { if (!Utils.isPropertyName(key)) { @@ -1411,7 +1411,7 @@ public final class NodeDocument extends Document { NodeDocument prev = store.find(NODES, prevId); if (prev == null) { LOG.warn("Split document {} does not exist anymore. Main document is {}", - prevId, Utils.getIdFromPath(getMainPath())); + prevId, Utils.getIdFromPath(Path.fromString(getMainPath()))); continue; } // recurse into the split hierarchy @@ -1864,7 +1864,7 @@ public final class NodeDocument extends Document { // older than one minute. We don't want to invalidate a document // too frequently if the document structure is really broken. String path = getMainPath(); - String id = Utils.getIdFromPath(path); + String id = Utils.getIdFromPath(Path.fromString(path)); NodeDocument doc = store.getIfCached(NODES, id); long now = Revision.getCurrentTimestamp(); while (doc != null @@ -1991,7 +1991,7 @@ public final class NodeDocument extends Document { } } // get root of commit - return store.find(Collection.NODES, Utils.getIdFromPath(commitRootPath)); + return store.find(Collection.NODES, Utils.getIdFromPath(Path.fromString(commitRootPath))); } /** diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweepListener.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweepListener.java index a75ee916a5..a3e915f2ef 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweepListener.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweepListener.java @@ -32,6 +32,6 @@ interface NodeDocumentSweepListener { * of the documents to update. * @throws DocumentStoreException if the operation fails. */ - void sweepUpdate(Map updates) throws DocumentStoreException; + void sweepUpdate(Map updates) throws DocumentStoreException; } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweeper.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweeper.java index 5930affb2e..1cb847518f 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweeper.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweeper.java @@ -137,10 +137,10 @@ final class NodeDocumentSweeper { return null; } - Iterable> ops = sweepOperations(documents); - for (List> batch : partition(ops, INVALIDATE_BATCH_SIZE)) { - Map updates = newHashMap(); - for (Map.Entry entry : batch) { + Iterable> ops = sweepOperations(documents); + for (List> batch : partition(ops, INVALIDATE_BATCH_SIZE)) { + Map updates = newHashMap(); + for (Map.Entry entry : batch) { updates.put(entry.getKey(), entry.getValue()); } listener.sweepUpdate(updates); @@ -149,17 +149,17 @@ final class NodeDocumentSweeper { return head; } - private Iterable> sweepOperations( + private Iterable> sweepOperations( final Iterable docs) { return filter(transform(docs, - new Function>() { + new Function>() { @Override - public Map.Entry apply(NodeDocument doc) { - return immutableEntry(doc.getPath(), sweepOne(doc)); + public Map.Entry apply(NodeDocument doc) { + return immutableEntry(Path.fromString(doc.getPath()), sweepOne(doc)); } - }), new Predicate>() { + }), new Predicate>() { @Override - public boolean apply(Map.Entry input) { + public boolean apply(Map.Entry input) { return input.getValue() != null; } }); diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Path.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Path.java new file mode 100644 index 0000000000..439770ea57 --- /dev/null +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Path.java @@ -0,0 +1,287 @@ +/* + * 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.document; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.regex.Pattern; + +import com.google.common.collect.Lists; + +import org.apache.jackrabbit.oak.cache.CacheValue; +import org.apache.jackrabbit.oak.commons.PathUtils; +import org.apache.jackrabbit.oak.commons.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.elementsEqual; + +/** + * Path... + */ +public final class Path implements CacheValue, Comparable { + + private static final String NULL_PATH_STRING = ""; + + private static final Pattern DELIMITED = Pattern.compile("/"); + + public static final Path ROOT = new Path(null, "", "".hashCode()); + + public static final Path NULL = new Path(null, NULL_PATH_STRING, NULL_PATH_STRING.hashCode()); + + @Nullable + private final Path parent; + + @NotNull + private final String name; + + private int hash; + + private Path(@Nullable Path parent, + @NotNull String name, + int hash) { + this.parent = parent; + this.name = name; + this.hash = hash; + } + + public Path(@NotNull Path parent, @NotNull String name) { + this(checkNotNull(parent), checkNotNull(name), -1); + checkArgument(!name.isEmpty(), "name cannot be the empty String"); + checkArgument(parent != NULL, "parent cannot be the NULL path"); + } + + @NotNull + public String getName() { + return name; + } + + @NotNull + public Iterable elements() { + List elements = new ArrayList<>(getDepth()); + Path p = this; + while (p.parent != null) { + elements.add(p.name); + p = p.parent; + } + return Lists.reverse(elements); + } + + public boolean isRoot() { + return name.isEmpty(); + } + + @Nullable + public Path getParent() { + return parent; + } + + public int length() { + if (isRoot()) { + // root + return 1; + } else if (this == NULL) { + return name.length(); + } + int length = 0; + Path p = this; + while (p.parent != null) { + length += p.name.length() + 1; + p = p.parent; + } + return length; + } + + /** + * The depth of this path. The {@link #ROOT} and {@link #NULL} paths have + * a depth of 0. The path {@code /foo/bar} has depth 2. + * + * @return the depth of the path. + */ + public int getDepth() { + int depth = 0; + Path p = this; + while (p.parent != null) { + depth++; + p = p.parent; + } + return depth; + } + + /** + * Get the nth ancestor of a path. The 1st ancestor is the parent path, + * 2nd ancestor the grandparent path, and so on... + *

+ * If {@code nth <= 0}, then this path is returned. + * + * @param nth indicates the ancestor level for which the path should be + * calculated. + * @return the ancestor path + */ + @NotNull + public Path getAncestor(int nth) { + Path p = this; + while (nth-- > 0 && p.parent != null) { + p = p.parent; + } + return p; + } + + /** + * Return {@code true} if {@code this} path is an ancestor of the + * {@code other} path, otherwise {@code false}. + * + * @param other the other path. + * @return whether this path is an ancestor of the other path. + */ + public boolean isAncestorOf(@NotNull Path other) { + checkNotNull(other); + if (this == NULL || other == NULL) { + return false; + } + int depthDiff = other.getDepth() - getDepth(); + return depthDiff > 0 + && elementsEqual(elements(), other.getAncestor(depthDiff).elements()); + } + + @NotNull + public static Path fromCharSequence(@NotNull CharSequence path) throws IllegalArgumentException { + checkNotNull(path); + if (NULL_PATH_STRING.contentEquals(path)) { + return NULL; + } + if (path.length() == 0 || path.charAt(0) != '/') { + throw new IllegalArgumentException("path must be absolute"); + } + Path p = ROOT; + for (String name : DELIMITED.split(path)) { + if (!name.isEmpty()) { + p = new Path(p, StringCache.get(name)); + } + } + return p; + } + + @NotNull + public static Path fromString(@NotNull String path) throws IllegalArgumentException { + checkNotNull(path); + if (path.equals(NULL_PATH_STRING)) { + return NULL; + } + if (!PathUtils.isAbsolute(path)) { + throw new IllegalArgumentException("path must be absolute"); + } + Path p = ROOT; + for (String name : PathUtils.elements(path)) { + p = new Path(p, StringCache.get(name)); + } + return p; + } + + @NotNull + public StringBuilder toStringBuilder(@NotNull StringBuilder sb) { + if (name.isEmpty()) { + sb.append('/'); + } else { + buildPath(sb); + } + return sb; + } + + @Override + public int getMemory() { + int memory = 0; + Path p = this; + while (p.parent != null) { + memory += 24; // shallow size + memory += StringUtils.estimateMemoryUsage(name); + p = p.parent; + } + return memory; + } + + @Override + public int compareTo(@NotNull Path other) { + checkNotNull(other); + // a few special cases + if (this == NULL) { + return other == NULL ? 0 : -1; + } else if (other == NULL) { + return 1; + } + // regular case (neither this nor other is NULL) + int depth = getDepth(); + int otherDepth = other.getDepth(); + int minDepth = Math.min(depth, otherDepth); + Iterable elements = getAncestor(depth - minDepth).elements(); + Iterator otherElements = other.getAncestor(otherDepth - minDepth).elements().iterator(); + for (String name : elements) { + String otherName = otherElements.next(); + int c = name.compareTo(otherName); + if (c != 0) { + return c; + } + } + return Integer.compare(depth, otherDepth); + } + + @Override + public String toString() { + if (name.isEmpty()) { + return "/"; + } else if (this == NULL) { + return NULL_PATH_STRING; + } else { + return buildPath(new StringBuilder()).toString(); + } + } + + @Override + public int hashCode() { + int h = hash; + if (h == -1 && parent != null) { + h = parent.hashCode() ^ name.hashCode(); + hash = h; + } + return h; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj instanceof Path) { + Path other = (Path) obj; + return this.name.equals(other.name) + && Objects.equals(this.parent, other.parent); + } + return false; + } + + private StringBuilder buildPath(StringBuilder sb) { + if (parent != null) { + parent.buildPath(sb).append("/").append(name); + } + return sb; + } +} diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/PathComparator.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/PathComparator.java index 09988d0235..9f569f9ae0 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/PathComparator.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/PathComparator.java @@ -27,22 +27,22 @@ import org.apache.jackrabbit.oak.plugins.document.util.Utils; * Implements a comparator, which sorts path string according to 1) their * depth (highest first) and 2) the path string itself. */ -public class PathComparator implements Comparator, Serializable { +public class PathComparator implements Comparator, Serializable { private static final long serialVersionUID = -1523171906146067782L; - public static final Comparator INSTANCE = new PathComparator(); + public static final Comparator INSTANCE = new PathComparator(); private PathComparator() { } @Override - public int compare(String o1, String o2) { - int d1 = Utils.pathDepth(o1); - int d2 = Utils.pathDepth(o2); + public int compare(Path p1, Path p2) { + int d1 = p1.getDepth(); + int d2 = p2.getDepth(); if (d1 != d2) { return Integer.signum(d2 - d1); } - return o1.compareTo(o2); + return p1.toString().compareTo(p2.toString()); } } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/PathRev.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/PathRev.java index bc60fa21df..39f39def1c 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/PathRev.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/PathRev.java @@ -19,7 +19,6 @@ package org.apache.jackrabbit.oak.plugins.document; import org.apache.jackrabbit.oak.cache.CacheValue; -import org.apache.jackrabbit.oak.commons.StringUtils; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,31 +26,35 @@ import org.slf4j.LoggerFactory; import static com.google.common.base.Preconditions.checkNotNull; /** - * A cache key implementation, which is a combination of a path string and a - * revision. + * A cache key implementation, which is a combination of a path and a revision + * vector. */ -public final class PathRev implements CacheValue { +public final class PathRev implements CacheValue, Comparable { private static final Logger LOG = LoggerFactory.getLogger(PathRev.class); - private final String path; + private final Path path; private final RevisionVector revision; - public PathRev(@NotNull String path, @NotNull RevisionVector revision) { + public PathRev(@NotNull Path path, @NotNull RevisionVector revision) { this.path = checkNotNull(path); this.revision = checkNotNull(revision); } - public String getPath() { + public Path getPath() { return path; } + public RevisionVector getRevision() { + return revision; + } + @Override public int getMemory() { - long size = 24 // shallow size - + (long)StringUtils.estimateMemoryUsage(path) // path - + revision.getMemory(); // revision + long size = 24L // shallow size + + path.getMemory() // path + + revision.getMemory(); // revision if (size > Integer.MAX_VALUE) { LOG.debug("Estimated memory footprint larger than Integer.MAX_VALUE: {}.", size); size = Integer.MAX_VALUE; @@ -59,8 +62,20 @@ public final class PathRev implements CacheValue { return (int) size; } - //----------------------------< Object >------------------------------------ + //---------------------------< Comparable >--------------------------------- + + public int compareTo(@NotNull PathRev b) { + if (this == b) { + return 0; + } + int compare = path.compareTo(b.path); + if (compare == 0) { + compare = revision.compareTo(b.revision); + } + return compare; + } + //----------------------------< Object >------------------------------------ @Override public int hashCode() { @@ -88,25 +103,4 @@ public final class PathRev implements CacheValue { return sb.toString(); } - public String asString() { - return toString(); - } - - public static PathRev fromString(String s) { - int index = s.lastIndexOf('@'); - return new PathRev(s.substring(0, index), - RevisionVector.fromString(s.substring(index + 1))); - } - - public int compareTo(PathRev b) { - if (this == b) { - return 0; - } - int compare = path.compareTo(b.path); - if (compare == 0) { - compare = revision.compareTo(b.revision); - } - return compare; - } - } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ResetDiff.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ResetDiff.java index a60fecb712..0714a62103 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ResetDiff.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ResetDiff.java @@ -19,7 +19,6 @@ package org.apache.jackrabbit.oak.plugins.document; import java.util.Map; import org.apache.jackrabbit.oak.api.PropertyState; -import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.plugins.document.util.Utils; import org.apache.jackrabbit.oak.spi.state.NodeState; import org.apache.jackrabbit.oak.spi.state.NodeStateDiff; @@ -38,19 +37,19 @@ class ResetDiff implements NodeStateDiff { private final ResetDiff parent; private final Revision revision; - private final String path; - private final Map operations; + private final Path path; + private final Map operations; private UpdateOp update; ResetDiff(@NotNull Revision revision, - @NotNull Map operations) { - this(null, revision, "/", operations); + @NotNull Map operations) { + this(null, revision, Path.ROOT, operations); } private ResetDiff(@Nullable ResetDiff parent, @NotNull Revision revision, - @NotNull String path, - @NotNull Map operations) { + @NotNull Path path, + @NotNull Map operations) { this.parent = parent; this.revision = checkNotNull(revision); this.path = checkNotNull(path); @@ -78,7 +77,7 @@ class ResetDiff implements NodeStateDiff { @Override public boolean childNodeAdded(String name, NodeState after) { NodeDocument.removeCommitRoot(getUpdateOp(), revision); - String p = PathUtils.concat(path, name); + Path p = new Path(path, name); ResetDiff diff = new ResetDiff(this, revision, p, operations); UpdateOp op = diff.getUpdateOp(); NodeDocument.removeDeleted(op, revision); @@ -94,20 +93,20 @@ class ResetDiff implements NodeStateDiff { public boolean childNodeChanged(String name, NodeState before, NodeState after) { - String p = PathUtils.concat(path, name); + Path p = new Path(path, name); return after.compareAgainstBaseState(before, new ResetDiff(this, revision, p, operations)); } @Override public boolean childNodeDeleted(String name, NodeState before) { - String p = PathUtils.concat(path, name); + Path p = new Path(path, name); ResetDiff diff = new ResetDiff(this, revision, p, operations); NodeDocument.removeDeleted(diff.getUpdateOp(), revision); return MISSING_NODE.compareAgainstBaseState(before, diff); } - Map getOperations() { + Map getOperations() { return operations; } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitDocumentCleanUp.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitDocumentCleanUp.java index 45ad2f89d1..0c65d64643 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitDocumentCleanUp.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitDocumentCleanUp.java @@ -106,7 +106,7 @@ public class SplitDocumentCleanUp implements Closeable { private void disconnect(NodeDocument splitDoc) { String splitId = splitDoc.getId(); - String mainId = Utils.getIdFromPath(splitDoc.getMainPath()); + String mainId = Utils.getIdFromPath(Path.fromString(splitDoc.getMainPath())); NodeDocument doc = store.find(NODES, mainId); if (doc == null) { LOG.warn("Main document {} already removed. Split document is {}", diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java index 3059be8e06..955647a91e 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java @@ -317,7 +317,7 @@ class SplitOperations { String prevPath = Utils.getPreviousPathFor(path, h, entry.getKey() + 1); String prevId = Utils.getIdFromPath(prevPath); UpdateOp intermediate = new UpdateOp(prevId, true); - if (Utils.isLongPath(prevPath)) { + if (Utils.isIdFromLongPath(prevId)) { intermediate.set(NodeDocument.PATH, prevPath); } setPrevious(main, new Range(h, l, entry.getKey() + 1)); @@ -352,8 +352,9 @@ class SplitOperations { main = new UpdateOp(id, false); setPrevious(main, new Range(high, low, 0)); String oldPath = Utils.getPreviousPathFor(path, high, 0); - UpdateOp old = new UpdateOp(Utils.getIdFromPath(oldPath), true); - if (Utils.isLongPath(oldPath)) { + String oldId = Utils.getIdFromPath(oldPath); + UpdateOp old = new UpdateOp(oldId, true); + if (Utils.isIdFromLongPath(oldId)) { old.set(NodeDocument.PATH, oldPath); } for (String property : committedChanges.keySet()) { diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/StringCache.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/StringCache.java index 9f92ebaa04..2a5f014a64 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/StringCache.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/StringCache.java @@ -16,13 +16,13 @@ */ package org.apache.jackrabbit.oak.plugins.document; +import java.util.HashMap; import java.util.Map; import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants; import org.apache.jackrabbit.oak.spi.version.VersionConstants; -import com.google.common.collect.ImmutableMap; import org.apache.jackrabbit.oak.spi.state.ConflictType; /** @@ -139,11 +139,11 @@ public final class StringCache { } private static Map createStringMap(String... strings) { - ImmutableMap.Builder builder = ImmutableMap.builder(); + Map map = new HashMap<>(); for (String string : strings) { - builder.put(string, string); + map.put(string, string); } - return builder.build(); + return map; } // must be a power of 2 diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/TieredDiffCache.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/TieredDiffCache.java index c9ad45c245..97b19d2eaa 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/TieredDiffCache.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/TieredDiffCache.java @@ -53,7 +53,7 @@ class TieredDiffCache extends DiffCache { @Override public String getChanges(@NotNull RevisionVector from, @NotNull RevisionVector to, - @NotNull String path, + @NotNull Path path, @Nullable Loader loader) { // do not check local cache when changes are external if (isLocalChange(from, to, clusterId)) { diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnmergedBranches.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnmergedBranches.java index 3f7913f4af..b42923b044 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnmergedBranches.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnmergedBranches.java @@ -71,7 +71,7 @@ class UnmergedBranches { if (!initialized.compareAndSet(false, true)) { throw new IllegalStateException("already initialized"); } - NodeDocument doc = store.find(Collection.NODES, Utils.getIdFromPath("/")); + NodeDocument doc = store.find(Collection.NODES, Utils.getIdFromPath(Path.ROOT)); if (doc == null) { return; } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnsavedModifications.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnsavedModifications.java index 490d156d23..2278fcb043 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnsavedModifications.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/UnsavedModifications.java @@ -24,7 +24,6 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; -import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.plugins.document.util.MapFactory; import org.apache.jackrabbit.oak.plugins.document.util.Utils; import org.jetbrains.annotations.NotNull; @@ -59,7 +58,7 @@ class UnsavedModifications { */ static final int BACKGROUND_MULTI_UPDATE_LIMIT = 100; - private final ConcurrentMap map = MapFactory.getInstance().create(); + private final ConcurrentMap map = MapFactory.getInstance().create(); /** * Puts a revision for the given path. The revision for the given path is @@ -72,7 +71,7 @@ class UnsavedModifications { * was none or the current revision is newer. */ @Nullable - public Revision put(@NotNull String path, @NotNull Revision revision) { + public Revision put(@NotNull Path path, @NotNull Revision revision) { checkNotNull(path); checkNotNull(revision); for (;;) { @@ -95,12 +94,12 @@ class UnsavedModifications { } @Nullable - public Revision get(String path) { + public Revision get(Path path) { return map.get(path); } @NotNull - public Collection getPaths() { + public Collection getPaths() { return map.keySet(); } @@ -112,19 +111,19 @@ class UnsavedModifications { * @return matching paths with pending modifications. */ @NotNull - public Iterable getPaths(@NotNull final Revision start) { + public Iterable getPaths(@NotNull final Revision start) { if (map.isEmpty()) { return Collections.emptyList(); } else { return Iterables.transform(Iterables.filter(map.entrySet(), - new Predicate>() { + new Predicate>() { @Override - public boolean apply(Map.Entry input) { + public boolean apply(Map.Entry input) { return start.compareRevisionTime(input.getValue()) < 1; } - }), new Function, String>() { + }), new Function, Path>() { @Override - public String apply(Map.Entry input) { + public Path apply(Map.Entry input) { return input.getKey(); } }); @@ -163,7 +162,7 @@ class UnsavedModifications { stats.lock = sw.elapsed(TimeUnit.MILLISECONDS); sw.reset().start(); Revision sweepRev; - Map pending; + Map pending; try { snapshot.acquiring(getMostRecentRevision()); pending = Maps.newTreeMap(PathComparator.INSTANCE); @@ -174,23 +173,23 @@ class UnsavedModifications { } stats.num = pending.size(); List updates = Lists.newArrayList(); - Map pathToRevision = Maps.newHashMap(); - for (Iterable> batch : Iterables.partition( + Map pathToRevision = Maps.newHashMap(); + for (Iterable> batch : Iterables.partition( pending.entrySet(), BACKGROUND_MULTI_UPDATE_LIMIT)) { - for (Map.Entry entry : batch) { - String p = entry.getKey(); + for (Map.Entry entry : batch) { + Path p = entry.getKey(); Revision r = entry.getValue(); - if (PathUtils.denotesRoot(entry.getKey())) { + if (p.isRoot()) { // update root individually at the end continue; } updates.add(newUpdateOp(p, r)); - pathToRevision.put(p, r); + pathToRevision.put(entry.getKey(), r); } if (!updates.isEmpty()) { store.createOrUpdate(NODES, updates); stats.calls++; - for (Map.Entry entry : pathToRevision.entrySet()) { + for (Map.Entry entry : pathToRevision.entrySet()) { map.remove(entry.getKey(), entry.getValue()); LOG.debug("Updated _lastRev to {} on {}", entry.getValue(), entry.getKey()); } @@ -200,9 +199,9 @@ class UnsavedModifications { } } // finally update remaining root document - Revision rootRev = pending.get(ROOT_PATH); + Revision rootRev = pending.get(Path.ROOT); if (rootRev != null) { - UpdateOp rootUpdate = newUpdateOp(ROOT_PATH, rootRev); + UpdateOp rootUpdate = newUpdateOp(Path.ROOT, rootRev); // also update to most recent sweep revision if (sweepRev != null) { NodeDocument.setSweepRevision(rootUpdate, sweepRev); @@ -210,7 +209,7 @@ class UnsavedModifications { } store.findAndUpdate(NODES, rootUpdate); stats.calls++; - map.remove(ROOT_PATH, rootRev); + map.remove(Path.ROOT, rootRev); LOG.debug("Updated _lastRev to {} on {}", rootRev, ROOT_PATH); int cid = rootRev.getClusterId(); @@ -228,7 +227,7 @@ class UnsavedModifications { return map.toString(); } - private static UpdateOp newUpdateOp(String path, Revision r) { + private static UpdateOp newUpdateOp(Path path, Revision r) { UpdateOp updateOp = createUpdateOp(path, r, false); NodeDocument.setLastRev(updateOp, r); return updateOp; @@ -236,7 +235,7 @@ class UnsavedModifications { private Revision getMostRecentRevision() { // use revision of root document - Revision rev = map.get(ROOT_PATH); + Revision rev = map.get(Path.ROOT); // otherwise find most recent if (rev == null) { for (Revision r : map.values()) { diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/bundlor/BundledDocumentDiffer.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/bundlor/BundledDocumentDiffer.java index 71dfafc225..bde3b3862a 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/bundlor/BundledDocumentDiffer.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/bundlor/BundledDocumentDiffer.java @@ -26,6 +26,7 @@ import org.apache.jackrabbit.oak.commons.json.JsopWriter; import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeState; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.spi.state.NodeStateUtils; import static com.google.common.base.Preconditions.checkNotNull; @@ -103,7 +104,7 @@ public class BundledDocumentDiffer { //and from there traverse down to the actual child node checkState(BundlorUtils.isBundledChild(state)); String bundlingPath = state.getString(DocumentBundlor.META_PROP_BUNDLING_PATH); - String bundlingRootPath = PathUtils.getAncestorPath(state.getPath(), PathUtils.getDepth(bundlingPath)); + Path bundlingRootPath = state.getPath().getAncestor(PathUtils.getDepth(bundlingPath)); DocumentNodeState bundlingRoot = nodeStore.getNode(bundlingRootPath, state.getLastRevision()); result = (DocumentNodeState) NodeStateUtils.getNode(bundlingRoot, bundlingPath); } else { diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/bundlor/BundlingHandler.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/bundlor/BundlingHandler.java index e744a88da8..4c8be3f1ed 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/bundlor/BundlingHandler.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/bundlor/BundlingHandler.java @@ -24,25 +24,25 @@ import java.util.Set; import com.google.common.collect.Sets; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.commons.PathUtils; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.spi.state.NodeState; import static com.google.common.base.Preconditions.checkNotNull; -import static org.apache.jackrabbit.oak.commons.PathUtils.ROOT_PATH; import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE; import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty; public class BundlingHandler { private final BundledTypesRegistry registry; - private final String path; + private final Path path; private final BundlingContext ctx; private final NodeState nodeState; public BundlingHandler(BundledTypesRegistry registry) { - this(checkNotNull(registry), BundlingContext.NULL, ROOT_PATH, EMPTY_NODE); + this(checkNotNull(registry), BundlingContext.NULL, Path.ROOT, EMPTY_NODE); } - private BundlingHandler(BundledTypesRegistry registry, BundlingContext ctx, String path, NodeState nodeState) { + private BundlingHandler(BundledTypesRegistry registry, BundlingContext ctx, Path path, NodeState nodeState) { this.registry = registry; this.path = path; this.ctx = ctx; @@ -67,7 +67,7 @@ public class BundlingHandler { /** * Returns absolute path of the current node */ - public String getNodeFullPath() { + public Path getNodeFullPath() { return path; } @@ -86,12 +86,12 @@ public class BundlingHandler { return ctx.removedProps; } - public String getRootBundlePath() { + public Path getRootBundlePath() { return ctx.isBundling() ? ctx.bundlingPath : path; } public BundlingHandler childAdded(String name, NodeState state){ - String childPath = childPath(name); + Path childPath = childPath(name); BundlingContext childContext; Matcher childMatcher = ctx.matcher.next(name); if (childMatcher.isMatch()) { @@ -111,7 +111,7 @@ public class BundlingHandler { } public BundlingHandler childDeleted(String name, NodeState state){ - String childPath = childPath(name); + Path childPath = childPath(name); BundlingContext childContext; Matcher childMatcher = ctx.matcher.next(name); if (childMatcher.isMatch()) { @@ -127,7 +127,7 @@ public class BundlingHandler { } public BundlingHandler childChanged(String name, NodeState before, NodeState after){ - String childPath = childPath(name); + Path childPath = childPath(name); BundlingContext childContext; Matcher childMatcher = ctx.matcher.next(name); if (childMatcher.isMatch()) { @@ -151,22 +151,22 @@ public class BundlingHandler { @Override public String toString() { - String result = path; + String result = path.toString(); if (isBundledNode()){ result = path + "( Bundling root - " + getRootBundlePath() + ")"; } return result; } - private String childPath(String name){ - return PathUtils.concat(path, name); + private Path childPath(String name){ + return new Path(path, name); } private BundlingContext createChildContext(Matcher childMatcher) { return ctx.child(childMatcher); } - private static BundlingContext getBundlorContext(String path, NodeState state) { + private static BundlingContext getBundlorContext(Path path, NodeState state) { BundlingContext result = BundlingContext.NULL; PropertyState bundlorConfig = state.getProperty(DocumentBundlor.META_PROP_PATTERN); if (bundlorConfig != null){ @@ -199,13 +199,13 @@ public class BundlingHandler { } private static class BundlingContext { - static final BundlingContext NULL = new BundlingContext("", Matcher.NON_MATCHING); - final String bundlingPath; + static final BundlingContext NULL = new BundlingContext(Path.NULL, Matcher.NON_MATCHING); + final Path bundlingPath; final Matcher matcher; final Set metaProps = Sets.newHashSet(); final Set removedProps = Sets.newHashSet(); - public BundlingContext(String bundlingPath, Matcher matcher) { + public BundlingContext(Path bundlingPath, Matcher matcher) { this.bundlingPath = bundlingPath; this.matcher = matcher; } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/StripedNodeDocumentLocks.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/StripedNodeDocumentLocks.java index e3ab63ea3d..6453179099 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/StripedNodeDocumentLocks.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/StripedNodeDocumentLocks.java @@ -18,13 +18,14 @@ package org.apache.jackrabbit.oak.plugins.document.locks; import java.util.concurrent.locks.Lock; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.util.Utils; import com.google.common.util.concurrent.Striped; public class StripedNodeDocumentLocks implements NodeDocumentLocks { - private static final String ROOT = Utils.getIdFromPath("/"); + private static final String ROOT = Utils.getIdFromPath(Path.ROOT); /** * Locks to ensure cache consistency on reads, writes and invalidation. diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheType.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheType.java index a790df6a5d..5cf48529d2 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheType.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheType.java @@ -16,44 +16,60 @@ */ package org.apache.jackrabbit.oak.plugins.document.persistentCache; +import java.nio.ByteBuffer; + import org.apache.jackrabbit.oak.plugins.document.LocalDiffCache; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeState; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore; import org.apache.jackrabbit.oak.plugins.document.DocumentStore; +import org.apache.jackrabbit.oak.plugins.document.MemoryDiffCache; +import org.apache.jackrabbit.oak.plugins.document.NamePathRev; import org.apache.jackrabbit.oak.plugins.document.NodeDocument; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.PathRev; +import org.apache.jackrabbit.oak.plugins.document.RevisionVector; import org.apache.jackrabbit.oak.plugins.document.util.RevisionsKey; import org.apache.jackrabbit.oak.plugins.document.util.StringValue; +import org.h2.mvstore.WriteBuffer; +import org.h2.mvstore.type.StringDataType; public enum CacheType { NODE { + @Override - public String keyToString(K key) { - return ((PathRev) key).asString(); + public void writeKey(WriteBuffer buffer, K key) { + PathRev pr = (PathRev) key; + DataTypeUtil.pathRevToBuffer(pr, buffer); } + @SuppressWarnings("unchecked") @Override - public K keyFromString(String key) { - return (K) PathRev.fromString(key); + public K readKey(ByteBuffer buffer) { + return (K) DataTypeUtil.pathRevFromBuffer(buffer); } + @Override public int compareKeys(K a, K b) { return ((PathRev) a).compareTo((PathRev) b); } + @Override - public String valueToString(V value) { - return ((DocumentNodeState) value).asString(); + public void writeValue(WriteBuffer buffer, V value) { + DataTypeUtil.stateToBuffer((DocumentNodeState) value, buffer); } + @SuppressWarnings("unchecked") @Override - public V valueFromString( - DocumentNodeStore store, DocumentStore docStore, String value) { - return (V) DocumentNodeState.fromString(store, value); + public V readValue(DocumentNodeStore store, + DocumentStore docStore, + ByteBuffer buffer) { + return (V) DataTypeUtil.stateFromBuffer(store, buffer); } + @Override public boolean shouldCache(DocumentNodeStore store, K key) { - String path = ((PathRev) key).getPath(); + Path path = ((PathRev) key).getPath(); if (!store.getNodeCachePredicate().apply(path)){ return false; } @@ -62,33 +78,40 @@ public enum CacheType { }, CHILDREN { + @Override - public String keyToString(K key) { - return ((PathRev) key).asString(); + public void writeKey(WriteBuffer buffer, K key) { + DataTypeUtil.namePathRevToBuffer((NamePathRev) key, buffer); } + @SuppressWarnings("unchecked") @Override - public K keyFromString(String key) { - return (K) PathRev.fromString(key); + public K readKey(ByteBuffer buffer) { + return (K) DataTypeUtil.namePathRevFromBuffer(buffer); } + @Override public int compareKeys(K a, K b) { - return ((PathRev) a).compareTo((PathRev) b); + return ((NamePathRev) a).compareTo((NamePathRev) b); } + @Override - public String valueToString(V value) { - return ((DocumentNodeState.Children) value).asString(); + public void writeValue(WriteBuffer buffer, V value) { + String s = ((DocumentNodeState.Children) value).asString(); + StringDataType.INSTANCE.write(buffer, s); } + @SuppressWarnings("unchecked") @Override - public V valueFromString( - DocumentNodeStore store, DocumentStore docStore, String value) { - return (V) DocumentNodeState.Children.fromString(value); + public V readValue(DocumentNodeStore store, + DocumentStore docStore, + ByteBuffer buffer) { + return (V) DocumentNodeState.Children.fromString(readString(buffer)); } @Override public boolean shouldCache(DocumentNodeStore store, K key) { - String path = ((PathRev) key).getPath(); + Path path = ((NamePathRev) key).getPath(); if (!store.getNodeCachePredicate().apply(path)){ return false; } @@ -97,29 +120,44 @@ public enum CacheType { }, DIFF { + @Override - public String keyToString(K key) { - return ((PathRev) key).asString(); + public void writeKey(WriteBuffer buffer, K key) { + MemoryDiffCache.Key k = ((MemoryDiffCache.Key) key); + DataTypeUtil.pathToBuffer(k.getPath(), buffer); + DataTypeUtil.revisionVectorToBuffer(k.getFromRevision(), buffer); + DataTypeUtil.revisionVectorToBuffer(k.getToRevision(), buffer); } + @SuppressWarnings("unchecked") @Override - public K keyFromString(String key) { - return (K) PathRev.fromString(key); + public K readKey(ByteBuffer buffer) { + Path p = DataTypeUtil.pathFromBuffer(buffer); + RevisionVector from = DataTypeUtil.revisionVectorFromBuffer(buffer); + RevisionVector to = DataTypeUtil.revisionVectorFromBuffer(buffer); + return (K) new MemoryDiffCache.Key(p, from, to); } + + @Override public int compareKeys(K a, K b) { - return ((PathRev) a).compareTo((PathRev) b); - } + return ((MemoryDiffCache.Key) a).compareTo((MemoryDiffCache.Key) b); + } + @Override - public String valueToString(V value) { - return ((StringValue) value).asString(); + public void writeValue(WriteBuffer buffer, V value) { + String s = ((StringValue) value).asString(); + StringDataType.INSTANCE.write(buffer, s); } + @SuppressWarnings("unchecked") @Override - public V valueFromString( - DocumentNodeStore store, DocumentStore docStore, String value) { - return (V) StringValue.fromString(value); + public V readValue(DocumentNodeStore store, + DocumentStore docStore, + ByteBuffer buffer) { + return (V) StringValue.fromString(readString(buffer)); } + @Override public boolean shouldCache(DocumentNodeStore store, K key) { return true; @@ -127,27 +165,34 @@ public enum CacheType { }, DOCUMENT { + @Override - public String keyToString(K key) { + public void writeKey(WriteBuffer buffer, K key) { throw new UnsupportedOperationException(); } + @Override - public K keyFromString(String key) { + public K readKey(ByteBuffer buffer) { throw new UnsupportedOperationException(); } + @Override public int compareKeys(K a, K b) { throw new UnsupportedOperationException(); - } + } + @Override - public String valueToString(V value) { + public void writeValue(WriteBuffer buffer, V value) { throw new UnsupportedOperationException(); } + @Override - public V valueFromString( - DocumentNodeStore store, DocumentStore docStore, String value) { + public V readValue(DocumentNodeStore store, + DocumentStore docStore, + ByteBuffer buffer) { throw new UnsupportedOperationException(); } + @Override public boolean shouldCache(DocumentNodeStore store, K key) { return false; @@ -155,29 +200,38 @@ public enum CacheType { }, PREV_DOCUMENT { + @Override - public String keyToString(K key) { - return ((StringValue) key).asString(); + public void writeKey(WriteBuffer buffer, K key) { + String s = ((StringValue) key).asString(); + StringDataType.INSTANCE.write(buffer, s); } + @SuppressWarnings("unchecked") @Override - public K keyFromString(String key) { - return (K) StringValue.fromString(key); + public K readKey(ByteBuffer buffer) { + return (K) StringValue.fromString(readString(buffer)); } + @Override public int compareKeys(K a, K b) { return ((StringValue) a).asString().compareTo(((StringValue) b).asString()); } + @Override - public String valueToString(V value) { - return ((NodeDocument) value).asString(); + public void writeValue(WriteBuffer buffer, V value) { + String s = ((NodeDocument) value).asString(); + StringDataType.INSTANCE.write(buffer, s); } + @SuppressWarnings("unchecked") @Override - public V valueFromString( - DocumentNodeStore store, DocumentStore docStore, String value) { - return (V) NodeDocument.fromString(docStore, value); + public V readValue(DocumentNodeStore store, + DocumentStore docStore, + ByteBuffer buffer) { + return (V) NodeDocument.fromString(docStore, readString(buffer)); } + @Override public boolean shouldCache(DocumentNodeStore store, K key) { return true; @@ -185,29 +239,41 @@ public enum CacheType { }, LOCAL_DIFF { + @Override - public String keyToString(K key) { - return ((RevisionsKey) key).asString(); + public void writeKey(WriteBuffer buffer, K key) { + RevisionsKey revisionsKey = ((RevisionsKey) key); + DataTypeUtil.revisionVectorToBuffer(revisionsKey.getRev1(), buffer); + DataTypeUtil.revisionVectorToBuffer(revisionsKey.getRev2(), buffer); } + @SuppressWarnings("unchecked") @Override - public K keyFromString(String key) { - return (K) RevisionsKey.fromString(key); + public K readKey(ByteBuffer buffer) { + RevisionVector rv1 = DataTypeUtil.revisionVectorFromBuffer(buffer); + RevisionVector rv2 = DataTypeUtil.revisionVectorFromBuffer(buffer); + return (K) new RevisionsKey(rv1, rv2); } + @Override public int compareKeys(K a, K b) { return ((RevisionsKey) a).compareTo((RevisionsKey) b); } + @Override - public String valueToString(V value) { - return ((LocalDiffCache.Diff) value).asString(); + public void writeValue(WriteBuffer buffer, V value) { + String s = ((LocalDiffCache.Diff) value).asString(); + StringDataType.INSTANCE.write(buffer, s); } + @SuppressWarnings("unchecked") @Override - public V valueFromString( - DocumentNodeStore store, DocumentStore docStore, String value) { - return (V) LocalDiffCache.Diff.fromString(value); + public V readValue(DocumentNodeStore store, + DocumentStore docStore, + ByteBuffer buffer) { + return (V) LocalDiffCache.Diff.fromString(readString(buffer)); } + @Override public boolean shouldCache(DocumentNodeStore store, K key) { return true; @@ -216,13 +282,22 @@ public enum CacheType { public static final CacheType[] VALUES = CacheType.values(); - public abstract String keyToString(K key); - public abstract K keyFromString(String key); + public String getMapName() { + // add a version suffix to the name and increment the version + // whenever the serialization for a key or value changes + return name() + "-v1"; + } + + public abstract void writeKey(WriteBuffer buffer, K key); + public abstract K readKey(ByteBuffer buffer); public abstract int compareKeys(K a, K b); - public abstract String valueToString(V value); - public abstract V valueFromString( - DocumentNodeStore store, DocumentStore docStore, String value); + public abstract void writeValue(WriteBuffer buffer, V value); + public abstract V readValue( + DocumentNodeStore store, DocumentStore docStore, ByteBuffer buffer); public abstract boolean shouldCache(DocumentNodeStore store, K key); + private static String readString(ByteBuffer buffer) { + return StringDataType.INSTANCE.read(buffer); + } } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/DataTypeUtil.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/DataTypeUtil.java new file mode 100644 index 0000000000..7387b63109 --- /dev/null +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/DataTypeUtil.java @@ -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.plugins.document.persistentCache; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.plugins.document.DocumentNodeState; +import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore; +import org.apache.jackrabbit.oak.plugins.document.NamePathRev; +import org.apache.jackrabbit.oak.plugins.document.Path; +import org.apache.jackrabbit.oak.plugins.document.PathRev; +import org.apache.jackrabbit.oak.plugins.document.Revision; +import org.apache.jackrabbit.oak.plugins.document.RevisionVector; +import org.h2.mvstore.DataUtils; +import org.h2.mvstore.WriteBuffer; +import org.h2.mvstore.type.StringDataType; + +/** + * Utility class to write various types to a buffer and read it back again. + */ +class DataTypeUtil { + + static void booleanToBuffer(boolean b, WriteBuffer buffer) { + buffer.put((byte) (b ? 1 : 0)); + } + + static boolean booleanFromBuffer(ByteBuffer buffer) { + return buffer.get() != 0; + } + + static void revisionVectorToBuffer(RevisionVector rv, WriteBuffer buffer) { + buffer.putVarInt(rv.getDimensions()); + for (Revision r : rv) { + buffer.putLong(r.getTimestamp()); + buffer.putVarInt(r.getCounter()); + buffer.putVarInt(r.getClusterId()); + booleanToBuffer(r.isBranch(), buffer); + } + } + + static RevisionVector revisionVectorFromBuffer(ByteBuffer buffer) { + int dim = DataUtils.readVarInt(buffer); + List revisions = new ArrayList<>(); + for (int i = 0; i < dim; i++) { + revisions.add(new Revision( + buffer.getLong(), + DataUtils.readVarInt(buffer), + DataUtils.readVarInt(buffer), + booleanFromBuffer(buffer)) + ); + } + return new RevisionVector(revisions); + } + + static void pathToBuffer(Path p, WriteBuffer buffer) { + buffer.putVarInt(p.getDepth() + 1); + // write path elements backwards + while (p != null) { + StringDataType.INSTANCE.write(buffer, p.getName()); + p = p.getParent(); + } + } + + static Path pathFromBuffer(ByteBuffer buffer) { + int numElements = DataUtils.readVarInt(buffer); + List elements = new ArrayList<>(numElements); + for (int i = 0; i < numElements; i++) { + elements.add(StringDataType.INSTANCE.read(buffer)); + } + Path p; + if (elements.size() == 1 && !elements.get(0).isEmpty()) { + p = Path.NULL; + } else { + p = Path.ROOT; + for (int i = elements.size() - 2; i >= 0; i--) { + p = new Path(p, elements.get(i)); + } + } + return p; + } + + static void pathRevToBuffer(PathRev pr, WriteBuffer buffer) { + pathToBuffer(pr.getPath(), buffer); + revisionVectorToBuffer(pr.getRevision(), buffer); + } + + static PathRev pathRevFromBuffer(ByteBuffer buffer) { + return new PathRev( + pathFromBuffer(buffer), + revisionVectorFromBuffer(buffer) + ); + } + + static void namePathRevToBuffer(NamePathRev pnr, WriteBuffer buffer) { + StringDataType.INSTANCE.write(buffer, pnr.getName()); + pathToBuffer(pnr.getPath(), buffer); + revisionVectorToBuffer(pnr.getRevision(), buffer); + } + + static NamePathRev namePathRevFromBuffer(ByteBuffer buffer) { + return new NamePathRev( + StringDataType.INSTANCE.read(buffer), + pathFromBuffer(buffer), + revisionVectorFromBuffer(buffer) + ); + } + + static void stateToBuffer(DocumentNodeState state, WriteBuffer buffer) { + pathToBuffer(state.getPath(), buffer); + revisionVectorToBuffer(state.getRootRevision(), buffer); + RevisionVector lastRevision = state.getLastRevision(); + if (lastRevision == null) { + lastRevision = RevisionVector.fromString(""); + } + revisionVectorToBuffer(lastRevision, buffer); + buffer.putVarInt(state.getMemory()); + booleanToBuffer(state.hasNoChildren(), buffer); + Map props = state.getAllBundledProperties(); + buffer.putVarInt(props.size()); + for (Map.Entry e : props.entrySet()) { + StringDataType.INSTANCE.write(buffer, e.getKey()); + StringDataType.INSTANCE.write(buffer, e.getValue()); + } + } + + static DocumentNodeState stateFromBuffer(DocumentNodeStore store, + ByteBuffer buffer) { + Path p = pathFromBuffer(buffer); + RevisionVector rootRevision = revisionVectorFromBuffer(buffer); + RevisionVector lastRevision = revisionVectorFromBuffer(buffer); + if (lastRevision.getDimensions() == 0) { + lastRevision = null; + } + int mem = DataUtils.readVarInt(buffer); + boolean noChildren = booleanFromBuffer(buffer); + int numProps = DataUtils.readVarInt(buffer); + Map props = new HashMap<>(numProps); + for (int i = 0; i < numProps; i++) { + String name = StringDataType.INSTANCE.read(buffer); + String value = StringDataType.INSTANCE.read(buffer); + props.put(name, store.createPropertyState(name, value)); + } + return new DocumentNodeState(store, p, rootRevision, props, + !noChildren, mem, lastRevision, false); + } +} diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/KeyDataType.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/KeyDataType.java index d6a37892a6..9d2b356ae1 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/KeyDataType.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/KeyDataType.java @@ -21,7 +21,6 @@ import java.nio.ByteBuffer; import org.apache.jackrabbit.oak.cache.CacheValue; import org.h2.mvstore.WriteBuffer; import org.h2.mvstore.type.DataType; -import org.h2.mvstore.type.StringDataType; public class KeyDataType implements DataType { @@ -43,14 +42,12 @@ public class KeyDataType implements DataType { @Override public void write(WriteBuffer buff, Object obj) { - String s = type.keyToString(obj); - StringDataType.INSTANCE.write(buff, s); + type.writeKey(buff, obj); } @Override public Object read(ByteBuffer buff) { - String s = StringDataType.INSTANCE.read(buff); - return type.keyFromString(s); + return type.readKey(buff); } @Override diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java index f3e7cc4595..4df170f508 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java @@ -112,8 +112,7 @@ class NodeCache public void addGeneration(int generation, boolean readOnly) { MVMap.Builder b = new MVMap.Builder(). keyType(keyType).valueType(valueType); - String mapName = type.name(); - CacheMap m = cache.openMap(generation, mapName, b); + CacheMap m = cache.openMap(generation, type.getMapName(), b); map.addReadMap(generation, m); if (!readOnly) { map.setWriteMap(m); diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/ValueDataType.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/ValueDataType.java index a0080eb324..36442c2951 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/ValueDataType.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/ValueDataType.java @@ -23,7 +23,9 @@ import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore; import org.apache.jackrabbit.oak.plugins.document.DocumentStore; import org.h2.mvstore.WriteBuffer; import org.h2.mvstore.type.DataType; -import org.h2.mvstore.type.StringDataType; +import org.jetbrains.annotations.NotNull; + +import static com.google.common.base.Preconditions.checkNotNull; public class ValueDataType implements DataType { @@ -31,9 +33,9 @@ public class ValueDataType implements DataType { private final DocumentStore docStore; private final CacheType type; - public ValueDataType( - DocumentNodeStore docNodeStore, - DocumentStore docStore, CacheType type) { + ValueDataType(DocumentNodeStore docNodeStore, + DocumentStore docStore, + CacheType type) { this.docNodeStore = docNodeStore; this.docStore = docStore; this.type = type; @@ -51,14 +53,12 @@ public class ValueDataType implements DataType { @Override public void write(WriteBuffer buff, Object obj) { - String s = type.valueToString(obj); - StringDataType.INSTANCE.write(buff, s); + type.writeValue(buff, obj); } @Override public Object read(ByteBuffer buff) { - String s = StringDataType.INSTANCE.read(buff); - return type.valueFromString(docNodeStore, docStore, s); + return type.readValue(docNodeStore, docStore, buff); } @Override diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java index 78dd9b69b2..98e3bd1bff 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java @@ -25,6 +25,7 @@ import com.google.common.collect.Iterables; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState; import org.apache.jackrabbit.oak.plugins.document.NodeStateDiffer; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.RevisionVector; import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry; import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeBuilder; @@ -61,7 +62,7 @@ public class DelegatingDocumentNodeState extends AbstractDocumentNodeState { private final NodeState delegate; private final RevisionVector rootRevision; private final boolean fromExternalChange; - private final String path; + private final Path path; private RevisionVector lastRevision; @@ -75,7 +76,7 @@ public class DelegatingDocumentNodeState extends AbstractDocumentNodeState { public static NodeState wrapIfPossible(NodeState delegate, NodeStateDiffer differ) { if (hasMetaProps(delegate)) { String revVector = getRequiredProp(delegate, PROP_REVISION); - return new DelegatingDocumentNodeState(delegate, ROOT_PATH, RevisionVector.fromString(revVector), false, differ); + return new DelegatingDocumentNodeState(delegate, Path.ROOT, RevisionVector.fromString(revVector), false, differ); } return delegate; } @@ -86,10 +87,10 @@ public class DelegatingDocumentNodeState extends AbstractDocumentNodeState { public static AbstractDocumentNodeState wrap(NodeState delegate, NodeStateDiffer differ) { String revVector = getRequiredProp(delegate, PROP_REVISION); - return new DelegatingDocumentNodeState(delegate, ROOT_PATH, RevisionVector.fromString(revVector), false, differ); + return new DelegatingDocumentNodeState(delegate, Path.ROOT, RevisionVector.fromString(revVector), false, differ); } - private DelegatingDocumentNodeState(NodeState delegate, String path, RevisionVector rootRevision, + private DelegatingDocumentNodeState(NodeState delegate, Path path, RevisionVector rootRevision, boolean fromExternalChange, NodeStateDiffer differ) { this.differ = differ; this.delegate = delegate; @@ -111,7 +112,7 @@ public class DelegatingDocumentNodeState extends AbstractDocumentNodeState { //~----------------------------------< AbstractDocumentNodeState > @Override - public String getPath() { + public Path getPath() { return path; } @@ -192,7 +193,7 @@ public class DelegatingDocumentNodeState extends AbstractDocumentNodeState { @NotNull @Override public NodeBuilder builder() { - checkState(!denotesRoot(getPath()), "Builder cannot be opened for root " + + checkState(!getPath().isRoot(), "Builder cannot be opened for root " + "path for state of type [%s]", delegate.getClass()); return new MemoryNodeBuilder(this); } @@ -261,7 +262,7 @@ public class DelegatingDocumentNodeState extends AbstractDocumentNodeState { private NodeState decorate(String nodeName, NodeState childNode) { if (childNode.exists()) { - return new DelegatingDocumentNodeState(childNode, concat(path, nodeName), rootRevision, + return new DelegatingDocumentNodeState(childNode, new Path(path, nodeName), rootRevision, fromExternalChange, differ); } return childNode; diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/PathFilteringDiff.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/PathFilteringDiff.java index aaa4d2ce40..19b1917f21 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/PathFilteringDiff.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/PathFilteringDiff.java @@ -22,7 +22,6 @@ package org.apache.jackrabbit.oak.plugins.document.secondary; import java.util.List; import org.apache.jackrabbit.oak.api.PropertyState; -import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState; import org.apache.jackrabbit.oak.plugins.document.RevisionVector; import org.apache.jackrabbit.oak.spi.filter.PathFilter; @@ -56,7 +55,7 @@ class PathFilteringDiff extends ApplyDiff { @Override public boolean childNodeAdded(String name, NodeState after) { AbstractDocumentNodeState afterDoc = asDocumentState(after, name); - String nextPath = afterDoc.getPath(); + String nextPath = afterDoc.getPath().toString(); PathFilter.Result result = ctx.pathFilter.filter(nextPath); if (result == PathFilter.Result.EXCLUDE){ return true; @@ -75,7 +74,7 @@ class PathFilteringDiff extends ApplyDiff { @Override public boolean childNodeChanged(String name, NodeState before, NodeState after) { AbstractDocumentNodeState afterDoc = asDocumentState(after, name); - String nextPath = afterDoc.getPath(); + String nextPath = afterDoc.getPath().toString(); if (ctx.pathFilter.filter(nextPath) != PathFilter.Result.EXCLUDE) { ctx.traversingNode(nextPath); NodeBuilder childBuilder = builder.getChildNode(name); @@ -88,7 +87,7 @@ class PathFilteringDiff extends ApplyDiff { @Override public boolean childNodeDeleted(String name, NodeState before) { - String path = asDocumentState(before, name).getPath(); + String path = asDocumentState(before, name).getPath().toString(); if (ctx.pathFilter.filter(path) != PathFilter.Result.EXCLUDE) { return super.childNodeDeleted(name, before); } @@ -104,7 +103,7 @@ class PathFilteringDiff extends ApplyDiff { static void copyMetaProperties(AbstractDocumentNodeState state, NodeBuilder builder, List metaPropNames) { //Only set root revision on root node - if (PathUtils.denotesRoot(state.getPath())) { + if (state.getPath().isRoot()) { builder.setProperty(asPropertyState(PROP_REVISION, state.getRootRevision())); } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java index 34575f25f1..5edd5f8e2a 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java @@ -20,10 +20,10 @@ package org.apache.jackrabbit.oak.plugins.document.secondary; import com.google.common.collect.EvictingQueue; -import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStateCache; import org.apache.jackrabbit.oak.plugins.document.NodeStateDiffer; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.RevisionVector; import org.apache.jackrabbit.oak.spi.filter.PathFilter; import org.apache.jackrabbit.oak.spi.state.NodeState; @@ -73,11 +73,12 @@ public class SecondaryStoreCache implements DocumentNodeStateCache, SecondarySto @Nullable @Override - public AbstractDocumentNodeState getDocumentNodeState(String path, RevisionVector rootRevision, - RevisionVector lastRev) { + public AbstractDocumentNodeState getDocumentNodeState(Path path, RevisionVector rootRevision, + RevisionVector lastRev) { //TODO We might need skip the calls if they occur due to SecondaryStoreObserver //doing the diff or in the startup when we try to sync the state - PathFilter.Result result = pathFilter.filter(path); + String p = path.toString(); + PathFilter.Result result = pathFilter.filter(p); if (result != PathFilter.Result.INCLUDE) { unknownPaths.mark(); return null; @@ -103,7 +104,7 @@ public class SecondaryStoreCache implements DocumentNodeStateCache, SecondarySto AbstractDocumentNodeState matchingRoot = findMatchingRoot(rootRevision); if (matchingRoot != null){ - NodeState state = NodeStateUtils.getNode(matchingRoot, path); + NodeState state = NodeStateUtils.getNode(matchingRoot, p); if (state.exists()){ AbstractDocumentNodeState docState = asDocState(state); prevRevMatched.mark(); @@ -116,16 +117,16 @@ public class SecondaryStoreCache implements DocumentNodeStateCache, SecondarySto } @Override - public boolean isCached(String path) { - return pathFilter.filter(path) == PathFilter.Result.INCLUDE; + public boolean isCached(Path path) { + return pathFilter.filter(path.toString()) == PathFilter.Result.INCLUDE; } @Nullable - private AbstractDocumentNodeState findByMatchingLastRev(AbstractDocumentNodeState root, String path, + private AbstractDocumentNodeState findByMatchingLastRev(AbstractDocumentNodeState root, Path path, RevisionVector lastRev){ NodeState state = root; - for (String name : PathUtils.elements(path)) { + for (String name : path.elements()) { state = state.getChildNode(name); if (!state.exists()){ diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/MapFactory.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/MapFactory.java index bdae982297..d07831a51a 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/MapFactory.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/MapFactory.java @@ -22,6 +22,7 @@ package org.apache.jackrabbit.oak.plugins.document.util; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.Revision; /** @@ -31,12 +32,12 @@ import org.apache.jackrabbit.oak.plugins.document.Revision; public abstract class MapFactory { private static MapFactory DEFAULT = new MapFactory() { @Override - public ConcurrentMap create() { - return new ConcurrentHashMap(); + public ConcurrentMap create() { + return new ConcurrentHashMap<>(); } }; - public abstract ConcurrentMap create(); + public abstract ConcurrentMap create(); private static MapFactory instance = DEFAULT; diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/RevisionsKey.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/RevisionsKey.java index 78531b35b2..9434ef2287 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/RevisionsKey.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/RevisionsKey.java @@ -39,6 +39,14 @@ public final class RevisionsKey implements CacheValue, Comparable this.r2 = checkNotNull(r2); } + public RevisionVector getRev1() { + return r1; + } + + public RevisionVector getRev2() { + return r2; + } + @Override public int getMemory() { long size = 32 + (long)r1.getMemory() + (long)r2.getMemory(); diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java index 0a01787621..29c34f0d90 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java @@ -43,6 +43,7 @@ import org.apache.jackrabbit.oak.plugins.document.Collection; import org.apache.jackrabbit.oak.plugins.document.DocumentStore; import org.apache.jackrabbit.oak.plugins.document.DocumentStoreException; import org.apache.jackrabbit.oak.plugins.document.NodeDocument; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.Revision; import org.apache.jackrabbit.oak.plugins.document.RevisionVector; import org.apache.jackrabbit.oak.plugins.document.StableRevisionComparator; @@ -232,27 +233,45 @@ public class Utils { return !key.startsWith("_") || key.startsWith("__") || key.startsWith("_$"); } - public static String getIdFromPath(String path) { + public static String getIdFromPath(@NotNull String path) { + int depth = Utils.pathDepth(path); if (isLongPath(path)) { - MessageDigest digest; - try { - digest = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - int depth = Utils.pathDepth(path); String parent = PathUtils.getParentPath(path); - byte[] hash = digest.digest(parent.getBytes(UTF_8)); - String name = PathUtils.getName(path); - StringBuilder sb = new StringBuilder(digest.getDigestLength() * 2 + name.length() + 6); - sb.append(depth).append(":h"); - encodeHexString(hash, sb).append("/").append(name); - return sb.toString(); + byte[] hash = createSHA256Digest(parent); + return createHashedId(depth, hash, PathUtils.getName(path)); + } + return depth + ":" + path; + } + + public static String getIdFromPath(@NotNull Path path) { + checkNotNull(path); + if (path == Path.NULL) { + throw new IllegalArgumentException("Cannot get Id from NULL path"); + } + Path parent = path.getParent(); + int depth = path.getDepth(); + if (parent != null && isLongPath(path)) { + byte[] hash = createSHA256Digest(parent.toString()); + return createHashedId(depth, hash, path.getName()); } - int depth = Utils.pathDepth(path); return depth + ":" + path; } + private static String createHashedId(int depth, byte[] hash, String name) { + StringBuilder sb = new StringBuilder(hash.length * 2 + name.length() + 6); + sb.append(depth).append(":h"); + encodeHexString(hash, sb).append("/").append(name); + return sb.toString(); + } + + private static byte[] createSHA256Digest(String input) { + try { + return MessageDigest.getInstance("SHA-256").digest(input.getBytes(UTF_8)); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + /** * Encodes the given data as hexadecimal string representation and appends * it to the {@code StringBuilder}. The hex digits are in lower case. @@ -298,6 +317,10 @@ public class Utils { return Utils.getIdFromPath(parentPath); } + /** + * @deprecated Use {@link #isLongPath(Path)} instead. + */ + @Deprecated public static boolean isLongPath(String path) { // the most common case: a short path // avoid calculating the parent path @@ -315,7 +338,27 @@ public class Utils { } return true; } - + + public static boolean isLongPath(Path path) { + // the most common case: a short path + // avoid calculating the parent path + if (path.length() < PATH_SHORT) { + return false; + } + // check if the parent path is long + Path parent = path.getParent(); + if (parent == null) { + return false; + } + if (parent.toString().getBytes(UTF_8).length < PATH_LONG) { + return false; + } + if (path.getName().getBytes(UTF_8).length > NODE_NAME_LIMIT) { + throw new IllegalArgumentException("Node name is too long: " + path); + } + return true; + } + public static boolean isIdFromLongPath(String id) { int index = id.indexOf(':'); return index != -1 && index < id.length() - 1 && id.charAt(index + 1) == 'h'; @@ -416,9 +459,8 @@ public class Utils { * @param path a path. * @return the lower key limit. */ - public static String getKeyLowerLimit(String path) { - String from = PathUtils.concat(path, "a"); - from = getIdFromPath(from); + public static String getKeyLowerLimit(Path path) { + String from = getIdFromPath(new Path(path, "a")); from = from.substring(0, from.length() - 1); return from; } @@ -430,9 +472,8 @@ public class Utils { * @param path a path. * @return the upper key limit. */ - public static String getKeyUpperLimit(String path) { - String to = PathUtils.concat(path, "z"); - to = getIdFromPath(to); + public static String getKeyUpperLimit(Path path) { + String to = getIdFromPath(new Path(path, "z")); to = to.substring(0, to.length() - 2) + "0"; return to; } @@ -608,7 +649,7 @@ public class Utils { */ @NotNull public static NodeDocument getRootDocument(@NotNull DocumentStore store) { - String rootId = Utils.getIdFromPath("/"); + String rootId = Utils.getIdFromPath(Path.ROOT); NodeDocument root = store.find(Collection.NODES, rootId); if (root == null) { throw new IllegalStateException("missing root document"); @@ -950,7 +991,7 @@ public class Utils { ClusterNodeInfo info, Clock clock) throws DocumentStoreException { - NodeDocument root = store.find(Collection.NODES, getIdFromPath("/")); + NodeDocument root = store.find(Collection.NODES, getIdFromPath(Path.ROOT)); if (root == null) { return; } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/AmnesiaDiffCache.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/AmnesiaDiffCache.java index 9c56ecf10f..6be9feedc2 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/AmnesiaDiffCache.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/AmnesiaDiffCache.java @@ -36,7 +36,7 @@ class AmnesiaDiffCache extends DiffCache { @Override public String getChanges(@NotNull RevisionVector from, @NotNull RevisionVector to, - @NotNull String path, + @NotNull Path path, @Nullable Loader loader) { if (loader != null) { return loader.call(); @@ -49,7 +49,7 @@ class AmnesiaDiffCache extends DiffCache { public Entry newEntry(@NotNull RevisionVector from, @NotNull RevisionVector to, boolean local) { return new Entry() { @Override - public void append(@NotNull String path, @NotNull String changes) { + public void append(@NotNull Path path, @NotNull String changes) { } @Override diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/AsyncCacheTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/AsyncCacheTest.java index c99af187ce..43c97a5603 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/AsyncCacheTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/AsyncCacheTest.java @@ -37,14 +37,15 @@ public class AsyncCacheTest { DocumentMK.Builder builder = builderProvider.newBuilder(); builder.setPersistentCache("target/cacheTest"); DocumentNodeStore nodeStore = builder.getNodeStore(); - Cache cache = builder.buildChildrenCache(nodeStore); + Cache cache = builder.buildChildrenCache(nodeStore); DocumentNodeState.Children c = new DocumentNodeState.Children(); for (int i = 0; i < 100; i++) { c.children.add("node-" + i); } - PathRev key = null; + Path path = Path.fromString("/foo/bar"); + NamePathRev key = null; for (int i = 0; i < 1000; i++) { - key = new PathRev("/foo/bar", new RevisionVector(new Revision(i, 0, 1))); + key = new NamePathRev("", path, new RevisionVector(new Revision(i, 0, 1))); cache.put(key, c); } cache.invalidate(key); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BackgroundWriteTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BackgroundWriteTest.java index e83d5f9bea..6b65cf7a36 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BackgroundWriteTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BackgroundWriteTest.java @@ -53,9 +53,9 @@ public class BackgroundWriteTest { mk.runBackgroundOperations(); Revision r = mk.getNodeStore().newRevision(); UnsavedModifications pending = mk.getNodeStore().getPendingModifications(); - pending.put("/", r); + pending.put(Path.ROOT, r); for (String p : paths) { - pending.put(p, r); + pending.put(Path.fromString(p), r); } mk.runBackgroundOperations(); mk.dispose(); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BranchTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BranchTest.java index 6f88da389f..91dbb62ba9 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BranchTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BranchTest.java @@ -56,12 +56,12 @@ public class BranchTest { Branch b = branches.create(base, c1, null); BranchCommit bc1 = b.getCommit(c1); - bc1.track("/foo"); + bc1.track(Path.fromString("/foo")); Revision c2 = Revision.newRevision(1).asBranchRevision(); b.addCommit(c2); BranchCommit bc2 = b.getCommit(c2); - bc2.track("/bar"); + bc2.track(Path.fromString("/bar")); Revision c3 = Revision.newRevision(1).asBranchRevision(); b.rebase(c3, new RevisionVector(Revision.newRevision(1))); @@ -69,7 +69,7 @@ public class BranchTest { Revision c4 = Revision.newRevision(1).asBranchRevision(); b.addCommit(c4); BranchCommit bc4 = b.getCommit(c4); - bc4.track("/baz"); + bc4.track(Path.fromString("/baz")); Revision c5 = Revision.newRevision(1).asBranchRevision(); @@ -138,7 +138,11 @@ public class BranchTest { assertEquals("a", state.getString("p")); } - private void assertModifiedPaths(Iterable actual, String... expected) { - assertEquals(Sets.newHashSet(expected), Sets.newHashSet(actual)); + private void assertModifiedPaths(Iterable actual, String... expected) { + Set expectedSet = Sets.newHashSet(); + for (String p : expected) { + expectedSet.add(Path.fromString(p)); + } + assertEquals(expectedSet, Sets.newHashSet(actual)); } } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CacheConsistencyRDBTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CacheConsistencyRDBTest.java index 038b36e591..244420184b 100755 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CacheConsistencyRDBTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CacheConsistencyRDBTest.java @@ -65,7 +65,7 @@ public class CacheConsistencyRDBTest extends AbstractRDBConnectionTest { Thread t = new Thread(new Runnable() { @Override public void run() { - store.query(NODES, Utils.getKeyLowerLimit("/"), Utils.getKeyUpperLimit("/"), 10); + store.query(NODES, Utils.getKeyLowerLimit(Path.ROOT), Utils.getKeyUpperLimit(Path.ROOT), 10); } }, "query"); // block thread when it tries to convert db objects diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CacheConsistencyTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CacheConsistencyTest.java index 6846609829..85be44bc31 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CacheConsistencyTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CacheConsistencyTest.java @@ -71,8 +71,8 @@ public class CacheConsistencyTest extends AbstractMongoConnectionTest { @Override public void run() { store.query(NODES, - Utils.getKeyLowerLimit("/"), - Utils.getKeyUpperLimit("/"), 10); + Utils.getKeyLowerLimit(Path.ROOT), + Utils.getKeyUpperLimit(Path.ROOT), 10); } }); // block thread when it tries to convert db objects diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java index d08e65a280..aab3fba170 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java @@ -225,9 +225,9 @@ public class ClusterTest { mk3.runBackgroundOperations(); // pick up changes from mk2 - DocumentNodeState base = ns3.getNode("/", RevisionVector.fromString(base3)); + DocumentNodeState base = ns3.getNode(Path.ROOT, RevisionVector.fromString(base3)); assertNotNull(base); - NodeState branchHead = ns3.getNode("/", RevisionVector.fromString(b3)); + NodeState branchHead = ns3.getNode(Path.ROOT, RevisionVector.fromString(b3)); assertNotNull(branchHead); TrackingDiff diff = new TrackingDiff(); branchHead.compareAgainstBaseState(base, diff); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitBuilderTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitBuilderTest.java index be001b6db3..c6428ffd1e 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitBuilderTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitBuilderTest.java @@ -92,8 +92,9 @@ public class CommitBuilderTest { @Test public void addNode() { RevisionVector baseRev = ns.getHeadRevision(); + Path foo = Path.fromString("/foo"); CommitBuilder builder = new CommitBuilder(ns, baseRev); - builder.addNode("/foo"); + builder.addNode(foo); Commit c = builder.build(ns.newRevision()); assertNotNull(c); assertFalse(c.isEmpty()); @@ -102,10 +103,11 @@ public class CommitBuilderTest { @Test public void addNodeTwice() { RevisionVector baseRev = ns.getHeadRevision(); + Path foo = Path.fromString("/foo"); CommitBuilder builder = new CommitBuilder(ns, baseRev); - builder.addNode("/foo"); + builder.addNode(foo); try { - builder.addNode("/foo"); + builder.addNode(foo); fail("Must fail with DocumentStoreException"); } catch (DocumentStoreException e) { assertThat(e.getMessage(), containsString("already added")); @@ -116,7 +118,7 @@ public class CommitBuilderTest { public void addNodePathNull() { CommitBuilder builder = new CommitBuilder(ns, null); try { - builder.addNode((String) null); + builder.addNode((Path) null); expectNPE(); } catch (NullPointerException e) { // expected @@ -136,7 +138,7 @@ public class CommitBuilderTest { @Test public void addNodeState() { - String path = "/foo"; + Path path = Path.fromString("/foo"); DocumentNodeState foo = addNode("foo"); CommitBuilder builder = new CommitBuilder(ns, null); @@ -153,10 +155,11 @@ public class CommitBuilderTest { RevisionVector baseRev = ns.getHeadRevision() .update(ns.newRevision().asBranchRevision()); CommitBuilder builder = new CommitBuilder(ns, baseRev); - builder.addNode("/foo"); + Path path = Path.fromString("/foo"); + builder.addNode(path); Revision commitRev = ns.newRevision(); Commit c = builder.build(commitRev); - UpdateOp up = c.getUpdateOperationForNode("/foo"); + UpdateOp up = c.getUpdateOperationForNode(path); UpdateOp.Operation op = up.getChanges().get( new UpdateOp.Key("_bc", commitRev)); assertNotNull(op); @@ -167,7 +170,7 @@ public class CommitBuilderTest { DocumentNodeState bar = addNode("bar"); CommitBuilder builder = new CommitBuilder(ns, null); - String path = "/bar"; + Path path = Path.fromString("/bar"); builder.removeNode(path, bar); Commit c = builder.build(ns.newRevision()); @@ -182,7 +185,7 @@ public class CommitBuilderTest { DocumentNodeState bar = addNode("bar"); CommitBuilder builder = new CommitBuilder(ns, null); - String path = "/bar"; + Path path = Path.fromString("/bar"); builder.removeNode(path, bar); try { @@ -210,7 +213,7 @@ public class CommitBuilderTest { public void removeNodeStateNull() { CommitBuilder builder = new CommitBuilder(ns, null); try { - builder.removeNode("/bar", null); + builder.removeNode(Path.fromString("/bar"), null); expectNPE(); } catch (NullPointerException e) { // expected @@ -219,10 +222,11 @@ public class CommitBuilderTest { @Test public void updateProperty() { + Path path = Path.fromString("/foo"); CommitBuilder builder = new CommitBuilder(ns, null); - builder.updateProperty("/foo", "p", "v"); + builder.updateProperty(path, "p", "v"); Commit c = builder.build(ns.newRevision()); - UpdateOp up = c.getUpdateOperationForNode("/foo"); + UpdateOp up = c.getUpdateOperationForNode(path); UpdateOp.Operation op = up.getChanges().get( new UpdateOp.Key("p", c.getRevision())); assertNotNull(op); @@ -230,10 +234,11 @@ public class CommitBuilderTest { @Test public void updatePropertyValueNull() { + Path path = Path.fromString("/foo"); CommitBuilder builder = new CommitBuilder(ns, null); - builder.updateProperty("/foo", "p", null); + builder.updateProperty(path, "p", null); Commit c = builder.build(ns.newRevision()); - UpdateOp up = c.getUpdateOperationForNode("/foo"); + UpdateOp up = c.getUpdateOperationForNode(path); UpdateOp.Operation op = up.getChanges().get( new UpdateOp.Key("p", c.getRevision())); assertNotNull(op); @@ -243,7 +248,7 @@ public class CommitBuilderTest { public void updatePropertyPathNull() { CommitBuilder builder = new CommitBuilder(ns, null); try { - builder.updateProperty(null, "p", "v"); + builder.updateProperty((Path) null, "p", "v"); expectNPE(); } catch (NullPointerException e) { // expected @@ -254,7 +259,7 @@ public class CommitBuilderTest { public void updatePropertyPropertyNull() { CommitBuilder builder = new CommitBuilder(ns, null); try { - builder.updateProperty("/foo", null, "v"); + builder.updateProperty(Path.fromString("/foo"), null, "v"); expectNPE(); } catch (NullPointerException e) { // expected diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitRootUpdateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitRootUpdateTest.java index 118cdf864c..f47edd9b6f 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitRootUpdateTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitRootUpdateTest.java @@ -68,8 +68,8 @@ public class CommitRootUpdateTest { throwAfterUpdate.set(true); boolean success = false; Commit c = ns.newCommit(changes -> { - changes.addNode("/foo/node"); - changes.addNode("/bar/node"); + changes.addNode(Path.fromString("/foo/node")); + changes.addNode(Path.fromString("/bar/node")); }, ns.getHeadRevision(), null); try { c.apply(); @@ -125,7 +125,7 @@ public class CommitRootUpdateTest { throwAfterUpdate.set(true); boolean success = false; Commit c = ns.newCommit( - changes -> changes.updateProperty("/foo", "p", "v"), + changes -> changes.updateProperty(Path.fromString("/foo"), "p", "v"), ns.getHeadRevision(), null); try { c.apply(); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitTest.java index d34e51d149..152e050b63 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitTest.java @@ -59,10 +59,10 @@ public class CommitTest { // this commit should fail Commit c = ns.newCommit(changes -> { - changes.addNode("/foo/baz"); + changes.addNode(Path.fromString("/foo/baz")); }, ns.getHeadRevision(), null); try { - UpdateOp op = c.getUpdateOperationForNode("/bar"); + UpdateOp op = c.getUpdateOperationForNode(Path.fromString("/bar")); op.setMapEntry("p", c.getRevision(), "v"); try { c.apply(); @@ -94,7 +94,7 @@ public class CommitTest { // this commit should fail Commit c = ns.newCommit(changes -> { - changes.addNode("/foo"); + changes.addNode(Path.fromString("/foo")); }, ns.getHeadRevision(), null); try { c.apply(); @@ -117,7 +117,7 @@ public class CommitTest { // this branch commit must fail with a DocumentStoreException Commit c = ns.newCommit(changes -> { - changes.removeNode("/foo", EMPTY_NODE); + changes.removeNode(Path.fromString("/foo"), EMPTY_NODE); }, ns.getHeadRevision().asBranchRevision(ns.getClusterId()), null); try { try { diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentQueryAndInvalidateIT.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentQueryAndInvalidateIT.java index 85608a6e18..8a3e20f61b 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentQueryAndInvalidateIT.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentQueryAndInvalidateIT.java @@ -122,7 +122,7 @@ public class ConcurrentQueryAndInvalidateIT extends AbstractMultiDocumentStoreTe } private void queryDocuments() { - ds1.query(NODES, getKeyLowerLimit("/"), getKeyUpperLimit("/"), 100); + ds1.query(NODES, getKeyLowerLimit(Path.ROOT), getKeyUpperLimit(Path.ROOT), 100); } private Iterable updateDocuments() { diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentQueryAndUpdate2IT.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentQueryAndUpdate2IT.java index 62788469c6..d892568193 100755 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentQueryAndUpdate2IT.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentQueryAndUpdate2IT.java @@ -105,7 +105,7 @@ public class ConcurrentQueryAndUpdate2IT extends AbstractDocumentStoreTest { Thread.sleep(0, ThreadLocalRandom.current().nextInt(1000, 10000)); } catch (InterruptedException ignore) { } - ds.query(NODES, getKeyLowerLimit("/"), getKeyUpperLimit("/"), 100); + ds.query(NODES, getKeyLowerLimit(Path.ROOT), getKeyUpperLimit(Path.ROOT), 100); } private void updateDocuments() { diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentQueryAndUpdateIT.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentQueryAndUpdateIT.java index c5fe807066..951c698607 100755 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentQueryAndUpdateIT.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentQueryAndUpdateIT.java @@ -87,7 +87,7 @@ public class ConcurrentQueryAndUpdateIT extends AbstractDocumentStoreTest { } private void queryDocuments() { - ds.query(NODES, getKeyLowerLimit("/"), getKeyUpperLimit("/"), 100); + ds.query(NODES, getKeyLowerLimit(Path.ROOT), getKeyUpperLimit(Path.ROOT), 100); } private void updateDocuments() { diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CountingDiffCache.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CountingDiffCache.java index 35a1f9fd5c..931e0eaec7 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CountingDiffCache.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CountingDiffCache.java @@ -58,7 +58,7 @@ public class CountingDiffCache extends MemoryDiffCache { @Override public String getChanges(@NotNull RevisionVector from, @NotNull RevisionVector to, - @NotNull String path, + @NotNull Path path, @Nullable Loader loader) { return super.getChanges(from, to, path, new CountingLoader(loader)); } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java index 3ca24d53f7..0ca99b3994 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java @@ -126,8 +126,9 @@ public class DocumentMK { } RevisionVector fromRev = RevisionVector.fromString(fromRevisionId); RevisionVector toRev = RevisionVector.fromString(toRevisionId); - final DocumentNodeState before = nodeStore.getNode(path, fromRev); - final DocumentNodeState after = nodeStore.getNode(path, toRev); + Path p = Path.fromString(path); + final DocumentNodeState before = nodeStore.getNode(p, fromRev); + final DocumentNodeState after = nodeStore.getNode(p, toRev); if (before == null || after == null) { String msg = String.format("Diff is only supported if the node exists in both cases. " + "Node [%s], fromRev [%s] -> %s, toRev [%s] -> %s", @@ -149,7 +150,7 @@ public class DocumentMK { RevisionVector rev = RevisionVector.fromString(revisionId); DocumentNodeState n; try { - n = nodeStore.getNode(path, rev); + n = nodeStore.getNode(Path.fromString(path), rev); } catch (DocumentStoreException e) { throw new DocumentStoreException(e); } @@ -165,7 +166,7 @@ public class DocumentMK { revisionId = revisionId != null ? revisionId : nodeStore.getHeadRevision().toString(); RevisionVector rev = RevisionVector.fromString(revisionId); try { - DocumentNodeState n = nodeStore.getNode(path, rev); + DocumentNodeState n = nodeStore.getNode(Path.fromString(path), rev); if (n == null) { return null; } @@ -183,7 +184,7 @@ public class DocumentMK { long m = ((long) maxChildNodes) + offset; max = (int) Math.min(m, Integer.MAX_VALUE); } - Children c = nodeStore.getChildren(n, null, max); + Children c = nodeStore.getChildren(n, "", max); for (long i = offset; i < c.children.size(); i++) { if (maxChildNodes-- <= 0) { break; @@ -330,7 +331,7 @@ public class DocumentMK { added.add(path); break; case '-': - DocumentNodeState toRemove = nodeStore.getNode(path, commit.getBaseRevision()); + DocumentNodeState toRemove = nodeStore.getNode(Path.fromString(path), commit.getBaseRevision()); if (toRemove == null) { throw new DocumentStoreException("Node not found: " + path + " in revision " + baseRevId); } @@ -345,11 +346,11 @@ public class DocumentMK { value = t.readRawValue().trim(); } String p = PathUtils.getParentPath(path); - if (!added.contains(p) && nodeStore.getNode(p, commit.getBaseRevision()) == null) { + if (!added.contains(p) && nodeStore.getNode(Path.fromString(p), commit.getBaseRevision()) == null) { throw new DocumentStoreException("Node not found: " + path + " in revision " + baseRevId); } String propertyName = PathUtils.getName(path); - commit.updateProperty(p, propertyName, value); + commit.updateProperty(Path.fromString(p), propertyName, value); break; case '>': { t.read(':'); @@ -357,7 +358,7 @@ public class DocumentMK { if (!PathUtils.isAbsolute(targetPath)) { targetPath = PathUtils.concat(rootPath, targetPath); } - DocumentNodeState source = nodeStore.getNode(path, baseRev); + DocumentNodeState source = nodeStore.getNode(Path.fromString(path), baseRev); if (source == null) { throw new DocumentStoreException("Node not found: " + path + " in revision " + baseRevId); } else if (nodeExists(targetPath, baseRevId)) { @@ -372,7 +373,7 @@ public class DocumentMK { if (!PathUtils.isAbsolute(targetPath)) { targetPath = PathUtils.concat(rootPath, targetPath); } - DocumentNodeState source = nodeStore.getNode(path, baseRev); + DocumentNodeState source = nodeStore.getNode(Path.fromString(path), baseRev); if (source == null) { throw new DocumentStoreException("Node not found: " + path + " in revision " + baseRevId); } else if (nodeExists(targetPath, baseRevId)) { @@ -403,7 +404,7 @@ public class DocumentMK { } while (t.matches(',')); t.read('}'); } - DocumentNodeState n = new DocumentNodeState(nodeStore, path, + DocumentNodeState n = new DocumentNodeState(nodeStore, Path.fromString(path), new RevisionVector(commit.getRevision()), props, false, null); commit.addNode(n); } @@ -421,7 +422,7 @@ public class DocumentMK { if (subTreeAlso) { // recurse down the tree - for (DocumentNodeState child : nodeStore.getChildNodes(node, null, Integer.MAX_VALUE)) { + for (DocumentNodeState child : nodeStore.getChildNodes(node, "", Integer.MAX_VALUE)) { markAsDeleted(child, commit, true); } } @@ -432,15 +433,15 @@ public class DocumentMK { String targetPath, CommitBuilder commit) { RevisionVector destRevision = commit.getBaseRevision().update(commit.getRevision()); - DocumentNodeState newNode = new DocumentNodeState(nodeStore, targetPath, destRevision, + DocumentNodeState newNode = new DocumentNodeState(nodeStore, Path.fromString(targetPath), destRevision, source.getProperties(), false, null); commit.addNode(newNode); if (move) { markAsDeleted(source, commit, false); } - for (DocumentNodeState child : nodeStore.getChildNodes(source, null, Integer.MAX_VALUE)) { - String childName = PathUtils.getName(child.getPath()); + for (DocumentNodeState child : nodeStore.getChildNodes(source, "", Integer.MAX_VALUE)) { + String childName = child.getPath().getName(); String destChildPath = concat(targetPath, childName); moveOrCopyNode(move, child, destChildPath, commit); } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateTest.java index 39b69dd8b4..dd28757260 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateTest.java @@ -35,8 +35,8 @@ public class DocumentNodeStateTest { public void getMemory() { DocumentNodeStore store = builderProvider.newBuilder().getNodeStore(); RevisionVector rv = new RevisionVector(Revision.newRevision(1)); - DocumentNodeState state = new DocumentNodeState(store, "/foo", rv); - assertEquals(164, state.getMemory()); + DocumentNodeState state = new DocumentNodeState(store, Path.fromString("/foo"), rv); + assertEquals(186, state.getMemory()); } @Test diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceTest.java index 0e475e2157..820bce9196 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceTest.java @@ -195,10 +195,10 @@ public class DocumentNodeStoreServiceTest { MockOsgi.activate(service, context.bundleContext()); DocumentNodeStore dns = context.getService(DocumentNodeStore.class); - assertTrue(dns.getNodeCachePredicate().apply("/a/b/c")); - assertTrue(dns.getNodeCachePredicate().apply("/c/d/e")); + assertTrue(dns.getNodeCachePredicate().apply(Path.fromString("/a/b/c"))); + assertTrue(dns.getNodeCachePredicate().apply(Path.fromString("/c/d/e"))); - assertFalse(dns.getNodeCachePredicate().apply("/x")); + assertFalse(dns.getNodeCachePredicate().apply(Path.fromString("/x"))); } @Test diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java index 1f9162b32f..ce847c2504 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java @@ -295,9 +295,9 @@ public class DocumentNodeStoreTest { @Override public void run() { Commit c = new CommitBuilder(store, store.newRevision(), head) - .addNode("/newConflictingNode") - .addNode("/deletedNode") - .updateProperty("/updateNode", "foo", "baz") + .addNode(Path.fromString("/newConflictingNode")) + .addNode(Path.fromString("/deletedNode")) + .updateProperty(Path.fromString("/updateNode"), "foo", "baz") .build(); try { c.apply(); @@ -316,8 +316,8 @@ public class DocumentNodeStoreTest { // commit will succeed and add collision marker to writer commit Revision r = store.newRevision(); Commit c = new CommitBuilder(store, r, head) - .addNode("/newConflictingNode") - .addNode("/newNonConflictingNode") + .addNode(Path.fromString("/newConflictingNode")) + .addNode(Path.fromString("/newNonConflictingNode")) .build(); c.apply(); // allow writer to continue @@ -2144,7 +2144,7 @@ public class DocumentNodeStoreTest { RevisionVector to = ns.getHeadRevision(); DiffCache.Entry entry = ns.getDiffCache().newEntry(from, to, true); - entry.append("/", "-\"foo\""); + entry.append(Path.ROOT, "-\"foo\""); entry.done(); ns.compare(ns.getRoot(), ns.getRoot(from), new DefaultNodeStateDiff() { @@ -2928,7 +2928,7 @@ public class DocumentNodeStoreTest { RevisionVector headRev = ns.getHeadRevision(); Iterable nodes = ns.getChildNodes( - asDocumentNodeState(ns.getRoot().getChildNode("foo")), null, 10); + asDocumentNodeState(ns.getRoot().getChildNode("foo")), "", 10); assertEquals(2, Iterables.size(nodes)); for (DocumentNodeState c : nodes) { assertEquals(headRev, c.getRootRevision()); @@ -3259,7 +3259,7 @@ public class DocumentNodeStoreTest { Revision rev = ns.newRevision(); RevisionVector after = new RevisionVector(ns.newRevision()); - String path = "/foo"; + Path path = Path.fromString("/foo"); ns.getNode(path, before); assertNotNull(ns.getNodeCache().getIfPresent(new PathRev(path, before))); @@ -3282,7 +3282,7 @@ public class DocumentNodeStoreTest { RevisionVector head = ns.getHeadRevision(); // simulate an incorrect cache entry - PathRev key = new PathRev("/", head); + NamePathRev key = new NamePathRev("", Path.ROOT, head); DocumentNodeState.Children c = new DocumentNodeState.Children(); c.children.add("a"); c.children.add("b"); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java index cb9d1dcd9a..1c556d1e23 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java @@ -107,7 +107,7 @@ public class DocumentSplitTest extends BaseDocumentMKTest { assertTrue(isCommitted(ns.getCommitValue(rev, doc))); } // check if document is still there - assertNotNull(ns.getNode("/", RevisionVector.fromString(head))); + assertNotNull(ns.getNode(Path.ROOT, RevisionVector.fromString(head))); NodeDocument prevDoc = Iterators.getOnlyElement(doc.getAllPreviousDocs()); assertThat(prevDoc.getSplitDocType(), either(is(SplitDocType.DEFAULT)).or(is(SplitDocType.DEFAULT_NO_BRANCH))); @@ -147,7 +147,7 @@ public class DocumentSplitTest extends BaseDocumentMKTest { || doc.getCommitRootPath(rev) != null); assertTrue(isCommitted(ns.getCommitValue(rev, doc))); } - DocumentNodeState node = ns.getNode("/foo", RevisionVector.fromString(head)); + DocumentNodeState node = ns.getNode(Path.fromString("/foo"), RevisionVector.fromString(head)); // check status of node if (create) { assertNull(node); @@ -305,7 +305,7 @@ public class DocumentSplitTest extends BaseDocumentMKTest { NodeDocument doc = ds.find(NODES, Utils.getIdFromPath("/test")); assertNotNull(doc); RevisionVector head = ns.getHeadRevision(); - Revision lastRev = ns.getPendingModifications().get("/test"); + Revision lastRev = ns.getPendingModifications().get(Path.fromString("/test")); DocumentNodeState n = doc.getNodeAtRevision(mk.getNodeStore(), head, lastRev); assertNotNull(n); String value = n.getPropertyAsString(name); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/JournalEntryTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/JournalEntryTest.java index 0eccb69bdd..630c6dd0d7 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/JournalEntryTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/JournalEntryTest.java @@ -52,20 +52,20 @@ public class JournalEntryTest { @Test public void applyTo() throws Exception { DiffCache cache = new MemoryDiffCache(new DocumentMK.Builder()); - List paths = Lists.newArrayList(); + List paths = Lists.newArrayList(); addRandomPaths(paths); StringSort sort = JournalEntry.newSorter(); add(sort, paths); RevisionVector from = new RevisionVector(new Revision(1, 0, 1)); RevisionVector to = new RevisionVector(new Revision(2, 0, 1)); sort.sort(); - JournalEntry.applyTo(sort, cache, "/", from, to); + JournalEntry.applyTo(sort, cache, Path.ROOT, from, to); - for (String p : paths) { + for (Path p : paths) { String changes = cache.getChanges(from, to, p, null); assertNotNull("missing changes for " + p, changes); for (String c : getChildren(changes)) { - assertTrue(paths.contains(PathUtils.concat(p, c))); + assertTrue(paths.contains(new Path(p, c))); } } sort.close(); @@ -85,13 +85,13 @@ public class JournalEntryTest { RevisionVector from = new RevisionVector(Revision.newRevision(1)); RevisionVector to = new RevisionVector(Revision.newRevision(1)); sort.sort(); - JournalEntry.applyTo(sort, cache, "/foo", from, to); - assertNotNull(cache.getChanges(from, to, "/foo", null)); - assertNotNull(cache.getChanges(from, to, "/foo/a", null)); - assertNotNull(cache.getChanges(from, to, "/foo/b", null)); - assertNull(cache.getChanges(from, to, "/bar", null)); - assertNull(cache.getChanges(from, to, "/bar/a", null)); - assertNull(cache.getChanges(from, to, "/bar/b", null)); + JournalEntry.applyTo(sort, cache, p("/foo"), from, to); + assertNotNull(cache.getChanges(from, to, p("/foo"), null)); + assertNotNull(cache.getChanges(from, to, p("/foo/a"), null)); + assertNotNull(cache.getChanges(from, to, p("/foo/b"), null)); + assertNull(cache.getChanges(from, to, p("/bar"), null)); + assertNull(cache.getChanges(from, to, p("/bar/a"), null)); + assertNull(cache.getChanges(from, to, p("/bar/b"), null)); } //OAK-3494 @@ -104,7 +104,7 @@ public class JournalEntryTest { //Put one entry for (from, to, "/a/b")->["c1", "c2"] manually DiffCache.Entry entry = cache.newEntry(from, to, false); - entry.append("/a/b", "^\"c1\":{}^\"c2\":{}"); + entry.append(p("/a/b"), "^\"c1\":{}^\"c2\":{}"); entry.done(); //NOTE: calling validateCacheUsage fills the cache with an empty diff for the path being validated. @@ -120,11 +120,14 @@ public class JournalEntryTest { validateCacheUsage(cache, from, to, "/c", false);//there is no cache entry for the whole hierarchy //Fill cache using journal - List paths = Lists.newArrayList("/content/changed", "/content/changed1/child1"); + List paths = Lists.newArrayList( + p("/content/changed"), + p("/content/changed1/child1") + ); StringSort sort = JournalEntry.newSorter(); add(sort, paths); sort.sort(); - JournalEntry.applyTo(sort, cache, "/", from, to); + JournalEntry.applyTo(sort, cache, Path.ROOT, from, to); validateCacheUsage(cache, from, to, "/topUnchanged", true); validateCacheUsage(cache, from, to, "/content/changed/unchangedLeaf", true); @@ -138,7 +141,7 @@ public class JournalEntryTest { public void fillExternalChanges() throws Exception { DocumentStore store = new MemoryDocumentStore(); JournalEntry entry = JOURNAL.newDocument(store); - Set paths = Sets.newHashSet(); + Set paths = Sets.newHashSet(); addRandomPaths(paths); entry.modified(paths); Revision r1 = new Revision(1, 0, 1); @@ -170,7 +173,7 @@ public class JournalEntryTest { public void invalidateOnly() throws Exception { DocumentStore store = new MemoryDocumentStore(); JournalEntry invalidateEntry = JOURNAL.newDocument(store); - Set paths = Sets.newHashSet(); + Set paths = Sets.newHashSet(); addRandomPaths(paths); invalidateEntry.modified(paths); Revision r1 = new Revision(1, 0, 1); @@ -213,14 +216,14 @@ public class JournalEntryTest { Revision r4 = new Revision(4, 0, 1); DocumentStore store = new MemoryDocumentStore(); JournalEntry entry = JOURNAL.newDocument(store); - entry.modified("/"); - entry.modified("/foo"); + entry.modified(p("/")); + entry.modified(p("/foo")); UpdateOp op = entry.asUpdateOp(r2); assertTrue(store.create(JOURNAL, Collections.singletonList(op))); entry = JOURNAL.newDocument(store); - entry.modified("/"); - entry.modified("/bar"); + entry.modified(p("/")); + entry.modified(p("/bar")); op = entry.asUpdateOp(r4); assertTrue(store.create(JOURNAL, Collections.singletonList(op))); @@ -271,15 +274,15 @@ public class JournalEntryTest { Revision r2 = new Revision(2, 0, 1); DocumentStore store = new MemoryDocumentStore(); JournalEntry entry = JOURNAL.newDocument(store); - entry.modified("/"); - entry.modified("/foo"); - entry.modified("/foo/a"); - entry.modified("/foo/b"); - entry.modified("/foo/c"); - entry.modified("/bar"); - entry.modified("/bar/a"); - entry.modified("/bar/b"); - entry.modified("/bar/c"); + entry.modified(p("/")); + entry.modified(p("/foo")); + entry.modified(p("/foo/a")); + entry.modified(p("/foo/b")); + entry.modified(p("/foo/c")); + entry.modified(p("/bar")); + entry.modified(p("/bar/a")); + entry.modified(p("/bar/b")); + entry.modified(p("/bar/c")); UpdateOp op = entry.asUpdateOp(r2); assertTrue(store.create(JOURNAL, Collections.singletonList(op))); @@ -297,7 +300,7 @@ public class JournalEntryTest { public void getRevisionTimestamp() throws Exception { DocumentStore store = new MemoryDocumentStore(); JournalEntry entry = JOURNAL.newDocument(store); - entry.modified("/foo"); + entry.modified(p("/foo")); Revision r = Revision.newRevision(1); assertTrue(store.create(JOURNAL, Collections.singletonList(entry.asUpdateOp(r)))); @@ -315,7 +318,7 @@ public class JournalEntryTest { @Override public void run() { for (int i = 0; i < 100000; i++) { - entry.modified("/node-" + i); + entry.modified(p("/node-" + i)); } } }); @@ -336,15 +339,15 @@ public class JournalEntryTest { public void addToWithPath() throws Exception { DocumentStore store = new MemoryDocumentStore(); JournalEntry entry = JOURNAL.newDocument(store); - entry.modified("/"); - entry.modified("/foo"); - entry.modified("/foo/a"); - entry.modified("/foo/b"); - entry.modified("/foo/c"); - entry.modified("/bar"); - entry.modified("/bar/a"); - entry.modified("/bar/b"); - entry.modified("/bar/c"); + entry.modified(p("/")); + entry.modified(p("/foo")); + entry.modified(p("/foo/a")); + entry.modified(p("/foo/b")); + entry.modified(p("/foo/c")); + entry.modified(p("/bar")); + entry.modified(p("/bar/a")); + entry.modified(p("/bar/b")); + entry.modified(p("/bar/c")); StringSort sort = JournalEntry.newSorter(); entry.addTo(sort, "/foo"); assertEquals(4, sort.getSize()); @@ -359,24 +362,24 @@ public class JournalEntryTest { assertEquals("Incorrect number of initial paths", 0, entry.getNumChangedNodes()); assertFalse("Incorrect hasChanges", entry.hasChanges()); - entry.modified("/foo"); - entry.modified("/bar"); + entry.modified(p("/foo")); + entry.modified(p("/bar")); assertEquals("Incorrect number of paths", 2, entry.getNumChangedNodes()); assertTrue("Incorrect hasChanges", entry.hasChanges()); - entry.modified(Arrays.asList("/foo1", "/bar1")); + entry.modified(Arrays.asList(p("/foo1"), p("/bar1"))); assertEquals("Incorrect number of paths", 4, entry.getNumChangedNodes()); assertTrue("Incorrect hasChanges", entry.hasChanges()); - entry.modified("/foo/bar2"); + entry.modified(p("/foo/bar2")); assertEquals("Incorrect number of paths", 5, entry.getNumChangedNodes()); assertTrue("Incorrect hasChanges", entry.hasChanges()); - entry.modified("/foo3/bar3"); + entry.modified(p("/foo3/bar3")); assertEquals("Incorrect number of paths", 7, entry.getNumChangedNodes()); assertTrue("Incorrect hasChanges", entry.hasChanges()); - entry.modified(Arrays.asList("/foo/bar4", "/foo5/bar5")); + entry.modified(Arrays.asList(p("/foo/bar4"), p("/foo5/bar5"))); assertEquals("Incorrect number of paths", 10, entry.getNumChangedNodes()); assertTrue("Incorrect hasChanges", entry.hasChanges()); } @@ -405,24 +408,24 @@ public class JournalEntryTest { assertNull(entry.get(JournalEntry.BRANCH_COMMITS)); } - private static void addRandomPaths(java.util.Collection paths) throws IOException { - paths.add("/"); + private static void addRandomPaths(java.util.Collection paths) throws IOException { + paths.add(Path.ROOT); Random random = new Random(42); for (int i = 0; i < 1000; i++) { - String path = "/"; + Path path = Path.ROOT; int depth = random.nextInt(6); for (int j = 0; j < depth; j++) { char name = (char) ('a' + random.nextInt(26)); - path = PathUtils.concat(path, String.valueOf(name)); + path = new Path(path, String.valueOf(name)); paths.add(path); } } } - private static void add(StringSort sort, List paths) + private static void add(StringSort sort, List paths) throws IOException { - for (String p : paths) { - sort.add(p); + for (Path p : paths) { + sort.add(p.toString()); } } @@ -453,9 +456,10 @@ public class JournalEntryTest { RevisionVector to, String path, boolean cacheExpected) { - String nonLoaderDiff = cache.getChanges(from, to, path, null); + Path p = p(path); + String nonLoaderDiff = cache.getChanges(from, to, p, null); final AtomicBoolean loaderCalled = new AtomicBoolean(false); - cache.getChanges(from, to, path, new DiffCache.Loader() { + cache.getChanges(from, to, p, new DiffCache.Loader() { @Override public String call() { loaderCalled.set(true); @@ -484,4 +488,8 @@ public class JournalEntryTest { invalidate.close(); } return changes; } + + private static Path p(String path) { + return Path.fromString(path); + } } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCacheTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCacheTest.java index 11fed28c3e..885454e38c 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCacheTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCacheTest.java @@ -34,8 +34,6 @@ import org.apache.jackrabbit.oak.spi.state.NodeStore; import org.junit.After; import org.junit.Test; -import static com.google.common.collect.Maps.newHashMap; -import static com.google.common.collect.Sets.newHashSet; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -85,11 +83,11 @@ public class LocalDiffCacheTest { @Test public void diffFromAsString() { - Map changes = Maps.newHashMap(); - changes.put("/", "+\"foo\":{}^\"bar\":{}-\"baz\""); - changes.put("/foo", ""); - changes.put("/bar", "+\"qux\""); - changes.put("/bar/qux", ""); + Map changes = Maps.newHashMap(); + changes.put(Path.ROOT, "+\"foo\":{}^\"bar\":{}-\"baz\""); + changes.put(Path.fromString("/foo"), ""); + changes.put(Path.fromString("/bar"), "+\"qux\""); + changes.put(Path.fromString("/bar/qux"), ""); Diff diff = new Diff(changes, 0); assertEquals(changes, Diff.fromString(diff.asString()).getChanges()); @@ -97,7 +95,7 @@ public class LocalDiffCacheTest { @Test public void emptyDiff() throws Exception{ - Map changes = new HashMap(); + Map changes = new HashMap<>(); Diff diff = new Diff(changes, 100); String asString = diff.asString(); Diff diff2 = Diff.fromString(asString); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MeasureMemory.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MeasureMemory.java index b2e524c49f..82037eae9f 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MeasureMemory.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MeasureMemory.java @@ -29,6 +29,7 @@ import com.mongodb.BasicDBObject; import org.apache.jackrabbit.oak.api.Blob; import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.plugins.document.util.RevisionsKey; import org.apache.jackrabbit.oak.plugins.document.util.Utils; import org.apache.jackrabbit.oak.plugins.memory.BinaryPropertyState; @@ -43,7 +44,7 @@ import org.junit.Test; */ public class MeasureMemory { - static final boolean TRACE = false; + static final boolean TRACE = true; static final int TEST_COUNT = 10000; static final int OVERHEAD = 24; @@ -178,6 +179,19 @@ public class MeasureMemory { }); } + @Test + public void revisionsKey2() throws Exception { + measureMemory(new Callable() { + @Override + public Object[] call() { + RevisionsKey k = new RevisionsKey( + new RevisionVector(Revision.newRevision(0)), + new RevisionVector(Revision.newRevision(0))); + return new Object[]{k, k.getMemory() + OVERHEAD}; + } + }); + } + @Test public void revisionVector() throws Exception { measureMemory(new Callable() { @@ -215,6 +229,61 @@ public class MeasureMemory { }); } + @Test + public void memoryDiffCacheKey() throws Exception { + measureMemory(new Callable() { + private int counter = 0; + @Override + public Object[] call() { + Path p = Path.fromString(generatePath()); + RevisionVector rv1 = new RevisionVector( + Revision.newRevision(0), + Revision.newRevision(1)); + RevisionVector rv2 = new RevisionVector( + Revision.newRevision(0), + Revision.newRevision(1)); + MemoryDiffCache.Key k = new MemoryDiffCache.Key(p, rv1, rv2); + return new Object[]{k, k.getMemory() + OVERHEAD}; + } + + private String generatePath() { + String p = "/"; + for (int i = 0; i < 5; i++) { + p = PathUtils.concat(p, generateName()); + } + return p; + } + + private String generateName() { + return String.format("node-%05d", counter++); + } + }); + } + + @Test + public void path() throws Exception { + measureMemory(new Callable() { + private int counter = 0; + @Override + public Object[] call() { + Path p = Path.fromString(generatePath()); + return new Object[]{p, p.getMemory() + OVERHEAD}; + } + + private String generatePath() { + String p = "/"; + for (int i = 0; i < 5; i++) { + p = PathUtils.concat(p, generateName()); + } + return p; + } + + private String generateName() { + return String.format("node-%05d", counter++); + } + }); + } + private static void measureMemory(Callable c) throws Exception { LinkedList list = new LinkedList(); long base = getMemoryUsed(); @@ -252,7 +321,7 @@ public class MeasureMemory { String key = "property" + i; props.add(STORE.createPropertyState(key, "\"values " + i + "\"")); } - return new DocumentNodeState(STORE, new String("/hello/world"), + return new DocumentNodeState(STORE, Path.fromString("/hello/world"), new RevisionVector(new Revision(1, 2, 3)), props, false, new RevisionVector(new Revision(1, 2, 3))); } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCacheKeyTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCacheKeyTest.java new file mode 100644 index 0000000000..85635214be --- /dev/null +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCacheKeyTest.java @@ -0,0 +1,50 @@ +/* + * 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.document; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class MemoryDiffCacheKeyTest { + + @Test + public void asString() { + Path p = Path.fromString("/foo/bar"); + RevisionVector from = RevisionVector.fromString("r3-0-1"); + RevisionVector to = RevisionVector.fromString("r7-0-1"); + MemoryDiffCache.Key key = new MemoryDiffCache.Key(p, from, to); + assertEquals("r3-0-1/foo/bar@r7-0-1", key.asString()); + } + + @Test + public void fromString() { + Path p = Path.fromString("/foo/bar"); + RevisionVector from = RevisionVector.fromString("r3-0-1"); + RevisionVector to = RevisionVector.fromString("r7-0-1"); + MemoryDiffCache.Key expected = new MemoryDiffCache.Key(p, from, to); + + String s = "r3-0-1/foo/bar@r7-0-1"; + MemoryDiffCache.Key key = MemoryDiffCache.Key.fromString(s); + assertEquals(expected, key); + } + + @Test(expected = IllegalArgumentException.class) + public void fromStringIllegalArgumentException() { + MemoryDiffCache.Key.fromString("foo@r7-0-1"); + } +} diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCacheTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCacheTest.java index fb317bd4c9..fdffb47e2d 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCacheTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCacheTest.java @@ -37,11 +37,11 @@ public class MemoryDiffCacheTest { RevisionVector from = new RevisionVector(Revision.newRevision(1)); RevisionVector to = new RevisionVector(Revision.newRevision(1)); DiffCache.Entry entry = cache.newEntry(from, to, false); - entry.append("/", "^\"foo\":{}"); - entry.append("/foo", changes(MemoryDiffCache.CACHE_VALUE_LIMIT)); + entry.append(Path.ROOT, "^\"foo\":{}"); + entry.append(Path.fromString("/foo"), changes(MemoryDiffCache.CACHE_VALUE_LIMIT)); entry.done(); - assertNotNull(cache.getChanges(from, to, "/", null)); - assertNull(cache.getChanges(from, to, "/foo", null)); + assertNotNull(cache.getChanges(from, to, Path.ROOT, null)); + assertNull(cache.getChanges(from, to, Path.fromString("/foo"), null)); } private static String changes(int minLength) { diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MongoDocumentStoreTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MongoDocumentStoreTest.java index aeafe0e9e4..5d95a805dd 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MongoDocumentStoreTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MongoDocumentStoreTest.java @@ -229,13 +229,13 @@ public class MongoDocumentStoreTest { Revision rev = Revision.newRevision(0); List inserts = new ArrayList(); for (int i = 0; i < DocumentMK.MANY_CHILDREN_THRESHOLD * 2; i++) { - DocumentNodeState n = new DocumentNodeState(store, "/node-" + i, + DocumentNodeState n = new DocumentNodeState(store, Path.fromString("/node-" + i), new RevisionVector(rev)); inserts.add(n.asOperation(rev)); } docStore.create(Collection.NODES, inserts); List docs = docStore.query(Collection.NODES, - Utils.getKeyLowerLimit("/"), Utils.getKeyUpperLimit("/"), + Utils.getKeyLowerLimit(Path.ROOT), Utils.getKeyUpperLimit(Path.ROOT), DocumentMK.MANY_CHILDREN_THRESHOLD); assertEquals(DocumentMK.MANY_CHILDREN_THRESHOLD, docs.size()); store.dispose(); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweeperTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweeperTest.java index fadc9707d4..0601513bfe 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweeperTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweeperTest.java @@ -198,7 +198,7 @@ public class NodeDocumentSweeperTest { Iterable docs = seeker.getCandidates(startRev.getTimestamp()); return sweeper.sweep(docs, new NodeDocumentSweepListener() { @Override - public void sweepUpdate(Map updates) + public void sweepUpdate(Map updates) throws DocumentStoreException { ops.addAll(updates.values()); } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/PathComparatorTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/PathComparatorTest.java index 2b434f3e50..3eb2000152 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/PathComparatorTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/PathComparatorTest.java @@ -35,18 +35,22 @@ public class PathComparatorTest { @Test public void sort() { - List paths = new ArrayList(); - paths.add("/foo"); - paths.add("/foo/bar"); - paths.add("/bar/qux"); - paths.add("/"); - paths.add("/bar"); + List paths = new ArrayList<>(); + paths.add(p("/foo")); + paths.add(p("/foo/bar")); + paths.add(p("/bar/qux")); + paths.add(p("/")); + paths.add(p("/bar")); - Collections.sort(paths, PathComparator.INSTANCE); + paths.sort(PathComparator.INSTANCE); - List expected = Lists.newArrayList( - "/bar/qux", "/foo/bar", "/bar", "/foo", "/"); + List expected = Lists.newArrayList( + p("/bar/qux"), p("/foo/bar"), p("/bar"), p("/foo"), p("/")); assertEquals(expected, paths); } + + private static Path p(String path) { + return Path.fromString(path); + } } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/PathRevTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/PathRevTest.java index 45347169e6..b2b7c702a0 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/PathRevTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/PathRevTest.java @@ -25,11 +25,11 @@ import static org.junit.Assert.assertEquals; public class PathRevTest { @Test - public void roundTrip() { - String path = "/this/is/a/test/path"; + public void equals() { + Path path = Path.fromString("/this/is/a/test/path"); RevisionVector rv = new RevisionVector( Revision.newRevision(1), Revision.newRevision(2)); - PathRev key = new PathRev(path, rv); - assertEquals(key, PathRev.fromString(key.asString())); + PathRev expected = new PathRev(path, rv); + assertEquals(expected, new PathRev(path, rv)); } } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/PathTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/PathTest.java new file mode 100644 index 0000000000..afab17fb46 --- /dev/null +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/PathTest.java @@ -0,0 +1,195 @@ +/* + * 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.document; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.jackrabbit.oak.commons.PathUtils; +import org.junit.Test; + +import static org.apache.jackrabbit.oak.plugins.document.Path.NULL; +import static org.apache.jackrabbit.oak.plugins.document.Path.ROOT; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.emptyIterable; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +public class PathTest { + + private final Path root = ROOT; + private final Path foo = new Path(root, "foo"); + private final Path fooBar = new Path(foo, "bar"); + private final Path fooBarQuux = new Path(fooBar, "quux"); + + + @Test + public void pathToString() { + assertEquals("/", root.toString()); + assertEquals("/foo", foo.toString()); + assertEquals("/foo/bar", fooBar.toString()); + } + + @Test + public void toStringBuilder() { + StringBuilder sb = new StringBuilder(); + root.toStringBuilder(sb); + assertEquals(root.toString(), sb.toString()); + sb.setLength(0); + foo.toStringBuilder(sb); + assertEquals(foo.toString(), sb.toString()); + sb.setLength(0); + fooBar.toStringBuilder(sb); + assertEquals(fooBar.toString(), sb.toString()); + } + + @Test + public void fromString() { + assertEquals(NULL, Path.fromString(NULL.toString())); + assertEquals(root, Path.fromString(root.toString())); + assertEquals(foo, Path.fromString(foo.toString())); + assertEquals(fooBar, Path.fromString(fooBar.toString())); + } + + @Test(expected = IllegalArgumentException.class) + public void fromStringWithEmptyString() { + Path.fromString(""); + } + + @Test(expected = IllegalArgumentException.class) + public void fromStringWithRelativePath() { + Path.fromString("foo/bar"); + } + + @Test + public void fromCharSequence() { + assertEquals(NULL, Path.fromCharSequence(NULL.toString())); + assertEquals(root, Path.fromCharSequence(root.toString())); + assertEquals(foo, Path.fromCharSequence(foo.toString())); + assertEquals(fooBar, Path.fromCharSequence(fooBar.toString())); + } + + @Test + public void length() { + assertEquals(NULL.toString().length(), NULL.length()); + assertEquals(root.toString().length(), root.length()); + assertEquals(foo.toString().length(), foo.length()); + assertEquals(fooBar.toString().length(), fooBar.length()); + assertEquals(fooBarQuux.toString().length(), fooBarQuux.length()); + } + + @Test + public void isRoot() { + assertFalse(NULL.isRoot()); + assertTrue(root.isRoot()); + assertFalse(foo.isRoot()); + assertFalse(fooBar.isRoot()); + } + + @Test + public void getParent() { + assertNull(NULL.getParent()); + assertNull(root.getParent()); + assertEquals(foo.getParent(), root); + assertEquals(fooBar.getParent(), foo); + } + + @Test + public void getDepth() { + assertEquals(PathUtils.getDepth(root.toString()), root.getDepth()); + assertEquals(root.getDepth(), NULL.getDepth()); + assertEquals(PathUtils.getDepth(foo.toString()), foo.getDepth()); + assertEquals(PathUtils.getDepth(fooBar.toString()), fooBar.getDepth()); + } + + @Test + public void getAncestor() { + assertEquals(NULL, NULL.getAncestor(0)); + assertEquals(NULL, NULL.getAncestor(1)); + assertEquals(root, root.getAncestor(-1)); + assertEquals(root, root.getAncestor(0)); + assertEquals(root, root.getAncestor(1)); + assertEquals(foo, foo.getAncestor(0)); + assertEquals(root, foo.getAncestor(1)); + assertEquals(root, foo.getAncestor(2)); + assertEquals(fooBar, fooBar.getAncestor(0)); + assertEquals(foo, fooBar.getAncestor(1)); + assertEquals(root, fooBar.getAncestor(2)); + assertEquals(root, fooBar.getAncestor(3)); + } + + @Test + public void getName() { + assertEquals("", NULL.getName()); + assertEquals("", root.getName()); + assertEquals("foo", foo.getName()); + assertEquals("bar", fooBar.getName()); + } + + @Test + public void elements() { + assertThat(NULL.elements(), emptyIterable()); + assertThat(root.elements(), emptyIterable()); + assertThat(foo.elements(), contains("foo")); + assertThat(fooBar.elements(), contains("foo", "bar")); + } + + @Test + public void isAncestorOf() { + assertFalse(NULL.isAncestorOf(root)); + assertFalse(NULL.isAncestorOf(foo)); + assertFalse(NULL.isAncestorOf(NULL)); + assertFalse(root.isAncestorOf(NULL)); + assertTrue(root.isAncestorOf(foo)); + assertTrue(root.isAncestorOf(fooBar)); + assertTrue(foo.isAncestorOf(fooBar)); + assertFalse(root.isAncestorOf(root)); + assertFalse(foo.isAncestorOf(root)); + assertFalse(foo.isAncestorOf(foo)); + assertFalse(fooBar.isAncestorOf(fooBar)); + assertFalse(fooBar.isAncestorOf(foo)); + assertFalse(fooBar.isAncestorOf(root)); + } + + @Test(expected = IllegalArgumentException.class) + public void nullParent() { + new Path(NULL, "foo"); + } + + @Test + public void compareTo() { + Path baz = Path.fromString("/baz"); + Path fooBaz = Path.fromString("/foo/baz"); + List paths = new ArrayList<>(); + paths.add(NULL); + paths.add(root); + paths.add(baz); + paths.add(foo); + paths.add(fooBar); + paths.add(fooBaz); + paths.add(fooBarQuux); + for (int i = 0; i < 20; i++) { + Collections.shuffle(paths); + Collections.sort(paths); + assertThat(paths, contains(NULL, root, baz, foo, fooBar, fooBarQuux, fooBaz)); + } + } +} diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/SimpleTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/SimpleTest.java index 4c54d9c406..4cf1b0a575 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/SimpleTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/SimpleTest.java @@ -80,13 +80,13 @@ public class SimpleTest { DocumentStore s = mk.getDocumentStore(); DocumentNodeStore ns = mk.getNodeStore(); RevisionVector rev = RevisionVector.fromString(mk.getHeadRevision()); - DocumentNodeState n = new DocumentNodeState(ns, "/test", rev, + DocumentNodeState n = new DocumentNodeState(ns, Path.fromString("/test"), rev, Collections.singleton(ns.createPropertyState("name", "\"Hello\"")), false, null); UpdateOp op = n.asOperation(rev.getRevision(ns.getClusterId())); // mark as commit root NodeDocument.setRevision(op, rev.getRevision(ns.getClusterId()), "c"); assertTrue(s.create(Collection.NODES, Lists.newArrayList(op))); - DocumentNodeState n2 = ns.getNode("/test", rev); + DocumentNodeState n2 = ns.getNode(Path.fromString("/test"), rev); assertNotNull(n2); PropertyState p = n2.getProperty("name"); assertNotNull(p); @@ -249,13 +249,13 @@ public class SimpleTest { String r0 = mk.commit("/test", "+\"a\":{\"name\": \"World\"}", null, null); String r1 = mk.commit("/test", "+\"b\":{\"name\": \"!\"}", null, null); test = mk.getNodes("/test", r0, 0, 0, Integer.MAX_VALUE, null); - DocumentNodeState n = ns.getNode("/", RevisionVector.fromString(r0)); + DocumentNodeState n = ns.getNode(Path.ROOT, RevisionVector.fromString(r0)); assertNotNull(n); - Children c = ns.getChildren(n, null, Integer.MAX_VALUE); + Children c = ns.getChildren(n, "", Integer.MAX_VALUE); assertEquals("[test]", c.toString()); - n = ns.getNode("/test", RevisionVector.fromString(r1)); + n = ns.getNode(Path.fromString("/test"), RevisionVector.fromString(r1)); assertNotNull(n); - c = ns.getChildren(n, null, Integer.MAX_VALUE); + c = ns.getChildren(n, "", Integer.MAX_VALUE); assertEquals("[a, b]", c.toString()); rev = mk.commit("", "^\"/test\":1", null, null); @@ -275,19 +275,19 @@ public class SimpleTest { mk.commit("/testDel", "+\"b\":{\"name\": \"!\"}", null, null); String r1 = mk.commit("/testDel", "+\"c\":{\"name\": \"!\"}", null, null); - DocumentNodeState n = ns.getNode("/testDel", RevisionVector.fromString(r1)); + DocumentNodeState n = ns.getNode(Path.fromString("/testDel"), RevisionVector.fromString(r1)); assertNotNull(n); - Children c = ns.getChildren(n, null, Integer.MAX_VALUE); + Children c = ns.getChildren(n, "", Integer.MAX_VALUE); assertEquals(3, c.children.size()); String r2 = mk.commit("/testDel", "-\"c\"", null, null); - n = ns.getNode("/testDel", RevisionVector.fromString(r2)); + n = ns.getNode(Path.fromString("/testDel"), RevisionVector.fromString(r2)); assertNotNull(n); - c = ns.getChildren(n, null, Integer.MAX_VALUE); + c = ns.getChildren(n, "", Integer.MAX_VALUE); assertEquals(2, c.children.size()); String r3 = mk.commit("/", "-\"testDel\"", null, null); - n = ns.getNode("/testDel", RevisionVector.fromString(r3)); + n = ns.getNode(Path.fromString("/testDel"), RevisionVector.fromString(r3)); assertNull(n); } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/StringCachePerformance.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/StringCachePerformance.java new file mode 100644 index 0000000000..258e40a787 --- /dev/null +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/StringCachePerformance.java @@ -0,0 +1,34 @@ +/* + * 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.document; + +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +public class StringCachePerformance { + + @Test + public void get() { + long time = System.nanoTime(); + for (int i = 0; i < 1000000; i++) { + StringCache.get("some string"); + } + time = System.nanoTime() - time; + System.out.println("time: " + TimeUnit.NANOSECONDS.toMicros(time) + " us."); + } +} diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCDeletionTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCDeletionTest.java index c2cc5356de..f9792127f8 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCDeletionTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCDeletionTest.java @@ -428,11 +428,11 @@ public class VersionGCDeletionTest { * /x /x/y /x/y/z */ private static class NodeDocComparator implements Comparator { - private static Comparator reverse = Collections.reverseOrder(PathComparator.INSTANCE); + private static Comparator reverse = Collections.reverseOrder(PathComparator.INSTANCE); @Override public int compare(NodeDocument o1, NodeDocument o2) { - return reverse.compare(o1.getPath(), o2.getPath()); + return reverse.compare(Path.fromString(o1.getPath()), Path.fromString(o2.getPath())); } } } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java index c5a62d7572..6683fef2bd 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java @@ -523,7 +523,7 @@ public class VersionGarbageCollectorIT { } // invalidate cached DocumentNodeState DocumentNodeState state = (DocumentNodeState) store.getRoot().getChildNode(name); - store.invalidateNodeCache(state.getPath(), store.getRoot().getLastRevision()); + store.invalidateNodeCache(state.getPath().toString(), store.getRoot().getLastRevision()); while (!f.isDone()) { docs.poll(); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/bundlor/BundlingHandlerTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/bundlor/BundlingHandlerTest.java index 20d103c755..0138753dd9 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/bundlor/BundlingHandlerTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/bundlor/BundlingHandlerTest.java @@ -20,6 +20,7 @@ package org.apache.jackrabbit.oak.plugins.document.bundlor; import org.apache.jackrabbit.oak.commons.PathUtils; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; import org.junit.Test; @@ -42,17 +43,17 @@ public class BundlingHandlerTest { childBuilder(builder, "/x/y/z"); NodeState state = builder.getNodeState(); - assertEquals("/", handler.getRootBundlePath()); + assertEquals(Path.ROOT, handler.getRootBundlePath()); assertTrue(handler.isBundlingRoot()); assertEquals("foo", handler.getPropertyPath("foo")); BundlingHandler xh = childHandler(handler, state, "/x"); - assertEquals("/x", xh.getRootBundlePath()); + assertEquals(Path.fromString("/x"), xh.getRootBundlePath()); assertTrue(xh.isBundlingRoot()); assertEquals("foo", xh.getPropertyPath("foo")); BundlingHandler xz = childHandler(handler, state, "/x/y/z"); - assertEquals("/x/y/z", xz.getRootBundlePath()); + assertEquals(Path.fromString("/x/y/z"), xz.getRootBundlePath()); assertTrue(xz.isBundlingRoot()); assertEquals("foo", xz.getPropertyPath("foo")); } @@ -70,26 +71,26 @@ public class BundlingHandlerTest { BundlingHandler handler = new BundlingHandler(registry); BundlingHandler fileHandler = childHandler(handler, state, "/sunrise.jpg"); - assertEquals("/sunrise.jpg", fileHandler.getRootBundlePath()); + assertEquals(Path.fromString("/sunrise.jpg"), fileHandler.getRootBundlePath()); assertTrue(fileHandler.isBundlingRoot()); assertFalse(fileHandler.isBundledNode()); assertEquals("foo", fileHandler.getPropertyPath("foo")); BundlingHandler jcrContentHandler = childHandler(handler, state, "/sunrise.jpg/jcr:content"); - assertEquals("/sunrise.jpg", jcrContentHandler.getRootBundlePath()); + assertEquals(Path.fromString("/sunrise.jpg"), jcrContentHandler.getRootBundlePath()); assertFalse(jcrContentHandler.isBundlingRoot()); assertTrue(jcrContentHandler.isBundledNode()); assertEquals("jcr:content/foo", jcrContentHandler.getPropertyPath("foo")); BundlingHandler metadataHandler = childHandler(handler, state, "/sunrise.jpg/metadata"); - assertEquals("/sunrise.jpg/metadata", metadataHandler.getRootBundlePath()); + assertEquals(Path.fromString("/sunrise.jpg/metadata"), metadataHandler.getRootBundlePath()); assertTrue(metadataHandler.isBundlingRoot()); assertFalse(metadataHandler.isBundledNode()); assertEquals("foo", metadataHandler.getPropertyPath("foo")); // /sunrise.jpg/jcr:content/bar should have bundle root reset BundlingHandler barHandler = childHandler(handler, state, "/sunrise.jpg/jcr:content/bar"); - assertEquals("/sunrise.jpg/jcr:content/bar", barHandler.getRootBundlePath()); + assertEquals(Path.fromString("/sunrise.jpg/jcr:content/bar"), barHandler.getRootBundlePath()); assertTrue(barHandler.isBundlingRoot()); assertEquals("foo", barHandler.getPropertyPath("foo")); } @@ -104,7 +105,7 @@ public class BundlingHandlerTest { NodeState state = builder.getNodeState(); BundlingHandler fileHandler = handler.childAdded("sunrise.jpg", state.getChildNode("sunrise.jpg")); - assertEquals("/sunrise.jpg", fileHandler.getRootBundlePath()); + assertEquals(Path.fromString("/sunrise.jpg"), fileHandler.getRootBundlePath()); assertTrue(fileHandler.isBundlingRoot()); assertEquals("foo", fileHandler.getPropertyPath("foo")); assertEquals(1, fileHandler.getMetaProps().size()); @@ -118,7 +119,7 @@ public class BundlingHandlerTest { NodeState state = builder.getNodeState(); BundlingHandler fileHandler = handler.childAdded("sunrise.jpg", state.getChildNode("sunrise.jpg")); - assertEquals("/sunrise.jpg", fileHandler.getRootBundlePath()); + assertEquals(Path.fromString("/sunrise.jpg"), fileHandler.getRootBundlePath()); assertTrue(fileHandler.isBundlingRoot()); assertEquals("foo", fileHandler.getPropertyPath("foo")); assertEquals(0, fileHandler.getMetaProps().size()); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/bundlor/DocumentBundlingTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/bundlor/DocumentBundlingTest.java index 68834956a5..7afd813121 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/bundlor/DocumentBundlingTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/bundlor/DocumentBundlingTest.java @@ -19,6 +19,7 @@ package org.apache.jackrabbit.oak.plugins.document.bundlor; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -38,11 +39,12 @@ import org.apache.jackrabbit.oak.plugins.document.Document; import org.apache.jackrabbit.oak.plugins.document.DocumentMKBuilderProvider; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeState; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore; +import org.apache.jackrabbit.oak.plugins.document.NamePathRev; import org.apache.jackrabbit.oak.plugins.document.NodeDocument; -import org.apache.jackrabbit.oak.plugins.document.PathRev; import org.apache.jackrabbit.oak.plugins.document.RandomStream; import org.apache.jackrabbit.oak.plugins.document.TestNodeObserver; import org.apache.jackrabbit.oak.plugins.document.memory.MemoryDocumentStore; +import org.apache.jackrabbit.oak.plugins.document.persistentCache.CacheType; import org.apache.jackrabbit.oak.plugins.document.util.Utils; import org.apache.jackrabbit.oak.InitialContent; import org.apache.jackrabbit.oak.spi.commit.CommitInfo; @@ -57,6 +59,7 @@ import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; import org.apache.jackrabbit.oak.spi.state.NodeStateDiff; import org.apache.jackrabbit.oak.spi.state.NodeStateUtils; +import org.h2.mvstore.WriteBuffer; import org.jetbrains.annotations.NotNull; import org.junit.After; import org.junit.Before; @@ -186,7 +189,7 @@ public class DocumentBundlingTest { int nonBundledMem = nonBundledFile.getMemory() + nonBundledContent.getMemory(); int bundledMem = bundledFile.getMemory(); - assertEquals(1386, bundledMem); + assertEquals(1486, bundledMem); assertThat(bundledMem, is(greaterThan(nonBundledMem))); } @@ -274,7 +277,7 @@ public class DocumentBundlingTest { List bundledPaths = new ArrayList<>(); for (DocumentNodeState bs : appNode.getAllBundledNodesStates()) { - bundledPaths.add(bs.getPath()); + bundledPaths.add(bs.getPath().toString()); } assertThat(bundledPaths, containsInAnyOrder( "/test/book.jpg/jcr:content", @@ -613,7 +616,7 @@ public class DocumentBundlingTest { } @Test - public void jsonSerialization() throws Exception{ + public void bufferSerialization() throws Exception { NodeBuilder builder = store.getRoot().builder(); NodeBuilder appNB = newNode("app:Asset"); createChild(appNB, @@ -629,8 +632,11 @@ public class DocumentBundlingTest { merge(builder); DocumentNodeState appNode = (DocumentNodeState) getNode(store.getRoot(), "test/book.jpg"); - String json = appNode.asString(); - NodeState appNode2 = DocumentNodeState.fromString(store, json); + WriteBuffer wb = new WriteBuffer(1024); + CacheType.NODE.writeValue(wb, appNode); + ByteBuffer rb = wb.getBuffer(); + rb.rewind(); + NodeState appNode2 = CacheType.NODE.readValue(store, ds, rb); AssertingDiff.assertEquals(appNode, appNode2); } @@ -723,9 +729,9 @@ public class DocumentBundlingTest { merge(builder); - Set cachedPaths = store.getNodeChildrenCache().asMap().keySet(); - for (PathRev pr : cachedPaths){ - assertFalse(pr.getPath().contains("jcr:content/renditions")); + Set cachedPaths = store.getNodeChildrenCache().asMap().keySet(); + for (NamePathRev pr : cachedPaths){ + assertFalse(pr.getPath().toString().contains("jcr:content/renditions")); } } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTrackerTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTrackerTest.java index f684676785..499d67df5c 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTrackerTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/cache/CacheChangesTrackerTest.java @@ -25,6 +25,7 @@ import org.apache.jackrabbit.oak.cache.CacheValue; import org.apache.jackrabbit.oak.plugins.document.Collection; import org.apache.jackrabbit.oak.plugins.document.DocumentStore; import org.apache.jackrabbit.oak.plugins.document.NodeDocument; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.locks.NodeDocumentLocks; import org.apache.jackrabbit.oak.plugins.document.locks.StripedNodeDocumentLocks; import org.apache.jackrabbit.oak.plugins.document.memory.MemoryDocumentStore; @@ -82,7 +83,8 @@ public class CacheChangesTrackerTest { @Test public void testRegisterChildrenTracker() { NodeDocumentCache cache = createCache(); - CacheChangesTracker tracker = cache.registerTracker(getKeyLowerLimit("/parent"), getKeyUpperLimit("/parent")); + Path parent = Path.fromString("/parent"); + CacheChangesTracker tracker = cache.registerTracker(getKeyLowerLimit(parent), getKeyUpperLimit(parent)); assertFalse(tracker.mightBeenAffected("2:/parent/xyz")); assertFalse(tracker.mightBeenAffected("2:/parent/abc")); @@ -105,7 +107,8 @@ public class CacheChangesTrackerTest { @Test public void testGetLoaderAffectsTracker() throws ExecutionException { NodeDocumentCache cache = createCache(); - CacheChangesTracker tracker = cache.registerTracker(getKeyLowerLimit("/parent"), getKeyUpperLimit("/parent")); + Path parent = Path.fromString("/parent"); + CacheChangesTracker tracker = cache.registerTracker(getKeyLowerLimit(parent), getKeyUpperLimit(parent)); assertFalse(tracker.mightBeenAffected("2:/parent/xyz")); @@ -149,7 +152,8 @@ public class CacheChangesTrackerTest { @Test public void testOnlyExternalChanges() { NodeDocumentCache cache = createCache(); - CacheChangesTracker tracker = cache.registerTracker(getKeyLowerLimit("/parent"), getKeyUpperLimit("/parent")); + Path parent = Path.fromString("/parent"); + CacheChangesTracker tracker = cache.registerTracker(getKeyLowerLimit(parent), getKeyUpperLimit(parent)); cache.putNonConflictingDocs(tracker, ImmutableSet.of(createDoc("2:/parent/local"))); assertFalse(tracker.mightBeenAffected("2:/parent/local")); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDBExceptionTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDBExceptionTest.java index 5a9877732e..73a1d9d49d 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDBExceptionTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDBExceptionTest.java @@ -21,6 +21,7 @@ import org.apache.jackrabbit.oak.plugins.document.DocumentMK; import org.apache.jackrabbit.oak.plugins.document.DocumentStoreException; import org.apache.jackrabbit.oak.plugins.document.MongoUtils; import org.apache.jackrabbit.oak.plugins.document.NodeDocument; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.Revision; import org.apache.jackrabbit.oak.plugins.document.UpdateOp; import org.apache.jackrabbit.oak.plugins.document.util.Utils; @@ -114,8 +115,9 @@ public class MongoDBExceptionTest { e.getMessage().contains(id)); } - String fromKey = Utils.getKeyLowerLimit("/foo"); - String toKey = Utils.getKeyUpperLimit("/foo"); + Path foo = Path.fromString("/foo"); + String fromKey = Utils.getKeyLowerLimit(foo); + String toKey = Utils.getKeyUpperLimit(foo); exceptionMsg = "query failed"; setExceptionMsg(); try { diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/QueryHintTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/QueryHintTest.java index b5b20af12d..33f0f94d31 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/QueryHintTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/QueryHintTest.java @@ -32,6 +32,7 @@ import org.apache.jackrabbit.oak.plugins.document.AbstractMongoConnectionTest; import org.apache.jackrabbit.oak.plugins.document.Collection; import org.apache.jackrabbit.oak.plugins.document.DocumentMK; import org.apache.jackrabbit.oak.plugins.document.NodeDocument; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.util.Utils; import org.apache.jackrabbit.oak.stats.Clock; import org.junit.After; @@ -75,8 +76,8 @@ public class QueryHintTest extends AbstractMongoConnectionTest { assertTrue(mongoDS.canUseModifiedTimeIdx(10)); mongoDS.query(Collection.NODES, - Utils.getKeyLowerLimit("/"), - Utils.getKeyUpperLimit("/"), + Utils.getKeyLowerLimit(Path.ROOT), + Utils.getKeyUpperLimit(Path.ROOT), NodeDocument.MODIFIED_IN_SECS, 50, 10); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/RetryReadIT.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/RetryReadIT.java index 3d6ec676fd..c70376b1d9 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/RetryReadIT.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/RetryReadIT.java @@ -25,6 +25,7 @@ import org.apache.jackrabbit.oak.plugins.document.DocumentMK; import org.apache.jackrabbit.oak.plugins.document.DocumentStoreException; import org.apache.jackrabbit.oak.plugins.document.MongoUtils; import org.apache.jackrabbit.oak.plugins.document.NodeDocument; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.util.Utils; import org.jetbrains.annotations.NotNull; import org.junit.Test; @@ -78,15 +79,17 @@ public class RetryReadIT extends AbstractMongoConnectionTest { @Test public void retryQuery() { - String fromKey = Utils.getKeyLowerLimit("/foo"); - String toKey = Utils.getKeyUpperLimit("/foo"); + Path foo = Path.fromString("/foo"); + String fromKey = Utils.getKeyLowerLimit(foo); + String toKey = Utils.getKeyUpperLimit(foo); // must survive two consecutive failures. -> 2 retries store.failRead = 2; List docs = store.query(NODES, fromKey, toKey, 100); assertThat(docs, is(empty())); - fromKey = Utils.getKeyLowerLimit("/bar"); - toKey = Utils.getKeyUpperLimit("/bar"); + Path bar = Path.fromString("/bar"); + fromKey = Utils.getKeyLowerLimit(bar); + toKey = Utils.getKeyUpperLimit(bar); // must fail with three consecutive failures store.failRead = 3; try { diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/AsyncQueueTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/AsyncQueueTest.java index 994eca0e4d..0445b3b0dc 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/AsyncQueueTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/AsyncQueueTest.java @@ -22,6 +22,7 @@ import com.google.common.cache.RemovalCause; import org.apache.commons.io.FileUtils; import org.apache.jackrabbit.oak.cache.CacheLIRS; import org.apache.jackrabbit.oak.plugins.document.DocumentMKBuilderProvider; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.PathRev; import org.apache.jackrabbit.oak.plugins.document.Revision; import org.apache.jackrabbit.oak.plugins.document.RevisionVector; @@ -124,7 +125,7 @@ public class AsyncQueueTest { } private PathRev generatePathRev() { - return new PathRev("/" + id++, new RevisionVector(new Revision(0, 0, 0))); + return new PathRev(Path.fromString("/" + id++), new RevisionVector(new Revision(0, 0, 0))); } private void flush() { diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/BroadcastTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/BroadcastTest.java index 7e162df7ef..48df5e991c 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/BroadcastTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/BroadcastTest.java @@ -30,8 +30,8 @@ import java.util.concurrent.Callable; import org.apache.commons.io.FileUtils; import org.apache.jackrabbit.oak.cache.CacheLIRS; -import org.apache.jackrabbit.oak.plugins.document.PathRev; -import org.apache.jackrabbit.oak.plugins.document.Revision; +import org.apache.jackrabbit.oak.plugins.document.MemoryDiffCache.Key; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.RevisionVector; import org.apache.jackrabbit.oak.plugins.document.persistentCache.broadcast.Broadcaster; import org.apache.jackrabbit.oak.plugins.document.persistentCache.broadcast.TCPBroadcaster; @@ -63,9 +63,11 @@ public class BroadcastTest { ArrayList nodeList = new ArrayList(); for (int nodes = 1; nodes < 20; nodes++) { PersistentCache pc = new PersistentCache("target/broadcastTest/p" + nodes + ",broadcast=" + type); - Cache cache = openCache(pc); - String key = "/test" + Math.random(); - PathRev k = new PathRev(key, new RevisionVector(new Revision(0, 0, 0))); + Cache cache = openCache(pc); + Path key = Path.fromString("/test" + Math.random()); + RevisionVector from = RevisionVector.fromString("r1-0-1"); + RevisionVector to = RevisionVector.fromString("r2-0-1"); + Key k = new Key(key, from, to); long time = System.currentTimeMillis(); for (int i = 0; i < 2000; i++) { cache.put(k, new StringValue("Hello World " + i)); @@ -204,10 +206,12 @@ public class BroadcastTest { new File("target/broadcastTest").mkdirs(); PersistentCache p1 = new PersistentCache("target/broadcastTest/p1,broadcast=" + type); PersistentCache p2 = new PersistentCache("target/broadcastTest/p2,broadcast=" + type); - Cache c1 = openCache(p1); - Cache c2 = openCache(p2); - String key = "/test" + Math.random(); - PathRev k = new PathRev(key, new RevisionVector(new Revision(0, 0, 0))); + Cache c1 = openCache(p1); + Cache c2 = openCache(p2); + Path key = Path.fromString("/test" + Math.random()); + RevisionVector from = RevisionVector.fromString("r1-0-1"); + RevisionVector to = RevisionVector.fromString("r2-0-1"); + Key k = new Key(key, from, to); int correct = 0; for (int i = 0; i < 50; i++) { c1.put(k, new StringValue("Hello World " + i)); @@ -280,8 +284,8 @@ public class BroadcastTest { }, timeoutInMilliseconds); } - private static Cache openCache(PersistentCache p) { - CacheLIRS cache = new CacheLIRS.Builder(). + private static Cache openCache(PersistentCache p) { + CacheLIRS cache = new CacheLIRS.Builder(). maximumSize(1).build(); return p.wrap(null, null, cache, CacheType.DIFF); } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheTest.java index 6983a21745..8f1d3f4c64 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheTest.java @@ -30,6 +30,7 @@ import com.google.common.cache.Cache; import org.apache.commons.io.FileUtils; import org.apache.jackrabbit.oak.cache.CacheLIRS; import org.apache.jackrabbit.oak.commons.junit.LogCustomizer; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.PathRev; import org.apache.jackrabbit.oak.plugins.document.Revision; import org.apache.jackrabbit.oak.plugins.document.RevisionVector; @@ -63,7 +64,7 @@ public class CacheTest { Thread.yield(); } for (int i = 0; i < 100; i++) { - PathRev k = new PathRev("/" + counter, new RevisionVector(new Revision(0, 0, i))); + PathRev k = new PathRev(Path.fromString("/" + counter), new RevisionVector(new Revision(0, 0, i))); map.getIfPresent(k); map.put(k, new StringValue(largeString)); } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/DataTypeUtilTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/DataTypeUtilTest.java new file mode 100644 index 0000000000..cbefb0c61f --- /dev/null +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/DataTypeUtilTest.java @@ -0,0 +1,148 @@ +/* + * 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.document.persistentCache; + +import java.nio.ByteBuffer; +import java.util.Collections; + +import org.apache.jackrabbit.oak.plugins.document.DocumentMKBuilderProvider; +import org.apache.jackrabbit.oak.plugins.document.DocumentNodeState; +import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore; +import org.apache.jackrabbit.oak.plugins.document.Path; +import org.apache.jackrabbit.oak.plugins.document.NamePathRev; +import org.apache.jackrabbit.oak.plugins.document.PathRev; +import org.apache.jackrabbit.oak.plugins.document.RevisionVector; +import org.h2.mvstore.WriteBuffer; +import org.junit.Rule; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class DataTypeUtilTest { + + @Rule + public DocumentMKBuilderProvider builderProvider = new DocumentMKBuilderProvider(); + + private WriteBuffer wb = new WriteBuffer(1024); + + @Test + public void booleanToBufferTrue() { + DataTypeUtil.booleanToBuffer(true, wb); + ByteBuffer rb = readBufferFrom(wb); + assertTrue(DataTypeUtil.booleanFromBuffer(rb)); + } + + @Test + public void booleanToBufferFalse() { + DataTypeUtil.booleanToBuffer(false, wb); + ByteBuffer rb = readBufferFrom(wb); + assertFalse(DataTypeUtil.booleanFromBuffer(rb)); + } + + @Test + public void revisionVectorToBufferEmpty() { + RevisionVector empty = new RevisionVector(); + DataTypeUtil.revisionVectorToBuffer(empty, wb); + ByteBuffer rb = readBufferFrom(wb); + assertEquals(empty, DataTypeUtil.revisionVectorFromBuffer(rb)); + } + + @Test + public void revisionVectorToBuffer() { + RevisionVector revisions = RevisionVector.fromString("r9-0-1,br7-0-2"); + DataTypeUtil.revisionVectorToBuffer(revisions, wb); + ByteBuffer rb = readBufferFrom(wb); + assertEquals(revisions, DataTypeUtil.revisionVectorFromBuffer(rb)); + } + + @Test + public void pathToBufferRoot() { + Path p = Path.ROOT; + DataTypeUtil.pathToBuffer(p, wb); + ByteBuffer rb = readBufferFrom(wb); + assertEquals(p, DataTypeUtil.pathFromBuffer(rb)); + } + + @Test + public void pathToBufferNull() { + Path p = Path.NULL; + DataTypeUtil.pathToBuffer(p, wb); + ByteBuffer rb = readBufferFrom(wb); + assertEquals(p, DataTypeUtil.pathFromBuffer(rb)); + } + + @Test + public void pathToBuffer() { + Path p = Path.fromString("/foo/bar/quux"); + DataTypeUtil.pathToBuffer(p, wb); + ByteBuffer rb = readBufferFrom(wb); + assertEquals(p, DataTypeUtil.pathFromBuffer(rb)); + } + + @Test + public void pathRevToBuffer() { + Path p = Path.fromString("/foo/bar/quux"); + RevisionVector rv = RevisionVector.fromString("r3-4-1,br4-9-2"); + PathRev expected = new PathRev(p, rv); + DataTypeUtil.pathRevToBuffer(expected, wb); + ByteBuffer rb = readBufferFrom(wb); + assertEquals(expected, DataTypeUtil.pathRevFromBuffer(rb)); + } + + @Test + public void pathNameRevToBuffer() { + Path p = Path.fromString("/foo/bar/quux"); + String name = "baz"; + RevisionVector rv = RevisionVector.fromString("r3-4-1,br4-9-2"); + NamePathRev expected = new NamePathRev(name, p, rv); + DataTypeUtil.namePathRevToBuffer(expected, wb); + ByteBuffer rb = readBufferFrom(wb); + assertEquals(expected, DataTypeUtil.namePathRevFromBuffer(rb)); + } + + @Test + public void stateToBufferLastRevNull() { + DocumentNodeStore ns = builderProvider.newBuilder().build(); + Path p = Path.fromString("/foo/bar"); + RevisionVector rootRev = ns.getHeadRevision(); + DocumentNodeState expected = new DocumentNodeState(ns, p, rootRev, + Collections.emptyMap(), true, 0, null, false); + DataTypeUtil.stateToBuffer(expected, wb); + ByteBuffer rb = readBufferFrom(wb); + assertEquals(expected, DataTypeUtil.stateFromBuffer(ns, rb)); + } + + @Test + public void stateToBuffer() { + DocumentNodeStore ns = builderProvider.newBuilder().build(); + Path p = Path.fromString("/foo/bar"); + RevisionVector rootRev = ns.getHeadRevision(); + DocumentNodeState expected = new DocumentNodeState(ns, p, rootRev, + Collections.emptyMap(), true, 0, rootRev, false); + DataTypeUtil.stateToBuffer(expected, wb); + ByteBuffer rb = readBufferFrom(wb); + assertEquals(expected, DataTypeUtil.stateFromBuffer(ns, rb)); + } + + private static ByteBuffer readBufferFrom(WriteBuffer wb) { + ByteBuffer rb = wb.getBuffer(); + rb.rewind(); + return rb; + } +} diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCacheTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCacheTest.java index 8ddba41098..3c3690640c 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCacheTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCacheTest.java @@ -25,7 +25,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.function.Consumer; -import com.google.common.base.Predicate; import com.google.common.cache.RemovalCause; import com.google.common.collect.Lists; @@ -39,12 +38,13 @@ import org.apache.jackrabbit.oak.plugins.document.DocumentNodeState; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStateCache; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore; import org.apache.jackrabbit.oak.plugins.document.DocumentStore; +import org.apache.jackrabbit.oak.plugins.document.NamePathRev; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.PathRev; import org.apache.jackrabbit.oak.plugins.document.RevisionVector; import org.apache.jackrabbit.oak.plugins.document.memory.MemoryDocumentStore; import org.apache.jackrabbit.oak.spi.commit.CommitInfo; import org.apache.jackrabbit.oak.spi.commit.EmptyHook; -import org.apache.jackrabbit.oak.spi.filter.PathFilter; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; import org.apache.jackrabbit.oak.stats.Counting; @@ -57,9 +57,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -74,7 +71,7 @@ public class NodeCacheTest { private DocumentStore store; private DocumentNodeStore ns; private NodeCache nodeCache; - private NodeCache nodeChildren; + private NodeCache nodeChildren; private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); private StatisticsProvider statsProvider = new DefaultStatisticsProvider(executor); @@ -93,8 +90,8 @@ public class NodeCacheTest { builder.child("c").child("d"); AbstractDocumentNodeState root = (AbstractDocumentNodeState) ns.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); - PathRev prc = new PathRev("/c", root.getRootRevision()); - PathRev pra = new PathRev("/a", root.getRootRevision()); + PathRev prc = new PathRev(Path.fromString("/c"), root.getRootRevision()); + PathRev pra = new PathRev(Path.fromString("/a"), root.getRootRevision()); Counting counter = nodeCache.getPersistentCacheStats().getPutRejectedAsCachedInSecCounter(); long count0 = counter.getCount(); @@ -121,7 +118,7 @@ public class NodeCacheTest { assertContains(nodeCache, "/a/b"); assertContains(nodeCache, "/a"); - assertContains(nodeChildren, "/a"); + assertPathNameRevs(nodeChildren, "/a", true); ns.setNodeStateCache(new PathExcludingCache("/c")); @@ -131,14 +128,15 @@ public class NodeCacheTest { ns.getRoot().getChildNode("c").getChildNode("d"); assertNotContains(nodeCache, "/c/d"); assertNotContains(nodeCache, "/c"); - assertNotContains(nodeChildren, "/c"); + assertPathNameRevs(nodeChildren, "/c", false); } @Test public void cachePredicateSync() throws Exception{ - PathFilter pf = new PathFilter(asList("/a"), emptyList()); - Predicate p = path -> pf.filter(path) == PathFilter.Result.INCLUDE; - initializeNodeStore(false, b -> b.setNodeCachePredicate(p)); + Path a = Path.fromString("/a"); + initializeNodeStore(false, b -> b.setNodeCachePathPredicate( + path -> path != null && (a.equals(path) || a.isAncestorOf(path)) + )); NodeBuilder builder = ns.getRoot().builder(); builder.child("a").child("c1"); @@ -158,9 +156,10 @@ public class NodeCacheTest { // OAK-7153 @Test public void persistentCacheAccessForIncludedPathOnly() throws Exception { - PathFilter pf = new PathFilter(singletonList("/a"), emptyList()); - Predicate p = path -> pf.filter(path) == PathFilter.Result.INCLUDE; - initializeNodeStore(false, b -> b.setNodeCachePredicate(p)); + Path a = Path.fromString("/a"); + initializeNodeStore(false, b -> b.setNodeCachePathPredicate( + path -> path != null && (a.equals(path) || a.isAncestorOf(path)) + )); NodeBuilder builder = ns.getRoot().builder(); builder.child("x"); @@ -234,7 +233,7 @@ public class NodeCacheTest { ns = builder.getNodeStore(); nodeCache = (NodeCache) ns.getNodeCache(); - nodeChildren = (NodeCache) ns.getNodeChildrenCache(); + nodeChildren = (NodeCache) ns.getNodeChildrenCache(); } @@ -264,10 +263,38 @@ public class NodeCacheTest { } } + private static void assertPathNameRevs(NodeCache cache, String path, boolean contains) { + List revs = getPathNameRevs(cache, path); + List matchingRevs = Lists.newArrayList(); + for (NamePathRev pr : revs) { + if (cache.getGenerationalMap().containsKey(pr)) { + matchingRevs.add(pr); + } + } + + if (contains && matchingRevs.isEmpty()) { + fail(String.format("Expecting entry for [%s]. Did not found in %s", path, matchingRevs)); + } + + if (!contains && !matchingRevs.isEmpty()) { + fail(String.format("Expecting entry for [%s]. Found %s", path, revs)); + } + } + private static List getPathRevs(NodeCache cache, String path) { List revs = Lists.newArrayList(); for (PathRev pr : cache.asMap().keySet()) { - if (pr.getPath().equals(path)) { + if (pr.getPath().toString().equals(path)) { + revs.add(pr); + } + } + return revs; + } + + private static List getPathNameRevs(NodeCache cache, String path) { + List revs = Lists.newArrayList(); + for (NamePathRev pr : cache.asMap().keySet()) { + if (pr.getPath().toString().equals(path)) { revs.add(pr); } } @@ -282,14 +309,14 @@ public class NodeCacheTest { } @Override - public AbstractDocumentNodeState getDocumentNodeState(String path, RevisionVector rootRevision, + public AbstractDocumentNodeState getDocumentNodeState(Path path, RevisionVector rootRevision, RevisionVector lastRev) { return null; } @Override - public boolean isCached(String path) { - if (path.startsWith(excludeRoot)) { + public boolean isCached(Path path) { + if (path.toString().startsWith(excludeRoot)) { return true; } return false; diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreTest.java index d34d61b4ed..40a998d79a 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreTest.java @@ -38,6 +38,7 @@ import org.apache.jackrabbit.oak.plugins.document.Collection; import org.apache.jackrabbit.oak.plugins.document.DocumentStoreFixture; import org.apache.jackrabbit.oak.plugins.document.MissingLastRevSeeker; import org.apache.jackrabbit.oak.plugins.document.NodeDocument; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.Revision; import org.apache.jackrabbit.oak.plugins.document.UpdateOp; import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentStore.QueryCondition; @@ -183,7 +184,8 @@ public class RDBDocumentStoreTest extends AbstractDocumentStoreTest { super.ds.create(Collection.NODES, Collections.singletonList(up2)); // query - List results = super.ds.query(Collection.NODES, Utils.getKeyLowerLimit("/testRDBJDBCPerfLog"), Utils.getKeyUpperLimit("/testRDBJDBCPerfLog"), 10); + Path p = Path.fromString("/testRDBJDBCPerfLog"); + List results = super.ds.query(Collection.NODES, Utils.getKeyLowerLimit(p), Utils.getKeyUpperLimit(p), 10); assertEquals(1, results.size()); assertEquals(2, logCustomizerQuery.getLogs().size()); } finally { diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java index 7c64a7dc55..0822f93afd 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java @@ -31,6 +31,7 @@ import org.apache.jackrabbit.oak.plugins.document.Collection; import org.apache.jackrabbit.oak.plugins.document.DocumentMKBuilderProvider; import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore; import org.apache.jackrabbit.oak.plugins.document.NodeDocument; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.Revision; import org.apache.jackrabbit.oak.plugins.document.RevisionVector; import org.apache.jackrabbit.oak.plugins.document.bundlor.BundledTypesRegistry; @@ -85,8 +86,8 @@ public class SecondaryStoreCacheTest { RevisionVector rv1 = new RevisionVector(new Revision(1,0,1)); RevisionVector rv2 = new RevisionVector(new Revision(1,0,3)); - assertNull(cache.getDocumentNodeState("/a/b", rv1, rv2)); - assertNull(cache.getDocumentNodeState("/x", rv1, rv2)); + assertNull(cache.getDocumentNodeState(Path.fromString("/a/b"), rv1, rv2)); + assertNull(cache.getDocumentNodeState(Path.fromString("/x"), rv1, rv2)); } @Test @@ -105,7 +106,7 @@ public class SecondaryStoreCacheTest { //Lookup should work fine AbstractDocumentNodeState a_r2 = documentState(r2, "/a/c"); AbstractDocumentNodeState result - = cache.getDocumentNodeState("/a/c", r2.getRootRevision(), a_r2.getLastRevision()); + = cache.getDocumentNodeState(Path.fromString("/a/c"), r2.getRootRevision(), a_r2.getLastRevision()); assertTrue(EqualsDiff.equals(a_r2, result)); //Child docs should only have lastRev and not root rev @@ -121,7 +122,7 @@ public class SecondaryStoreCacheTest { AbstractDocumentNodeState r3 = merge(nb); //Now look from older revision - result = cache.getDocumentNodeState("/a/c", r3.getRootRevision(), a_r2.getLastRevision()); + result = cache.getDocumentNodeState(Path.fromString("/a/c"), r3.getRootRevision(), a_r2.getLastRevision()); //now as its not visible from head it would not be visible assertNull(result); @@ -143,11 +144,11 @@ public class SecondaryStoreCacheTest { AbstractDocumentNodeState a_c_1 = documentState(primary.getRoot(), "/a/c"); AbstractDocumentNodeState result - = cache.getDocumentNodeState("/a/c", r1.getRootRevision(), a_c_1.getLastRevision()); + = cache.getDocumentNodeState(Path.fromString("/a/c"), r1.getRootRevision(), a_c_1.getLastRevision()); assertTrue(EqualsDiff.equals(a_c_1, result)); //Read from older revision - result = cache.getDocumentNodeState("/a/c", r0.getRootRevision(), a_c_0.getLastRevision()); + result = cache.getDocumentNodeState(Path.fromString("/a/c"), r0.getRootRevision(), a_c_0.getLastRevision()); assertTrue(EqualsDiff.equals(a_c_0, result)); } @@ -195,7 +196,7 @@ public class SecondaryStoreCacheTest { observer.contentChanged(r0, CommitInfo.EMPTY); - AbstractDocumentNodeState result = cache.getDocumentNodeState("/a/c", r0.getRootRevision(), a_c_0 + AbstractDocumentNodeState result = cache.getDocumentNodeState(Path.fromString("/a/c"), r0.getRootRevision(), a_c_0 .getLastRevision()); assertTrue(EqualsDiff.equals(a_c_0, result)); @@ -206,13 +207,13 @@ public class SecondaryStoreCacheTest { //Change is yet not pushed to secondary i.e. observer not invoked //but lookup with latest root should still work fine if lastRev matches - result = cache.getDocumentNodeState("/a/c", r1.getRootRevision(), a_c_0 + result = cache.getDocumentNodeState(Path.fromString("/a/c"), r1.getRootRevision(), a_c_0 .getLastRevision()); assertTrue(EqualsDiff.equals(a_c_0, result)); //Change which is not pushed would though not be visible AbstractDocumentNodeState a_e_1 = documentState(primary.getRoot(), "/a/e"); - result = cache.getDocumentNodeState("/a/e", r1.getRootRevision(), a_e_1 + result = cache.getDocumentNodeState(Path.fromString("/a/e"), r1.getRootRevision(), a_e_1 .getLastRevision()); assertNull(result); } @@ -221,9 +222,9 @@ public class SecondaryStoreCacheTest { public void isCached() throws Exception{ SecondaryStoreCache cache = createCache(new PathFilter(of("/a"), empty)); - assertTrue(cache.isCached("/a")); - assertTrue(cache.isCached("/a/b")); - assertFalse(cache.isCached("/x")); + assertTrue(cache.isCached(Path.fromString("/a"))); + assertTrue(cache.isCached(Path.fromString("/a/b"))); + assertFalse(cache.isCached(Path.fromString("/x"))); } @Test diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/util/UtilsTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/util/UtilsTest.java index 426618e0b5..d1417cf185 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/util/UtilsTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/util/UtilsTest.java @@ -35,6 +35,7 @@ import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore; import org.apache.jackrabbit.oak.plugins.document.DocumentStore; import org.apache.jackrabbit.oak.plugins.document.DocumentStoreException; import org.apache.jackrabbit.oak.plugins.document.NodeDocument; +import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.Revision; import org.apache.jackrabbit.oak.plugins.document.RevisionVector; import org.apache.jackrabbit.oak.plugins.document.UpdateOp; @@ -102,19 +103,19 @@ public class UtilsTest { @Test public void getParentIdFromLowerLimit() throws Exception{ - assertEquals("1:/foo",Utils.getParentIdFromLowerLimit(Utils.getKeyLowerLimit("/foo"))); + assertEquals("1:/foo",Utils.getParentIdFromLowerLimit(Utils.getKeyLowerLimit(Path.fromString("/foo")))); assertEquals("1:/foo",Utils.getParentIdFromLowerLimit("2:/foo/bar")); } @Test public void getParentId() throws Exception{ - String longPath = PathUtils.concat("/"+Strings.repeat("p", Utils.PATH_LONG + 1), "foo"); + Path longPath = Path.fromString(PathUtils.concat("/"+Strings.repeat("p", Utils.PATH_LONG + 1), "foo")); assertTrue(Utils.isLongPath(longPath)); assertNull(Utils.getParentId(Utils.getIdFromPath(longPath))); - assertNull(Utils.getParentId(Utils.getIdFromPath("/"))); - assertEquals("1:/foo",Utils.getParentId("2:/foo/bar")); + assertNull(Utils.getParentId(Utils.getIdFromPath(Path.ROOT))); + assertEquals("1:/foo", Utils.getParentId("2:/foo/bar")); } @Test @@ -312,6 +313,11 @@ public class UtilsTest { assertFalse(Utils.isIdFromLongPath(":")); } + @Test(expected = IllegalArgumentException.class) + public void getIdFromNullPath() { + Utils.getIdFromPath(Path.NULL); + } + @Test public void encodeHexString() { Random r = new Random(42); diff --git a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/MetadataExposingNodeState.java b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/MetadataExposingNodeState.java index 2a53104c45..f493667f11 100644 --- a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/MetadataExposingNodeState.java +++ b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/MetadataExposingNodeState.java @@ -37,7 +37,7 @@ public class MetadataExposingNodeState extends AbstractDecoratedNodeState { super(documentNodeState, true); metadataProperties = new ArrayList<>(2); - if (PathUtils.denotesRoot(documentNodeState.getPath())) { + if (documentNodeState.getPath().isRoot()) { metadataProperties.add(createProperty(PROP_REVISION, documentNodeState.getRootRevision().asString())); } metadataProperties.add(createProperty(PROP_LAST_REV, documentNodeState.getLastRevision().asString()));