diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java index 0fa9990be7..700fb9412d 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java @@ -85,11 +85,13 @@ import org.apache.jackrabbit.oak.spi.commit.CommitInfo; import org.apache.jackrabbit.oak.spi.commit.CompositeEditorProvider; import org.apache.jackrabbit.oak.spi.commit.CompositeHook; import org.apache.jackrabbit.oak.spi.commit.ConflictHandler; +import org.apache.jackrabbit.oak.spi.commit.ConflictHandlers; import org.apache.jackrabbit.oak.spi.commit.Editor; import org.apache.jackrabbit.oak.spi.commit.EditorHook; import org.apache.jackrabbit.oak.spi.commit.EditorProvider; import org.apache.jackrabbit.oak.spi.commit.Observable; import org.apache.jackrabbit.oak.spi.commit.Observer; +import org.apache.jackrabbit.oak.spi.commit.ThreeWayConflictHandler; import org.apache.jackrabbit.oak.spi.lifecycle.CompositeInitializer; import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer; import org.apache.jackrabbit.oak.spi.lifecycle.WorkspaceInitializer; @@ -492,8 +494,14 @@ public class Oak { * @param conflictHandler conflict handler * @return this builder */ + @Deprecated @Nonnull public Oak with(@Nonnull ConflictHandler conflictHandler) { + return with(ConflictHandlers.wrap(conflictHandler)); + } + + @Nonnull + public Oak with(@Nonnull ThreeWayConflictHandler conflictHandler) { checkNotNull(conflictHandler); withEditorHook(); commitHooks.add(new ConflictHook(conflictHandler)); diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/AnnotatingConflictHandler.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/AnnotatingConflictHandler.java index b6f8e0b9c1..88fb08e0a3 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/AnnotatingConflictHandler.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/AnnotatingConflictHandler.java @@ -37,14 +37,14 @@ import java.util.List; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants; -import org.apache.jackrabbit.oak.spi.commit.ConflictHandler; +import org.apache.jackrabbit.oak.spi.commit.ThreeWayConflictHandler; import org.apache.jackrabbit.oak.spi.state.ConflictType; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; /** * This {@link ConflictHandler} implementation resolves conflicts to - * {@link org.apache.jackrabbit.oak.spi.commit.ConflictHandler.Resolution#THEIRS} and in addition marks nodes where a + * {@link org.apache.jackrabbit.oak.spi.commit.ThreeWayConflictHandler.Resolution#THEIRS} and in addition marks nodes where a * conflict occurred with the mixin {@code rep:MergeConflict}: * *
@@ -59,66 +59,72 @@ import org.apache.jackrabbit.oak.spi.state.NodeState;
*
* @see ConflictValidator
*/
-public class AnnotatingConflictHandler implements ConflictHandler {
+public class AnnotatingConflictHandler implements ThreeWayConflictHandler {
+
+ // TODO this class might need a look following update to
+ // ThreeWayConflictHandler
@Override
- public Resolution addExistingProperty(NodeBuilder parent, PropertyState ours, PropertyState theirs) {
+ public Resolution addExistingProperty(NodeBuilder parent, PropertyState ours, PropertyState theirs,
+ PropertyState base) {
NodeBuilder marker = addConflictMarker(parent);
createChild(marker, ADD_EXISTING_PROPERTY).setProperty(ours);
return Resolution.THEIRS;
}
@Override
- public Resolution changeDeletedProperty(NodeBuilder parent, PropertyState ours) {
+ public Resolution changeDeletedProperty(NodeBuilder parent, PropertyState ours, PropertyState base) {
NodeBuilder marker = addConflictMarker(parent);
createChild(marker, CHANGE_DELETED_PROPERTY).setProperty(ours);
return Resolution.THEIRS;
}
@Override
- public Resolution changeChangedProperty(NodeBuilder parent, PropertyState ours, PropertyState theirs) {
+ public Resolution changeChangedProperty(NodeBuilder parent, PropertyState ours, PropertyState theirs,
+ PropertyState base) {
NodeBuilder marker = addConflictMarker(parent);
createChild(marker, CHANGE_CHANGED_PROPERTY).setProperty(ours);
return Resolution.THEIRS;
}
@Override
- public Resolution deleteChangedProperty(NodeBuilder parent, PropertyState theirs) {
+ public Resolution deleteChangedProperty(NodeBuilder parent, PropertyState theirs, PropertyState base) {
NodeBuilder marker = addConflictMarker(parent);
createChild(marker, DELETE_CHANGED_PROPERTY).setProperty(theirs);
return Resolution.THEIRS;
}
@Override
- public Resolution deleteDeletedProperty(NodeBuilder parent, PropertyState ours) {
+ public Resolution deleteDeletedProperty(NodeBuilder parent, PropertyState ours, PropertyState base) {
NodeBuilder marker = addConflictMarker(parent);
createChild(marker, DELETE_DELETED_PROPERTY).setProperty(ours);
return Resolution.THEIRS;
}
@Override
- public Resolution addExistingNode(NodeBuilder parent, String name, NodeState ours, NodeState theirs) {
+ public Resolution addExistingNode(NodeBuilder parent, String name, NodeState ours, NodeState theirs,
+ NodeState base) {
NodeBuilder marker = addConflictMarker(parent);
createChild(marker, ADD_EXISTING_NODE).setChildNode(name, ours);
return Resolution.THEIRS;
}
@Override
- public Resolution changeDeletedNode(NodeBuilder parent, String name, NodeState ours) {
+ public Resolution changeDeletedNode(NodeBuilder parent, String name, NodeState ours, NodeState base) {
NodeBuilder marker = addConflictMarker(parent);
createChild(marker, CHANGE_DELETED_NODE).setChildNode(name, ours);
return Resolution.THEIRS;
}
@Override
- public Resolution deleteChangedNode(NodeBuilder parent, String name, NodeState theirs) {
+ public Resolution deleteChangedNode(NodeBuilder parent, String name, NodeState theirs, NodeState base) {
NodeBuilder marker = addConflictMarker(parent);
markChild(createChild(marker, DELETE_CHANGED_NODE), name);
return Resolution.THEIRS;
}
@Override
- public Resolution deleteDeletedNode(NodeBuilder parent, String name) {
+ public Resolution deleteDeletedNode(NodeBuilder parent, String name, NodeState base) {
NodeBuilder marker = addConflictMarker(parent);
markChild(createChild(marker, DELETE_DELETED_NODE), name);
return Resolution.THEIRS;
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ConflictHook.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ConflictHook.java
index 0e1979497e..9fc12f0ea5 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ConflictHook.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/ConflictHook.java
@@ -22,26 +22,37 @@ import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.spi.commit.CommitHook;
import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
import org.apache.jackrabbit.oak.spi.commit.ConflictHandler;
+import org.apache.jackrabbit.oak.spi.commit.ConflictHandlers;
+import org.apache.jackrabbit.oak.spi.commit.ThreeWayConflictHandler;
import org.apache.jackrabbit.oak.spi.state.NodeState;
/**
* This commit hook implementation is responsible for resolving
* conflicts. It does so by detecting the presence of conflict
* markers added by the Microkernel and delegating to a
- * {@link org.apache.jackrabbit.oak.spi.commit.ConflictHandler}
+ * {@link org.apache.jackrabbit.oak.spi.commit.ThreeWayConflictHandler}
* for resolving the conflicts.
*
* @see org.apache.jackrabbit.oak.spi.state.NodeStore#rebase(org.apache.jackrabbit.oak.spi.state.NodeBuilder)
*/
public class ConflictHook implements CommitHook {
- private final ConflictHandler conflictHandler;
+ private final ThreeWayConflictHandler conflictHandler;
+
+ @Deprecated
+ public static final ConflictHook of(ConflictHandler handler) {
+ return of(ConflictHandlers.wrap(handler));
+ }
+
+ public static final ConflictHook of(ThreeWayConflictHandler handler) {
+ return new ConflictHook(handler);
+ }
/**
* Create a new instance of the conflict hook using the
* passed conflict handler for resolving conflicts.
* @param conflictHandler a conflict handler
*/
- public ConflictHook(ConflictHandler conflictHandler) {
+ public ConflictHook(ThreeWayConflictHandler conflictHandler) {
this.conflictHandler = conflictHandler;
}
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/DefaultConflictHandler.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/DefaultConflictHandler.java
index c8080dd6a9..0393cc5cdb 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/DefaultConflictHandler.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/DefaultConflictHandler.java
@@ -28,6 +28,7 @@ import org.apache.jackrabbit.oak.spi.state.NodeState;
* It can be used to implement default behaviour or as a base class for more specialised
* implementations.
*/
+@Deprecated
public class DefaultConflictHandler implements ConflictHandler {
/**
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandler.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandler.java
new file mode 100644
index 0000000000..7718aa22b3
--- /dev/null
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/DefaultThreeWayConflictHandler.java
@@ -0,0 +1,109 @@
+/*
+ * 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.commit;
+
+import org.apache.jackrabbit.oak.spi.commit.ThreeWayConflictHandler;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * This implementation of a {@link ThreeWayConflictHandler} always returns the
+ * same resolution. It can be used to implement default behaviour or as a base
+ * class for more specialised implementations.
+ */
+public class DefaultThreeWayConflictHandler implements ThreeWayConflictHandler {
+
+ // TODO spouldn't this move to the ThreeWayConflictHandler package?
+
+ /**
+ * A {@code ConflictHandler} which always return
+ * {@link org.apache.jackrabbit.oak.spi.commit.ConflictHandler.Resolution#OURS}.
+ */
+ public static final ThreeWayConflictHandler OURS = new DefaultThreeWayConflictHandler(Resolution.OURS);
+
+ /**
+ * A {@code ConflictHandler} which always return
+ * {@link org.apache.jackrabbit.oak.spi.commit.ConflictHandler.Resolution#THEIRS}.
+ */
+ public static final ThreeWayConflictHandler THEIRS = new DefaultThreeWayConflictHandler(Resolution.THEIRS);
+
+ private final Resolution resolution;
+
+ /**
+ * Create a new {@code ConflictHandler} which always returns
+ * {@code resolution}.
+ *
+ * @param resolution
+ * the resolution to return from all methods of this
+ * {@code ConflictHandler} instance.
+ */
+ public DefaultThreeWayConflictHandler(Resolution resolution) {
+ this.resolution = resolution;
+ }
+
+ @Override
+ public Resolution addExistingProperty(NodeBuilder parent, PropertyState ours, PropertyState theirs,
+ PropertyState base) {
+ return resolution;
+ }
+
+ @Override
+ public Resolution changeDeletedProperty(NodeBuilder parent, PropertyState ours, PropertyState base) {
+ return resolution;
+ }
+
+ @Override
+ public Resolution changeChangedProperty(NodeBuilder parent, PropertyState ours, PropertyState theirs,
+ PropertyState base) {
+ return resolution;
+ }
+
+ @Override
+ public Resolution deleteDeletedProperty(NodeBuilder parent, PropertyState ours, PropertyState base) {
+ return resolution;
+ }
+
+ @Override
+ public Resolution deleteChangedProperty(NodeBuilder parent, PropertyState theirs, PropertyState base) {
+ return resolution;
+ }
+
+ @Override
+ public Resolution addExistingNode(NodeBuilder parent, String name, NodeState ours, NodeState theirs,
+ NodeState base) {
+ return resolution;
+ }
+
+ @Override
+ public Resolution changeDeletedNode(NodeBuilder parent, String name, NodeState ours, NodeState base) {
+ return resolution;
+ }
+
+ @Override
+ public Resolution deleteChangedNode(NodeBuilder parent, String name, NodeState theirs, NodeState base) {
+ return resolution;
+ }
+
+ @Override
+ public Resolution deleteDeletedNode(NodeBuilder parent, String name, NodeState base) {
+ return resolution;
+ }
+
+}
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/JcrConflictHandler.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/JcrConflictHandler.java
index 5282a88408..e3dfdeb9c7 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/JcrConflictHandler.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/JcrConflictHandler.java
@@ -18,8 +18,10 @@
*/
package org.apache.jackrabbit.oak.plugins.commit;
-import com.google.common.collect.ImmutableList;
import org.apache.jackrabbit.oak.spi.commit.CompositeConflictHandler;
+import org.apache.jackrabbit.oak.spi.commit.ConflictHandlers;
+
+import com.google.common.collect.ImmutableList;
/**
* Utility class providing conflict handlers used for JCR.
@@ -32,10 +34,9 @@ public final class JcrConflictHandler {
*/
public static CompositeConflictHandler createJcrConflictHandler() {
return new CompositeConflictHandler(ImmutableList.of(
- new JcrLastModifiedConflictHandler(),
- new ChildOrderConflictHandler(),
- new AnnotatingConflictHandler()
- ));
+ ConflictHandlers.wrap(new JcrLastModifiedConflictHandler()),
+ ConflictHandlers.wrap(new ChildOrderConflictHandler()),
+ new AnnotatingConflictHandler()));
}
private JcrConflictHandler() {
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/MergingNodeStateDiff.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/MergingNodeStateDiff.java
index 57cf46789c..3469352f60 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/MergingNodeStateDiff.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/commit/MergingNodeStateDiff.java
@@ -17,7 +17,9 @@
package org.apache.jackrabbit.oak.plugins.commit;
import static org.apache.jackrabbit.oak.api.Type.NAME;
+import static org.apache.jackrabbit.oak.spi.state.ConflictAnnotatingRebaseDiff.BASE;
import static org.apache.jackrabbit.oak.spi.state.ConflictAnnotatingRebaseDiff.CONFLICT;
+import static org.apache.jackrabbit.oak.spi.state.ConflictAnnotatingRebaseDiff.OURS;
import static org.apache.jackrabbit.oak.spi.state.ConflictType.ADD_EXISTING_NODE;
import static org.apache.jackrabbit.oak.spi.state.ConflictType.ADD_EXISTING_PROPERTY;
import static org.apache.jackrabbit.oak.spi.state.ConflictType.CHANGE_CHANGED_PROPERTY;
@@ -29,15 +31,21 @@ import static org.apache.jackrabbit.oak.spi.state.ConflictType.DELETE_DELETED_NO
import static org.apache.jackrabbit.oak.spi.state.ConflictType.DELETE_DELETED_PROPERTY;
import java.util.Map;
+import java.util.Set;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.json.JsopDiff;
+import org.apache.jackrabbit.oak.plugins.memory.EmptyPropertyState;
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;
-import org.apache.jackrabbit.oak.spi.commit.PartialConflictHandler.Resolution;
+import org.apache.jackrabbit.oak.spi.commit.ThreeWayConflictHandler.Resolution;
+import org.apache.jackrabbit.oak.spi.commit.ThreeWayConflictHandler;
import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.ConflictAnnotatingRebaseDiff;
import org.apache.jackrabbit.oak.spi.state.ConflictType;
import org.apache.jackrabbit.oak.spi.state.DefaultNodeStateDiff;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -53,20 +61,20 @@ public final class MergingNodeStateDiff extends DefaultNodeStateDiff {
private final NodeState parent;
private final NodeBuilder target;
- private final ConflictHandler conflictHandler;
+ private final ThreeWayConflictHandler conflictHandler;
- private MergingNodeStateDiff(NodeState parent, NodeBuilder target, ConflictHandler conflictHandler) {
+ private MergingNodeStateDiff(NodeState parent, NodeBuilder target, ThreeWayConflictHandler conflictHandler) {
this.parent = parent;
this.target = target;
this.conflictHandler = conflictHandler;
}
- static NodeState merge(NodeState fromState, NodeState toState, ConflictHandler conflictHandler) {
+ static NodeState merge(NodeState fromState, NodeState toState, ThreeWayConflictHandler conflictHandler) {
return merge(fromState, toState, toState.builder(), conflictHandler).getNodeState();
}
private static NodeBuilder merge(NodeState fromState, NodeState toState, NodeBuilder target,
- ConflictHandler conflictHandler) {
+ ThreeWayConflictHandler conflictHandler) {
toState.compareAgainstBaseState(fromState,
new MergingNodeStateDiff(toState, target, conflictHandler));
@@ -98,23 +106,44 @@ public final class MergingNodeStateDiff extends DefaultNodeStateDiff {
private void resolveConflict(ConflictType conflictType, NodeState conflictInfo) {
PropertyConflictHandler propertyConflictHandler = propertyConflictHandlers.get(conflictType);
if (propertyConflictHandler != null) {
- for (PropertyState ours : conflictInfo.getProperties()) {
- PropertyState theirs = parent.getProperty(ours.getName());
- Resolution resolution = propertyConflictHandler.resolve(ours, theirs);
- applyResolution(resolution, conflictType, ours);
+ NodeState oursNS = conflictInfo.getChildNode(OURS);
+ NodeState baseNS = conflictInfo.getChildNode(BASE);
+
+ Set processed = Sets.newHashSet();
+ for (PropertyState ours : oursNS.getProperties()) {
+ String name = ours.getName();
+ processed.add(name);
+ PropertyState base = baseNS.getProperty(name);
+ PropertyState theirs = parent.getProperty(name);
+ Resolution resolution = propertyConflictHandler.resolve(ours, theirs, base);
+ applyPropertyResolution(resolution, conflictType, name, ours);
}
- }
- else {
+ for (PropertyState base : baseNS.getProperties()) {
+ String name = base.getName();
+ if (processed.contains(name)) {
+ continue;
+ }
+ PropertyState theirs = parent.getProperty(name);
+ Resolution resolution = propertyConflictHandler.resolve(null, theirs, base);
+ applyPropertyResolution(resolution, conflictType, name, null);
+ }
+ } else {
NodeConflictHandler nodeConflictHandler = nodeConflictHandlers.get(conflictType);
if (nodeConflictHandler != null) {
- for (ChildNodeEntry oursCNE : conflictInfo.getChildNodeEntries()) {
- String name = oursCNE.getName();
- NodeState ours = oursCNE.getNodeState();
+ NodeState oursNS = conflictInfo.getChildNode(OURS);
+ NodeState baseNS = conflictInfo.getChildNode(BASE);
+
+ Set candidates = Sets.union(Sets.newHashSet(oursNS.getChildNodeNames()),
+ Sets.newHashSet(baseNS.getChildNodeNames()));
+ for (String name : candidates) {
+ NodeState ours = oursNS.getChildNode(name);
+ NodeState base = baseNS.getChildNode(name);
NodeState theirs = parent.getChildNode(name);
- Resolution resolution = nodeConflictHandler.resolve(name, ours, theirs);
+ Resolution resolution = nodeConflictHandler.resolve(name, ours, theirs, base);
applyResolution(resolution, conflictType, name, ours);
+
if (LOG.isDebugEnabled()) {
- String diff = JsopDiff.diffToJsop(ours, theirs);
+ String diff = JsopDiff.diffToJsop(base, theirs);
LOG.debug(
"{} resolved conflict of type {} with resolution {} on node {}, conflict trace {}",
nodeConflictHandler, conflictType, resolution,
@@ -129,12 +158,12 @@ public final class MergingNodeStateDiff extends DefaultNodeStateDiff {
NodeBuilder conflictMarker = getConflictMarker(conflictType);
if (conflictMarker != null) {
- assert conflictMarker.getChildNodeCount(1) == 0;
+ assert conflictMarker.getChildNode(ConflictAnnotatingRebaseDiff.BASE).getChildNodeCount(1) == 0;
+ assert conflictMarker.getChildNode(ConflictAnnotatingRebaseDiff.OURS).getChildNodeCount(1) == 0;
}
}
- private void applyResolution(Resolution resolution, ConflictType conflictType, PropertyState ours) {
- String name = ours.getName();
+ private void applyPropertyResolution(Resolution resolution, ConflictType conflictType, String name, PropertyState ours) {
NodeBuilder conflictMarker = getConflictMarker(conflictType);
if (resolution == Resolution.OURS) {
if (DELETE_CHANGED_PROPERTY == conflictType) {
@@ -145,7 +174,14 @@ public final class MergingNodeStateDiff extends DefaultNodeStateDiff {
}
}
- conflictMarker.removeProperty(name);
+ NodeBuilder baseClean = conflictMarker.getChildNode(ConflictAnnotatingRebaseDiff.BASE);
+ if (baseClean.exists()) {
+ baseClean.removeProperty(name);
+ }
+ NodeBuilder oursClean = conflictMarker.getChildNode(ConflictAnnotatingRebaseDiff.OURS);
+ if (oursClean.exists()) {
+ oursClean.removeProperty(name);
+ }
}
private void applyResolution(Resolution resolution, ConflictType conflictType, String name, NodeState ours) {
@@ -158,7 +194,23 @@ public final class MergingNodeStateDiff extends DefaultNodeStateDiff {
addChild(target, name, ours);
}
}
- conflictMarker.getChildNode(name).remove();
+
+ NodeBuilder baseClean = conflictMarker.getChildNode(ConflictAnnotatingRebaseDiff.BASE);
+ if (baseClean.exists()) {
+ baseClean.getChildNode(name).remove();
+ // if (baseClean.getChildNodeCount(1) == 0) {
+ // baseClean.remove();
+ // }
+ }
+
+ NodeBuilder oursClean = conflictMarker.getChildNode(ConflictAnnotatingRebaseDiff.OURS);
+ if (oursClean.exists()) {
+ oursClean.getChildNode(name).remove();
+ // if (oursClean.getChildNodeCount(1) == 0) {
+ // oursClean.remove();
+ // }
+ }
+
}
private NodeBuilder getConflictMarker(ConflictType conflictType) {
@@ -174,18 +226,19 @@ public final class MergingNodeStateDiff extends DefaultNodeStateDiff {
}
private interface PropertyConflictHandler {
- Resolution resolve(PropertyState ours, PropertyState theirs);
+ Resolution resolve(PropertyState ours, PropertyState theirs, PropertyState base);
}
private interface NodeConflictHandler {
- Resolution resolve(String name, NodeState ours, NodeState theirs);
+ Resolution resolve(String name, NodeState ours, NodeState theirs, NodeState base);
}
private final Map propertyConflictHandlers = ImmutableMap.of(
ADD_EXISTING_PROPERTY, new PropertyConflictHandler() {
@Override
- public Resolution resolve(PropertyState ours, PropertyState theirs) {
- return conflictHandler.addExistingProperty(target, ours, theirs);
+ public Resolution resolve(PropertyState ours, PropertyState theirs, PropertyState base) {
+ Preconditions.checkNotNull(ours);
+ return conflictHandler.addExistingProperty(target, ours, theirs, base);
}
@Override
@@ -195,8 +248,9 @@ public final class MergingNodeStateDiff extends DefaultNodeStateDiff {
},
CHANGE_DELETED_PROPERTY, new PropertyConflictHandler() {
@Override
- public Resolution resolve(PropertyState ours, PropertyState theirs) {
- return conflictHandler.changeDeletedProperty(target, ours);
+ public Resolution resolve(PropertyState ours, PropertyState theirs, PropertyState base) {
+ Preconditions.checkNotNull(ours);
+ return conflictHandler.changeDeletedProperty(target, ours, base);
}
@Override
@@ -206,8 +260,9 @@ public final class MergingNodeStateDiff extends DefaultNodeStateDiff {
},
CHANGE_CHANGED_PROPERTY, new PropertyConflictHandler() {
@Override
- public Resolution resolve(PropertyState ours, PropertyState theirs) {
- return conflictHandler.changeChangedProperty(target, ours, theirs);
+ public Resolution resolve(PropertyState ours, PropertyState theirs, PropertyState base) {
+ Preconditions.checkNotNull(ours);
+ return conflictHandler.changeChangedProperty(target, ours, theirs, base);
}
@Override
@@ -217,8 +272,9 @@ public final class MergingNodeStateDiff extends DefaultNodeStateDiff {
},
DELETE_DELETED_PROPERTY, new PropertyConflictHandler() {
@Override
- public Resolution resolve(PropertyState ours, PropertyState theirs) {
- return conflictHandler.deleteDeletedProperty(target, ours);
+ public Resolution resolve(PropertyState ours, PropertyState theirs, PropertyState base) {
+ Preconditions.checkNotNull(ours);
+ return conflictHandler.deleteDeletedProperty(target, ours, base);
}
@Override
@@ -228,8 +284,8 @@ public final class MergingNodeStateDiff extends DefaultNodeStateDiff {
},
DELETE_CHANGED_PROPERTY, new PropertyConflictHandler() {
@Override
- public Resolution resolve(PropertyState ours, PropertyState theirs) {
- return conflictHandler.deleteChangedProperty(target, theirs);
+ public Resolution resolve(PropertyState ours, PropertyState theirs, PropertyState base) {
+ return conflictHandler.deleteChangedProperty(target, theirs, base);
}
@Override
@@ -242,8 +298,8 @@ public final class MergingNodeStateDiff extends DefaultNodeStateDiff {
private final Map nodeConflictHandlers = ImmutableMap.of(
ADD_EXISTING_NODE, new NodeConflictHandler() {
@Override
- public Resolution resolve(String name, NodeState ours, NodeState theirs) {
- return conflictHandler.addExistingNode(target, name, ours, theirs);
+ public Resolution resolve(String name, NodeState ours, NodeState theirs, NodeState base) {
+ return conflictHandler.addExistingNode(target, name, ours, theirs, base);
}
@Override
@@ -253,8 +309,8 @@ public final class MergingNodeStateDiff extends DefaultNodeStateDiff {
},
CHANGE_DELETED_NODE, new NodeConflictHandler() {
@Override
- public Resolution resolve(String name, NodeState ours, NodeState theirs) {
- return conflictHandler.changeDeletedNode(target, name, ours);
+ public Resolution resolve(String name, NodeState ours, NodeState theirs, NodeState base) {
+ return conflictHandler.changeDeletedNode(target, name, ours, base);
}
@Override
@@ -264,8 +320,8 @@ public final class MergingNodeStateDiff extends DefaultNodeStateDiff {
},
DELETE_CHANGED_NODE, new NodeConflictHandler() {
@Override
- public Resolution resolve(String name, NodeState ours, NodeState theirs) {
- return conflictHandler.deleteChangedNode(target, name, theirs);
+ public Resolution resolve(String name, NodeState ours, NodeState theirs, NodeState base) {
+ return conflictHandler.deleteChangedNode(target, name, theirs, base);
}
@Override
@@ -275,8 +331,8 @@ public final class MergingNodeStateDiff extends DefaultNodeStateDiff {
},
DELETE_DELETED_NODE, new NodeConflictHandler() {
@Override
- public Resolution resolve(String name, NodeState ours, NodeState theirs) {
- return conflictHandler.deleteDeletedNode(target, name);
+ public Resolution resolve(String name, NodeState ours, NodeState theirs, NodeState base) {
+ return conflictHandler.deleteDeletedNode(target, name, base);
}
@Override
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java
index 087ee97c43..5c558d1614 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java
@@ -851,7 +851,7 @@ public class AsyncIndexUpdate implements Runnable, Closeable {
editorProviders.addAll(validatorProviders);
CompositeHook hooks = new CompositeHook(
ResetCommitAttributeHook.INSTANCE,
- new ConflictHook(new AnnotatingConflictHandler()),
+ ConflictHook.of(new AnnotatingConflictHandler()),
new EditorHook(CompositeEditorProvider.compose(editorProviders)),
concurrentUpdateCheck);
try {
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentPropertyUpdateTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentPropertyUpdateTest.java
index 4ba4875b32..e4124a819b 100644
--- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentPropertyUpdateTest.java
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ConcurrentPropertyUpdateTest.java
@@ -46,7 +46,7 @@ public class ConcurrentPropertyUpdateTest extends BaseDocumentMKTest {
private static final int NUM_THREADS = 2;
private static final CommitHook HOOK = new CompositeHook(
- new ConflictHook(new AnnotatingConflictHandler()),
+ ConflictHook.of(new AnnotatingConflictHandler()),
new EditorHook(new ConflictValidatorProvider()));
private ExecutorService service = Executors.newFixedThreadPool(NUM_THREADS);
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java
index 75f269e1b5..878f255d21 100644
--- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java
@@ -1930,7 +1930,7 @@ public class DocumentNodeStoreTest {
hookList.add(hook);
}
hookList.add(blockingHook);
- hookList.add(new ConflictHook(new AnnotatingConflictHandler()));
+ hookList.add(ConflictHook.of(new AnnotatingConflictHandler()));
hookList.add(new EditorHook(new ConflictValidatorProvider()));
store.merge(builder, CompositeHook.compose(hookList), CommitInfo.EMPTY);
} catch (CommitFailedException cfe) {
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/HierarchyConflictTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/HierarchyConflictTest.java
index 8adf1aa605..15382db640 100644
--- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/HierarchyConflictTest.java
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/HierarchyConflictTest.java
@@ -208,7 +208,7 @@ public class HierarchyConflictTest {
return null;
}
}),
- new ConflictHook(new AnnotatingConflictHandler()),
+ ConflictHook.of(new AnnotatingConflictHandler()),
new EditorHook(new ConflictValidatorProvider()));
store.merge(root, hooks, CommitInfo.EMPTY);
}
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeStoreDiffTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeStoreDiffTest.java
index 939f224ae6..22be630b88 100644
--- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeStoreDiffTest.java
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeStoreDiffTest.java
@@ -98,7 +98,7 @@ public class NodeStoreDiffTest {
//For now exception would be thrown
ns.merge(b1,
new CompositeHook(
- new ConflictHook(new AnnotatingConflictHandler()),
+ ConflictHook.of(new AnnotatingConflictHandler()),
new EditorHook(new ConflictValidatorProvider())
),
CommitInfo.EMPTY);
diff --git a/oak-it/src/test/java/org/apache/jackrabbit/oak/api/TreeTest.java b/oak-it/src/test/java/org/apache/jackrabbit/oak/api/TreeTest.java
index 6552f39151..16e37b83f1 100644
--- a/oak-it/src/test/java/org/apache/jackrabbit/oak/api/TreeTest.java
+++ b/oak-it/src/test/java/org/apache/jackrabbit/oak/api/TreeTest.java
@@ -37,6 +37,7 @@ import org.apache.jackrabbit.oak.plugins.commit.AnnotatingConflictHandler;
import org.apache.jackrabbit.oak.plugins.commit.ChildOrderConflictHandler;
import org.apache.jackrabbit.oak.plugins.commit.ConflictValidatorProvider;
import org.apache.jackrabbit.oak.spi.commit.CompositeConflictHandler;
+import org.apache.jackrabbit.oak.spi.commit.ConflictHandlers;
import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -60,7 +61,7 @@ public class TreeTest extends OakBaseTest {
repository = new Oak(store)
.with(new OpenSecurityProvider())
.with(new CompositeConflictHandler(ImmutableList.of(
- new ChildOrderConflictHandler() {
+ ConflictHandlers.wrap(new ChildOrderConflictHandler() {
/**
* Allow deleting changed node.
* See {@link TreeTest#removeWithConcurrentOrderBefore()}
@@ -71,7 +72,7 @@ public class TreeTest extends OakBaseTest {
NodeState theirs) {
return Resolution.OURS;
}
- },
+ }),
new AnnotatingConflictHandler()
)))
.with(new ConflictValidatorProvider())
diff --git a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java
index e6903174ac..1283fc6263 100644
--- a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java
+++ b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java
@@ -53,10 +53,12 @@ import org.apache.jackrabbit.oak.security.SecurityProviderImpl;
import org.apache.jackrabbit.oak.spi.commit.BackgroundObserver;
import org.apache.jackrabbit.oak.spi.commit.CommitHook;
import org.apache.jackrabbit.oak.spi.commit.CompositeConflictHandler;
+import org.apache.jackrabbit.oak.spi.commit.ConflictHandlers;
import org.apache.jackrabbit.oak.spi.commit.Editor;
import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
import org.apache.jackrabbit.oak.spi.commit.Observer;
import org.apache.jackrabbit.oak.spi.commit.PartialConflictHandler;
+import org.apache.jackrabbit.oak.spi.commit.ThreeWayConflictHandler;
import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer;
import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
@@ -216,8 +218,14 @@ public class Jcr {
return this;
}
+ @Deprecated
@Nonnull
public final Jcr with(@Nonnull PartialConflictHandler conflictHandler) {
+ return with(ConflictHandlers.wrap(conflictHandler));
+ }
+
+ @Nonnull
+ public final Jcr with(@Nonnull ThreeWayConflictHandler conflictHandler) {
ensureRepositoryIsNotCreated();
this.conflictHandler.addHandler(checkNotNull(conflictHandler));
return this;
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentCompactionIT.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentCompactionIT.java
index cbd6933d09..0de4d57910 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentCompactionIT.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentCompactionIT.java
@@ -77,7 +77,7 @@ import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.jmx.CacheStatsMBean;
import org.apache.jackrabbit.oak.commons.jmx.AnnotatedStandardMBean;
import org.apache.jackrabbit.oak.plugins.commit.ConflictHook;
-import org.apache.jackrabbit.oak.plugins.commit.DefaultConflictHandler;
+import org.apache.jackrabbit.oak.plugins.commit.DefaultThreeWayConflictHandler;
import org.apache.jackrabbit.oak.plugins.metric.MetricStatisticsProvider;
import org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions;
import org.apache.jackrabbit.oak.segment.compaction.SegmentRevisionGC;
@@ -471,8 +471,8 @@ public class SegmentCompactionIT {
if (!cancelled) {
try {
CommitHook commitHook = rnd.nextBoolean()
- ? new CompositeHook(new ConflictHook(DefaultConflictHandler.OURS))
- : new CompositeHook(new ConflictHook(DefaultConflictHandler.THEIRS));
+ ? new CompositeHook(ConflictHook.of(DefaultThreeWayConflictHandler.OURS))
+ : new CompositeHook(ConflictHook.of(DefaultThreeWayConflictHandler.THEIRS));
nodeStore.merge(root, commitHook, CommitInfo.EMPTY);
segmentCompactionMBean.committed();
} catch (CommitFailedException e) {
diff --git a/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/CompositeConflictHandler.java b/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/CompositeConflictHandler.java
index cc76adfd9b..dbe08827a9 100644
--- a/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/CompositeConflictHandler.java
+++ b/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/CompositeConflictHandler.java
@@ -30,6 +30,7 @@ import static org.apache.jackrabbit.oak.spi.state.ConflictType.DELETE_CHANGED_NO
import static org.apache.jackrabbit.oak.spi.state.ConflictType.DELETE_CHANGED_PROPERTY;
import static org.apache.jackrabbit.oak.spi.state.ConflictType.DELETE_DELETED_NODE;
import static org.apache.jackrabbit.oak.spi.state.ConflictType.DELETE_DELETED_PROPERTY;
+import static org.apache.jackrabbit.oak.spi.commit.ThreeWayConflictHandler.Resolution.IGNORED;
import java.util.LinkedList;
@@ -47,8 +48,8 @@ import org.apache.jackrabbit.oak.spi.state.NodeState;
* conflict none of the backing handlers returns a valid resolution
* this implementation throws an {@code IllegalStateException}.
*/
-public class CompositeConflictHandler implements ConflictHandler {
- private final LinkedList handlers;
+public class CompositeConflictHandler implements ThreeWayConflictHandler {
+ private final LinkedList handlers;
/**
* Create a new {@code CompositeConflictHandler} with an initial set of
@@ -56,7 +57,7 @@ public class CompositeConflictHandler implements ConflictHandler {
* handlers.
* @param handlers the backing handlers
*/
- public CompositeConflictHandler(@Nonnull Iterable handlers) {
+ public CompositeConflictHandler(@Nonnull Iterable handlers) {
this.handlers = newLinkedList(checkNotNull(handlers));
}
@@ -74,16 +75,17 @@ public class CompositeConflictHandler implements ConflictHandler {
* @param handler
* @return this
*/
- public CompositeConflictHandler addHandler(PartialConflictHandler handler) {
+ public CompositeConflictHandler addHandler(ThreeWayConflictHandler handler) {
handlers.addFirst(handler);
return this;
}
@Override
- public Resolution addExistingProperty(NodeBuilder parent, PropertyState ours, PropertyState theirs) {
- for (PartialConflictHandler handler : handlers) {
- Resolution resolution = handler.addExistingProperty(parent, ours, theirs);
- if (resolution != null) {
+ public Resolution addExistingProperty(NodeBuilder parent, PropertyState ours, PropertyState theirs,
+ PropertyState base) {
+ for (ThreeWayConflictHandler handler : handlers) {
+ Resolution resolution = handler.addExistingProperty(parent, ours, theirs, base);
+ if (resolution != /* null */ IGNORED) {
return resolution;
}
}
@@ -92,10 +94,10 @@ public class CompositeConflictHandler implements ConflictHandler {
}
@Override
- public Resolution changeDeletedProperty(NodeBuilder parent, PropertyState ours) {
- for (PartialConflictHandler handler : handlers) {
- Resolution resolution = handler.changeDeletedProperty(parent, ours);
- if (resolution != null) {
+ public Resolution changeDeletedProperty(NodeBuilder parent, PropertyState ours, PropertyState base) {
+ for (ThreeWayConflictHandler handler : handlers) {
+ Resolution resolution = handler.changeDeletedProperty(parent, ours, base);
+ if (resolution != /* null */ IGNORED) {
return resolution;
}
}
@@ -104,10 +106,11 @@ public class CompositeConflictHandler implements ConflictHandler {
}
@Override
- public Resolution changeChangedProperty(NodeBuilder parent, PropertyState ours, PropertyState theirs) {
- for (PartialConflictHandler handler : handlers) {
- Resolution resolution = handler.changeChangedProperty(parent, ours, theirs);
- if (resolution != null) {
+ public Resolution changeChangedProperty(NodeBuilder parent, PropertyState ours, PropertyState theirs,
+ PropertyState base) {
+ for (ThreeWayConflictHandler handler : handlers) {
+ Resolution resolution = handler.changeChangedProperty(parent, ours, theirs, base);
+ if (resolution != /* null */ IGNORED) {
return resolution;
}
}
@@ -116,10 +119,10 @@ public class CompositeConflictHandler implements ConflictHandler {
}
@Override
- public Resolution deleteDeletedProperty(NodeBuilder parent, PropertyState ours) {
- for (PartialConflictHandler handler : handlers) {
- Resolution resolution = handler.deleteDeletedProperty(parent, ours);
- if (resolution != null) {
+ public Resolution deleteDeletedProperty(NodeBuilder parent, PropertyState ours, PropertyState base) {
+ for (ThreeWayConflictHandler handler : handlers) {
+ Resolution resolution = handler.deleteDeletedProperty(parent, ours, base);
+ if (resolution != /* null */ IGNORED) {
return resolution;
}
}
@@ -128,10 +131,10 @@ public class CompositeConflictHandler implements ConflictHandler {
}
@Override
- public Resolution deleteChangedProperty(NodeBuilder parent, PropertyState theirs) {
- for (PartialConflictHandler handler : handlers) {
- Resolution resolution = handler.deleteChangedProperty(parent, theirs);
- if (resolution != null) {
+ public Resolution deleteChangedProperty(NodeBuilder parent, PropertyState theirs, PropertyState base) {
+ for (ThreeWayConflictHandler handler : handlers) {
+ Resolution resolution = handler.deleteChangedProperty(parent, theirs, base);
+ if (resolution != /* null */ IGNORED) {
return resolution;
}
}
@@ -140,10 +143,10 @@ public class CompositeConflictHandler implements ConflictHandler {
}
@Override
- public Resolution addExistingNode(NodeBuilder parent, String name, NodeState ours, NodeState theirs) {
- for (PartialConflictHandler handler : handlers) {
- Resolution resolution = handler.addExistingNode(parent, name, ours, theirs);
- if (resolution != null) {
+ public Resolution addExistingNode(NodeBuilder parent, String name, NodeState ours, NodeState theirs, NodeState base) {
+ for (ThreeWayConflictHandler handler : handlers) {
+ Resolution resolution = handler.addExistingNode(parent, name, ours, theirs, base);
+ if (resolution !=IGNORED) {
return resolution;
}
}
@@ -152,10 +155,10 @@ public class CompositeConflictHandler implements ConflictHandler {
}
@Override
- public Resolution changeDeletedNode(NodeBuilder parent, String name, NodeState ours) {
- for (PartialConflictHandler handler : handlers) {
- Resolution resolution = handler.changeDeletedNode(parent, name, ours);
- if (resolution != null) {
+ public Resolution changeDeletedNode(NodeBuilder parent, String name, NodeState ours, NodeState base) {
+ for (ThreeWayConflictHandler handler : handlers) {
+ Resolution resolution = handler.changeDeletedNode(parent, name, ours, base);
+ if (resolution != IGNORED) {
return resolution;
}
}
@@ -164,10 +167,10 @@ public class CompositeConflictHandler implements ConflictHandler {
}
@Override
- public Resolution deleteChangedNode(NodeBuilder parent, String name, NodeState theirs) {
- for (PartialConflictHandler handler : handlers) {
- Resolution resolution = handler.deleteChangedNode(parent, name, theirs);
- if (resolution != null) {
+ public Resolution deleteChangedNode(NodeBuilder parent, String name, NodeState theirs, NodeState base) {
+ for (ThreeWayConflictHandler handler : handlers) {
+ Resolution resolution = handler.deleteChangedNode(parent, name, theirs, base);
+ if (resolution != IGNORED) {
return resolution;
}
}
@@ -176,14 +179,15 @@ public class CompositeConflictHandler implements ConflictHandler {
}
@Override
- public Resolution deleteDeletedNode(NodeBuilder parent, String name) {
- for (PartialConflictHandler handler : handlers) {
- Resolution resolution = handler.deleteDeletedNode(parent, name);
- if (resolution != null) {
+ public Resolution deleteDeletedNode(NodeBuilder parent, String name, NodeState base) {
+ for (ThreeWayConflictHandler handler : handlers) {
+ Resolution resolution = handler.deleteDeletedNode(parent, name, base);
+ if (resolution != IGNORED) {
return resolution;
}
}
throw new IllegalStateException("No conflict handler for " +
DELETE_DELETED_NODE + " conflict");
}
+
}
diff --git a/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/ConflictHandler.java b/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/ConflictHandler.java
index 985ca82483..34710bcca9 100644
--- a/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/ConflictHandler.java
+++ b/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/ConflictHandler.java
@@ -39,6 +39,7 @@ import org.apache.jackrabbit.oak.spi.state.NodeState;
*
* @see ConflictHandler
*/
+@Deprecated
public interface ConflictHandler extends PartialConflictHandler {
/**
diff --git a/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/ConflictHandlers.java b/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/ConflictHandlers.java
new file mode 100644
index 0000000000..180e295c29
--- /dev/null
+++ b/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/ConflictHandlers.java
@@ -0,0 +1,108 @@
+/*
+ * 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.spi.commit;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.spi.commit.ThreeWayConflictHandler.Resolution;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+public class ConflictHandlers {
+
+ // TODO SubtreeConflictHandlers?
+
+ private ConflictHandlers() {
+ }
+
+ @SuppressWarnings("deprecation")
+ public static ThreeWayConflictHandler wrap(PartialConflictHandler handler) {
+ return new ThreeWayConflictHandlerWrapper(handler);
+ }
+
+ @SuppressWarnings("deprecation")
+ private static Resolution wrap(org.apache.jackrabbit.oak.spi.commit.PartialConflictHandler.Resolution r) {
+ if (r == null) {
+ return Resolution.IGNORED;
+ }
+ switch (r) {
+ case OURS:
+ return Resolution.OURS;
+ case THEIRS:
+ return Resolution.THEIRS;
+ case MERGED:
+ return Resolution.MERGED;
+ }
+ return Resolution.IGNORED;
+ }
+
+ @SuppressWarnings("deprecation")
+ private static class ThreeWayConflictHandlerWrapper implements ThreeWayConflictHandler {
+ private final PartialConflictHandler handler;
+
+ public ThreeWayConflictHandlerWrapper(PartialConflictHandler handler) {
+ this.handler = handler;
+ }
+
+ @Override
+ public Resolution addExistingProperty(NodeBuilder parent, PropertyState ours, PropertyState theirs,
+ PropertyState base) {
+ return wrap(handler.addExistingProperty(parent, ours, theirs));
+ }
+
+ @Override
+ public Resolution changeDeletedProperty(NodeBuilder parent, PropertyState ours, PropertyState base) {
+ return wrap(handler.changeDeletedProperty(parent, ours));
+ }
+
+ @Override
+ public Resolution changeChangedProperty(NodeBuilder parent, PropertyState ours, PropertyState theirs,
+ PropertyState base) {
+ return wrap(handler.changeChangedProperty(parent, ours, theirs));
+ }
+
+ @Override
+ public Resolution deleteDeletedProperty(NodeBuilder parent, PropertyState ours, PropertyState base) {
+ return wrap(handler.deleteDeletedProperty(parent, ours));
+ }
+
+ @Override
+ public Resolution deleteChangedProperty(NodeBuilder parent, PropertyState theirs, PropertyState base) {
+ return wrap(handler.deleteChangedProperty(parent, theirs));
+ }
+
+ @Override
+ public Resolution addExistingNode(NodeBuilder parent, String name, NodeState ours, NodeState theirs,
+ NodeState base) {
+ return wrap(handler.addExistingNode(parent, name, ours, theirs));
+ }
+
+ @Override
+ public Resolution changeDeletedNode(NodeBuilder parent, String name, NodeState ours, NodeState base) {
+ return wrap(handler.changeDeletedNode(parent, name, ours));
+ }
+
+ @Override
+ public Resolution deleteChangedNode(NodeBuilder parent, String name, NodeState theirs, NodeState base) {
+ return wrap(handler.deleteChangedNode(parent, name, theirs));
+ }
+
+ @Override
+ public Resolution deleteDeletedNode(NodeBuilder parent, String name, NodeState base) {
+ return wrap(handler.deleteDeletedNode(parent, name));
+ }
+ }
+}
diff --git a/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/PartialConflictHandler.java b/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/PartialConflictHandler.java
index 8769c622f0..a427d80a4c 100644
--- a/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/PartialConflictHandler.java
+++ b/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/PartialConflictHandler.java
@@ -41,6 +41,7 @@ import org.apache.jackrabbit.oak.spi.state.NodeState;
*
* @see ConflictHandler
*/
+@Deprecated
public interface PartialConflictHandler {
/**
diff --git a/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/ThreeWayConflictHandler.java b/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/ThreeWayConflictHandler.java
new file mode 100644
index 0000000000..0021c84cc8
--- /dev/null
+++ b/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/commit/ThreeWayConflictHandler.java
@@ -0,0 +1,175 @@
+/*
+ * 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.spi.commit;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * A {@code ConflictHandler} is responsible for handling conflicts which happen
+ * on {@link org.apache.jackrabbit.oak.api.Root#rebase()} and on the implicit rebase operation which
+ * takes part on {@link org.apache.jackrabbit.oak.api.Root#commit()}.
+ *
+ * This interface contains one method per type of conflict which might occur.
+ * Each of these methods must return a {@link Resolution} for the current conflict.
+ * The resolution indicates to use the changes in the current {@code Root} instance
+ * ({@link Resolution#OURS}) or to use the changes from the underlying persistence
+ * store ({@link Resolution#THEIRS}). Alternatively the resolution can also indicate
+ * that the changes have been successfully merged by this {@code ConflictHandler}
+ * instance ({@link Resolution#MERGED}).
+ *
+ * @see ConflictHandler
+ */
+public interface ThreeWayConflictHandler {
+ /**
+ * Resolutions for conflicts
+ */
+ enum Resolution {
+ /**
+ * Use the changes from the current {@link org.apache.jackrabbit.oak.api.Root} instance
+ */
+ OURS,
+
+ /**
+ * Use the changes from the underlying persistence store
+ */
+ THEIRS,
+
+ /**
+ * Indicated changes have been merged by this {@code ConflictHandler} instance.
+ */
+ MERGED,
+
+ /**
+ * Changes are ignored by this handler (replaces null value).
+ */
+ IGNORED
+ }
+
+ /**
+ * The property {@code ours} has been added to {@code parent} which conflicts
+ * with property {@code theirs} which has been added in the persistence store.
+ *
+ * @param parent root of the conflict
+ * @param ours our version of the property
+ * @param theirs their version of the property
+ * @param base the base version of the property
+ * @return {@link Resolution} of the conflict
+ */
+ @Nonnull
+ Resolution addExistingProperty(NodeBuilder parent, PropertyState ours, PropertyState theirs, PropertyState base);
+
+ /**
+ * The property {@code ours} has been changed in {@code parent} while it was
+ * removed in the persistence store.
+ *
+ * @param parent root of the conflict
+ * @param ours our version of the property
+ * @param base the base version of the property
+ * @return {@link Resolution} of the conflict
+ */
+ @Nonnull
+ Resolution changeDeletedProperty(NodeBuilder parent, PropertyState ours, PropertyState base);
+
+ /**
+ * The property {@code ours} has been changed in {@code parent} while it was
+ * also changed to a different value ({@code theirs}) in the persistence store.
+ *
+ * @param parent root of the conflict
+ * @param ours our version of the property
+ * @param theirs their version of the property
+ * @param base the base version of the property
+ * @return {@link Resolution} of the conflict
+ */
+ @Nonnull
+ Resolution changeChangedProperty(NodeBuilder parent, PropertyState ours, PropertyState theirs, PropertyState base);
+
+ /**
+ * The property {@code ours} has been removed in {@code parent} while it was
+ * also removed in the persistence store.
+ *
+ * @param parent root of the conflict
+ * @param ours our version of the property
+ * @return {@link Resolution} of the conflict
+ */
+ @Nonnull
+ Resolution deleteDeletedProperty(NodeBuilder parent, PropertyState ours, PropertyState base);
+
+ /**
+ * The property {@code theirs} changed in the persistence store while it has been
+ * deleted locally.
+ *
+ * @param parent root of the conflict
+ * @param theirs their version of the property
+ * @return {@link Resolution} of the conflict
+ */
+ @Nonnull
+ Resolution deleteChangedProperty(NodeBuilder parent, PropertyState theirs, PropertyState base);
+
+ /**
+ * The node {@code ours} has been added to {@code parent} which conflicts
+ * with node {@code theirs} which has been added in the persistence store.
+ *
+ * @param parent root of the conflict
+ * @param name name of the node
+ * @param ours our version of the node
+ * @param theirs their version of the node
+ * @return {@link Resolution} of the conflict
+ */
+ @Nonnull
+ Resolution addExistingNode(NodeBuilder parent, String name, NodeState ours, NodeState theirs, NodeState base);
+
+ /**
+ * The node {@code ours} has been changed in {@code parent} while it was
+ * removed in the persistence store.
+ *
+ * @param parent root of the conflict
+ * @param name name of the node
+ * @param ours our version of the node
+ * @return {@link Resolution} of the conflict
+ */
+ @Nonnull
+ Resolution changeDeletedNode(NodeBuilder parent, String name, NodeState ours, NodeState base);
+
+ /**
+ * The node {@code theirs} changed in the persistence store while it has been
+ * deleted locally.
+ *
+ * @param parent root of the conflict
+ * @param name name of the node
+ * @param theirs their version of the node
+ * @return {@link Resolution} of the conflict
+ */
+ @Nonnull
+ Resolution deleteChangedNode(NodeBuilder parent, String name, NodeState theirs, NodeState base);
+
+ /**
+ * The node {@code name} has been removed in {@code parent} while it was
+ * also removed in the persistence store.
+ *
+ * @param parent root of the conflict
+ * @param name name of the node
+ * @return {@link Resolution} of the conflict
+ */
+ @Nonnull
+ Resolution deleteDeletedNode(NodeBuilder parent, String name, NodeState base);
+}
diff --git a/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/state/ConflictAnnotatingRebaseDiff.java b/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/state/ConflictAnnotatingRebaseDiff.java
index d846560d4a..6a22931451 100644
--- a/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/state/ConflictAnnotatingRebaseDiff.java
+++ b/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/spi/state/ConflictAnnotatingRebaseDiff.java
@@ -36,6 +36,8 @@ import static org.apache.jackrabbit.oak.spi.state.ConflictType.DELETE_DELETED_NO
*/
public class ConflictAnnotatingRebaseDiff extends AbstractRebaseDiff {
public static final String CONFLICT = ":conflict";
+ public static final String BASE = ":base";
+ public static final String OURS = ":ours";
public ConflictAnnotatingRebaseDiff(NodeBuilder builder) {
super(builder);
@@ -48,47 +50,59 @@ public class ConflictAnnotatingRebaseDiff extends AbstractRebaseDiff {
@Override
protected void addExistingProperty(NodeBuilder builder, PropertyState before, PropertyState after) {
- conflictMarker(builder, ADD_EXISTING_PROPERTY).setProperty(after);
+ NodeBuilder cb = conflictMarker(builder, ADD_EXISTING_PROPERTY);
+ cb.child(BASE).setProperty(before);
+ cb.child(OURS).setProperty(after);
}
@Override
protected void changeDeletedProperty(NodeBuilder builder, PropertyState after) {
- conflictMarker(builder, CHANGE_DELETED_PROPERTY).setProperty(after);
+ NodeBuilder cb = conflictMarker(builder, CHANGE_DELETED_PROPERTY);
+ cb.child(OURS).setProperty(after);
}
@Override
protected void changeChangedProperty(NodeBuilder builder, PropertyState before, PropertyState after) {
- conflictMarker(builder, CHANGE_CHANGED_PROPERTY).setProperty(after);
+ NodeBuilder cb = conflictMarker(builder, CHANGE_CHANGED_PROPERTY);
+ cb.child(BASE).setProperty(before);
+ cb.child(OURS).setProperty(after);
}
@Override
protected void deleteDeletedProperty(NodeBuilder builder, PropertyState before) {
- conflictMarker(builder, DELETE_DELETED_PROPERTY).setProperty(before);
+ NodeBuilder cb = conflictMarker(builder, DELETE_DELETED_PROPERTY);
+ cb.child(BASE).setProperty(before);
}
@Override
protected void deleteChangedProperty(NodeBuilder builder, PropertyState before) {
- conflictMarker(builder, DELETE_CHANGED_PROPERTY).setProperty(before);
+ NodeBuilder cb = conflictMarker(builder, DELETE_CHANGED_PROPERTY);
+ cb.child(BASE).setProperty(before);
}
@Override
protected void addExistingNode(NodeBuilder builder, String name, NodeState before, NodeState after) {
- conflictMarker(builder, ADD_EXISTING_NODE).setChildNode(name, after);
+ NodeBuilder cb = conflictMarker(builder, ADD_EXISTING_NODE);
+ cb.child(BASE).setChildNode(name, before);
+ cb.child(OURS).setChildNode(name, after);
}
@Override
protected void changeDeletedNode(NodeBuilder builder, String name, NodeState after) {
- conflictMarker(builder, CHANGE_DELETED_NODE).setChildNode(name, after);
+ NodeBuilder cb = conflictMarker(builder, CHANGE_DELETED_NODE);
+ cb.child(OURS).setChildNode(name, after);
}
@Override
protected void deleteDeletedNode(NodeBuilder builder, String name, NodeState before) {
- conflictMarker(builder, DELETE_DELETED_NODE).setChildNode(name, before);
+ NodeBuilder cb = conflictMarker(builder, DELETE_DELETED_NODE);
+ cb.child(BASE).setChildNode(name, before);
}
@Override
protected void deleteChangedNode(NodeBuilder builder, String name, NodeState before) {
- conflictMarker(builder, DELETE_CHANGED_NODE).setChildNode(name, before);
+ NodeBuilder cb = conflictMarker(builder, DELETE_CHANGED_NODE);
+ cb.child(BASE).setChildNode(name, before);
}
private static NodeBuilder conflictMarker(NodeBuilder builder, ConflictType ct) {