diff --git a/source/java/org/alfresco/repo/webdav/LockInfo.java b/source/java/org/alfresco/repo/webdav/LockInfo.java index d1a118e98d..cf8b15a1b0 100644 --- a/source/java/org/alfresco/repo/webdav/LockInfo.java +++ b/source/java/org/alfresco/repo/webdav/LockInfo.java @@ -22,9 +22,13 @@ import java.io.Serializable; import java.util.Date; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** - * Class to represent a WebDAV lock info + * Class to represent a WebDAV lock info. Instances of this class are accessible + * my multiple threads as they are kept in the {@link LockStore}. Clients of this + * class are expected to synchronise externally using the provided + * ReentrantReadWriteLock (use {@link #getRWLock()}). * * @author Ivan Rybnikov * @@ -33,6 +37,8 @@ public final class LockInfo implements Serializable { private static final long serialVersionUID = 1L; + private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); + // Exclusive lock token private String exclusiveLockToken = null; @@ -73,6 +79,20 @@ public final class LockInfo implements Serializable this.depth = depth; } + /** + * Retrieves the {@link ReentrantReadWriteLock} associated with this LockInfo. This is + * to allow client code to protect against invalid concurrent access to the state of + * this class. + *
+ * Not to be confused with WebDAV locks.
+ *
+ * @return
+ */
+ public ReentrantReadWriteLock getRWLock()
+ {
+ return rwLock;
+ }
+
/**
* Returns true if node has shared or exclusive locks
*
@@ -294,15 +314,23 @@ public final class LockInfo implements Serializable
}
/**
- * Sets the expiry date/time to lockTimeout seconds into the future.
+ * Sets the expiry date/time to lockTimeout seconds into the future. Provide
+ * a lockTimeout of WebDAV.TIMEOUT_INFINITY for never expires.
*
* @param lockTimeout
*/
public void setTimeoutSeconds(int lockTimeout)
{
- int timeoutMillis = (lockTimeout * 60 * 1000);
- Date now = new Date();
- Date nextExpiry = new Date(now.getTime() + timeoutMillis);
- setExpires(nextExpiry);
+ if (lockTimeout == WebDAV.TIMEOUT_INFINITY)
+ {
+ setExpires(null);
+ }
+ else
+ {
+ int timeoutMillis = (lockTimeout * 60 * 1000);
+ Date now = new Date();
+ Date nextExpiry = new Date(now.getTime() + timeoutMillis);
+ setExpires(nextExpiry);
+ }
}
}
diff --git a/source/java/org/alfresco/repo/webdav/LockMethod.java b/source/java/org/alfresco/repo/webdav/LockMethod.java
index 8552df1e2b..9ba07c4729 100644
--- a/source/java/org/alfresco/repo/webdav/LockMethod.java
+++ b/source/java/org/alfresco/repo/webdav/LockMethod.java
@@ -336,25 +336,39 @@ public class LockMethod extends WebDAVMethod
m_response.setStatus(HttpServletResponse.SC_CREATED);
}
-
-
// Check if this is a new lock or a lock refresh
if (hasLockToken())
{
- this.lockInfo = checkNode(lockNodeInfo);
- // If a request body is not defined and "If" header is sent we have createExclusive as false,
- // but we need to check a previous LOCK was an exclusive. I.e. get the property for node. It
- // is already has got in a checkNode method, so we need just get a scope from lockInfo.
- // This particular case was raised as ALF-4008.
- this.createExclusive = WebDAV.XML_EXCLUSIVE.equals(this.lockInfo.getScope());
- // Refresh an existing lock
- refreshLock(lockNodeInfo, userName);
+ lockInfo = checkNode(lockNodeInfo);
+ lockInfo.getRWLock().writeLock().lock();
+ try
+ {
+ // If a request body is not defined and "If" header is sent we have createExclusive as false,
+ // but we need to check a previous LOCK was an exclusive. I.e. get the property for node. It
+ // is already has got in a checkNode method, so we need just get a scope from lockInfo.
+ // This particular case was raised as ALF-4008.
+ this.createExclusive = WebDAV.XML_EXCLUSIVE.equals(this.lockInfo.getScope());
+ // Refresh an existing lock
+ refreshLock(lockNodeInfo, userName);
+ }
+ finally
+ {
+ lockInfo.getRWLock().writeLock().unlock();
+ }
}
else
{
- this.lockInfo = checkNode(lockNodeInfo, true, this.createExclusive);
- // Create a new lock
- createLock(lockNodeInfo, userName);
+ lockInfo = checkNode(lockNodeInfo, true, createExclusive);
+ lockInfo.getRWLock().writeLock().lock();
+ try
+ {
+ // Create a new lock
+ createLock(lockNodeInfo, userName);
+ }
+ finally
+ {
+ lockInfo.getRWLock().writeLock().unlock();
+ }
}
@@ -391,30 +405,38 @@ public class LockMethod extends WebDAVMethod
// Create Lock token
lockToken = WebDAV.makeLockToken(lockNode.getNodeRef(), userName);
- if (createExclusive)
+ lockInfo.getRWLock().writeLock().lock();
+ try
{
- // Lock the node
- lockInfo.setTimeoutSeconds(getLockTimeout());
- lockInfo.setExclusiveLockToken(lockToken);
- }
- else
- {
- lockInfo.addSharedLockToken(lockToken);
- }
+ if (createExclusive)
+ {
+ // Lock the node
+ lockInfo.setTimeoutSeconds(getLockTimeout());
+ lockInfo.setExclusiveLockToken(lockToken);
+ }
+ else
+ {
+ lockInfo.addSharedLockToken(lockToken);
+ }
- // Store lock depth
- lockInfo.setDepth(WebDAV.getDepthName(m_depth));
- // Store lock scope (shared/exclusive)
- String scope = createExclusive ? WebDAV.XML_EXCLUSIVE : WebDAV.XML_SHARED;
- lockInfo.setScope(scope);
- // Store the owner of this lock
- lockInfo.setOwner(userName);
- // Lock the node
- getLockStore().put(lockNode.getNodeRef(), lockInfo);
-
- if (logger.isDebugEnabled())
+ // Store lock depth
+ lockInfo.setDepth(WebDAV.getDepthName(m_depth));
+ // Store lock scope (shared/exclusive)
+ String scope = createExclusive ? WebDAV.XML_EXCLUSIVE : WebDAV.XML_SHARED;
+ lockInfo.setScope(scope);
+ // Store the owner of this lock
+ lockInfo.setOwner(userName);
+ // Lock the node
+ getLockStore().put(lockNode.getNodeRef(), lockInfo);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Locked node " + lockNode + ": " + lockInfo);
+ }
+ }
+ finally
{
- logger.debug("Locked node " + lockNode + ": " + lockInfo);
+ lockInfo.getRWLock().writeLock().unlock();
}
}
@@ -429,11 +451,16 @@ public class LockMethod extends WebDAVMethod
{
if (this.createExclusive)
{
- // Update the expiry for the lock
- if (lockInfo.getExpires() != null)
+ lockInfo.getRWLock().writeLock().lock();
+ try
{
+ // Update the expiry for the lock
lockInfo.setTimeoutSeconds(getLockTimeout());
}
+ finally
+ {
+ lockInfo.getRWLock().writeLock().unlock();
+ }
}
}
@@ -444,21 +471,33 @@ public class LockMethod extends WebDAVMethod
{
String scope;
String lt;
- if (lockToken != null)
+ String owner;
+ Date expiry;
+
+ lockInfo.getRWLock().readLock().lock();
+ try
{
- // In case of lock creation take the scope from request header
- scope = this.createExclusive ? WebDAV.XML_EXCLUSIVE : WebDAV.XML_SHARED;
- // Output created lock
- lt = lockToken;
+ if (lockToken != null)
+ {
+ // In case of lock creation take the scope from request header
+ scope = this.createExclusive ? WebDAV.XML_EXCLUSIVE : WebDAV.XML_SHARED;
+ // Output created lock
+ lt = lockToken;
+ }
+ else
+ {
+ // In case of lock refreshing take the scope from previously stored lock
+ scope = this.lockInfo.getScope();
+ // Output refreshed lock
+ lt = this.lockInfo.getExclusiveLockToken();
+ }
+ owner = lockInfo.getOwner();
+ expiry = lockInfo.getExpires();
}
- else
+ finally
{
- // In case of lock refreshing take the scope from previously stored lock
- scope = this.lockInfo.getScope();
- // Output refreshed lock
- lt = this.lockInfo.getExclusiveLockToken();
+ lockInfo.getRWLock().readLock().unlock();
}
- String owner = lockInfo.getOwner();
XMLWriter xml = createXMLWriter();
@@ -468,7 +507,6 @@ public class LockMethod extends WebDAVMethod
xml.startElement(WebDAV.DAV_NS, WebDAV.XML_PROP + nsdec, WebDAV.XML_NS_PROP + nsdec, getDAVHelper().getNullAttributes());
// Output the lock details
- Date expiry = lockInfo.getExpires();
generateLockDiscoveryXML(xml, lockNodeInfo, false, scope, WebDAV.getDepthName(m_depth), lt, owner, expiry);
// Close off the XML
diff --git a/source/java/org/alfresco/repo/webdav/LockStore.java b/source/java/org/alfresco/repo/webdav/LockStore.java
index 0437b61988..2565c50ad1 100644
--- a/source/java/org/alfresco/repo/webdav/LockStore.java
+++ b/source/java/org/alfresco/repo/webdav/LockStore.java
@@ -54,5 +54,11 @@ public interface LockStore
LockInfo get(NodeRef nodeRef);
+ /**
+ * Remove LockInfo for the specified NodeRef. The LockInfo cannot be considered locked
+ * once removed from the LockStore.
+ *
+ * @param nodeRef
+ */
void remove(NodeRef nodeRef);
}
diff --git a/source/java/org/alfresco/repo/webdav/PropFindMethod.java b/source/java/org/alfresco/repo/webdav/PropFindMethod.java
index 7e49e519a0..7f9dffdf47 100644
--- a/source/java/org/alfresco/repo/webdav/PropFindMethod.java
+++ b/source/java/org/alfresco/repo/webdav/PropFindMethod.java
@@ -924,9 +924,17 @@ public class PropFindMethod extends WebDAVMethod
// Output the lock status response
LockInfo lockInfo = getNodeLockInfo(nodeInfo);
- if (lockInfo.isLocked())
+ lockInfo.getRWLock().readLock().lock();
+ try
{
- generateLockDiscoveryXML(xml, nodeInfo, lockInfo);
+ if (lockInfo.isLocked())
+ {
+ generateLockDiscoveryXML(xml, nodeInfo, lockInfo);
+ }
+ }
+ finally
+ {
+ lockInfo.getRWLock().readLock().unlock();
}
}
diff --git a/source/java/org/alfresco/repo/webdav/PutMethod.java b/source/java/org/alfresco/repo/webdav/PutMethod.java
index a5cc1612a3..e70c0187bb 100644
--- a/source/java/org/alfresco/repo/webdav/PutMethod.java
+++ b/source/java/org/alfresco/repo/webdav/PutMethod.java
@@ -190,18 +190,29 @@ public class PutMethod extends WebDAVMethod
String userName = getDAVHelper().getAuthenticationService().getCurrentUserName();
LockInfo lockInfo = getLockStore().get(contentNodeInfo.getNodeRef());
- if (lockInfo != null && lockInfo.isLocked() && !lockInfo.getOwner().equals(userName))
+ if (lockInfo != null)
{
- if (logger.isDebugEnabled())
+ lockInfo.getRWLock().readLock().lock();
+ try
{
- String path = getPath();
- String owner = lockInfo.getOwner();
- logger.debug("Node locked: path=["+path+"], owner=["+owner+"], current user=["+userName+"]");
+ if (lockInfo.isLocked() && !lockInfo.getOwner().equals(userName))
+ {
+ if (logger.isDebugEnabled())
+ {
+ String path = getPath();
+ String owner = lockInfo.getOwner();
+ logger.debug("Node locked: path=["+path+"], owner=["+owner+"], current user=["+userName+"]");
+ }
+ // Indicate that the resource is locked
+ throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
+ }
+ }
+ finally
+ {
+ lockInfo.getRWLock().readLock().unlock();
}
- // Indicate that the resource is locked
- throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
}
-
+
try
{
// Access the content
diff --git a/source/java/org/alfresco/repo/webdav/UnlockMethod.java b/source/java/org/alfresco/repo/webdav/UnlockMethod.java
index 3ee277bab1..0b2909c282 100644
--- a/source/java/org/alfresco/repo/webdav/UnlockMethod.java
+++ b/source/java/org/alfresco/repo/webdav/UnlockMethod.java
@@ -132,74 +132,92 @@ public class UnlockMethod extends WebDAVMethod
NodeRef nodeRef = lockNodeInfo.getNodeRef();
LockInfo lockInfo = getLockStore().get(nodeRef);
- if (lockInfo == null || !lockInfo.isLocked())
+ if (lockInfo == null)
{
if (logger.isDebugEnabled())
{
- logger.debug("Unlock token=" + getLockToken() + " Not locked");
+ logger.debug("Unlock token=" + getLockToken() + " Not locked - no info in lock store.");
}
// Node is not locked
throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
}
- else if (lockInfo.isExpired())
+
+ lockInfo.getRWLock().writeLock().lock();
+ try
{
- if (logger.isDebugEnabled())
- {
- logger.debug("Unlock token=" + getLockToken() + " Lock expired");
- }
- // Return a success status
- m_response.setStatus(HttpServletResponse.SC_NO_CONTENT);
- removeNoContentAspect(nodeRef);
- }
- else if (lockInfo.isExclusive())
- {
- String currentUser = getAuthenticationService().getCurrentUserName();
- if (currentUser.equals(lockInfo.getOwner()))
- {
- getLockStore().remove(nodeRef);
-
- // Indicate that the unlock was successful
- m_response.setStatus(HttpServletResponse.SC_NO_CONTENT);
- removeNoContentAspect(nodeRef);
-
- if (logger.isDebugEnabled())
- {
- logger.debug("Unlock token=" + getLockToken() + " Successful");
- }
- }
- else
+ if (!lockInfo.isLocked())
{
if (logger.isDebugEnabled())
{
- logger.debug("Unlock token=" + getLockToken() + " Not lock owner");
+ logger.debug("Unlock token=" + getLockToken() + " Not locked");
}
// Node is not locked
throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
}
- }
- else if (lockInfo.isShared())
- {
- Set