Author: mduerig
Date: Tue Aug 27 08:13:23 2013
New Revision: 1517759
URL: http://svn.apache.org/r1517759
Log:
OAK-960: Enable session refresh state coordination between multiple session in
single thread
Factor refresh logic into its own RefreshManager class
Added:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/RefreshManager.java
(with props)
Modified:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/RepositoryImpl.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/SessionDelegate.java
Modified:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/RepositoryImpl.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/RepositoryImpl.java?rev=1517759&r1=1517758&r2=1517759&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/RepositoryImpl.java
(original)
+++
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/RepositoryImpl.java
Tue Aug 27 08:13:23 2013
@@ -17,6 +17,8 @@
package org.apache.jackrabbit.oak.jcr;
import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
import java.util.Collections;
import java.util.Map;
@@ -37,6 +39,7 @@ import org.apache.jackrabbit.api.securit
import org.apache.jackrabbit.commons.SimpleValueFactory;
import org.apache.jackrabbit.oak.api.ContentRepository;
import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.jcr.delegate.RefreshManager;
import org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate;
import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
@@ -57,7 +60,7 @@ public class RepositoryImpl implements J
* Name of the session attribute value determining the session refresh
* behaviour.
*
- * @see SessionDelegate#SessionDelegate(ContentSession, SecurityProvider,
long)
+ * @see SessionDelegate#SessionDelegate(ContentSession, RefreshManager,
SecurityProvider)
*/
public static final String REFRESH_INTERVAL = "oak.refresh-interval";
@@ -70,6 +73,7 @@ public class RepositoryImpl implements J
private final ContentRepository contentRepository;
protected final Whiteboard whiteboard;
private final SecurityProvider securityProvider;
+ private final ThreadLocal<Integer> threadSafeCount;
public RepositoryImpl(@Nonnull ContentRepository contentRepository,
@Nonnull Whiteboard whiteboard,
@@ -77,6 +81,7 @@ public class RepositoryImpl implements J
this.contentRepository = checkNotNull(contentRepository);
this.whiteboard = checkNotNull(whiteboard);
this.securityProvider = checkNotNull(securityProvider);
+ this.threadSafeCount = new ThreadLocal<Integer>();
}
//---------------------------------------------------------< Repository
>---
@@ -203,17 +208,19 @@ public class RepositoryImpl implements J
}
ContentSession contentSession =
contentRepository.login(credentials, workspaceName);
+ RefreshManager refreshManager = new RefreshManager(
+ MILLISECONDS.convert(refreshInterval, SECONDS),
threadSafeCount);
+ SessionDelegate sessionDelegate = new SessionDelegate(
+ contentSession, refreshManager, securityProvider);
SessionContext context = createSessionContext(
Collections.<String, Object>singletonMap(REFRESH_INTERVAL,
refreshInterval),
- new
SessionDelegate(contentSession,securityProvider,refreshInterval));
+ sessionDelegate);
return context.getSession();
} catch (LoginException e) {
throw new javax.jcr.LoginException(e.getMessage(), e);
}
}
-
-
@Override
public void shutdown() {
// empty
Added:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/RefreshManager.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/RefreshManager.java?rev=1517759&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/RefreshManager.java
(added)
+++
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/RefreshManager.java
Tue Aug 27 08:13:23 2013
@@ -0,0 +1,104 @@
+/*
+ * 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.delegate;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.MINUTES;
+
+import org.apache.jackrabbit.oak.jcr.operation.SessionOperation;
+
+/**
+* michid document
+*/
+public class RefreshManager {
+ private final Exception initStackTrace = new Exception("The session was
created here:");
+ private final long refreshInterval;
+
+ /**
+ * ThreadLocal instance to keep track of the save operations performed in
the thread so far
+ * This is is then used to determine if the current session needs to be
refreshed to see the
+ * changes done by another session in current thread.
+ * <p>
+ * <b>Note</b> - This thread local is never cleared. However, we only store
+ * java.lang.Integer and do not derive from ThreadLocal such that (class
loader)
+ * leaks typically associated with thread locals do not occur.
+ */
+ private final ThreadLocal<Integer> threadSaveCount;
+
+ private long lastAccessed = System.currentTimeMillis();
+ private boolean warnIfIdle = true;
+ private boolean refreshAtNextAccess;
+ private int sessionSaveCount;
+
+ public RefreshManager(long refreshInterval, ThreadLocal<Integer>
threadSaveCount) {
+ this.refreshInterval = refreshInterval;
+ this.threadSaveCount = threadSaveCount;
+
+ sessionSaveCount = getOr0(threadSaveCount);
+ }
+
+ boolean refreshIfNecessary(SessionDelegate delegate, SessionOperation<?>
sessionOperation) {
+ long now = System.currentTimeMillis();
+ long timeElapsed = now - lastAccessed;
+ lastAccessed = now;
+
+ // Don't refresh if this operation is a refresh operation itself or
+ // a save operation, which does an implicit refresh
+ if (!sessionOperation.isRefresh() && !sessionOperation.isSave()) {
+ if (warnIfIdle && !refreshAtNextAccess
+ && timeElapsed > MILLISECONDS.convert(1, MINUTES)) {
+ // Warn once if this session has been idle too long
+ SessionDelegate.log.warn("This session has been idle for " +
MINUTES.convert(timeElapsed, MILLISECONDS) +
+ " minutes and might be out of date. Consider using a
fresh session or explicitly" +
+ " refresh the session.", initStackTrace);
+ warnIfIdle = false;
+ }
+ if (refreshAtNextAccess || hasInThreadCommit() || timeElapsed >=
refreshInterval) {
+ // Refresh if forced or if the session has been idle too long
+ refreshAtNextAccess = false;
+ sessionSaveCount = getOr0(threadSaveCount);
+ delegate.refresh(true);
+ return true;
+ }
+ }
+
+ if (sessionOperation.isSave()) {
+ threadSaveCount.set(sessionSaveCount = (getOr0(threadSaveCount) +
1));
+ }
+
+ return false;
+ }
+
+ void refreshAtNextAccess() {
+ refreshAtNextAccess = true;
+ }
+
+ private boolean hasInThreadCommit() {
+ // If the threadLocal counter differs from our seen sessionSaveCount
so far then
+ // some other session would have done a commit. If that is the case a
refresh would
+ // be required
+ return getOr0(threadSaveCount) != sessionSaveCount;
+ }
+
+ private static int getOr0(ThreadLocal<Integer> threadLocal) {
+ Integer c = threadLocal.get();
+ return c == null ? 0 : c;
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/RefreshManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/RefreshManager.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision Rev URL
Modified:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/SessionDelegate.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/SessionDelegate.java?rev=1517759&r1=1517758&r2=1517759&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/SessionDelegate.java
(original)
+++
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/SessionDelegate.java
Tue Aug 27 08:13:23 2013
@@ -17,9 +17,6 @@
package org.apache.jackrabbit.oak.jcr.delegate;
import static com.google.common.base.Preconditions.checkNotNull;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.MINUTES;
-import static java.util.concurrent.TimeUnit.SECONDS;
import java.io.IOException;
@@ -53,40 +50,17 @@ import org.slf4j.LoggerFactory;
public class SessionDelegate {
static final Logger log = LoggerFactory.getLogger(SessionDelegate.class);
- /**
- * Threadlocal instance to keep track of the save operations performed in
the thread so far
- * This is is then used to determine if the current session needs to be
refreshed to see the
- * changes done by another session in current thread.
- *
- * <p><b>Note</b> - This thread local is never cleared. However we are
only storing java.lang.Integer
- * in it thus it would not cause leaks typically associated with usage of
thread locals which are not
- * cleared
- * </p>
- */
- private static final ThreadLocal<Integer> SAVE_COUNT = new
ThreadLocal<Integer>() {
- @Override
- protected Integer initialValue() {
- return 0;
- }
- };
-
private final ContentSession contentSession;
- private final long refreshInterval;
+ private final RefreshManager refreshManager;
+
private final Root root;
private final IdentifierManager idManager;
- private final Exception initStackTrace;
private final PermissionProvider permissionProvider;
private boolean isAlive = true;
private int sessionOpCount;
private long updateCount = 0;
- private long lastAccessed = System.currentTimeMillis();
- private boolean warnIfIdle = true;
- private boolean refreshAtNextAccess = false;
-
- private int saveCount = SAVE_COUNT.get();
-
/**
* Create a new session delegate for a {@code ContentSession}. The refresh
behaviour of the
* session is governed by the value of the {@code refreshInterval}
argument: if the session
@@ -96,22 +70,22 @@ public class SessionDelegate {
* dispatcher in order.
*
* @param contentSession the content session
+ * @param refreshManager the refresh manager used to handle auto
refreshing this session
* @param securityProvider the security provider
- * @param refreshInterval refresh interval in seconds.
*/
- public SessionDelegate(@Nonnull ContentSession contentSession,
SecurityProvider securityProvider,long refreshInterval) {
+ public SessionDelegate(@Nonnull ContentSession contentSession,
RefreshManager refreshManager,
+ SecurityProvider securityProvider) {
this.contentSession = checkNotNull(contentSession);
- this.refreshInterval = MILLISECONDS.convert(refreshInterval, SECONDS);
+ this.refreshManager = checkNotNull(refreshManager);
this.root = contentSession.getLatestRoot();
this.idManager = new IdentifierManager(root);
- this.initStackTrace = new Exception("The session was created here:");
- this.permissionProvider =
securityProvider.getConfiguration(AuthorizationConfiguration.class)
+ this.permissionProvider = checkNotNull(securityProvider)
+ .getConfiguration(AuthorizationConfiguration.class)
.getPermissionProvider(root,
contentSession.getAuthInfo().getPrincipals());
-
}
public synchronized void refreshAtNextAccess() {
- refreshAtNextAccess = true;
+ refreshManager.refreshAtNextAccess();
}
/**
@@ -130,28 +104,10 @@ public class SessionDelegate {
throws RepositoryException {
// Synchronize to avoid conflicting refreshes from concurrent JCR API
calls
if (sessionOpCount == 0) {
- // Refresh and checks only for non re-entrant session operations
- long now = System.currentTimeMillis();
- long timeElapsed = now - lastAccessed;
- // Don't refresh if this operation is a refresh operation itself
- if (!sessionOperation.isRefresh()) {
- if (warnIfIdle && !refreshAtNextAccess
- && timeElapsed > MILLISECONDS.convert(1, MINUTES)) {
- // Warn once if this session has been idle too long
- log.warn("This session has been idle for " +
MINUTES.convert(timeElapsed, MILLISECONDS) +
- " minutes and might be out of date. Consider using
a fresh session or explicitly" +
- " refresh the session.", initStackTrace);
- warnIfIdle = false;
- }
- if (refreshAtNextAccess ||
commitDoneByOtherSessionInCurrentThread() || timeElapsed >= refreshInterval) {
- // Refresh if forced or if the session has been idle too
long
- refreshAtNextAccess = false;
- saveCount = SAVE_COUNT.get();
- refresh(true);
- updateCount++;
- }
+ // Refresh and precondition checks only for non re-entrant session
operations
+ if (refreshManager.refreshIfNecessary(this, sessionOperation)) {
+ updateCount++;
}
- lastAccessed = now;
sessionOperation.checkPreconditions();
}
try {
@@ -162,9 +118,6 @@ public class SessionDelegate {
if (sessionOperation.isUpdate()) {
updateCount++;
}
- if (sessionOperation.isSave()) {
- SAVE_COUNT.set(saveCount = (SAVE_COUNT.get() + 1));
- }
}
}
@@ -460,15 +413,7 @@ public class SessionDelegate {
return contentSession.toString();
}
-//-----------------------------------------------------------< internal >---
-
- private boolean commitDoneByOtherSessionInCurrentThread() {
- //if the threadLocal counter differs from our seen saveCount so far
then
- //some other session would have done a commit. If that is the case a
refresh would
- //be required
- return SAVE_COUNT.get() != saveCount;
- }
-
+ //------------------------------------------------------------< internal
>---
/**
* Wraps the given {@link CommitFailedException} instance using the