Author: mduerig
Date: Wed Feb 25 23:14:08 2015
New Revision: 1662323

URL: http://svn.apache.org/r1662323
Log:
OAK-2407: Auto-refresh sessions on revision gc
Register GCMonitor to detect compaction and auto-refresh

Added:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/package-info.java
    
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RefreshOnGCTest.java
Modified:
    jackrabbit/oak/trunk/oak-core/pom.xml
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitor.java
    
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java

Modified: jackrabbit/oak/trunk/oak-core/pom.xml
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/pom.xml?rev=1662323&r1=1662322&r2=1662323&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-core/pom.xml Wed Feb 25 23:14:08 2015
@@ -85,6 +85,7 @@
               org.apache.jackrabbit.oak.plugins.value,
               org.apache.jackrabbit.oak.plugins.version,
               org.apache.jackrabbit.oak.spi.commit,
+              org.apache.jackrabbit.oak.spi.gc,
               org.apache.jackrabbit.oak.spi.lifecycle,
               org.apache.jackrabbit.oak.spi.query,
               org.apache.jackrabbit.oak.spi.security,

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitor.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitor.java?rev=1662323&r1=1662322&r2=1662323&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitor.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitor.java
 Wed Feb 25 23:14:08 2015
@@ -83,4 +83,13 @@ public interface GCMonitor {
      * @param currentSize    number of bytes after garbage collection
      */
     void cleaned(long reclaimedSize, long currentSize);
+
+    class Empty implements GCMonitor {
+        @Override public void info(String message, Object[] arguments) { }
+        @Override public void warn(String message, Object[] arguments) { }
+        @Override public void error(String message, Exception e) { }
+        @Override public void skipped(String reason, Object[] arguments) { }
+        @Override public void compacted() { }
+        @Override public void cleaned(long reclaimedSize, long currentSize) { }
+    }
 }

Added: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/package-info.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/package-info.java?rev=1662323&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/package-info.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/package-info.java
 Wed Feb 25 23:14:08 2015
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+@Version("1.0")
+@Export(optional = "provide:=true")
+package org.apache.jackrabbit.oak.spi.gc;
+
+import aQute.bnd.annotation.Export;
+import aQute.bnd.annotation.Version;
\ No newline at end of file

Modified: 
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java?rev=1662323&r1=1662322&r2=1662323&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java
 Wed Feb 25 23:14:08 2015
@@ -22,7 +22,6 @@ import static java.util.Collections.sing
 import static 
org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
 
 import java.io.Closeable;
-import java.util.Collections;
 import java.util.Map;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ScheduledExecutorService;
@@ -44,7 +43,6 @@ import javax.jcr.Value;
 import javax.security.auth.login.LoginException;
 
 import com.google.common.collect.ImmutableMap;
-
 import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.api.JackrabbitRepository;
 import 
org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
@@ -54,9 +52,11 @@ import org.apache.jackrabbit.oak.api.Con
 import org.apache.jackrabbit.oak.api.jmx.SessionMBean;
 import org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate;
 import org.apache.jackrabbit.oak.jcr.session.RefreshStrategy;
+import org.apache.jackrabbit.oak.jcr.session.RefreshStrategy.Composite;
 import org.apache.jackrabbit.oak.jcr.session.SessionContext;
 import org.apache.jackrabbit.oak.jcr.session.SessionStats;
 import org.apache.jackrabbit.oak.plugins.observation.CommitRateLimiter;
+import org.apache.jackrabbit.oak.spi.gc.GCMonitor;
 import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
 import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
 import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
@@ -244,7 +244,7 @@ public class RepositoryImpl implements J
             @CheckForNull Map<String, Object> attributes) throws 
