hi marcel
could it be that this commit is related to the following test failure?
i experience it in a up to date checkout of the trunk.
regards
angela
concurrent[1](org.apache.jackrabbit.oak.jcr.ConcurrentFileOperationsTest)
Time elapsed: 0.473 sec <<< ERROR!
javax.jcr.RepositoryException: OakKernel0001: Failed to merge changes to
the underlying MicroKernel
at
org.apache.jackrabbit.oak.api.CommitFailedException.asRepositoryException(C
ommitFailedException.java:242)
at
org.apache.jackrabbit.oak.api.CommitFailedException.asRepositoryException(C
ommitFailedException.java:207)
at
org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.newRepositoryExcepti
on(SessionDelegate.java:434)
at
org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.save(SessionDelegate
.java:297)
at
org.apache.jackrabbit.oak.jcr.session.SessionImpl$8.perform(SessionImpl.jav
a:399)
at
org.apache.jackrabbit.oak.jcr.session.SessionImpl$8.perform(SessionImpl.jav
a:396)
at
org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.perform(SessionDeleg
ate.java:124)
at
org.apache.jackrabbit.oak.jcr.session.SessionImpl.perform(SessionImpl.java:
117)
at
org.apache.jackrabbit.oak.jcr.session.SessionImpl.save(SessionImpl.java:396
)
at
org.apache.jackrabbit.oak.jcr.ConcurrentFileOperationsTest$1.run(Concurrent
FileOperationsTest.java:103)
at java.lang.Thread.run(Thread.java:680)
Caused by: org.apache.jackrabbit.oak.api.CommitFailedException:
OakKernel0001: Failed to merge changes to the underlying MicroKernel
at
org.apache.jackrabbit.oak.kernel.KernelNodeStoreBranch$Persisted.merge(Kern
elNodeStoreBranch.java:384)
at
org.apache.jackrabbit.oak.kernel.KernelNodeStoreBranch.merge(KernelNodeStor
eBranch.java:136)
at
org.apache.jackrabbit.oak.core.AbstractRoot$2.run(AbstractRoot.java:247)
at
org.apache.jackrabbit.oak.core.AbstractRoot$2.run(AbstractRoot.java:243)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:337)
at
org.apache.jackrabbit.oak.core.AbstractRoot.commit(AbstractRoot.java:242)
at
org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.save(SessionDelegate
.java:295)
... 7 more
Caused by: org.apache.jackrabbit.mk.api.MicroKernelException: Conflicting
concurrent change. Update operation failed: key: 0:/ update
{_collisions.r1411315f24a-0-1=CONTAINS_MAP_ENTRY false,
_collisions.r1411315f251-0-1=CONTAINS_MAP_ENTRY false,
_collisions.r1411315f3f6-0-1=CONTAINS_MAP_ENTRY false,
_collisions.r1411315f402-0-1=CONTAINS_MAP_ENTRY false, _modified=SET
275800941, _revisions.r1411315f24a-0-1=SET_MAP_ENTRY c-r1411315f404-0-1,
_revisions.r1411315f251-0-1=SET_MAP_ENTRY c-r1411315f404-0-1,
_revisions.r1411315f3f6-0-1=SET_MAP_ENTRY c-r1411315f404-0-1,
_revisions.r1411315f402-0-1=SET_MAP_ENTRY c-r1411315f404-0-1}
at
org.apache.jackrabbit.oak.plugins.mongomk.MongoMK.merge(MongoMK.java:1130)
at
org.apache.jackrabbit.oak.kernel.KernelNodeStore.merge(KernelNodeStore.java
:208)
at
org.apache.jackrabbit.oak.kernel.KernelNodeStoreBranch$Persisted.merge(Kern
elNodeStoreBranch.java:378)
... 14 more
On 9/12/13 2:45 PM, "[email protected]" <[email protected]> wrote:
>Author: mreutegg
>Date: Thu Sep 12 12:45:19 2013
>New Revision: 1522553
>
>URL: http://svn.apache.org/r1522553
>Log:
>OAK-926: MongoMK: split documents when they are too large
>- Generalize access to previous documents (WIP)
>
>Added:
>
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/Range.java (with props)
>
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/ValueMap.java (with props)
>Modified:
>
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/NodeDocument.java
>
>jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/DocumentSplitTest.java
>
>Modified:
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/NodeDocument.java
>URL:
>http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/o
>rg/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java?rev=1522553&r1=
>1522552&r2=1522553&view=diff
>==========================================================================
>====
>---
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/NodeDocument.java (original)
>+++
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/NodeDocument.java Thu Sep 12 12:45:19 2013
>@@ -16,8 +16,6 @@
> */
> package org.apache.jackrabbit.oak.plugins.mongomk;
>
>-import java.util.AbstractMap;
>-import java.util.AbstractSet;
> import java.util.ArrayList;
> import java.util.Collections;
> import java.util.Comparator;
>@@ -44,10 +42,8 @@ import org.slf4j.LoggerFactory;
> import com.google.common.base.Function;
> import com.google.common.base.Predicate;
> import com.google.common.collect.Iterables;
>-import com.google.common.collect.Iterators;
> import com.google.common.collect.Maps;
>
>-import static com.google.common.base.Preconditions.checkArgument;
> import static com.google.common.base.Preconditions.checkNotNull;
>
> /**
>@@ -186,7 +182,7 @@ public class NodeDocument extends Docume
> */
> public boolean containsRevision(@Nonnull Revision revision) {
> String rev = checkNotNull(revision).toString();
>- if (getRevisionsMap().containsKey(rev)) {
>+ if (getLocalRevisions().containsKey(rev)) {
> return true;
> }
> for (NodeDocument prev : getPreviousDocs(revision)) {
>@@ -219,7 +215,7 @@ public class NodeDocument extends Docume
> public SortedMap<Revision, Revision>
>getUncommittedRevisions(RevisionContext context) {
> // only look at revisions in this document.
> // uncommitted revisions are not split off
>- Map<String, String> valueMap = getRevisionsMap();
>+ Map<String, String> valueMap = getLocalRevisions();
> SortedMap<Revision, Revision> revisions =
> new TreeMap<Revision,
>Revision>(context.getRevisionComparator());
> for (Map.Entry<String, String> commit : valueMap.entrySet()) {
>@@ -244,11 +240,7 @@ public class NodeDocument extends Docume
> */
> @CheckForNull
> public String getCommitRootPath(String revision) {
>- @SuppressWarnings("unchecked")
>- Map<String, Integer> valueMap = (Map<String, Integer>)
>get(COMMIT_ROOT);
>- if (valueMap == null) {
>- return null;
>- }
>+ Map<String, Integer> valueMap = getCommitRoot();
> Integer depth = valueMap.get(revision);
> if (depth != null) {
> String p = Utils.getPathFromId(getId());
>@@ -274,13 +266,9 @@ public class NodeDocument extends Docume
> CollisionHandler handler) {
> SortedSet<String> revisions = new
>TreeSet<String>(Collections.reverseOrder());
> revisions.addAll(getRevisions().keySet());
>- if (data.containsKey(COMMIT_ROOT)) {
>- revisions.addAll(((Map<String, Integer>)
>get(COMMIT_ROOT)).keySet());
>- }
>- Map<String, String> deletedMap = (Map<String, String>)
>get(DELETED);
>- if (deletedMap != null) {
>- revisions.addAll(deletedMap.keySet());
>- }
>+ revisions.addAll(getCommitRoot().keySet());
>+ Map<String, String> deletedMap = getDeleted();
>+ revisions.addAll(deletedMap.keySet());
> Revision newestRev = null;
> for (String r : revisions) {
> Revision propRev = Revision.fromString(r);
>@@ -304,12 +292,10 @@ public class NodeDocument extends Docume
> if (newestRev == null) {
> return null;
> }
>- if (deletedMap != null) {
>- String value = deletedMap.get(newestRev.toString());
>- if ("true".equals(value)) {
>- // deleted in the newest revision
>- return null;
>- }
>+ String value = deletedMap.get(newestRev.toString());
>+ if ("true".equals(value)) {
>+ // deleted in the newest revision
>+ return null;
> }
> return newestRev;
> }
>@@ -444,9 +430,8 @@ public class NodeDocument extends Docume
> public boolean isDeleted(RevisionContext context,
> Revision readRevision,
> Set<Revision> validRevisions) {
>- @SuppressWarnings("unchecked")
>- Map<String, String> valueMap = (Map<String, String>)
>get(NodeDocument.DELETED);
>- if (valueMap == null) {
>+ Map<String, String> valueMap = getDeleted();
>+ if (valueMap.isEmpty()) {
> return false;
> }
> if (valueMap instanceof NavigableMap) {
>@@ -487,9 +472,8 @@ public class NodeDocument extends Docume
> @CheckForNull
> public Revision getLiveRevision(RevisionContext context, Revision
>maxRev,
> Set<Revision> validRevisions) {
>- @SuppressWarnings("unchecked")
>- Map<String, String> valueMap = (Map<String, String>)
>get(NodeDocument.DELETED);
>- if (valueMap == null) {
>+ Map<String, String> valueMap = getDeleted();
>+ if (valueMap.isEmpty()) {
> return null;
> }
> // first, search the newest deleted revision
>@@ -552,13 +536,10 @@ public class NodeDocument extends Docume
> @Nonnull Revision baseRevision,
> @Nonnull RevisionContext context) {
> // did existence of node change after baseRevision?
>- @SuppressWarnings("unchecked")
>- Map<String, String> deleted = (Map<String, String>) get(DELETED);
>- if (deleted != null) {
>- for (Map.Entry<String, String> entry : deleted.entrySet()) {
>- if (isRevisionNewer(context,
>Revision.fromString(entry.getKey()), baseRevision)) {
>- return true;
>- }
>+ Map<String, String> deleted = getDeleted();
>+ for (Map.Entry<String, String> entry : deleted.entrySet()) {
>+ if (isRevisionNewer(context,
>Revision.fromString(entry.getKey()), baseRevision)) {
>+ return true;
> }
> }
>
>@@ -615,7 +596,7 @@ public class NodeDocument extends Docume
> }
> NavigableMap<Revision, String> splitRevs
> = new TreeMap<Revision,
>String>(context.getRevisionComparator());
>- Map<String, String> revisions = getRevisionsMap();
>+ Map<String, String> revisions = getLocalRevisions();
> // only consider if there are enough revisions
> if (revisions.size() > REVISIONS_SPLIT_OFF_SIZE) {
> // collect commits of this cluster node after the
>@@ -690,6 +671,88 @@ public class NodeDocument extends Docume
> return super.transformAndSeal(map, key, level);
> }
>
>+ /**
>+ * Returns previous revision ranges for this document. The revision
>keys are
>+ * sorted descending, newest first!
>+ *
>+ * @return the previous ranges for this document.
>+ */
>+ @Nonnull
>+ SortedMap<Revision, Range> getPreviousRanges() {
>+ @SuppressWarnings("unchecked")
>+ SortedMap<Revision, Range> previous = (SortedMap<Revision,
>Range>) get(PREVIOUS);
>+ if (previous == null) {
>+ previous = EMPTY_RANGE_MAP;
>+ }
>+ return previous;
>+ }
>+
>+ /**
>+ * Returns previous {@link NodeDocument}, which include the given
>revision.
>+ * If the <code>revision</code> is <code>null</code>, then all
>previous
>+ * documents are returned.
>+ *
>+ * @param revision the revision to match or <code>null</code>.
>+ * @return previous documents.
>+ */
>+ Iterable<NodeDocument> getPreviousDocs(final @Nullable Revision
>revision) {
>+ Iterable<NodeDocument> docs = Iterables.transform(
>+ Iterables.filter(getPreviousRanges().entrySet(),
>+ new Predicate<Map.Entry<Revision, Range>>() {
>+ @Override
>+ public boolean apply(Map.Entry<Revision,
>Range> input) {
>+ return revision == null ||
>input.getValue().includes(revision);
>+ }
>+ }), new Function<Map.Entry<Revision, Range>,
>NodeDocument>() {
>+ @Nullable
>+ @Override
>+ public NodeDocument apply(Map.Entry<Revision, Range> input) {
>+ Revision r = input.getKey();
>+ String prevId = Utils.getPreviousIdFor(getId(), r);
>+ NodeDocument prev = store.find(Collection.NODES, prevId);
>+ if (prev == null) {
>+ LOG.warn("Document with previous revisions not
>found: " + prevId);
>+ }
>+ return prev;
>+ }
>+ });
>+ // filter out null docs and check if the revision is actually in
>there
>+ return Iterables.filter(docs, new Predicate<NodeDocument>() {
>+ @Override
>+ public boolean apply(@Nullable NodeDocument input) {
>+ if (input == null) {
>+ return false;
>+ }
>+ return revision == null ||
>input.containsRevision(revision.toString());
>+ }
>+ });
>+ }
>+
>+ /**
>+ * Returns the local value map for the given key. Returns
><code>null</code>
>+ * if no such value map exists.
>+ *
>+ * @param key the key.
>+ * @return local value map.
>+ */
>+ @Nonnull
>+ Map<String, String> getLocalMap(String key) {
>+ @SuppressWarnings("unchecked")
>+ Map<String, String> map = (Map<String, String>) get(key);
>+ if (map == null) {
>+ map = Collections.emptyMap();
>+ }
>+ return map;
>+ }
>+
>+ /**
>+ * @return the {@link #REVISIONS} stored on this document.
>+ */
>+ @Nonnull
>+ Map<String, String> getLocalRevisions() {
>+ return getLocalMap(REVISIONS);
>+ }
>+
> //-------------------------< UpdateOp modifiers
>>---------------------------
>
> public static void setModified(@Nonnull UpdateOp op,
>@@ -745,15 +808,12 @@ public class NodeDocument extends Docume
> return this;
> }
> // check commit root
>- @SuppressWarnings("unchecked")
>- Map<String, Integer> commitRoot = (Map<String, Integer>)
>get(COMMIT_ROOT);
>+ Map<String, Integer> commitRoot = getCommitRoot();
> String commitRootPath = null;
>- if (commitRoot != null) {
>- Integer depth = commitRoot.get(rev.toString());
>- if (depth != null) {
>- String p = Utils.getPathFromId(getId());
>- commitRootPath = PathUtils.getAncestorPath(p,
>PathUtils.getDepth(p) - depth);
>- }
>+ Integer depth = commitRoot.get(rev.toString());
>+ if (depth != null) {
>+ String p = Utils.getPathFromId(getId());
>+ commitRootPath = PathUtils.getAncestorPath(p,
>PathUtils.getDepth(p) - depth);
> }
> if (commitRootPath == null) {
> // shouldn't happen, either node is commit root for a
>revision
>@@ -835,11 +895,11 @@ public class NodeDocument extends Docume
> @CheckForNull
> private String getCommitValue(Revision revision) {
> String r = revision.toString();
>- String value = getRevisionsMap().get(r);
>+ String value = getLocalRevisions().get(r);
> if (value == null) {
> // check previous
> for (NodeDocument prev : getPreviousDocs(revision)) {
>- value = prev.getRevisionsMap().get(r);
>+ value = prev.getLocalRevisions().get(r);
> if (value != null) {
> break;
> }
>@@ -907,193 +967,23 @@ public class NodeDocument extends Docume
> return value;
> }
>
>- Map<String, String> getRevisions() {
>- final Map<String, String> map = getRevisionsMap();
>- if (!data.containsKey(PREVIOUS)) {
>- return map;
>- }
>- final Set<Map.Entry<String, String>> revisions
>- = new AbstractSet<Map.Entry<String, String>>() {
>-
>- @Override
>- @Nonnull
>- public Iterator<Map.Entry<String, String>> iterator() {
>- return Iterators.concat(map.entrySet().iterator(),
>- Iterators.concat(new
>Iterator<Iterator<Map.Entry<String, String>>>() {
>- private final Iterator<NodeDocument> previous
>- = getPreviousDocs(null).iterator();
>-
>- @Override
>- public boolean hasNext() {
>- return previous.hasNext();
>- }
>-
>- @Override
>- public Iterator<Map.Entry<String, String>>
>next() {
>- return
>previous.next().getRevisions().entrySet().iterator();
>- }
>-
>- @Override
>- public void remove() {
>- throw new
>UnsupportedOperationException();
>- }
>- }));
>- }
>-
>- @Override
>- public int size() {
>- int size = map.size();
>- for (NodeDocument prev : getPreviousDocs(null)) {
>- size += prev.getRevisions().size();
>- }
>- return size;
>- }
>- };
>- return new AbstractMap<String, String>() {
>-
>- private final Map<String, String> map = getRevisionsMap();
>-
>- @Override
>- @Nonnull
>- public Set<Entry<String, String>> entrySet() {
>- return revisions;
>- }
>-
>- @Override
>- public String get(Object key) {
>- // first check revisions map of this document
>- String value = map.get(key);
>- if (value != null) {
>- return value;
>- }
>- Revision r = Revision.fromString(key.toString());
>- for (NodeDocument prev : getPreviousDocs(r)) {
>- value = prev.getRevisions().get(key);
>- if (value != null) {
>- return value;
>- }
>- }
>- // not found
>- return null;
>- }
>-
>- @Override
>- public boolean containsKey(Object key) {
>- // can use get()
>- // the revisions map does not have null values
>- return get(key) != null;
>- }
>-
>- };
>- }
>-
> @Nonnull
>- Map<String, String> getRevisionsMap() {
>- @SuppressWarnings("unchecked")
>- Map<String, String> map = (Map<String, String>) get(REVISIONS);
>- if (map == null) {
>- map = Collections.emptyMap();
>- }
>- return map;
>+ private Map<String, String> getRevisions() {
>+ return ValueMap.create(this, REVISIONS);
> }
>
>- /**
>- * Returns previous {@link NodeDocument}, which include the given
>revision.
>- * If the <code>revision</code> is <code>null</code>, then all
>previous
>- * documents are returned.
>- *
>- * @param revision the revision to match or <code>null</code>.
>- * @return previous documents.
>- */
>- Iterable<NodeDocument> getPreviousDocs(@Nullable final Revision
>revision) {
>- Iterable<NodeDocument> docs = Iterables.transform(
>- Iterables.filter(getPreviousRanges().entrySet(),
>- new Predicate<Map.Entry<Revision, Range>>() {
>- @Override
>- public boolean apply(Map.Entry<Revision, Range> input) {
>- return revision == null ||
>input.getValue().includes(revision);
>- }
>- }), new Function<Map.Entry<Revision, Range>, NodeDocument>() {
>- @Nullable
>- @Override
>- public NodeDocument apply(Map.Entry<Revision, Range> input) {
>- Revision r = input.getKey();
>- String prevId = Utils.getPreviousIdFor(getId(), r);
>- NodeDocument prev = store.find(Collection.NODES, prevId);
>- if (prev == null) {
>- LOG.warn("Document with previous revisions not
>found: " + prevId);
>- }
>- return prev;
>- }
>- });
>- // filter out null docs and check if the revision is actually in
>there
>- return Iterables.filter(docs, new Predicate<NodeDocument>() {
>- @Override
>- public boolean apply(@Nullable NodeDocument input) {
>- if (input == null) {
>- return false;
>- }
>- return revision == null ||
>input.containsRevision(revision.toString());
>- }
>- });
>+ @Nonnull
>+ private Map<String, String> getDeleted() {
>+ return ValueMap.create(this, DELETED);
> }
>
>- /**
>- * Returns previous revision ranges for this document. The revision
>keys are
>- * sorted descending, newest first!
>- *
>- * @return the previous ranges for this document.
>- */
> @Nonnull
>- private SortedMap<Revision, Range> getPreviousRanges() {
>+ private Map<String, Integer> getCommitRoot() {
> @SuppressWarnings("unchecked")
>- SortedMap<Revision, Range> previous = (SortedMap<Revision,
>Range>) get(PREVIOUS);
>- if (previous == null) {
>- previous = EMPTY_RANGE_MAP;
>- }
>- return previous;
>- }
>-
>- /**
>- * A range of revisions.
>- */
>- private static final class Range {
>-
>- final Revision high;
>- final Revision low;
>-
>- /**
>- * A range of revisions, with both inclusive bounds.
>- *
>- * @param high the high bound.
>- * @param low the low bound.
>- */
>- Range(@Nonnull Revision high, @Nonnull Revision low) {
>- this.high = checkNotNull(high);
>- this.low = checkNotNull(low);
>- checkArgument(high.getClusterId() == low.getClusterId(),
>- "Revisions from have the same clusterId");
>- checkArgument(high.compareRevisionTime(low) > 0,
>- "High Revision must be later than low Revision");
>- }
>-
>- /**
>- * Returns <code>true</code> if the given revision is within
>this range.
>- *
>- * @param r the revision to check.
>- * @return <code>true</code> if within this range;
><code>false</code>
>- * otherwise.
>- */
>- boolean includes(Revision r) {
>- return high.compareRevisionTime(r) >= 0
>- && low.compareRevisionTime(r) <= 0;
>- }
>-
>- @Override
>- public String toString() {
>- return low.toString();
>+ Map<String, Integer> commitRoot = (Map<String, Integer>)
>get(COMMIT_ROOT);
>+ if (commitRoot == null) {
>+ commitRoot = Collections.emptyMap();
> }
>-
>+ return commitRoot;
> }
>-
> }
>
>Added:
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/Range.java
>URL:
>http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/o
>rg/apache/jackrabbit/oak/plugins/mongomk/Range.java?rev=1522553&view=auto
>==========================================================================
>====
>---
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/Range.java (added)
>+++
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/Range.java Thu Sep 12 12:45:19 2013
>@@ -0,0 +1,63 @@
>+/*
>+ * 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.mongomk;
>+
>+import javax.annotation.Nonnull;
>+
>+import static com.google.common.base.Preconditions.checkArgument;
>+import static com.google.common.base.Preconditions.checkNotNull;
>+
>+/**
>+* A revision range for {@link NodeDocument#PREVIOUS} documents.
>+*/
>+final class Range {
>+
>+ final Revision high;
>+ final Revision low;
>+
>+ /**
>+ * A range of revisions, with both inclusive bounds.
>+ *
>+ * @param high the high bound.
>+ * @param low the low bound.
>+ */
>+ Range(@Nonnull Revision high, @Nonnull Revision low) {
>+ this.high = checkNotNull(high);
>+ this.low = checkNotNull(low);
>+ checkArgument(high.getClusterId() == low.getClusterId(),
>+ "Revisions from have the same clusterId");
>+ checkArgument(high.compareRevisionTime(low) > 0,
>+ "High Revision must be later than low Revision");
>+ }
>+
>+ /**
>+ * Returns <code>true</code> if the given revision is within this
>range.
>+ *
>+ * @param r the revision to check.
>+ * @return <code>true</code> if within this range; <code>false</code>
>+ * otherwise.
>+ */
>+ boolean includes(Revision r) {
>+ return high.compareRevisionTime(r) >= 0
>+ && low.compareRevisionTime(r) <= 0;
>+ }
>+
>+ @Override
>+ public String toString() {
>+ return low.toString();
>+ }
>+}
>
>Propchange:
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/Range.java
>--------------------------------------------------------------------------
>----
> svn:eol-style = native
>
>Propchange:
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/Range.java
>--------------------------------------------------------------------------
>----
> svn:keywords = Author Date Id Revision Rev URL
>
>Added:
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/ValueMap.java
>URL:
>http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/o
>rg/apache/jackrabbit/oak/plugins/mongomk/ValueMap.java?rev=1522553&view=au
>to
>==========================================================================
>====
>---
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/ValueMap.java (added)
>+++
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/ValueMap.java Thu Sep 12 12:45:19 2013
>@@ -0,0 +1,113 @@
>+/*
>+ * Licensed to the Apache Software Foundation (ASF) under one or more
>+ * contributor license agreements. See the NOTICE file distributed with
>+ * this work for additional information regarding copyright ownership.
>+ * The ASF licenses this file to You under the Apache License, Version
>2.0
>+ * (the "License"); you may not use this file except in compliance with
>+ * the License. You may obtain a copy of the License at
>+ *
>+ * http://www.apache.org/licenses/LICENSE-2.0
>+ *
>+ * Unless required by applicable law or agreed to in writing, software
>+ * distributed under the License is distributed on an "AS IS" BASIS,
>+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
>implied.
>+ * See the License for the specific language governing permissions and
>+ * limitations under the License.
>+ */
>+package org.apache.jackrabbit.oak.plugins.mongomk;
>+
>+import java.util.AbstractMap;
>+import java.util.AbstractSet;
>+import java.util.Iterator;
>+import java.util.Map;
>+import java.util.Set;
>+
>+import javax.annotation.Nonnull;
>+
>+import com.google.common.collect.Iterators;
>+
>+/**
>+ * A value map contains the versioned values of a property. The key into
>this
>+ * map is the revision when the value was set.
>+ */
>+class ValueMap {
>+
>+ @Nonnull
>+ static Map<String, String> create(final @Nonnull NodeDocument doc,
>+ final @Nonnull String property) {
>+ final Map<String, String> map = doc.getLocalMap(property);
>+ if (doc.getPreviousRanges().isEmpty()) {
>+ return map;
>+ }
>+ final Set<Map.Entry<String, String>> values
>+ = new AbstractSet<Map.Entry<String, String>>() {
>+
>+ @Override
>+ @Nonnull
>+ public Iterator<Map.Entry<String, String>> iterator() {
>+ return Iterators.concat(map.entrySet().iterator(),
>Iterators.concat(new Iterator<Iterator<Map.Entry<String, String>>>() {
>+ private final Iterator<NodeDocument> previous =
>doc.getPreviousDocs(null).iterator();
>+
>+ @Override
>+ public boolean hasNext() {
>+ return previous.hasNext();
>+ }
>+
>+ @Override
>+ public Iterator<Map.Entry<String, String>> next() {
>+ return
>previous.next().getLocalMap(property).entrySet().iterator();
>+ }
>+
>+ @Override
>+ public void remove() {
>+ throw new UnsupportedOperationException();
>+ }
>+ }));
>+ }
>+
>+ @Override
>+ public int size() {
>+ int size = map.size();
>+ for (NodeDocument prev : doc.getPreviousDocs(null)) {
>+ size += prev.getLocalMap(property).size();
>+ }
>+ return size;
>+ }
>+ };
>+ return new AbstractMap<String, String>() {
>+
>+ private final Map<String, String> map =
>doc.getLocalMap(property);
>+
>+ @Override
>+ @Nonnull
>+ public Set<Entry<String, String>> entrySet() {
>+ return values;
>+ }
>+
>+ @Override
>+ public String get(Object key) {
>+ // first check values map of this document
>+ String value = map.get(key);
>+ if (value != null) {
>+ return value;
>+ }
>+ Revision r = Revision.fromString(key.toString());
>+ for (NodeDocument prev : doc.getPreviousDocs(r)) {
>+ value = prev.getLocalMap(property).get(key);
>+ if (value != null) {
>+ return value;
>+ }
>+ }
>+ // not found
>+ return null;
>+ }
>+
>+ @Override
>+ public boolean containsKey(Object key) {
>+ // can use get()
>+ // the values map does not have null values
>+ return get(key) != null;
>+ }
>+ };
>+ }
>+}
>
>Propchange:
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/ValueMap.java
>--------------------------------------------------------------------------
>----
> svn:eol-style = native
>
>Propchange:
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/ValueMap.java
>--------------------------------------------------------------------------
>----
> svn:keywords = Author Date Id Revision Rev URL
>
>Modified:
>jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/DocumentSplitTest.java
>URL:
>http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/o
>rg/apache/jackrabbit/oak/plugins/mongomk/DocumentSplitTest.java?rev=152255
>3&r1=1522552&r2=1522553&view=diff
>==========================================================================
>====
>---
>jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/DocumentSplitTest.java (original)
>+++
>jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/DocumentSplitTest.java Thu Sep 12 12:45:19 2013
>@@ -39,7 +39,7 @@ public class DocumentSplitTest extends B
> Set<String> revisions = Sets.newHashSet();
> NodeDocument doc = store.find(Collection.NODES,
>Utils.getIdFromPath("/"));
> assertNotNull(doc);
>- revisions.addAll(doc.getRevisionsMap().keySet());
>+ revisions.addAll(doc.getLocalRevisions().keySet());
>
> // MongoMK initializes with a root node with a single revision
> int numRevs = 1;
>@@ -55,7 +55,7 @@ public class DocumentSplitTest extends B
> String head = mk.getHeadRevision();
> doc = store.find(Collection.NODES, Utils.getIdFromPath("/"));
> assertNotNull(doc);
>- Map<String, String> revs = doc.getRevisionsMap();
>+ Map<String, String> revs = doc.getLocalRevisions();
> // one remaining in the local revisions map
> assertEquals(1, revs.size());
> for (String r : revisions) {
>
>