From 5903fb47edb489533109004eb73f93d9421afd61 Mon Sep 17 00:00:00 2001 From: Jukka Zitting Date: Tue, 4 Feb 2014 15:01:39 -0500 Subject: [PATCH 2/2] OAK-1387: Pass CommitInfo to commit hooks, editors and validators Extract ItemSaveValidator away from AbstractRoot by adding CommitInfo.path --- oak-core/pom.xml | 1 + .../apache/jackrabbit/oak/core/AbstractRoot.java | 82 ++-------------------- .../oak/plugins/itemsave/ItemSaveValidator.java | 77 ++++++++++++++++++++ .../itemsave/ItemSaveValidatorProvider.java | 49 +++++++++++++ .../jackrabbit/oak/spi/commit/CommitInfo.java | 23 +++++- .../java/org/apache/jackrabbit/oak/jcr/Jcr.java | 2 + .../oak/jcr/delegate/SessionDelegate.java | 2 +- 7 files changed, 157 insertions(+), 79 deletions(-) create mode 100644 oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/itemsave/ItemSaveValidator.java create mode 100644 oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/itemsave/ItemSaveValidatorProvider.java diff --git a/oak-core/pom.xml b/oak-core/pom.xml index 7784104..4d1767a 100644 --- a/oak-core/pom.xml +++ b/oak-core/pom.xml @@ -57,6 +57,7 @@ org.apache.jackrabbit.oak.plugins.index.nodetype, org.apache.jackrabbit.oak.plugins.index.property, org.apache.jackrabbit.oak.plugins.index.reference, + org.apache.jackrabbit.oak.plugins.itemsave, org.apache.jackrabbit.oak.plugins.memory, org.apache.jackrabbit.oak.plugins.name, org.apache.jackrabbit.oak.plugins.nodetype, diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/core/AbstractRoot.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/core/AbstractRoot.java index 3cabfa4..1ebe088 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/core/AbstractRoot.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/core/AbstractRoot.java @@ -20,7 +20,6 @@ package org.apache.jackrabbit.oak.core; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Lists.newArrayList; -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.commons.PathUtils.isAncestor; @@ -37,7 +36,6 @@ import javax.security.auth.Subject; import org.apache.jackrabbit.oak.api.Blob; import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.api.ContentSession; -import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.QueryEngine; import org.apache.jackrabbit.oak.api.Root; import org.apache.jackrabbit.oak.commons.PathUtils; @@ -48,15 +46,10 @@ import org.apache.jackrabbit.oak.spi.commit.CommitHook; 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.Editor; import org.apache.jackrabbit.oak.spi.commit.EditorHook; -import org.apache.jackrabbit.oak.spi.commit.EditorProvider; import org.apache.jackrabbit.oak.spi.commit.EmptyHook; -import org.apache.jackrabbit.oak.spi.commit.FailingValidator; import org.apache.jackrabbit.oak.spi.commit.MoveTracker; import org.apache.jackrabbit.oak.spi.commit.PostValidationHook; -import org.apache.jackrabbit.oak.spi.commit.SubtreeExcludingValidator; -import org.apache.jackrabbit.oak.spi.commit.Validator; import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider; import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider; import org.apache.jackrabbit.oak.spi.security.Context; @@ -255,17 +248,18 @@ public abstract class AbstractRoot implements Root { @Override public void commit() throws CommitFailedException { - commit(null, null); + commit(null, "/"); } @Override - public void commit(@Nullable String message, @Nullable String path) + public void commit(@Nullable String message, @Nonnull String path) throws CommitFailedException { checkLive(); ContentSession session = getContentSession(); CommitInfo info = new CommitInfo( - session.toString(), session.getAuthInfo().getUserID(), message); - store.merge(builder, getCommitHook(path), info); + session.toString(), session.getAuthInfo().getUserID(), + message, path); + store.merge(builder, getCommitHook(), info); secureBuilder.baseChanged(); modCount = 0; if (permissionProvider.hasValue()) { @@ -276,28 +270,14 @@ public abstract class AbstractRoot implements Root { /** * Combine the globally defined commit hook(s) and the hooks and validators defined by the - * various security related configurations. In addition a commit hook is added to check - * that the {@code path} to commit contains all unpersisted changes and to fail the - * commit otherwise. + * various security related configurations. * - * @param path path to commit * @return A commit hook combining repository global commit hook(s) with the pluggable hooks * defined with the security modules and the padded {@code hooks}. */ - private CommitHook getCommitHook(@Nullable final String path) { + private CommitHook getCommitHook() { List hooks = newArrayList(); - if (path != null) { - hooks.add(new EditorHook(new EditorProvider() { - @Override - public Editor getRootEditor( - NodeState before, NodeState after, - NodeBuilder builder, CommitInfo info) { - return new ItemSaveValidator(path); - } - })); - } - hooks.add(hook); List postValidationHooks = new ArrayList(); @@ -461,52 +441,4 @@ public abstract class AbstractRoot implements Root { } } - //------------------------------------------------------------< ItemSaveValidator >--- - - /** - * This validator checks that all changes are contained within the subtree - * rooted at a given path. - */ - private static class ItemSaveValidator extends SubtreeExcludingValidator { - - /** - * Name of the property whose {@link #propertyChanged(org.apache.jackrabbit.oak.api.PropertyState, org.apache.jackrabbit.oak.api.PropertyState)} to - * ignore or {@code null} if no property should be ignored. - */ - private final String ignorePropertyChange; - - /** - * Create a new validator that only throws a {@link CommitFailedException} whenever - * there are changes not contained in the subtree rooted at {@code path}. - * @param path - */ - public ItemSaveValidator(String path) { - this(new FailingValidator(CommitFailedException.UNSUPPORTED, 0, - "Failed to save subtree at " + path + ". There are " + - "transient modifications outside that subtree."), - newArrayList(elements(path))); - } - - private ItemSaveValidator(Validator validator, List path) { - super(validator, path); - // Ignore property changes if this is the head of the path. - // This allows for calling save on a changed property. - ignorePropertyChange = path.size() == 1 ? path.get(0) : null; - } - - @Override - public void propertyChanged(PropertyState before, PropertyState after) - throws CommitFailedException { - if (!before.getName().equals(ignorePropertyChange)) { - super.propertyChanged(before, after); - } - } - - @Override - protected SubtreeExcludingValidator createValidator( - Validator validator, final List path) { - return new ItemSaveValidator(validator, path); - } - } - } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/itemsave/ItemSaveValidator.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/itemsave/ItemSaveValidator.java new file mode 100644 index 0000000..7f3eee1 --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/itemsave/ItemSaveValidator.java @@ -0,0 +1,77 @@ +/* + * 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.itemsave; + +import static com.google.common.collect.Lists.newArrayList; +import static org.apache.jackrabbit.oak.commons.PathUtils.elements; + +import java.util.List; + +import org.apache.jackrabbit.oak.api.CommitFailedException; +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.spi.commit.FailingValidator; +import org.apache.jackrabbit.oak.spi.commit.SubtreeExcludingValidator; +import org.apache.jackrabbit.oak.spi.commit.Validator; + +/** + * This validator checks that all changes are contained within the subtree + * rooted at a given path. + */ +class ItemSaveValidator extends SubtreeExcludingValidator { + + /** + * Name of the property whose {@link #propertyChanged(org.apache.jackrabbit.oak.api.PropertyState, org.apache.jackrabbit.oak.api.PropertyState)} to + * ignore or {@code null} if no property should be ignored. + */ + private final String ignorePropertyChange; + + /** + * Create a new validator that only throws a {@link CommitFailedException} whenever + * there are changes not contained in the subtree rooted at {@code path}. + * @param path + */ + public ItemSaveValidator(String path) { + this(new FailingValidator(CommitFailedException.UNSUPPORTED, 0, + "Failed to save subtree at " + path + ". There are " + + "transient modifications outside that subtree."), + newArrayList(elements(path))); + } + + private ItemSaveValidator(Validator validator, List path) { + super(validator, path); + // Ignore property changes if this is the head of the path. + // This allows for calling save on a changed property. + ignorePropertyChange = path.size() == 1 ? path.get(0) : null; + } + + @Override + public void propertyChanged(PropertyState before, PropertyState after) + throws CommitFailedException { + if (!before.getName().equals(ignorePropertyChange)) { + super.propertyChanged(before, after); + } + } + + @Override + protected SubtreeExcludingValidator createValidator( + Validator validator, final List path) { + return new ItemSaveValidator(validator, path); + } + +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/itemsave/ItemSaveValidatorProvider.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/itemsave/ItemSaveValidatorProvider.java new file mode 100644 index 0000000..6b66653 --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/itemsave/ItemSaveValidatorProvider.java @@ -0,0 +1,49 @@ +/* + * 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.itemsave; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; +import org.apache.jackrabbit.oak.commons.PathUtils; +import org.apache.jackrabbit.oak.spi.commit.CommitInfo; +import org.apache.jackrabbit.oak.spi.commit.EditorProvider; +import org.apache.jackrabbit.oak.spi.commit.Validator; +import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider; +import org.apache.jackrabbit.oak.spi.state.NodeState; + +/** + * This validator checks that all changes are contained within the subtree + * rooted at a given path. + */ +@Component +@Service(EditorProvider.class) +public class ItemSaveValidatorProvider extends ValidatorProvider { + + @Override + protected Validator getRootValidator( + NodeState before, NodeState after, CommitInfo info) { + String path = info.getPath(); + if (PathUtils.denotesRoot(path)) { + return null; // no need to check saves of the whole transient space + } else { + return new ItemSaveValidator(path); + } + } + +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitInfo.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitInfo.java index 58cf52a..160639d 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitInfo.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitInfo.java @@ -49,6 +49,8 @@ public final class CommitInfo { private final long date = System.currentTimeMillis(); + private final String path; + /** * Creates a commit info for the given session and user. * @@ -56,10 +58,19 @@ public final class CommitInfo { * @param userId The user id. * @param message message attached to this commit, or {@code null} */ - public CommitInfo(@Nonnull String sessionId, @Nullable String userId, @Nullable String message) { + public CommitInfo( + @Nonnull String sessionId, @Nullable String userId, + @Nullable String message) { + this(sessionId, userId, message, "/"); + } + + public CommitInfo( + @Nonnull String sessionId, @Nullable String userId, + @Nullable String message, @Nonnull String path) { this.sessionId = checkNotNull(sessionId); this.userId = (userId == null) ? OAK_UNKNOWN : userId; this.message = message; + this.path = checkNotNull(path); } /** @@ -93,6 +104,10 @@ public final class CommitInfo { return date; } + public String getPath() { + return path; + } + //------------------------------------------------------------< Object >-- @Override @@ -104,7 +119,8 @@ public final class CommitInfo { return sessionId.equals(that.sessionId) && userId.equals(that.userId) && Objects.equal(this.message, that.message) - && this.date == that.date; + && this.date == that.date + && path.equals(that.path); } else { return false; } @@ -112,7 +128,7 @@ public final class CommitInfo { @Override public int hashCode() { - return Objects.hashCode(sessionId, userId, message, date); + return Objects.hashCode(sessionId, userId, message, date, path); } @Override @@ -122,6 +138,7 @@ public final class CommitInfo { .add("userId", userId) .add("userData", message) .add("date", date) + .add("path", path) .toString(); } 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 f767ece..33abc64 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 @@ -35,6 +35,7 @@ import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvi import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexProvider; import org.apache.jackrabbit.oak.plugins.index.reference.ReferenceEditorProvider; import org.apache.jackrabbit.oak.plugins.index.reference.ReferenceIndexProvider; +import org.apache.jackrabbit.oak.plugins.itemsave.ItemSaveValidatorProvider; import org.apache.jackrabbit.oak.plugins.name.NameValidatorProvider; import org.apache.jackrabbit.oak.plugins.name.NamespaceEditorProvider; import org.apache.jackrabbit.oak.plugins.nodetype.TypeEditorProvider; @@ -67,6 +68,7 @@ public class Jcr { with(new SecurityProviderImpl()); + with(new ItemSaveValidatorProvider()); with(new NameValidatorProvider()); with(new NamespaceEditorProvider()); with(new TypeEditorProvider()); diff --git a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/SessionDelegate.java b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/SessionDelegate.java index 3147755..d3461ad 100644 --- a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/SessionDelegate.java +++ b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/SessionDelegate.java @@ -224,7 +224,7 @@ public class SessionDelegate { * @throws CommitFailedException if the commit failed */ public void commit(Root root) throws CommitFailedException { - root.commit(userData, null); + root.commit(userData, "/"); } public void checkProtectedNode(String path) throws RepositoryException { -- 1.8.3.msysgit.0