You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by al...@apache.org on 2015/07/09 11:59:48 UTC

svn commit: r1690043 - in /jackrabbit/oak/trunk: oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/junit/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/

Author: alexparvulescu
Date: Thu Jul  9 09:59:48 2015
New Revision: 1690043

URL: http://svn.apache.org/r1690043
Log:
OAK-3057 Simplify debugging conflict related errors

Added:
    jackrabbit/oak/trunk/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/junit/LogCustomizer.java   (with props)
    jackrabbit/oak/trunk/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/junit/LogCustomizerTest.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ConflictValidator.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ConflictValidatorProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/MergingNodeStateDiff.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/package-info.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStateUtils.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/package-info.java

Added: jackrabbit/oak/trunk/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/junit/LogCustomizer.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/junit/LogCustomizer.java?rev=1690043&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/junit/LogCustomizer.java (added)
+++ jackrabbit/oak/trunk/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/junit/LogCustomizer.java Thu Jul  9 09:59:48 2015
@@ -0,0 +1,160 @@
+/*
+ * 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.commons.junit;
+
+import static org.slf4j.Logger.ROOT_LOGGER_NAME;
+
+import java.util.List;
+
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.AppenderBase;
+
+import com.google.common.collect.Lists;
+
+/**
+ * The LogCustomizer allows to enable log level for a specific logger and/or
+ * filter the received logs this logger on a dedicated log level
+ * 
+ * <pre>
+ * public class ConflictResolutionTest {
+ * 
+ *     private final LogCustomizer customLogs = LogCustomizer
+ *             .forLogger(
+ *                     &quot;org.apache.jackrabbit.oak.plugins.commit.MergingNodeStateDiff&quot;)
+ *             .enable(Level.DEBUG).create();
+ * 
+ *     &#064;Before
+ *     public void setup() throws RepositoryException {
+ *         customLogs.starting();
+ *     }
+ * 
+ *     &#064;After
+ *     public void after() {
+ *         customLogs.finished();
+ *     }
+ * 
+ *     &#064;Test
+ *     public void test() {
+ *         List&lt;String&gt; myLogs = customLogs.getLogs();
+ *         assertTrue(myLogs.size() == 1);
+ *     }
+ * 
+ * }
+ * </pre>
+ */
+
+public class LogCustomizer {
+
+    public static LogCustomizerBuilder forRootLogger() {
+        return forLogger(ROOT_LOGGER_NAME);
+    }
+
+    public static LogCustomizerBuilder forLogger(String name) {
+        return new LogCustomizerBuilder(name);
+    }
+
+    public static class LogCustomizerBuilder {
+
+        private final String name;
+        private Level enableLevel;
+        private Level filterLevel;
+
+        private LogCustomizerBuilder(String name) {
+            this.name = name;
+        }
+
+        public LogCustomizerBuilder enable(Level level) {
+            this.enableLevel = level;
+            return this;
+        }
+
+        public LogCustomizerBuilder filter(Level level) {
+            this.filterLevel = level;
+            return this;
+        }
+
+        public LogCustomizer create() {
+            return new LogCustomizer(name, enableLevel, filterLevel);
+        }
+    }
+
+    private final Logger logger;
+    private final List<String> logs = Lists.newArrayList();
+
+    private final Level enableLevel;
+    private final Level originalLevel;
+
+    private final Appender<ILoggingEvent> customLogger;
+
+    private LogCustomizer(String name, Level enableLevel,
+            final Level filterLevel) {
+        this.logger = getLogger(name);
+        if (enableLevel != null) {
+            this.enableLevel = enableLevel;
+            this.originalLevel = logger.getLevel();
+        } else {
+            this.enableLevel = null;
+            this.originalLevel = null;
+        }
+
+        customLogger = new AppenderBase<ILoggingEvent>() {
+            @Override
+            protected void append(ILoggingEvent e) {
+                if (filterLevel == null) {
+                    logs.add(e.getFormattedMessage());
+                } else if (filterLevel.isGreaterOrEqual(e.getLevel())) {
+                    filterLevel.isGreaterOrEqual(e.getLevel());
+                }
+            }
+        };
+    }
+
+    private static Logger getLogger(String name) {
+        return ((LoggerContext) LoggerFactory.getILoggerFactory())
+                .getLogger(name);
+    }
+
+    public List<String> getLogs() {
+        return logs;
+    }
+
+    public void starting() {
+        customLogger.start();
+        if (enableLevel != null) {
+            logger.setLevel(enableLevel);
+        }
+        logger.addAppender(customLogger);
+    }
+
+    public void finished() {
+        if (originalLevel != null) {
+            logger.setLevel(originalLevel);
+        }
+        logger.detachAppender(customLogger);
+        customLogger.stop();
+        logs.clear();
+    }
+
+}