RepositoryException {
         try {
             if (attributes == null) {
-                attributes = Collections.emptyMap();
+                attributes = emptyMap();
             }
             Long refreshInterval = getRefreshInterval(credentials);
             if (refreshInterval == null) {
@@ -254,7 +254,9 @@ public class RepositoryImpl implements J
             }
             boolean relaxedLocking = getRelaxedLocking(attributes);
 
-            RefreshStrategy refreshStrategy = 
createRefreshStrategy(refreshInterval);
+            RefreshStrategy refreshStrategy = refreshInterval == null
+                ? new RefreshStrategy.LogOnce(60)
+                : new RefreshStrategy.Timed(refreshInterval);
             ContentSession contentSession = 
contentRepository.login(credentials, workspaceName);
             SessionDelegate sessionDelegate = 
createSessionDelegate(refreshStrategy, contentSession);
             SessionContext context = createSessionContext(
@@ -268,8 +270,12 @@ public class RepositoryImpl implements J
     }
 
     private SessionDelegate createSessionDelegate(
-            final RefreshStrategy refreshStrategy,
-            final ContentSession contentSession) {
+            RefreshStrategy refreshStrategy,
+            ContentSession contentSession) {
+
+        final RefreshOnGC refreshOnGC = new RefreshOnGC();
+        refreshStrategy = new Composite(refreshStrategy, refreshOnGC);
+
         return new SessionDelegate(
                 contentSession, securityProvider, refreshStrategy,
                 threadSaveCount, statisticManager, clock) {
@@ -280,6 +286,7 @@ public class RepositoryImpl implements J
 
             @Override
             public void logout() {
+                refreshOnGC.close();
                 // Cancel session MBean registration
                 registrationTask.cancel();
                 scheduledTask.cancel(false);
@@ -444,31 +451,35 @@ public class RepositoryImpl implements J
         }
     }
 
-    /**
-     * Auto refresh logic for sessions, which is done to enhance backwards 
compatibility with
-     * Jackrabbit 2.
-     * <p>
-     * A sessions is automatically refreshed when
-     * <ul>
-     *     <li>it has not been accessed for the number of seconds specified by 
the
-     *         {@code refreshInterval} parameter,</li>
-     *     <li>an observation event has been delivered to a listener 
registered from within this
-     *         session,</li>
-     *     <li>an updated occurred through a different session from <em>within 
the same
-     *         thread.</em></li>
-     * </ul>
-     * In addition a warning is logged once per session if the session is 
accessed after one
-     * minute of inactivity.
-     */
-    private RefreshStrategy createRefreshStrategy(Long refreshInterval) {
-        if (refreshInterval == null) {
-            return new RefreshStrategy.LogOnce(60);
-        } else {
-            return new RefreshStrategy.Timed(refreshInterval);
+    private class RefreshOnGC implements RefreshStrategy {
+        private final GCMonitor gcMonitor = new GCMonitor.Empty() {
+            @Override
+            public void compacted() {
+                compactionCount++;
+            }
+        };
+
+        private final Registration reg = whiteboard.register(GCMonitor.class, 
gcMonitor, emptyMap());
+
+        private volatile int compactionCount;
+        private int refreshCount;
+
+        public void close() {
+            reg.unregister();
+        }
+
+        @Override
+        public boolean needsRefresh(long secondsSinceLastAccess) {
+            if (compactionCount > refreshCount) {
+                refreshCount = compactionCount;
+                return true;
+            } else {
+                return false;
+            }
         }
     }
 
-    static class RegistrationTask implements Runnable {
+    private static class RegistrationTask implements Runnable {
         private final SessionStats sessionStats;
         private final Whiteboard whiteboard;
         private boolean cancelled;

Added: 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RefreshOnGCTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RefreshOnGCTest.java?rev=1662323&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RefreshOnGCTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RefreshOnGCTest.java
 Wed Feb 25 23:14:08 2015
@@ -0,0 +1,134 @@
+/*
+ * 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.jcr;
+
+import static java.io.File.createTempFile;
+import static 
org.apache.jackrabbit.oak.plugins.segment.compaction.CompactionStrategy.CleanupType.CLEAN_NONE;
+import static 
org.apache.jackrabbit.oak.plugins.segment.file.FileStore.newFileStore;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+import javax.annotation.Nonnull;
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.jackrabbit.api.JackrabbitRepository;
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
+import org.apache.jackrabbit.oak.plugins.segment.compaction.CompactionStrategy;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.whiteboard.DefaultWhiteboard;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RefreshOnGCTest {
+    private FileStore fileStore;
+    private Repository repository;
+
+    @Before
+    public void setup() throws IOException {
+        File directory = createTempFile(getClass().getSimpleName(), "test", 
new File("target"));
+        directory.delete();
+        directory.mkdir();
+
+        Whiteboard whiteboard = new DefaultWhiteboard();
+        fileStore = newFileStore(directory)
+                .withWhiteBoard(whiteboard)
+                .create()
+                .setCompactionStrategy(new CompactionStrategy(
+                        false, false, CLEAN_NONE, 0, 
CompactionStrategy.MEMORY_THRESHOLD_DEFAULT) {
+                    @Override
+                    public boolean compacted(@Nonnull Callable<Boolean> 
setHead) throws Exception {
+                        setHead.call();
+                        return true;
+                    }
+                });
+
+        NodeStore nodeStore = new SegmentNodeStore(fileStore);
+        Oak oak = new Oak(nodeStore);
+        oak.with(whiteboard);
+        repository = new Jcr(oak).createRepository();
+    }
+
+    @After
+    public void tearDown() {
+        if (repository instanceof JackrabbitRepository) {
+            ((JackrabbitRepository) repository).shutdown();
+        }
+    }
+
+    @Test
+    public void compactionCausesRefresh() throws RepositoryException, 
InterruptedException, ExecutionException {
+        Session session = repository.login(new SimpleCredentials("admin", 
"admin".toCharArray()));
+        try {
+            Node root = session.getRootNode();
+            root.addNode("one");
+            session.save();
+
+            addNode(repository, "two");
+
+            fileStore.compact();
+            assertTrue(root.hasNode("one"));
+            assertTrue("Node two must be visible as compaction should cause 
the session to refresh",
+                    root.hasNode("two"));
+        } finally {
+            session.logout();
+        }
+    }
+
+    private static void addNode(final Repository repository, final String name)
+            throws ExecutionException, InterruptedException {
+        // Execute on different threads to ensure same thread session
+        // refreshing doesn't come into our way
+        run(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                Session session = repository.login(new 
SimpleCredentials("admin", "admin".toCharArray()));
+                try {
+                    Node root = session.getRootNode();
+                    root.addNode(name);
+                    session.save();
+                } finally {
+                    session.logout();
+                }
+                return null;
+            }
+        });
+    }
+
+    private static void run(Callable<Void> callable) throws 
InterruptedException, ExecutionException {
+        FutureTask<Void> task = new FutureTask<Void>(callable);
+        new Thread(task).start();
+        task.get();
+    }
+
+
+}


Reply via email to