You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ch...@apache.org on 2016/08/05 10:46:02 UTC

svn commit: r1755284 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/core/ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/ oak-it/src/test/java/org/apache/jackrabbit/oak/spi/commit/

Author: chetanm
Date: Fri Aug  5 10:46:02 2016
New Revision: 1755284

URL: http://svn.apache.org/viewvc?rev=1755284&view=rev
Log:
OAK-4640 - Provide a way for commit hook to record meta data for a given commit

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ResetCommitAttributeHook.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/SimpleCommitContext.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitContext.java   (with props)
    jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/spi/commit/
    jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/spi/commit/CommitContextTest.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java?rev=1755284&r1=1755283&r2=1755284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java Fri Aug  5 10:46:02 2016
@@ -34,6 +34,7 @@ import java.util.Map;
 import javax.annotation.Nonnull;
 import javax.security.auth.Subject;
 
+import com.google.common.collect.ImmutableMap;
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.ContentSession;
@@ -44,6 +45,7 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.query.ExecutionContext;
 import org.apache.jackrabbit.oak.query.QueryEngineImpl;
 import org.apache.jackrabbit.oak.query.QueryEngineSettings;
+import org.apache.jackrabbit.oak.spi.commit.CommitContext;
 import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.commit.CompositeEditorProvider;
@@ -243,7 +245,7 @@ class MutableRoot implements Root {
         checkLive();
         ContentSession session = getContentSession();
         CommitInfo commitInfo = new CommitInfo(
-                session.toString(), session.getAuthInfo().getUserID(), info);
+                session.toString(), session.getAuthInfo().getUserID(), newInfoWithCommitContext(info));
         store.merge(builder, getCommitHook(), commitInfo);
         secureBuilder.baseChanged();
         modCount = 0;