Propchange: jackrabbit/oak/trunk/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/junit/LogCustomizer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jackrabbit/oak/trunk/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/junit/LogCustomizerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/junit/LogCustomizerTest.java?rev=1690043&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/junit/LogCustomizerTest.java (added)
+++ jackrabbit/oak/trunk/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/junit/LogCustomizerTest.java Thu Jul  9 09:59:48 2015
@@ -0,0 +1,80 @@
+/*
+ * 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.commons.junit;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Level;
+
+/**
+ * Tests for the LogCustomizer class
+ **/
+public class LogCustomizerTest {
+
+    private static final Logger LOG = LoggerFactory
+            .getLogger(LogCustomizerTest.class);
+
+    @Test
+    public void testLogs1() {
+        LogCustomizer custom = LogCustomizer
+                .forLogger(
+                        "org.apache.jackrabbit.oak.commons.junit.LogCustomizerTest")
+                .enable(Level.DEBUG).create();
+
+        try {
+            custom.starting();
+            LOG.debug("test message");
+            List<String> logs = custom.getLogs();
+            assertTrue(logs.size() == 1);
+            assertThat("logs were recorded by custom logger", logs.toString(),
+                    containsString("test message"));
+        } finally {
+            custom.finished();
+        }
+    }
+
+    @Test
+    public void testLogs2() {
+        LogCustomizer custom = LogCustomizer
+                .forLogger(
+                        "org.apache.jackrabbit.oak.commons.junit.LogCustomizerTest")
+                .enable(Level.DEBUG).filter(Level.INFO).create();
+
+        try {
+            custom.starting();
+            LOG.debug("test message");
+
+            List<String> logs = custom.getLogs();
+            assertTrue(logs.isEmpty());
+
+        } finally {
+            custom.finished();
+        }
+    }
+
+}

Propchange: jackrabbit/oak/trunk/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/junit/LogCustomizerTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ConflictValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ConflictValidator.java?rev=1690043&r1=1690042&r2=1690043&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ConflictValidator.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ConflictValidator.java Thu Jul  9 09:59:48 2015
@@ -16,23 +16,25 @@
  */
 package org.apache.jackrabbit.oak.plugins.commit;
 
-import com.google.common.base.Joiner;
+import static org.apache.jackrabbit.oak.api.Type.STRINGS;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.MIX_REP_MERGE_CONFLICT;
+
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants;
 import org.apache.jackrabbit.oak.spi.commit.DefaultValidator;
 import org.apache.jackrabbit.oak.spi.commit.Validator;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.ConflictType;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static com.google.common.collect.Iterables.transform;
-import static org.apache.jackrabbit.oak.api.Type.STRINGS;
-import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.MIX_REP_MERGE_CONFLICT;
+import com.google.common.base.Joiner;
 
 /**
  * {@link Validator} which checks the presence of conflict markers
@@ -43,10 +45,50 @@ import static org.apache.jackrabbit.oak.
 public class ConflictValidator extends DefaultValidator {
     private static Logger log = LoggerFactory.getLogger(ConflictValidator.class);
 
-    private final Tree parentAfter;
+    /**
+     * Current processed path, or null if the debug log is not enabled at the
+     * beginning of the call. The null check will also be used to verify if a
+     * debug log will be needed or not
+     */
+    private final String path;
 
+    private NodeState after;
+
+    @Deprecated
     public ConflictValidator(Tree parentAfter) {
-        this.parentAfter = parentAfter;
+        this();
+    }
+
+    ConflictValidator() {
+        if (log.isDebugEnabled()) {
+            this.path = "/";
+        } else {
+            this.path = null;
+        }
+    }
+
+    private ConflictValidator(String path, String name) {
+        if (path != null) {
+            this.path = PathUtils.concat(path, name);
+        } else {
+            this.path = null;
+        }
+    }
+
+    private boolean isLogEnabled() {
+        return path != null;
+    }
+
+    @Override
+    public void enter(NodeState before, NodeState after)
+            throws CommitFailedException {
+        this.after = after;
+    }
+
+    @Override
+    public void leave(NodeState before, NodeState after)
+            throws CommitFailedException {
+        this.after = null;
     }
 
     @Override
@@ -62,12 +104,13 @@ public class ConflictValidator extends D
 
     @Override
     public Validator childNodeAdded(String name, NodeState after) {
-        return new ConflictValidator(parentAfter.getChild(name));
+        return new ConflictValidator(path, name);
     }
 
     @Override
