diff --git a/source/java/org/alfresco/repo/webdav/LockInfoImpl.java b/source/java/org/alfresco/repo/webdav/LockInfoImpl.java
index 1db0239b8a..bbc168fa60 100644
--- a/source/java/org/alfresco/repo/webdav/LockInfoImpl.java
+++ b/source/java/org/alfresco/repo/webdav/LockInfoImpl.java
@@ -33,7 +33,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
* @author Ivan Rybnikov
*
*/
-public final class LockInfoImpl implements Serializable, LockInfo
+public class LockInfoImpl implements Serializable, LockInfo
{
private static final long serialVersionUID = 1L;
@@ -262,7 +262,7 @@ public final class LockInfoImpl implements Serializable, LockInfo
{
return false;
}
- Date now = new Date();
+ Date now = dateNow();
return now.after(expires);
}
@@ -321,6 +321,25 @@ public final class LockInfoImpl implements Serializable, LockInfo
return expires;
}
+ /**
+ * Remaining time before lock expires, in seconds.
+ */
+ @Override
+ public long getRemainingTimeoutSeconds()
+ {
+ Date expires = getExpires();
+ if (expires == null)
+ {
+ return WebDAV.TIMEOUT_INFINITY;
+ }
+ else
+ {
+ Date now = dateNow();
+ long timeout = ((expires.getTime() - now.getTime()) / 1000);
+ return timeout;
+ }
+ }
+
/**
* Sanity check the state of this LockInfo.
*/
@@ -336,21 +355,50 @@ public final class LockInfoImpl implements Serializable, LockInfo
* Sets the expiry date/time to lockTimeout seconds into the future. Provide
* a lockTimeout of WebDAV.TIMEOUT_INFINITY for never expires.
*
- * @param lockTimeout
+ * @param lockTimeoutSecs
*/
@Override
- public void setTimeoutSeconds(int lockTimeout)
+ public void setTimeoutSeconds(int lockTimeoutSecs)
{
- if (lockTimeout == WebDAV.TIMEOUT_INFINITY)
+ if (lockTimeoutSecs == WebDAV.TIMEOUT_INFINITY)
{
setExpires(null);
}
else
{
- int timeoutMillis = (lockTimeout * 60 * 1000);
- Date now = new Date();
+ int timeoutMillis = (lockTimeoutSecs * 1000);
+ Date now = dateNow();
Date nextExpiry = new Date(now.getTime() + timeoutMillis);
setExpires(nextExpiry);
}
}
+
+ /**
+ * Sets the expiry date/time to lockTimeout minutes into the future. Provide
+ * a lockTimeout of WebDAV.TIMEOUT_INFINITY for never expires.
+ *
+ * @param lockTimeoutMins
+ */
+ @Override
+ public void setTimeoutMinutes(int lockTimeoutMins)
+ {
+ if (lockTimeoutMins != WebDAV.TIMEOUT_INFINITY)
+ {
+ setTimeoutSeconds(lockTimeoutMins * 60);
+ }
+ else
+ {
+ setTimeoutSeconds(WebDAV.TIMEOUT_INFINITY);
+ }
+ }
+
+ /**
+ * Hook to allow unit testing - gets the current date/time.
+ *
+ * @return Date
+ */
+ protected Date dateNow()
+ {
+ return new Date();
+ }
}
diff --git a/source/java/org/alfresco/repo/webdav/LockInfoImplTest.java b/source/java/org/alfresco/repo/webdav/LockInfoImplTest.java
new file mode 100644
index 0000000000..eae565b9a1
--- /dev/null
+++ b/source/java/org/alfresco/repo/webdav/LockInfoImplTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2005-2012 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.repo.webdav;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.Date;
+
+import org.junit.Test;
+
+
+public class LockInfoImplTest
+{
+ @Test
+ public void canSetTimeoutSeconds()
+ {
+ LockInfoImplEx lockInfo = new LockInfoImplEx();
+
+ // This should add 7 seconds (7000 millis) to the expiry date.
+ lockInfo.setTimeoutSeconds(7);
+
+ // Check the new date.
+ assertEquals(86407000, lockInfo.getExpires().getTime());
+ }
+
+ @Test
+ public void canSetTimeoutSecondsToInfinity()
+ {
+ LockInfoImplEx lockInfo = new LockInfoImplEx();
+
+ lockInfo.setTimeoutSeconds(WebDAV.TIMEOUT_INFINITY);
+
+ // Check the new date.
+ assertNull(lockInfo.getExpires());
+ }
+
+ @Test
+ public void canSetTimeoutMinutes()
+ {
+ LockInfoImplEx lockInfo = new LockInfoImplEx();
+
+ // This should add 5 minutes to the expiry date.
+ lockInfo.setTimeoutMinutes(5);
+
+ // Check the new date.
+ assertEquals(86700000, lockInfo.getExpires().getTime());
+ }
+
+ @Test
+ public void canSetTimeoutMinutesToInfinity()
+ {
+ LockInfoImplEx lockInfo = new LockInfoImplEx();
+
+ lockInfo.setTimeoutMinutes(WebDAV.TIMEOUT_INFINITY);
+
+ // Check the new date.
+ assertNull(lockInfo.getExpires());
+ }
+
+ @Test
+ public void canGetRemainingTimeoutSeconds()
+ {
+ LockInfoImplEx lockInfo = new LockInfoImplEx();
+
+ lockInfo.setTimeoutSeconds(7);
+
+ assertEquals(7, lockInfo.getRemainingTimeoutSeconds());
+ }
+
+ public static class LockInfoImplEx extends LockInfoImpl
+ {
+ public static final Date DATE_NOW = new Date(86400000);
+ private static final long serialVersionUID = 1669378516554195322L;
+
+ @Override
+ protected Date dateNow()
+ {
+ return DATE_NOW;
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/webdav/LockMethod.java b/source/java/org/alfresco/repo/webdav/LockMethod.java
index 6ad362a217..b07c56c162 100644
--- a/source/java/org/alfresco/repo/webdav/LockMethod.java
+++ b/source/java/org/alfresco/repo/webdav/LockMethod.java
@@ -90,7 +90,7 @@ public class LockMethod extends WebDAVMethod
}
/**
- * Return the lock timeout, in minutes
+ * Return the lock timeout, in seconds.
*
* @return int
*/
@@ -426,7 +426,7 @@ public class LockMethod extends WebDAVMethod
// Store the owner of this lock
lockInfo.setOwner(userName);
// Lock the node
- getLockStore().put(lockNode.getNodeRef(), lockInfo);
+ getDAVLockService().lock(lockNode.getNodeRef(), lockInfo);
if (logger.isDebugEnabled())
{
diff --git a/source/java/org/alfresco/repo/webdav/PutMethod.java b/source/java/org/alfresco/repo/webdav/PutMethod.java
index 17d43a3470..a0d4a9d455 100644
--- a/source/java/org/alfresco/repo/webdav/PutMethod.java
+++ b/source/java/org/alfresco/repo/webdav/PutMethod.java
@@ -190,7 +190,7 @@ public class PutMethod extends WebDAVMethod implements ActivityPostProducer
}
String userName = getDAVHelper().getAuthenticationService().getCurrentUserName();
- LockInfo lockInfo = getLockStore().get(contentNodeInfo.getNodeRef());
+ LockInfo lockInfo = getDAVLockService().getLockInfo(contentNodeInfo.getNodeRef());
if (lockInfo != null)
{
diff --git a/source/java/org/alfresco/repo/webdav/UnlockMethod.java b/source/java/org/alfresco/repo/webdav/UnlockMethod.java
index 0b2909c282..38d8ae24e3 100644
--- a/source/java/org/alfresco/repo/webdav/UnlockMethod.java
+++ b/source/java/org/alfresco/repo/webdav/UnlockMethod.java
@@ -130,7 +130,7 @@ public class UnlockMethod extends WebDAVMethod
}
NodeRef nodeRef = lockNodeInfo.getNodeRef();
- LockInfo lockInfo = getLockStore().get(nodeRef);
+ LockInfo lockInfo = getDAVLockService().getLockInfo(nodeRef);
if (lockInfo == null)
{
@@ -169,7 +169,7 @@ public class UnlockMethod extends WebDAVMethod
String currentUser = getAuthenticationService().getCurrentUserName();
if (currentUser.equals(lockInfo.getOwner()))
{
- getLockStore().remove(nodeRef);
+ getDAVLockService().unlock(nodeRef);
// Indicate that the unlock was successful
m_response.setStatus(HttpServletResponse.SC_NO_CONTENT);
diff --git a/source/java/org/alfresco/repo/webdav/WebDAVHelper.java b/source/java/org/alfresco/repo/webdav/WebDAVHelper.java
index 5dad8e58a1..796696391e 100644
--- a/source/java/org/alfresco/repo/webdav/WebDAVHelper.java
+++ b/source/java/org/alfresco/repo/webdav/WebDAVHelper.java
@@ -91,7 +91,6 @@ public class WebDAVHelper
private DictionaryService m_dictionaryService;
private MimetypeService m_mimetypeService;
private WebDAVLockService m_lockService;
- private LockStore m_lockStore;
private ActionService m_actionService;
private AuthenticationService m_authService;
private PermissionService m_permissionService;
@@ -104,7 +103,7 @@ public class WebDAVHelper
/**
* Class constructor
*/
- protected WebDAVHelper(ServiceRegistry serviceRegistry, LockStore lockStore, AuthenticationService authService, TenantService tenantService)
+ protected WebDAVHelper(ServiceRegistry serviceRegistry, AuthenticationService authService, TenantService tenantService)
{
m_serviceRegistry = serviceRegistry;
@@ -119,8 +118,6 @@ public class WebDAVHelper
m_permissionService = m_serviceRegistry.getPermissionService();
m_tenantService = tenantService;
m_authService = authService;
-
- m_lockStore = lockStore;
}
/**
@@ -191,14 +188,6 @@ public class WebDAVHelper
{
return m_lockService;
}
-
- /**
- * @return Return the {@link LockStore lock store}.
- */
- public final LockStore getLockStore()
- {
- return m_lockStore;
- }
/**
* @return Return the action service
diff --git a/source/java/org/alfresco/repo/webdav/WebDAVLockService.java b/source/java/org/alfresco/repo/webdav/WebDAVLockService.java
index 649c23f428..5c60c53381 100644
--- a/source/java/org/alfresco/repo/webdav/WebDAVLockService.java
+++ b/source/java/org/alfresco/repo/webdav/WebDAVLockService.java
@@ -19,262 +19,39 @@
package org.alfresco.repo.webdav;
-import java.util.ArrayList;
-import java.util.List;
-
import javax.servlet.http.HttpSession;
-import org.alfresco.model.ContentModel;
-import org.alfresco.repo.security.authentication.AuthenticationUtil;
-import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
-import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.Auditable;
-import org.alfresco.service.cmr.coci.CheckOutCheckInService;
import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.LockStatus;
import org.alfresco.service.cmr.lock.LockType;
import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.repository.NodeService;
-import org.alfresco.service.transaction.TransactionService;
-import org.alfresco.util.Pair;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
/**
- *
* WebDAVLockService is used to manage file locks for WebDAV and Sharepoint protocol. It ensures a lock never persists
* for more than 24 hours, and also ensures locks are timed out on session timeout.
*
* @author Pavel.Yurkevich
+ * @author Matt Ward
*/
-public class WebDAVLockService
+public interface WebDAVLockService
{
- public static final String BEAN_NAME = "webDAVLockService";
-
- /** The session attribute under which webdav/vti stores its locked documents. */
- private static final String LOCKED_RESOURCES = "_webdavLockedResources";
-
- private static Log logger = LogFactory.getLog(WebDAVLockService.class);
-
- private static ThreadLocal currentSession = new ThreadLocal();
-
- private LockService lockService;
- private NodeService nodeService;
- private TransactionService transactionService;
- private CheckOutCheckInService checkOutCheckInService;
-
- /**
- * Set the LockService
- *
- * @param lockService
- */
- public void setLockService(LockService lockService)
- {
- this.lockService = lockService;
- }
-
- /**
- * Set the NodeService
- *
- * @param nodeService
- */
- public void setNodeService(NodeService nodeService)
- {
- this.nodeService = nodeService;
- }
-
- /**
- * Set the TransactionService
- *
- * @param transactionService
- */
- public void setTransactionService(TransactionService transactionService)
- {
- this.transactionService = transactionService;
- }
-
- /**
- * Set the CheckOutCheckInService
- *
- * @param checkOutCheckInService
- */
- public void setCheckOutCheckInService(CheckOutCheckInService checkOutCheckInService)
- {
- this.checkOutCheckInService = checkOutCheckInService;
- }
-
- /**
- * Caches current session to the thread local variable
- *
- * @param currentSession
- */
- public static void setCurrentSession(HttpSession session)
- {
- currentSession.set(session);
- }
+ static final String BEAN_NAME = "webDAVLockService";
@SuppressWarnings("unchecked")
- public void sessionDestroyed()
- {
- HttpSession session = currentSession.get();
-
- if (session == null)
- {
- if (logger.isDebugEnabled())
- {
- logger.debug("Couldn't find current session.");
- }
- return;
- }
-
- // look for locked documents list in http session
- final List> lockedResources = (List>) session.getAttribute(LOCKED_RESOURCES);
-
- if (lockedResources != null && lockedResources.size() > 0)
- {
- if (logger.isDebugEnabled())
- {
- logger.debug("Found " + lockedResources.size() + " locked resources for session: " + session.getId());
- }
-
- for (Pair lockedResource : lockedResources)
- {
- String runAsUser = lockedResource.getFirst();
- final NodeRef nodeRef = lockedResource.getSecond();
-
- // there are some document that should be forcibly unlocked
- AuthenticationUtil.runAs(new RunAsWork()
- {
- @Override
- public Void doWork() throws Exception
- {
- return transactionService.getRetryingTransactionHelper().doInTransaction(
- new RetryingTransactionCallback()
- {
- @Override
- public Void execute() throws Throwable
- {
- // check whether this document still exists in repo
- if (nodeService.exists(nodeRef))
- {
- if (logger.isDebugEnabled())
- {
- logger.debug("Trying to release lock for: " + nodeRef);
- }
-
- // check the lock status of document
- LockStatus lockStatus = lockService.getLockStatus(nodeRef);
-
- // check if document was checked out
- boolean hasWorkingCopy = checkOutCheckInService.getWorkingCopy(nodeRef) != null;
- boolean isWorkingCopy = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY);
-
- // forcibly unlock document if it is still locked and not checked out
- if ((lockStatus.equals(LockStatus.LOCKED) ||
- lockStatus.equals(LockStatus.LOCK_OWNER)) && !hasWorkingCopy && !isWorkingCopy)
- {
- try
- {
- // try to unlock it
- lockService.unlock(nodeRef);
-
- if (logger.isDebugEnabled())
- {
- logger.debug("Lock was successfully released for: "
- + nodeRef);
- }
- }
- catch (Exception e)
- {
- if (logger.isDebugEnabled())
- {
- logger.debug("Unable to unlock " + nodeRef
- + " cause: " + e.getMessage());
- }
- }
- }
- else
- {
- // document is not locked or is checked out
- if (logger.isDebugEnabled())
- {
- logger.debug("Skip lock releasing for: " + nodeRef
- + " as it is not locked or is checked out");
- }
- }
- }
- else
- {
- // document no longer exists in repo
- if (logger.isDebugEnabled())
- {
- logger.debug("Skip lock releasing for an unexisting node: " + nodeRef);
- }
- }
- return null;
- }
- }, transactionService.isReadOnly());
- }
- }, runAsUser == null ? AuthenticationUtil.getSystemUserName() : runAsUser);
- }
- }
- else
- {
- // there are no documents with unexpected lock left on it
- if (logger.isDebugEnabled())
- {
- logger.debug("No locked resources were found for session: " + session.getId());
- }
- }
- }
+ void sessionDestroyed();
/**
* Shared method for webdav/vti protocols to lock node. If node is locked for more than 24 hours it is automatically added
* to the current session locked resources list.
*
* @param nodeRef the node to lock
- * @param lockType the lock type
+ * @param userName the current user's user name
* @param timeout the number of seconds before the locks expires
*/
- public void lock(NodeRef nodeRef, LockType lockType, int timeout)
- {
- boolean performSessionBehavior = false;
+ void lock(NodeRef nodeRef, String userName, int timeout);
- // ALF-11777 fix, do not lock node for more than 24 hours (webdav and vti)
- if (timeout >= WebDAV.TIMEOUT_24_HOURS || timeout == WebDAV.TIMEOUT_INFINITY)
- {
- timeout = WebDAV.TIMEOUT_24_HOURS;
- performSessionBehavior = true;
- }
-
- this.lockService.lock(nodeRef, lockType, timeout);
-
- if (logger.isDebugEnabled())
- {
- logger.debug(nodeRef + " was locked for " + timeout + " seconds.");
- }
-
- if (performSessionBehavior)
- {
- HttpSession session = currentSession.get();
-
- if (session == null)
- {
- if (logger.isDebugEnabled())
- {
- logger.debug("Couldn't find current session.");
- }
- return;
- }
-
- storeObjectInSessionList(session, LOCKED_RESOURCES, new Pair(AuthenticationUtil.getRunAsUser(), nodeRef));
-
- if (logger.isDebugEnabled())
- {
- logger.debug(nodeRef + " was added to the session " + session.getId() + " for post expiration processing.");
- }
- }
- }
+ void lock(NodeRef nodeRef, LockInfo lockInfo);
/**
* Shared method for webdav/vti to unlock node. Unlocked node is automatically removed from
@@ -282,108 +59,23 @@ public class WebDAVLockService
*
* @param nodeRef the node to lock
*/
- public void unlock(NodeRef nodeRef)
- {
- this.lockService.unlock(nodeRef);
+ void unlock(NodeRef nodeRef);
- if (logger.isDebugEnabled())
- {
- logger.debug(nodeRef + " was unlocked.");
- }
-
- HttpSession session = currentSession.get();
-
- if (session == null)
- {
- if (logger.isDebugEnabled())
- {
- logger.debug("Couldn't find current session.");
- }
- return;
- }
-
- boolean removed = removeObjectFromSessionList(session, LOCKED_RESOURCES, new Pair(AuthenticationUtil.getRunAsUser(), nodeRef));
-
- if (removed && logger.isDebugEnabled())
- {
- logger.debug(nodeRef + " was removed from the session " + session.getId());
- }
- }
-
/**
- * Gets the lock status for the node reference relative to the current user.
+ * Gets the lock info for the node reference relative to the current user.
*
* @see LockService#getLockStatus(NodeRef, NodeRef)
*
* @param nodeRef the node reference
* @return the lock status
*/
- @Auditable(parameters = {"nodeRef"})
- public LockStatus getLockStatus(NodeRef nodeRef)
- {
- return this.lockService.getLockStatus(nodeRef);
- }
+ @Auditable(parameters = { "nodeRef" })
+ LockInfo getLockInfo(NodeRef nodeRef);
/**
- * Add the given object
to the session list that is stored in session under listName
attribute
+ * Caches current session in a thread local variable.
*
- * @param session the session
- * @param listName the list name (session attribute name)
- * @param object the object to store in session list
+ * @param session
*/
- @SuppressWarnings("unchecked")
- private static final void storeObjectInSessionList(HttpSession session, String listName, Object object)
- {
- List