@@ -267,7 +269,7 @@ class MutableRoot implements Root {
      */
     private CommitHook getCommitHook() {
         List<CommitHook> hooks = newArrayList();
-
+        hooks.add(ResetCommitAttributeHook.INSTANCE);
         hooks.add(hook);
 
         List<CommitHook> postValidationHooks = new ArrayList<CommitHook>();
@@ -365,6 +367,13 @@ class MutableRoot implements Root {
         return securityProvider.getConfiguration(AuthorizationConfiguration.class);
     }
 
+    private static Map<String, Object> newInfoWithCommitContext(Map<String, Object> info){
+        return ImmutableMap.<String, Object>builder()
+                .putAll(info)
+                .put(CommitContext.NAME, new SimpleCommitContext())
+                .build();
+    }
+
     //---------------------------------------------------------< MoveRecord >---
 
     /**

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ResetCommitAttributeHook.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ResetCommitAttributeHook.java?rev=1755284&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ResetCommitAttributeHook.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ResetCommitAttributeHook.java Fri Aug  5 10:46:02 2016
@@ -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.core;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.commit.CommitContext;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public enum ResetCommitAttributeHook implements CommitHook {
+    INSTANCE;
+
+    @Nonnull
+    @Override
+    public NodeState processCommit(NodeState before, NodeState after, CommitInfo info)
+            throws CommitFailedException {
+        //Reset the attributes upon each commit attempt
+        resetAttributes(info);
+        return after;
+    }
+
+    private static void resetAttributes(CommitInfo info) {
+        SimpleCommitContext attrs = (SimpleCommitContext) info.getInfo().get(CommitContext.NAME);
+        //As per implementation this should not be null
+        checkNotNull(attrs, "No commit attribute instance found in info map").clear();
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ResetCommitAttributeHook.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/SimpleCommitContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/SimpleCommitContext.java?rev=1755284&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/SimpleCommitContext.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/SimpleCommitContext.java Fri Aug  5 10:46:02 2016
@@ -0,0 +1,50 @@
+/*
+ * 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.core;
+
+import java.util.Map;
+
+import com.google.common.collect.Maps;
+import org.apache.jackrabbit.oak.spi.commit.CommitContext;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class SimpleCommitContext implements CommitContext {
+    private final Map<String, Object> attrs = Maps.newHashMap();
+
+    @Override
+    public void set(String name, Object value) {
+        attrs.put(checkNotNull(name), value);
+    }
+
+    @Override
+    public Object get(String name) {
+        return attrs.get(checkNotNull(name));
+    }
+
+    @Override
+    public void remove(String name) {
+        attrs.remove(checkNotNull(name));
+    }
+
+    void clear(){
+        attrs.clear();
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/SimpleCommitContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitContext.java?rev=1755284&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitContext.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitContext.java Fri Aug  5 10:46:02 2016
@@ -0,0 +1,71 @@
+/*
+ * 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.CheckForNull;
+
+/**
+ * A CommitContext instance can be obtained from {@link CommitInfo#getInfo()}
+ * if it has been set before the merge call. This can then be used by CommitHook
+ * to record some metadata regarding the commit.
+ *
+ * <p>CommitContext state would be reset in case commit is retried from within
+ * NodeStore say when a merge exception occurs.
+ */
+public interface CommitContext {
+    /**
+     * Name of the entry of the mutable commit attributes map in the {@code info}
+     * map in {@link CommitInfo#getInfo()}
+     */
+    String NAME = "oak.commitAttributes";
+
+    /**
+     * Stores an attribute related to this commit.
+     * Attributes are reset if the commit is retried.
+     *
+     * <p>If the object passed in is null, the effect is the same as
+     * calling {@link #remove}.
+     *
+     * @param name a <code>String</code> specifying the name of the attribute
+     * @param value the <code>Object</code> to be stored
+     */
+    void set(String name, Object value);
+
+    /**
+     * Returns the value of the named attribute as an <code>Object</code>,
+     * or <code>null</code> if no attribute of the given name exists.
+     *
+     * @param name <code>String</code> specifying the name of
+     * the attribute
+     *
+     * @return an <code>Object</code> containing the value
+     * of the attribute, or <code>null</code> if the attribute does not exist
+     */
+    @CheckForNull
+    Object get(String name);
+
+    /**
+     * Removes an attribute from this commit.
+     *
+     * @param name a <code>String</code> specifying
+     * the name of the attribute to remove
+     */
+    void remove(String name);
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/spi/commit/CommitContextTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/spi/commit/CommitContextTest.java?rev=1755284&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/spi/commit/CommitContextTest.java (added)
+++ jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/spi/commit/CommitContextTest.java Fri Aug  5 10:46:02 2016
@@ -0,0 +1,198 @@
+/*
+ * 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 java.io.Closeable;
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.security.auth.login.LoginException;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.OakBaseTest;
+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.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.fixture.NodeStoreFixture;
+import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Test;
+
+import static org.apache.jackrabbit.oak.api.CommitFailedException.MERGE;
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class CommitContextTest extends OakBaseTest {
+    private CommitInfoCapturingObserver observer = new CommitInfoCapturingObserver();
+    private ContentSession session;
+    private ContentRepository repository;
+
+    public CommitContextTest(NodeStoreFixture fixture) {
+        super(fixture);
+    }
+
+    @After
+    public void closeRepository() throws IOException {
+        if (session != null) {
+            session.close();
+        }
+
+        if (repository instanceof Closeable) {
+            ((Closeable) repository).close();
+        }
+    }
+
+    @Test
+    public void basicSetup() throws Exception {
+        repository = new Oak(store)
+                .with(new OpenSecurityProvider())
+                .with(observer)
+                .createContentRepository();
+
+        session = newSession();
+        Root root = session.getLatestRoot();
+        Tree tree = root.getTree("/");
+        tree.setProperty("a", 1);
+        root.commit(ImmutableMap.<String, Object>of("foo", "bar"));
+
+        assertNotNull(observer.info);
+        assertTrue(observer.info.getInfo().containsKey("foo"));
+
+        assertTrue(observer.info.getInfo().containsKey(CommitContext.NAME));
+
+        try {
+            observer.info.getInfo().put("x", "y");
+            fail("Info map should be immutable");
+        } catch (Exception ignore) {
+
+        }
+    }
+
+    @Test
+    public void attributeAddedByCommitHook() throws Exception{
+        repository = new Oak(store)
+                .with(new OpenSecurityProvider())
+                .with(observer)
+                .with(new CommitHook() {
+                    @Nonnull
+                    @Override
+                    public NodeState processCommit(NodeState before, NodeState after, CommitInfo info)
+                            throws CommitFailedException {
+                        CommitContext attrs = (CommitContext) info.getInfo().get(CommitContext.NAME);
+                        assertNotNull(attrs);
+                        attrs.set("foo", "bar");
+                        return after;
+                    }
+                })
+                .createContentRepository();
+
+        session = newSession();
+        Root root = session.getLatestRoot();
+        Tree tree = root.getTree("/");
+        tree.setProperty("a", 1);
+        root.commit();
+
+        assertNotNull(observer.info);
+
+        CommitContext attrs = (CommitContext) observer.info.getInfo().get(CommitContext.NAME);
+        assertNotNull(attrs.get("foo"));
+    }
+
+    @Test
+    public void attributesBeingReset() throws Exception{
+        //This test can only work with DocumentNodeStore as only that
+        //reattempt a failed merge. SegmentNodeStore would not do another
+        //attempt
+        assumeDocumentStore();
+        final AtomicInteger invokeCount = new AtomicInteger();
+        repository = new Oak(store)
+                .with(new OpenSecurityProvider())
+                .with(observer)
+                .with(new CommitHook() {
+                    @Nonnull
+                    @Override
+                    public NodeState processCommit(NodeState before, NodeState after, CommitInfo info)
+                            throws CommitFailedException {
+                        CommitContext attrs = (CommitContext) info.getInfo().get(CommitContext.NAME);
+                        int count = invokeCount.getAndIncrement();
+                        if (count == 0) {
+                            attrs.set("a", "1");
+                            attrs.set("b", "2");
+                        } else {
+                            attrs.set("a", "3");
+                        }
+                        return after;
+                    }
+                })
+                .with(new CommitHook() {
+                    @Nonnull
+                    @Override
+                    public NodeState processCommit(NodeState before, NodeState after,
+                                                   CommitInfo info) throws CommitFailedException {
+                        if (invokeCount.get() == 1){
+                            throw new CommitFailedException(MERGE, 0, "attribute reset test");
+                        }
+                        return after;
+                    }
+                })
+                .createContentRepository();
+
+        session = newSession();
+        Root root = session.getLatestRoot();
+        Tree tree = root.getTree("/");
+        tree.setProperty("a", 1);
+        root.commit();
+
+        assertNotNull(observer.info);
+
+        CommitContext attrs = (CommitContext) observer.info.getInfo().get(CommitContext.NAME);
+        assertEquals("3", attrs.get("a"));
+        assertNull(attrs.get("b"));
+    }
+
+    private void assumeDocumentStore() {
+        Assume.assumeThat(fixture.toString(), containsString("DocumentNodeStore"));
+    }
+
+    private ContentSession newSession() throws LoginException, NoSuchWorkspaceException {
+        return repository.login(null, null);
+    }
+
+    private static class CommitInfoCapturingObserver implements Observer {
+        CommitInfo info;
+
+        @Override
+        public void contentChanged(@Nonnull NodeState root, @Nullable CommitInfo info) {
+            this.info = info;
+        }
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/spi/commit/CommitContextTest.java
------------------------------------------------------------------------------
    svn:eol-style = native