Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/MergingNodeStateDiff.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/MergingNodeStateDiff.java (revision 1689829) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/MergingNodeStateDiff.java (working copy) @@ -32,6 +32,8 @@ import com.google.common.collect.ImmutableMap; import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.commons.json.JsopBuilder; +import org.apache.jackrabbit.oak.json.JsopDiff; import org.apache.jackrabbit.oak.plugins.memory.PropertyBuilder; import org.apache.jackrabbit.oak.plugins.tree.impl.TreeConstants; import org.apache.jackrabbit.oak.spi.commit.ConflictHandler; @@ -41,6 +43,7 @@ import org.apache.jackrabbit.oak.spi.state.DefaultNodeStateDiff; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; +import org.apache.jackrabbit.oak.spi.state.NodeStateUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -110,6 +113,31 @@ String name = oursCNE.getName(); NodeState ours = oursCNE.getNodeState(); NodeState theirs = parent.getChildNode(name); + + // TODO rm me ! + System.err.println("[MergingNodeStateDiff] #resolveConflict " + + conflictType); + System.err + .println("[MergingNodeStateDiff] #resolveConflict ours:"); + System.err.println(NodeStateUtils.toString(ours)); + System.err + .println("[MergingNodeStateDiff] #resolveConflict theirs:"); + System.err.println(NodeStateUtils.toString(theirs)); + String differr = JsopDiff.diffToJsop(ours, theirs); + System.err + .println("[MergingNodeStateDiff] resolve conflict of type " + + conflictType + + ", conflict trace " + + differr); + // ---- + + if (LOG.isDebugEnabled()) { + String diff = JsopDiff.diffToJsop(ours, theirs); + LOG.debug( + "resolve conflict of type {}, conflict trace {}", + conflictType, diff); + } + Resolution resolution = nodeConflictHandler.resolve(name, ours, theirs); applyResolution(resolution, conflictType, name, ours); } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStateUtils.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStateUtils.java (revision 1689829) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStateUtils.java (working copy) @@ -21,6 +21,7 @@ import javax.annotation.CheckForNull; import javax.annotation.Nonnull; +import org.apache.commons.io.IOUtils; import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; @@ -80,4 +81,49 @@ return node; } + /** + * Provides a string representation of the given node state + * + * @param node + * node state + * @return a string representation of {@code node}. + */ + public static String toString(NodeState node) { + if (node == null) { + return "[null]"; + } + StringBuilder sb = new StringBuilder(); + sb.append(toString(node, 1, " ", "/")); + return sb.toString(); + } + + private static String toString(NodeState ns, int level, String prepend, + String name) { + StringBuilder node = new StringBuilder(); + node.append(prepend).append(name); + + StringBuilder props = new StringBuilder(); + boolean first = true; + for (PropertyState ps : ns.getProperties()) { + if (!first) { + props.append(", "); + } else { + first = false; + } + props.append(ps); + } + + if (props.length() > 0) { + node.append("{"); + node.append(props); + node.append("}"); + } + for (ChildNodeEntry c : ns.getChildNodeEntries()) { + node.append(IOUtils.LINE_SEPARATOR); + node.append(toString(c.getNodeState(), level++, prepend + prepend, + c.getName())); + } + return node.toString(); + } + } Index: oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java =================================================================== --- oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java (revision 1689791) +++ oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java (working copy) @@ -1694,6 +1694,38 @@ } @Test + public void deleteChanged() throws RepositoryException { + getAdminSession().getRootNode().addNode("node").addNode("jcr:content").addNode("metadata"); + getAdminSession().save(); + + Session session1 = createAdminSession(); + Session session2 = createAdminSession(); + try { + session1.getNode("/node/jcr:content").remove(); + session2.getNode("/node/jcr:content/metadata").setProperty("updated", "myself"); + session2.save(); + try { + session1.save(); + fail("Expected InvalidItemStateException"); + } catch (InvalidItemStateException expected) { + // expected.printStackTrace(); + // javax.jcr.InvalidItemStateException: OakState0001: Unresolved + // conflicts in /node + // + // ConflictValidator debug: Commit failed due to unresolved + // conflicts in /node = {deleteChangedNode = {jcr:content}} + // + // MergingNodeStateDif debug: resolve conflict of type + // DELETE_CHANGED_NODE, conflict trace + // ^"/metadata/updated":"myself" + } + } finally { + session1.logout(); + session2.logout(); + } + } + + @Test public void liveNodes() throws RepositoryException { Session session = getAdminSession(); Node n1 = (Node) session.getItem(TEST_PATH);