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(
+ * "org.apache.jackrabbit.oak.plugins.commit.MergingNodeStateDiff")
+ * .enable(Level.DEBUG).create();
+ *
+ * @Before
+ * public void setup() throws RepositoryException {
+ * customLogs.starting();
+ * }
+ *
+ * @After
+ * public void after() {
+ * customLogs.finished();
+ * }
+ *
+ * @Test
+ * public void test() {
+ * List<String> 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;