-    public Validator childNodeChanged(String name, NodeState before, NodeState after) {
-        return new ConflictValidator(parentAfter.getChild(name));
+    public Validator childNodeChanged(String name, NodeState before,
+            NodeState after) {
+        return new ConflictValidator(path, name);
     }
 
     @Override
@@ -82,12 +125,12 @@ public class ConflictValidator extends D
                 if (MIX_REP_MERGE_CONFLICT.equals(v)) {
 
                     CommitFailedException ex = new CommitFailedException(
-                            CommitFailedException.STATE, 1, "Unresolved conflicts in " + parentAfter.getPath());
+                            CommitFailedException.STATE, 1, "Unresolved conflicts in " + path);
 
                     //Conflict details are not made part of ExceptionMessage instead they are
                     //logged. This to avoid exposing property details to the caller as it might not have
                     //permission to access it
-                    if (log.isDebugEnabled()) {
+                    if (isLogEnabled()) {
                         log.debug(getConflictMessage(), ex);
                     }
                     throw ex;
@@ -98,16 +141,17 @@ public class ConflictValidator extends D
 
     private String getConflictMessage() {
         StringBuilder sb = new StringBuilder("Commit failed due to unresolved conflicts in ");
-        sb.append(parentAfter.getPath());
+        sb.append(path);
         sb.append(" = {");
-        for (Tree conflict : parentAfter.getChild(NodeTypeConstants.REP_OURS).getChildren()) {
-            ConflictType ct = ConflictType.fromName(conflict.getName());
 
+        for (ChildNodeEntry conflict : after.getChildNode(NodeTypeConstants.REP_OURS).getChildNodeEntries()) {
+            ConflictType ct = ConflictType.fromName(conflict.getName());
+            NodeState node = conflict.getNodeState();
             sb.append(ct.getName()).append(" = {");
             if (ct.effectsNode()) {
-                sb.append(getChildNodeNamesAsString(conflict));
+                sb.append(getChildNodeNamesAsString(node));
             } else {
-                for (PropertyState ps : conflict.getProperties()) {
+                for (PropertyState ps : node.getProperties()) {
                     PropertyState ours = null, theirs = null;
                     switch (ct) {
                         case DELETE_CHANGED_PROPERTY:
@@ -117,7 +161,7 @@ public class ConflictValidator extends D
                         case ADD_EXISTING_PROPERTY:
                         case CHANGE_CHANGED_PROPERTY:
                             ours = ps;
-                            theirs = parentAfter.getProperty(ps.getName());
+                            theirs = after.getProperty(ps.getName());
                             break;
                         case CHANGE_DELETED_PROPERTY:
                             ours = ps;
@@ -145,8 +189,8 @@ public class ConflictValidator extends D
         return sb.toString();
     }
 
-    private static String getChildNodeNamesAsString(Tree t) {
-        return Joiner.on(',').join(transform(t.getChildren(), Tree.GET_NAME));
+    private static String getChildNodeNamesAsString(NodeState ns) {
+        return Joiner.on(',').join(ns.getChildNodeNames());
     }
 
     private static String toString(PropertyState ps) {
@@ -154,7 +198,7 @@ public class ConflictValidator extends D
             return "<N/A>";
         }
 
-        final Type type = ps.getType();
+        final Type<?> type = ps.getType();
         if (type.isArray()) {
             return "<ARRAY>";
         }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ConflictValidatorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ConflictValidatorProvider.java?rev=1690043&r1=1690042&r2=1690043&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ConflictValidatorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ConflictValidatorProvider.java Thu Jul  9 09:59:48 2015
@@ -18,7 +18,6 @@ package org.apache.jackrabbit.oak.plugin
 
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Service;
-import org.apache.jackrabbit.oak.plugins.tree.TreeFactory;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
 import org.apache.jackrabbit.oak.spi.commit.Validator;
@@ -35,7 +34,7 @@ public class ConflictValidatorProvider e
     @Override
     public Validator getRootValidator(
             NodeState before, NodeState after, CommitInfo info) {
-        return new ConflictValidator(TreeFactory.createReadOnlyTree(after));
+        return new ConflictValidator();
     }
 
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/MergingNodeStateDiff.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/MergingNodeStateDiff.java?rev=1690043&r1=1690042&r2=1690043&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/MergingNodeStateDiff.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/MergingNodeStateDiff.java Thu Jul  9 09:59:48 2015
@@ -32,6 +32,7 @@ import java.util.Map;
 
 import com.google.common.collect.ImmutableMap;
 import org.apache.jackrabbit.oak.api.PropertyState;
+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;
@@ -112,6 +113,13 @@ public final class MergingNodeStateDiff
                     NodeState theirs = parent.getChildNode(name);
                     Resolution resolution = nodeConflictHandler.resolve(name, ours, theirs);
                     applyResolution(resolution, conflictType, name, ours);
+                    if (LOG.isDebugEnabled()) {
+                        String diff = JsopDiff.diffToJsop(ours, theirs);
+                        LOG.debug(
+                                "{} resolved conflict of type {} with resolution {}, conflict trace {}",
+                                nodeConflictHandler, conflictType, resolution,
+                                diff);
+                    }
                 }
             }
             else {
@@ -179,30 +187,55 @@ public final class MergingNodeStateDiff
             public Resolution resolve(PropertyState ours, PropertyState theirs) {
                 return conflictHandler.addExistingProperty(target, ours, theirs);
             }
+
+            @Override
+            public String toString() {
+                return "PropertyConflictHandler<ADD_EXISTING_PROPERTY>";
+            }
         },
         CHANGE_DELETED_PROPERTY, new PropertyConflictHandler() {
             @Override
             public Resolution resolve(PropertyState ours, PropertyState theirs) {
                 return conflictHandler.changeDeletedProperty(target, ours);
             }
+
+            @Override
+            public String toString() {
+                return "PropertyConflictHandler<CHANGE_DELETED_PROPERTY>";
+            }
         },
         CHANGE_CHANGED_PROPERTY, new PropertyConflictHandler() {
             @Override
             public Resolution resolve(PropertyState ours, PropertyState theirs) {
                 return conflictHandler.changeChangedProperty(target, ours, theirs);
             }
+
+            @Override
+            public String toString() {
+                return "PropertyConflictHandler<CHANGE_CHANGED_PROPERTY>";
+            }
         },
         DELETE_DELETED_PROPERTY, new PropertyConflictHandler() {
             @Override
             public Resolution resolve(PropertyState ours, PropertyState theirs) {
                 return conflictHandler.deleteDeletedProperty(target, ours);
             }
+
+            @Override
+            public String toString() {
+                return "PropertyConflictHandler<DELETE_DELETED_PROPERTY>";
+            }
         },
         DELETE_CHANGED_PROPERTY, new PropertyConflictHandler() {
             @Override
             public Resolution resolve(PropertyState ours, PropertyState theirs) {
                 return conflictHandler.deleteChangedProperty(target, theirs);
             }
+
+            @Override
+            public String toString() {
+                return "PropertyConflictHandler<DELETE_CHANGED_PROPERTY>";
+            }
         }
     );
 
@@ -212,24 +245,44 @@ public final class MergingNodeStateDiff
             public Resolution resolve(String name, NodeState ours, NodeState theirs) {
                 return conflictHandler.addExistingNode(target, name, ours, theirs);
             }
+
+            @Override
+            public String toString() {
+                return "NodeConflictHandler<ADD_EXISTING_NODE>";
+            }
         },
         CHANGE_DELETED_NODE, new NodeConflictHandler() {
             @Override
             public Resolution resolve(String name, NodeState ours, NodeState theirs) {
                 return conflictHandler.changeDeletedNode(target, name, ours);
             }
+
+            @Override
+            public String toString() {
+                return "NodeConflictHandler<CHANGE_DELETED_NODE>";
+            }
         },
         DELETE_CHANGED_NODE, new NodeConflictHandler() {
             @Override
             public Resolution resolve(String name, NodeState ours, NodeState theirs) {
                 return conflictHandler.deleteChangedNode(target, name, theirs);
             }
+
+            @Override
+            public String toString() {
+                return "NodeConflictHandler<DELETE_CHANGED_NODE>";
+            }
         },
         DELETE_DELETED_NODE, new NodeConflictHandler() {
             @Override
             public Resolution resolve(String name, NodeState ours, NodeState theirs) {
                 return conflictHandler.deleteDeletedNode(target, name);
             }
+
+            @Override
+            public String toString() {
+                return "NodeConflictHandler<DELETE_DELETED_NODE>";
+            }
         }
     );
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/package-info.java?rev=1690043&r1=1690042&r2=1690043&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/package-info.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/package-info.java Thu Jul  9 09:59:48 2015
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.0")
+@Version("1.1.0")
 @Export(optional = "provide:=true")
 package org.apache.jackrabbit.oak.plugins.commit;
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStateUtils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStateUtils.java?rev=1690043&r1=1690042&r2=1690043&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStateUtils.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStateUtils.java Thu Jul  9 09:59:48 2015
@@ -21,6 +21,7 @@ import static com.google.common.base.Pre
 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 @@ public final class NodeStateUtils {
         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();
+    }
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/package-info.java?rev=1690043&r1=1690042&r2=1690043&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/package-info.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/package-info.java Thu Jul  9 09:59:48 2015
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.1.0")
+@Version("1.2.0")
 @Export(optional = "provide:=true")
 package org.apache.jackrabbit.oak.spi.state;