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 md...@apache.org on 2012/06/14 15:27:38 UTC
svn commit: r1350229 - in /jackrabbit/oak/trunk: oak-core/
oak-core/src/main/java/org/apache/jackrabbit/oak/core/
oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/type/
oak-core/src/main/java/org/apache/jackrabbit/oak/util/
oak-jcr/src/main/jav...
Author: mduerig
Date: Thu Jun 14 13:27:38 2012
New Revision: 1350229
URL: http://svn.apache.org/viewvc?rev=1350229&view=rev
Log:
OAK-133: Session.refresh(true) should allow for manual conflict reconciliation
Add ConflictHandler which annotates conflicts on nodes
Modified:
jackrabbit/oak/trunk/oak-core/pom.xml
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/type/TypeValidatorProvider.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/util/Iterators.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java
Modified: jackrabbit/oak/trunk/oak-core/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/pom.xml?rev=1350229&r1=1350228&r2=1350229&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-core/pom.xml Thu Jun 14 13:27:38 2012
@@ -44,11 +44,12 @@
org.apache.jackrabbit.oak.util,
org.apache.jackrabbit.oak.namepath,
org.apache.jackrabbit.oak.plugins.name,
+ org.apache.jackrabbit.oak.spi.state,
org.apache.jackrabbit.oak.spi.security.authentication,
org.apache.jackrabbit.oak.spi.security.principal,
org.apache.jackrabbit.oak.spi.security.privilege,
- org.apache.jackrabbit.oak.security.privilege,
- org.apache.jackrabbit.oak.spi.security.user
+ org.apache.jackrabbit.oak.spi.security.user,
+ org.apache.jackrabbit.oak.security.privilege
</Export-Package>
<Bundle-Activator>
org.apache.jackrabbit.oak.osgi.Activator
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java?rev=1350229&r1=1350228&r2=1350229&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java Thu Jun 14 13:27:38 2012
@@ -26,6 +26,7 @@ import javax.annotation.Nonnull;
import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.api.ConflictHandler;
import org.apache.jackrabbit.oak.api.ConflictHandler.Resolution;
+import org.apache.jackrabbit.oak.api.CoreValue;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Root;
import org.apache.jackrabbit.oak.api.Tree;
@@ -38,10 +39,12 @@ import org.apache.jackrabbit.oak.spi.sta
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static org.apache.jackrabbit.oak.api.ConflictHandler.Resolution.*;
+import static org.apache.jackrabbit.oak.api.ConflictHandler.Resolution.MERGED;
+import static org.apache.jackrabbit.oak.api.ConflictHandler.Resolution.OURS;
import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
import static org.apache.jackrabbit.oak.commons.PathUtils.getName;
import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
+import static org.apache.jackrabbit.oak.util.Iterators.toList;
public class RootImpl implements Root {
static final Logger log = LoggerFactory.getLogger(RootImpl.class);
@@ -317,7 +320,7 @@ public class RootImpl implements Root {
switch (resolution) {
case OURS:
- setProperty(after, target);
+ setProperty(target, after);
break;
case THEIRS:
case MERGED:
@@ -347,7 +350,7 @@ public class RootImpl implements Root {
switch (resolution) {
case OURS:
- setProperty(after, target);
+ setProperty(target, after);
break;
case THEIRS:
case MERGED:
@@ -394,7 +397,7 @@ public class RootImpl implements Root {
switch (resolution) {
case OURS:
- addChild(name, after, target);
+ addChild(target, name, after);
break;
case THEIRS:
case MERGED:
@@ -417,7 +420,7 @@ public class RootImpl implements Root {
switch (resolution) {
case OURS:
- addChild(name, after, target);
+ addChild(target, name, after);
break;
case THEIRS:
case MERGED:
@@ -450,19 +453,20 @@ public class RootImpl implements Root {
}
}
- private void addChild(String name, NodeState state, Tree target) {
+ private void addChild(Tree target, String name, NodeState state) {
Tree child = target.addChild(name);
for (PropertyState property : state.getProperties()) {
- setProperty(property, child);
+ setProperty(child, property);
}
for (ChildNodeEntry entry : state.getChildNodeEntries()) {
- addChild(entry.getName(), entry.getNodeState(), child);
+ addChild(child, entry.getName(), entry.getNodeState());
}
}
- private void setProperty(PropertyState property, Tree target) {
+ private void setProperty(Tree target, PropertyState property) {
if (property.isArray()) {
- target.setProperty(property.getName(), toList(property.getValues()));
+ target.setProperty(property.getName(),
+ toList(property.getValues(), new ArrayList<CoreValue>()));
}
else {
target.setProperty(property.getName(), property.getValue());
@@ -472,12 +476,4 @@ public class RootImpl implements Root {
});
}
- private static <T> List<T> toList(Iterable<T> values) {
- List<T> l = new ArrayList<T>();
- for (T value : values) {
- l.add(value);
- }
- return l;
- }
-
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/type/TypeValidatorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/type/TypeValidatorProvider.java?rev=1350229&r1=1350228&r2=1350229&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/type/TypeValidatorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/type/TypeValidatorProvider.java Thu Jun 14 13:27:38 2012
@@ -16,14 +16,14 @@
*/
package org.apache.jackrabbit.oak.plugins.type;
+import java.util.HashSet;
+import java.util.Set;
+
import org.apache.jackrabbit.oak.spi.commit.Validator;
import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider;
import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
import org.apache.jackrabbit.oak.spi.state.NodeState;
-import java.util.HashSet;
-import java.util.Set;
-
public class TypeValidatorProvider implements ValidatorProvider {
@Override
@@ -88,6 +88,9 @@ public class TypeValidatorProvider imple
types.add("rep:Members");
types.add("rep:RetentionManageable");
+ // Oak types are always available
+ types.add("mix:mergeConflict");
+
// Find any extra types from /jcr:system/jcr:nodeTypes
NodeState system = after.getChildNode("jcr:system");
if (system != null) {
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/util/Iterators.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/util/Iterators.java?rev=1350229&r1=1350228&r2=1350229&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/util/Iterators.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/util/Iterators.java Thu Jun 14 13:27:38 2012
@@ -16,6 +16,14 @@
*/
package org.apache.jackrabbit.oak.util;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import javax.annotation.Nonnull;
+
import org.apache.commons.collections.iterators.ArrayIterator;
import org.apache.commons.collections.iterators.EmptyIterator;
import org.apache.commons.collections.iterators.FilterIterator;
@@ -23,13 +31,6 @@ import org.apache.commons.collections.it
import org.apache.commons.collections.iterators.SingletonIterator;
import org.apache.commons.collections.iterators.TransformIterator;
-import javax.annotation.Nonnull;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-
/**
* Utility class containing type safe adapters for some of the iterators of
* commons-collections.
@@ -242,4 +243,19 @@ public final class Iterators {
};
}
+ /**
+ * Spools the values of an iterator into a list.
+ * @param values the values to spool
+ * @param list the target list to receive the values
+ * @param <T>
+ * @return {@code list}
+ */
+ @Nonnull
+ public static <T> List<T> toList(Iterable<? extends T> values, List<T> list) {
+ for (T value : values) {
+ list.add(value);
+ }
+ return list;
+ }
+
}
Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java?rev=1350229&r1=1350228&r2=1350229&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java Thu Jun 14 13:27:38 2012
@@ -18,7 +18,9 @@ package org.apache.jackrabbit.oak.jcr;
import java.io.IOException;
import java.text.ParseException;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import javax.annotation.CheckForNull;
@@ -26,6 +28,8 @@ import javax.annotation.Nonnull;
import javax.jcr.ItemExistsException;
import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
@@ -38,8 +42,11 @@ import javax.jcr.version.VersionManager;
import org.apache.jackrabbit.oak.api.AuthInfo;
import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.ConflictHandler;
import org.apache.jackrabbit.oak.api.ContentSession;
import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.api.CoreValueFactory;
+import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.QueryEngine;
import org.apache.jackrabbit.oak.api.Result;
import org.apache.jackrabbit.oak.api.ResultRow;
@@ -52,9 +59,14 @@ import org.apache.jackrabbit.oak.namepat
import org.apache.jackrabbit.oak.namepath.NameMapper;
import org.apache.jackrabbit.oak.namepath.NamePathMapper;
import org.apache.jackrabbit.oak.namepath.NamePathMapperImpl;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.util.Iterators;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static org.apache.jackrabbit.oak.util.Iterators.toList;
+
public class SessionDelegate {
static final Logger log = LoggerFactory.getLogger(SessionDelegate.class);
@@ -66,6 +78,7 @@ public class SessionDelegate {
private final Workspace workspace;
private final Session session;
private final Root root;
+ private final ConflictHandler conflictHandler;
private boolean isAlive = true;
@@ -79,6 +92,7 @@ public class SessionDelegate {
this.workspace = new WorkspaceImpl(this);
this.session = new SessionImpl(this);
this.root = contentSession.getCurrentRoot();
+ this.conflictHandler = new AnnotatingConflictHandler();
}
public boolean isAlive() {
@@ -168,7 +182,7 @@ public class SessionDelegate {
public void save() throws RepositoryException {
try {
- root.commit(DefaultConflictHandler.OURS);
+ root.commit(conflictHandler);
}
catch (CommitFailedException e) {
throw new RepositoryException(e);
@@ -177,7 +191,7 @@ public class SessionDelegate {
public void refresh(boolean keepChanges) {
if (keepChanges) {
- root.rebase(DefaultConflictHandler.OURS);
+ root.rebase(conflictHandler);
}
else {
root.refresh();
@@ -441,4 +455,137 @@ public class SessionDelegate {
return true;
}
}
+
+ //------------------------------------------------------------< AnnotatingConflictHandler >---
+
+ /**
+ * This {@link ConflictHandler} implementation resolves conflicts to
+ * {@link Resolution#THEIRS} and in addition marks nodes where a conflict
+ * occurred with {@code mix:mergeConflict}:
+ *
+ * <pre>
+ * [mix:mergeConflict]
+ * mixin
+ * primaryitem jcr:ours
+ * + jcr:ours (nt:unstructured)
+ * </pre>
+ *
+ * The {@code jcr:ours} sub node contains our version of the node prior to
+ * the conflict.
+ *
+ * TODO: add corresponding commit hook which fails the commit on existence of mix:mergeConflict
+ */
+ private class AnnotatingConflictHandler implements ConflictHandler {
+ // TODO: move these constants to some common location for repository internal node types
+ public static final String MIX_MERGE_CONFLICT = "mix:mergeConflict";
+ public static final String JCR_OURS = "jcr:ours";
+ public static final String ADD_EXISTING = "addExisting";
+ public static final String CHANGE_DELETED = "changeDeleted";
+ public static final String CHANGE_CHANGED = "changeChanged";
+ public static final String DELETE_CHANGED = "deleteChanged";
+
+ private final CoreValueFactory valueFactory;
+ private final String jcrMixinTypes;
+
+ AnnotatingConflictHandler() throws RepositoryException {
+ valueFactory = contentSession.getCoreValueFactory();
+ jcrMixinTypes = getOakPathOrThrow(Property.JCR_MIXIN_TYPES);
+ }
+
+ @Override
+ public Resolution addExistingProperty(Tree parent, PropertyState ours, PropertyState theirs) {
+ Tree marker = addConflictMarker(parent);
+ setProperty(getOrCreateNode(marker, ADD_EXISTING), ours);
+ return Resolution.THEIRS;
+ }
+
+ @Override
+ public Resolution changeDeletedProperty(Tree parent, PropertyState ours) {
+ Tree marker = addConflictMarker(parent);
+ setProperty(getOrCreateNode(marker, CHANGE_DELETED), ours);
+ return Resolution.THEIRS;
+ }
+
+ @Override
+ public Resolution changeChangedProperty(Tree parent, PropertyState ours, PropertyState theirs) {
+ Tree marker = addConflictMarker(parent);
+ setProperty(getOrCreateNode(marker, CHANGE_CHANGED), ours);
+ return Resolution.THEIRS;
+ }
+
+ @Override
+ public Resolution deleteChangedProperty(Tree parent, PropertyState theirs) {
+ Tree marker = addConflictMarker(parent);
+ setProperty(getOrCreateNode(marker, DELETE_CHANGED), theirs);
+ return Resolution.THEIRS;
+ }
+
+ @Override
+ public Resolution addExistingNode(Tree parent, String name, NodeState ours, NodeState theirs) {
+ Tree marker = addConflictMarker(parent);
+ addChild(getOrCreateNode(marker, ADD_EXISTING), name, ours);
+ return Resolution.THEIRS;
+ }
+
+ @Override
+ public Resolution changeDeletedNode(Tree parent, String name, NodeState ours) {
+ Tree marker = addConflictMarker(parent);
+ addChild(getOrCreateNode(marker, CHANGE_DELETED), name, ours);
+ return Resolution.THEIRS;
+ }
+
+ @Override
+ public Resolution deleteChangedNode(Tree parent, String name, NodeState theirs) {
+ Tree marker = addConflictMarker(parent);
+ markChild(getOrCreateNode(marker, DELETE_CHANGED), name);
+ return Resolution.THEIRS;
+ }
+
+ private Tree addConflictMarker(Tree parent) {
+ PropertyState jcrMixin = parent.getProperty(jcrMixinTypes);
+ List<CoreValue> mixins = new ArrayList<CoreValue>();
+ if (jcrMixin != null) {
+ mixins = Iterators.toList(jcrMixin.getValues(), mixins);
+ }
+ if (!mixins.contains(MIX_MERGE_CONFLICT)) {
+ mixins.add(valueFactory.createValue(MIX_MERGE_CONFLICT, PropertyType.NAME));
+ parent.setProperty(jcrMixinTypes, mixins);
+ }
+
+ return getOrCreateNode(parent, JCR_OURS);
+ }
+
+ private Tree getOrCreateNode(Tree parent, String name) {
+ Tree child = parent.getChild(name);
+ if (child == null) {
+ child = parent.addChild(name);
+ }
+ return child;
+ }
+
+ private void addChild(Tree parent, String name, NodeState state) {
+ Tree child = parent.addChild(name);
+ for (PropertyState property : state.getProperties()) {
+ setProperty(child, property);
+ }
+ for (ChildNodeEntry entry : state.getChildNodeEntries()) {
+ addChild(child, entry.getName(), entry.getNodeState());
+ }
+ }
+
+ private void markChild(Tree parent, String name) {
+ parent.addChild(name);
+ }
+
+ private void setProperty(Tree parent, PropertyState property) {
+ if (property.isArray()) {
+ parent.setProperty(property.getName(),
+ toList(property.getValues(), new ArrayList<CoreValue>()));
+ }
+ else {
+ parent.setProperty(property.getName(), property.getValue());
+ }
+ }
+
+ }
}
Modified: jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java?rev=1350229&r1=1350228&r2=1350229&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java Thu Jun 14 13:27:38 2012
@@ -1230,12 +1230,7 @@ public class RepositoryTest extends Abst
session2.save();
assertFalse(session1.getRootNode().hasNode("node"));
- assertTrue(session2.getRootNode().hasNode("node"));
- assertTrue(session2.getRootNode().getNode("node").hasNode("2"));
-
- session1.refresh(true);
- assertTrue(session1.getRootNode().hasNode("node"));
- assertTrue(session1.getRootNode().getNode("node").hasNode("2"));
+ assertFalse(session2.getRootNode().hasNode("node"));
}
finally {
session1.logout();