in oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java [550:631]
private void checkConflicts(@NotNull UpdateOp op,
@Nullable NodeDocument before)
throws ConflictException {
DocumentStore store = nodeStore.getDocumentStore();
collisions.clear();
if (baseRevision != null) {
Revision newestRev = null;
Branch branch = null;
if (before != null) {
RevisionVector base = baseRevision;
if (nodeStore.isDisableBranches()) {
base = base.asTrunkRevision();
}
branch = getBranch();
newestRev = before.getNewestRevision(
nodeStore, base, revision, branch, collisions);
}
String conflictMessage = null;
Set<Revision> conflictRevisions = Sets.newHashSet();
if (newestRev == null) {
if ((op.isDelete() || !op.isNew())
&& !allowConcurrentAddRemove(before, op)) {
conflictMessage = "The node " +
op.getId() + " does not exist or is already deleted " +
"at base revision " + baseRevision + ", branch: " + branch;
if (before != null && !before.getLocalDeleted().isEmpty()) {
conflictRevisions.add(before.getLocalDeleted().firstKey());
}
}
} else {
conflictRevisions.add(newestRev);
if (op.isNew() && !allowConcurrentAddRemove(before, op)) {
conflictMessage = "The node " +
op.getId() + " already existed in revision\n" +
formatConflictRevision(newestRev);
} else if (baseRevision.isRevisionNewer(newestRev)
&& (op.isDelete() || isConflicting(before, op))) {
conflictMessage = "The node " +
op.getId() + " was changed in revision\n" +
formatConflictRevision(newestRev) +
", which was applied after the base revision\n" +
baseRevision;
}
}
if (conflictMessage == null && before != null) {
// the modification was successful
// -> check for collisions and conflict (concurrent updates
// on a node are possible if property updates do not overlap)
// TODO: unify above conflict detection and isConflicting()
boolean allowConflictingDeleteChange = allowConcurrentAddRemove(before, op);
for (Revision r : collisions) {
Collision c = new Collision(before, r, op, revision, nodeStore, startRevisions);
if (c.isConflicting() && !allowConflictingDeleteChange) {
// mark collisions on commit root
if (c.mark(store).equals(revision)) {
// our revision was marked
if (baseRevision.isBranch()) {
// this is a branch commit. do not fail immediately
// merging this branch will fail later.
} else {
// fail immediately
conflictMessage = "The node " +
op.getId() + " was changed in revision\n" +
formatConflictRevision(r) +
", which was applied after the base revision\n" +
baseRevision;
conflictRevisions.add(r);
}
}
}
}
}
if (conflictMessage != null) {
conflictMessage += ", commit revision: " + revision;
if (LOG.isDebugEnabled()) {
LOG.debug(conflictMessage + "; document:\n" +
(before == null ? "" : before.format()));
}
throw new ConflictException(conflictMessage, conflictRevisions);
}
}
}