Return-Path: X-Original-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Delivered-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 21A6B90B9 for ; Thu, 14 Jun 2012 15:14:14 +0000 (UTC) Received: (qmail 65309 invoked by uid 500); 14 Jun 2012 15:14:14 -0000 Delivered-To: apmail-jackrabbit-oak-commits-archive@jackrabbit.apache.org Received: (qmail 65281 invoked by uid 500); 14 Jun 2012 15:14:14 -0000 Mailing-List: contact oak-commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: oak-commits@jackrabbit.apache.org Delivered-To: mailing list oak-commits@jackrabbit.apache.org Received: (qmail 65273 invoked by uid 99); 14 Jun 2012 15:14:14 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 14 Jun 2012 15:14:14 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 14 Jun 2012 15:14:12 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id ADE792388847; Thu, 14 Jun 2012 15:13:51 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1350287 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/api/ oak-core/src/main/java/org/apache/jackrabbit/oak/core/ oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/ oak-jcr/src/main/java/org/apache/jack... Date: Thu, 14 Jun 2012 15:13:51 -0000 To: oak-commits@jackrabbit.apache.org From: mduerig@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120614151351.ADE792388847@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: mduerig Date: Thu Jun 14 15:13:50 2012 New Revision: 1350287 URL: http://svn.apache.org/viewvc?rev=1350287&view=rev Log: OAK-133: Session.refresh(true) should allow for manual conflict reconciliation Add Validator which fails the commit on presence of merge conflict markers (mix:mergeConflict) Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/ConflictHandler.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ContentRepositoryImpl.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/DefaultConflictHandler.java 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/kernel/KernelNodeStoreBranch.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/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java?rev=1350287&r1=1350286&r2=1350287&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java Thu Jun 14 15:13:50 2012 @@ -16,15 +16,11 @@ */ package org.apache.jackrabbit.oak.api; +import javax.jcr.RepositoryException; + /** * Main exception thrown by methods defined on the {@code ContentSession} interface * indicating that committing a given set of changes failed. - * - * TODO: define mechanism to inform the oak-jcr level about the specific type of exception - * possible ways: - * - CommitFailedException contains nested jcr exceptions - * - CommitFailedException extends from repository exception - * - CommitFailedException transports status code that are then converted to jcr exceptions */ public class CommitFailedException extends Exception { public CommitFailedException() { @@ -41,4 +37,21 @@ public class CommitFailedException exten public CommitFailedException(Throwable cause) { super(cause); } + + /** + * Rethrow this exception cast into a {@link RepositoryException}: if the cause + * for this exception already is a {@code RepositoryException}, the cause is + * thrown. Otherwise a new {@code RepositoryException} instance with this + * {@code CommitFailedException} is thrown. + * @throws RepositoryException + */ + public void throwRepositoryException() throws RepositoryException { + Throwable cause = getCause(); + if (cause instanceof RepositoryException) { + throw (RepositoryException) cause; + } + else { + throw new RepositoryException(cause); + } + } } Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/ConflictHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/ConflictHandler.java?rev=1350287&r1=1350286&r2=1350287&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/ConflictHandler.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/ConflictHandler.java Thu Jun 14 15:13:50 2012 @@ -18,6 +18,7 @@ */ package org.apache.jackrabbit.oak.api; +import org.apache.jackrabbit.oak.core.TreeImpl; import org.apache.jackrabbit.oak.spi.state.NodeState; /** @@ -52,7 +53,8 @@ public interface ConflictHandler { /** * Indicated changes have been merged by this {@code ConflictHandler} instance. */ - MERGED} + MERGED + } /** * The property {@code ours} has been added to {@code parent} which conflicts @@ -87,6 +89,16 @@ public interface ConflictHandler { Resolution changeChangedProperty(Tree parent, PropertyState ours, PropertyState theirs); /** + * 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 + */ + Resolution deleteDeletedProperty(TreeImpl parent, PropertyState ours); + + /** * The property {@code theirs} changed in the persistence store while it has been * deleted locally. * @@ -130,4 +142,14 @@ public interface ConflictHandler { * @return {@link Resolution} of the conflict */ Resolution deleteChangedNode(Tree parent, String name, NodeState theirs); + + /** + * 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 + */ + Resolution deleteDeletedNode(Tree parent, String name); } Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ContentRepositoryImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ContentRepositoryImpl.java?rev=1350287&r1=1350286&r2=1350287&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ContentRepositoryImpl.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ContentRepositoryImpl.java Thu Jun 14 15:13:50 2012 @@ -16,11 +16,24 @@ */ package org.apache.jackrabbit.oak.core; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nonnull; +import javax.jcr.Credentials; +import javax.jcr.InvalidItemStateException; +import javax.jcr.NoSuchWorkspaceException; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + import org.apache.jackrabbit.mk.api.MicroKernel; import org.apache.jackrabbit.mk.core.MicroKernelImpl; import org.apache.jackrabbit.mk.index.Indexer; +import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.api.ContentRepository; import org.apache.jackrabbit.oak.api.ContentSession; +import org.apache.jackrabbit.oak.api.CoreValue; +import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.QueryEngine; import org.apache.jackrabbit.oak.kernel.KernelNodeStore; import org.apache.jackrabbit.oak.plugins.name.NameValidatorProvider; @@ -32,21 +45,16 @@ import org.apache.jackrabbit.oak.spi.Que import org.apache.jackrabbit.oak.spi.commit.CommitHook; import org.apache.jackrabbit.oak.spi.commit.CompositeCommitHook; import org.apache.jackrabbit.oak.spi.commit.CompositeValidatorProvider; +import org.apache.jackrabbit.oak.spi.commit.DefaultValidator; import org.apache.jackrabbit.oak.spi.commit.ValidatingCommitHook; +import org.apache.jackrabbit.oak.spi.commit.Validator; import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider; import org.apache.jackrabbit.oak.spi.security.authentication.LoginContextProvider; +import org.apache.jackrabbit.oak.spi.state.NodeState; import org.apache.jackrabbit.oak.spi.state.NodeStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; -import javax.jcr.Credentials; -import javax.jcr.NoSuchWorkspaceException; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; -import java.util.ArrayList; -import java.util.List; - /** * {@link MicroKernel}-based implementation of * the {@link ContentRepository} interface. @@ -70,7 +78,7 @@ public class ContentRepositoryImpl imple * test cases only. */ public ContentRepositoryImpl() { - this(new MicroKernelImpl(), null, null); + this(new MicroKernelImpl(), null, createDefaultValidatorProvider()); // this(new IndexWrapper(new MicroKernelImpl()), null, null); } @@ -86,11 +94,7 @@ public class ContentRepositoryImpl imple ValidatorProvider validatorProvider) { if (validatorProvider == null) { - List providers = new ArrayList(); - providers.add(new NameValidatorProvider()); - providers.add(new NamespaceValidatorProvider()); - providers.add(new TypeValidatorProvider()); - validatorProvider = new CompositeValidatorProvider(providers); + validatorProvider = createDefaultValidatorProvider(); } List hooks = new ArrayList(); @@ -113,6 +117,15 @@ public class ContentRepositoryImpl imple null, null); } + private static ValidatorProvider createDefaultValidatorProvider() { + List providers = new ArrayList(); + providers.add(new NameValidatorProvider()); + providers.add(new NamespaceValidatorProvider()); + providers.add(new TypeValidatorProvider()); + providers.add(new ConflictValidatorProvider()); + return new CompositeValidatorProvider(providers); + } + private static QueryIndexProvider getDefaultIndexProvider(MicroKernel mk) { return Indexer.getInstance(mk); } @@ -135,4 +148,52 @@ public class ContentRepositoryImpl imple return new ContentSessionImpl(loginContext, workspaceName, nodeStore, queryEngine); } + + //------------------------------------------------------------< ConflictValidator >--- + + private static class ConflictValidatorProvider implements ValidatorProvider { + @Override + public Validator getRootValidator(NodeState before, NodeState after) { + return new DefaultValidator() { + @Override + public void propertyAdded(PropertyState after) throws CommitFailedException { + failOnMergeConflict(after); + } + + @Override + public void propertyChanged(PropertyState before, PropertyState after) + throws CommitFailedException { + failOnMergeConflict(after); + } + + @Override + public Validator childNodeAdded(String name, NodeState after) { + return this; + } + + @Override + public Validator childNodeChanged(String name, NodeState before, NodeState after) { + return this; + } + + @Override + public Validator childNodeDeleted(String name, NodeState before) { + return this; + } + + private void failOnMergeConflict(PropertyState property) throws CommitFailedException { + if ("jcr:mixinTypes".equals(property.getName())) { + assert property.isArray(); + Iterable mixins = property.getValues(); + for (CoreValue v : mixins) { + if ("mix:mergeConflict".equals(v.getString())) { + throw new CommitFailedException(new InvalidItemStateException("Item has unresolved conflicts")); + } + } + } + } + }; + } + } + } Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/DefaultConflictHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/DefaultConflictHandler.java?rev=1350287&r1=1350286&r2=1350287&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/DefaultConflictHandler.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/DefaultConflictHandler.java Thu Jun 14 15:13:50 2012 @@ -73,6 +73,11 @@ public class DefaultConflictHandler impl } @Override + public Resolution deleteDeletedProperty(TreeImpl parent, PropertyState ours) { + return resolution; + } + + @Override public Resolution addExistingNode(Tree parent, String name, NodeState ours, NodeState theirs) { return resolution; } @@ -86,4 +91,9 @@ public class DefaultConflictHandler impl public Resolution deleteChangedNode(Tree parent, String name, NodeState theirs) { return resolution; } + + @Override + public Resolution deleteDeletedNode(Tree parent, String name) { + return resolution; + } } 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=1350287&r1=1350286&r2=1350287&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 15:13:50 2012 @@ -311,9 +311,6 @@ public class RootImpl implements Root { if (p == null) { resolution = OURS; } - else if (after.equals(p)) { - resolution = MERGED; - } else { resolution = conflictHandler.addExistingProperty(target, after, p); } @@ -341,9 +338,6 @@ public class RootImpl implements Root { else if (before.equals(p)) { resolution = OURS; } - else if (after.equals(p)) { - resolution = MERGED; - } else { resolution = conflictHandler.changeChangedProperty(target, after, p); } @@ -367,7 +361,7 @@ public class RootImpl implements Root { resolution = OURS; } else if (p == null) { - resolution = MERGED; + resolution = conflictHandler.deleteDeletedProperty(target, before); } else { resolution = conflictHandler.deleteChangedProperty(target, p); @@ -434,7 +428,7 @@ public class RootImpl implements Root { TreeImpl n = target.getChild(name); if (n == null) { - resolution = MERGED; + resolution = conflictHandler.deleteDeletedNode(target, name); } else if (before.equals(n.getNodeState())) { resolution = OURS; Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java?rev=1350287&r1=1350286&r2=1350287&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java Thu Jun 14 15:13:50 2012 @@ -122,7 +122,6 @@ class KernelNodeStoreBranch implements N @Override public KernelNodeState merge() throws CommitFailedException { - // TODO rebase to current trunk? MicroKernel kernel = store.getKernel(); CommitHook commitHook = store.getCommitHook(); 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=1350287&r1=1350286&r2=1350287&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 15:13:50 2012 @@ -54,6 +54,7 @@ import org.apache.jackrabbit.oak.api.Roo import org.apache.jackrabbit.oak.api.Tree; import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.core.DefaultConflictHandler; +import org.apache.jackrabbit.oak.core.TreeImpl; import org.apache.jackrabbit.oak.jcr.value.ValueFactoryImpl; import org.apache.jackrabbit.oak.namepath.AbstractNameMapper; import org.apache.jackrabbit.oak.namepath.NameMapper; @@ -185,7 +186,7 @@ public class SessionDelegate { root.commit(conflictHandler); } catch (CommitFailedException e) { - throw new RepositoryException(e); + e.throwRepositoryException(); } } @@ -472,8 +473,6 @@ public class SessionDelegate { * * 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 @@ -483,6 +482,7 @@ public class SessionDelegate { public static final String CHANGE_DELETED = "changeDeleted"; public static final String CHANGE_CHANGED = "changeChanged"; public static final String DELETE_CHANGED = "deleteChanged"; + public static final String DELETE_DELETED = "deleteDeleted"; private final CoreValueFactory valueFactory; private final String jcrMixinTypes; @@ -521,6 +521,13 @@ public class SessionDelegate { } @Override + public Resolution deleteDeletedProperty(TreeImpl parent, PropertyState ours) { + Tree marker = addConflictMarker(parent); + setProperty(getOrCreateNode(marker, DELETE_DELETED), ours); + 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); @@ -541,10 +548,18 @@ public class SessionDelegate { return Resolution.THEIRS; } + @Override + public Resolution deleteDeletedNode(Tree parent, String name) { + Tree marker = addConflictMarker(parent); + markChild(getOrCreateNode(marker, DELETE_DELETED), name); + return Resolution.THEIRS; + } + private Tree addConflictMarker(Tree parent) { PropertyState jcrMixin = parent.getProperty(jcrMixinTypes); List mixins = new ArrayList(); if (jcrMixin != null) { + assert jcrMixin.isArray(); mixins = Iterators.toList(jcrMixin.getValues(), mixins); } if (!mixins.contains(MIX_MERGE_CONFLICT)) { 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=1350287&r1=1350286&r2=1350287&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 15:13:50 2012 @@ -1199,9 +1199,12 @@ public class RepositoryTest extends Abst assertTrue(session1.getRootNode().hasNode("node")); assertTrue(session2.getRootNode().hasNode("node")); - session2.save(); - assertTrue(session1.getRootNode().hasNode("node")); - assertTrue(session2.getRootNode().hasNode("node")); + try { + session2.save(); + fail("Expected InvalidItemStateException"); + } + catch (InvalidItemStateException expected) { + } } finally { session1.logout(); @@ -1228,9 +1231,12 @@ public class RepositoryTest extends Abst assertTrue(session2.getRootNode().hasNode("node")); assertTrue(session2.getRootNode().getNode("node").hasNode("2")); - session2.save(); - assertFalse(session1.getRootNode().hasNode("node")); - assertFalse(session2.getRootNode().hasNode("node")); + try { + session2.save(); + fail("Expected InvalidItemStateException"); + } + catch (InvalidItemStateException expected) { + } } finally { session1.logout();