mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-14 17:58:59 +00:00
ALF-12866: WebDAV should use in-memory locking for transient locks
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@34167 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -18,6 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.webdav;
|
package org.alfresco.repo.webdav;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@@ -27,10 +29,12 @@ import java.util.Set;
|
|||||||
* @author Ivan Rybnikov
|
* @author Ivan Rybnikov
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class LockInfo
|
public final class LockInfo implements Serializable
|
||||||
{
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
// Exclusive lock token
|
// Exclusive lock token
|
||||||
private String token = null;
|
private String exclusiveLockToken = null;
|
||||||
|
|
||||||
// Lock scope
|
// Lock scope
|
||||||
private String scope = null;
|
private String scope = null;
|
||||||
@@ -38,14 +42,14 @@ public class LockInfo
|
|||||||
// Lock depth
|
// Lock depth
|
||||||
private String depth = null;
|
private String depth = null;
|
||||||
|
|
||||||
// If lock is shared
|
|
||||||
private boolean shared = false;
|
|
||||||
|
|
||||||
// Shared lock tokens
|
// Shared lock tokens
|
||||||
private Set<String> sharedLockTokens = null;
|
private final Set<String> sharedLockTokens = new HashSet<String>(3);
|
||||||
|
|
||||||
// Shared lock token separator
|
// User name of the lock's owner
|
||||||
private static final String SHARED_LOCK_TOKEN_SEPARATOR = ",";
|
private String owner;
|
||||||
|
|
||||||
|
// When does the lock expire?
|
||||||
|
private Date expires;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor
|
* Default constructor
|
||||||
@@ -64,7 +68,7 @@ public class LockInfo
|
|||||||
*/
|
*/
|
||||||
public LockInfo(String token, String scope, String depth)
|
public LockInfo(String token, String scope, String depth)
|
||||||
{
|
{
|
||||||
this.token = token;
|
this.exclusiveLockToken = token;
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
this.depth = depth;
|
this.depth = depth;
|
||||||
}
|
}
|
||||||
@@ -76,11 +80,7 @@ public class LockInfo
|
|||||||
*/
|
*/
|
||||||
public boolean isLocked()
|
public boolean isLocked()
|
||||||
{
|
{
|
||||||
if (token != null || (sharedLockTokens != null && !sharedLockTokens.isEmpty()))
|
return (isExclusive() || isShared());
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,18 +88,20 @@ public class LockInfo
|
|||||||
*
|
*
|
||||||
* @param token Lock token
|
* @param token Lock token
|
||||||
*/
|
*/
|
||||||
public void setToken(String token)
|
public void setExclusiveLockToken(String token)
|
||||||
{
|
{
|
||||||
this.token = token;
|
this.exclusiveLockToken = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter for exclusive lock token
|
* Getter for exclusive lock token.
|
||||||
* @return
|
*
|
||||||
|
* @return String
|
||||||
*/
|
*/
|
||||||
public String getToken()
|
public String getExclusiveLockToken()
|
||||||
{
|
{
|
||||||
return token;
|
checkLockState();
|
||||||
|
return exclusiveLockToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -143,110 +145,48 @@ public class LockInfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms shared lock tokens string to list.
|
* Getter for sharedLockTokens list.
|
||||||
*
|
|
||||||
* @param sharedLockTokens String contains all node's shared lock tokens
|
|
||||||
* divided with SHARED_LOCK_TOKEN_SEPARATOR value.
|
|
||||||
* @return List of shared lock tokens
|
|
||||||
*/
|
|
||||||
public static Set<String> parseSharedLockTokens(String sharedLockTokens)
|
|
||||||
{
|
|
||||||
if (sharedLockTokens == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] sl = sharedLockTokens.split(SHARED_LOCK_TOKEN_SEPARATOR);
|
|
||||||
Set<String> result = new HashSet<String>(sl.length * 2);
|
|
||||||
for (int i = 0; i < sl.length; i++)
|
|
||||||
{
|
|
||||||
result.add(sl[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter for sharedLockTokens list
|
|
||||||
*
|
*
|
||||||
* @return LinkedList<String>
|
* @return LinkedList<String>
|
||||||
*/
|
*/
|
||||||
public Set<String> getSharedLockTokens()
|
public Set<String> getSharedLockTokens()
|
||||||
{
|
{
|
||||||
|
checkLockState();
|
||||||
return sharedLockTokens;
|
return sharedLockTokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setter for sharedLockTokens list
|
* Setter for sharedLockTokens list.
|
||||||
*
|
*
|
||||||
* @param sharedLockTokens
|
* @param sharedLockTokens
|
||||||
*/
|
*/
|
||||||
public void setSharedLockTokens(Set<String> sharedLockTokens)
|
public void setSharedLockTokens(Set<String> sharedLockTokens)
|
||||||
{
|
{
|
||||||
this.sharedLockTokens = sharedLockTokens;
|
this.sharedLockTokens.clear();
|
||||||
|
this.sharedLockTokens.addAll(sharedLockTokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds new shared lock token to sharedLockTokens list
|
* Adds new shared lock token to sharedLockTokens list.
|
||||||
*
|
*
|
||||||
* @param token new token
|
* @param token The token to add.
|
||||||
*/
|
*/
|
||||||
public void addSharedLockToken(String token)
|
public void addSharedLockToken(String token)
|
||||||
{
|
{
|
||||||
if (sharedLockTokens == null)
|
|
||||||
{
|
|
||||||
sharedLockTokens = new HashSet<String>(3);
|
|
||||||
}
|
|
||||||
sharedLockTokens.add(token);
|
sharedLockTokens.add(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms list of shared locks to string.
|
* Is it a shared lock?
|
||||||
* Lock tokens separated with SHARED_LOCK_TOKEN_SEPARATOR value.
|
|
||||||
*
|
*
|
||||||
* @param lockTokens list of shared locks
|
* @return true if shared.
|
||||||
* @return String
|
|
||||||
*/
|
|
||||||
public static String makeSharedLockTokensString(Set<String> lockTokens)
|
|
||||||
{
|
|
||||||
StringBuilder str = new StringBuilder();
|
|
||||||
|
|
||||||
boolean first = true;
|
|
||||||
for (String token : lockTokens)
|
|
||||||
{
|
|
||||||
if (!first)
|
|
||||||
{
|
|
||||||
str.append(SHARED_LOCK_TOKEN_SEPARATOR);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
str.append(token);
|
|
||||||
}
|
|
||||||
return str.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setter for shared property
|
|
||||||
*
|
|
||||||
* @param shared
|
|
||||||
*/
|
|
||||||
public void setShared(boolean shared)
|
|
||||||
{
|
|
||||||
this.shared = shared;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true is lock is shared
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
*/
|
||||||
public boolean isShared()
|
public boolean isShared()
|
||||||
{
|
{
|
||||||
return shared;
|
return (!sharedLockTokens.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the lock info as a string
|
* Return the lock info as a string
|
||||||
*
|
*
|
||||||
@@ -256,20 +196,113 @@ public class LockInfo
|
|||||||
{
|
{
|
||||||
StringBuilder str = new StringBuilder();
|
StringBuilder str = new StringBuilder();
|
||||||
|
|
||||||
str.append("[");
|
str.append("LockInfo[");
|
||||||
|
|
||||||
str.append("token=");
|
str.append("exclusiveLockToken=");
|
||||||
str.append(getToken());
|
str.append(getExclusiveLockToken());
|
||||||
str.append(",scope=");
|
str.append(", scope=");
|
||||||
str.append(getScope());
|
str.append(getScope());
|
||||||
str.append(",depth=");
|
str.append(", depth=");
|
||||||
str.append(getDepth());
|
str.append(getDepth());
|
||||||
str.append(",shared locks=");
|
str.append(", sharedLockTokens=");
|
||||||
str.append(getSharedLockTokens());
|
str.append(getSharedLockTokens());
|
||||||
|
str.append(", owner=");
|
||||||
|
str.append(owner);
|
||||||
|
str.append(", expires=");
|
||||||
|
str.append(expires);
|
||||||
|
|
||||||
str.append("]");
|
str.append("]");
|
||||||
|
|
||||||
return str.toString();
|
return str.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this lock has expired. If no expiry is set (i.e. expires is null)
|
||||||
|
* then false is always returned.
|
||||||
|
*
|
||||||
|
* @return true if expired.
|
||||||
|
*/
|
||||||
|
public boolean isExpired()
|
||||||
|
{
|
||||||
|
if (expires == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Date now = new Date();
|
||||||
|
return now.after(expires);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is it an exclusive lock?
|
||||||
|
*
|
||||||
|
* @return true if exclusive.
|
||||||
|
*/
|
||||||
|
public boolean isExclusive()
|
||||||
|
{
|
||||||
|
return (exclusiveLockToken != null && exclusiveLockToken.length() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Who owns the lock?
|
||||||
|
*
|
||||||
|
* @return the owner
|
||||||
|
*/
|
||||||
|
public String getOwner()
|
||||||
|
{
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the username of who owns the lock.
|
||||||
|
*
|
||||||
|
* @param owner Owner's username
|
||||||
|
*/
|
||||||
|
public void setOwner(String owner)
|
||||||
|
{
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the expiry date/time for this lock. Set to null for never expires.
|
||||||
|
*
|
||||||
|
* @param expires the expires to set
|
||||||
|
*/
|
||||||
|
public void setExpires(Date expires)
|
||||||
|
{
|
||||||
|
this.expires = expires;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the expiry date/time for this lock, or null if it never expires.
|
||||||
|
*
|
||||||
|
* @return the expires
|
||||||
|
*/
|
||||||
|
public Date getExpires()
|
||||||
|
{
|
||||||
|
return expires;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanity check the state of this LockInfo.
|
||||||
|
*/
|
||||||
|
private void checkLockState()
|
||||||
|
{
|
||||||
|
if (isShared() && isExclusive())
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Lock cannot be both shared and exclusive: " + toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the expiry date/time to lockTimeout seconds into the future.
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.webdav;
|
package org.alfresco.repo.webdav;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -27,12 +28,9 @@ import java.util.TimerTask;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.model.WebDAVModel;
|
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
import org.alfresco.service.cmr.lock.LockService;
|
|
||||||
import org.alfresco.service.cmr.lock.LockType;
|
|
||||||
import org.alfresco.service.cmr.model.FileFolderService;
|
import org.alfresco.service.cmr.model.FileFolderService;
|
||||||
import org.alfresco.service.cmr.model.FileFolderUtil;
|
import org.alfresco.service.cmr.model.FileFolderUtil;
|
||||||
import org.alfresco.service.cmr.model.FileInfo;
|
import org.alfresco.service.cmr.model.FileInfo;
|
||||||
@@ -390,40 +388,34 @@ public class LockMethod extends WebDAVMethod
|
|||||||
*/
|
*/
|
||||||
protected final void createLock(FileInfo lockNode, String userName) throws WebDAVServerException
|
protected final void createLock(FileInfo lockNode, String userName) throws WebDAVServerException
|
||||||
{
|
{
|
||||||
LockService lockService = getLockService();
|
|
||||||
|
|
||||||
// Create Lock token
|
// Create Lock token
|
||||||
lockToken = WebDAV.makeLockToken(lockNode.getNodeRef(), userName);
|
lockToken = WebDAV.makeLockToken(lockNode.getNodeRef(), userName);
|
||||||
|
|
||||||
if (this.createExclusive)
|
if (createExclusive)
|
||||||
{
|
{
|
||||||
// Lock the node
|
// Lock the node
|
||||||
lockService.lock(lockNode.getNodeRef(), LockType.WRITE_LOCK, getLockTimeout());
|
lockInfo.setTimeoutSeconds(getLockTimeout());
|
||||||
|
lockInfo.setExclusiveLockToken(lockToken);
|
||||||
// update local cache (will be read back when generating the response)
|
|
||||||
lockNode.getProperties().put(ContentModel.PROP_LOCK_OWNER, userName);
|
|
||||||
// ALF-3681, we should also cache the expiryDate for correct response generation
|
|
||||||
lockNode.getProperties().put(ContentModel.PROP_EXPIRY_DATE,
|
|
||||||
getNodeService().getProperty(lockNode.getNodeRef(), ContentModel.PROP_EXPIRY_DATE));
|
|
||||||
|
|
||||||
//this.lockInfo.setToken(lockToken);
|
|
||||||
getNodeService().setProperty(lockNode.getNodeRef(), WebDAVModel.PROP_OPAQUE_LOCK_TOKEN, lockToken);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.lockInfo.addSharedLockToken(lockToken);
|
lockInfo.addSharedLockToken(lockToken);
|
||||||
String sharedLockTokens = LockInfo.makeSharedLockTokensString(this.lockInfo.getSharedLockTokens());
|
|
||||||
getNodeService().setProperty(lockNode.getNodeRef(), WebDAVModel.PROP_SHARED_LOCK_TOKENS, sharedLockTokens);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store lock depth
|
// Store lock depth
|
||||||
getNodeService().setProperty(lockNode.getNodeRef(), WebDAVModel.PROP_LOCK_DEPTH, WebDAV.getDepthName(m_depth));
|
lockInfo.setDepth(WebDAV.getDepthName(m_depth));
|
||||||
|
|
||||||
// Store lock scope (shared/exclusive)
|
// Store lock scope (shared/exclusive)
|
||||||
getNodeService().setProperty(lockNode.getNodeRef(), WebDAVModel.PROP_LOCK_SCOPE, this.createExclusive ? WebDAV.XML_EXCLUSIVE : WebDAV.XML_SHARED);
|
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);
|
||||||
|
|
||||||
logger.debug("Properties of the " + lockNode + " was changed");
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Locked node " + lockNode + ": " + lockInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -438,7 +430,10 @@ public class LockMethod extends WebDAVMethod
|
|||||||
if (this.createExclusive)
|
if (this.createExclusive)
|
||||||
{
|
{
|
||||||
// Update the expiry for the lock
|
// Update the expiry for the lock
|
||||||
getLockService().lock(lockNode.getNodeRef(), LockType.WRITE_LOCK, getLockTimeout());
|
if (lockInfo.getExpires() != null)
|
||||||
|
{
|
||||||
|
lockInfo.setTimeoutSeconds(getLockTimeout());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,9 +456,9 @@ public class LockMethod extends WebDAVMethod
|
|||||||
// In case of lock refreshing take the scope from previously stored lock
|
// In case of lock refreshing take the scope from previously stored lock
|
||||||
scope = this.lockInfo.getScope();
|
scope = this.lockInfo.getScope();
|
||||||
// Output refreshed lock
|
// Output refreshed lock
|
||||||
lt = this.lockInfo.getToken();
|
lt = this.lockInfo.getExclusiveLockToken();
|
||||||
}
|
}
|
||||||
String owner = (String) lockNodeInfo.getProperties().get(ContentModel.PROP_LOCK_OWNER);
|
String owner = lockInfo.getOwner();
|
||||||
|
|
||||||
XMLWriter xml = createXMLWriter();
|
XMLWriter xml = createXMLWriter();
|
||||||
|
|
||||||
@@ -473,7 +468,8 @@ public class LockMethod extends WebDAVMethod
|
|||||||
xml.startElement(WebDAV.DAV_NS, WebDAV.XML_PROP + nsdec, WebDAV.XML_NS_PROP + nsdec, getDAVHelper().getNullAttributes());
|
xml.startElement(WebDAV.DAV_NS, WebDAV.XML_PROP + nsdec, WebDAV.XML_NS_PROP + nsdec, getDAVHelper().getNullAttributes());
|
||||||
|
|
||||||
// Output the lock details
|
// Output the lock details
|
||||||
generateLockDiscoveryXML(xml, lockNodeInfo, false, scope, WebDAV.getDepthName(m_depth), lt, owner);
|
Date expiry = lockInfo.getExpires();
|
||||||
|
generateLockDiscoveryXML(xml, lockNodeInfo, false, scope, WebDAV.getDepthName(m_depth), lt, owner, expiry);
|
||||||
|
|
||||||
// Close off the XML
|
// Close off the XML
|
||||||
xml.endElement(WebDAV.DAV_NS, WebDAV.XML_PROP, WebDAV.XML_NS_PROP);
|
xml.endElement(WebDAV.DAV_NS, WebDAV.XML_PROP, WebDAV.XML_NS_PROP);
|
||||||
|
58
source/java/org/alfresco/repo/webdav/LockStore.java
Normal file
58
source/java/org/alfresco/repo/webdav/LockStore.java
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.webdav;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides storage for WebDAV {@link LockInfo lock information}.
|
||||||
|
* <p>
|
||||||
|
* Note: the existence of LockInfo does NOT mean that a node is necessarily locked. It may have timed-out,
|
||||||
|
* been unlocked, or be left in an invalid state for some reason. The LockInfo is a record of a requested lock -
|
||||||
|
* the actual values should be examined as necessary.
|
||||||
|
* <p>
|
||||||
|
* Implementations of this interface should be fast, ideally an in-memory map. Implementations should also be thread-
|
||||||
|
* and cluster-safe.
|
||||||
|
*
|
||||||
|
* @author Matt Ward
|
||||||
|
*/
|
||||||
|
public interface LockStore
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Provide LockInfo about a specific node to the LockStore.
|
||||||
|
*
|
||||||
|
* @param nodeToLock
|
||||||
|
* @param lockInfo
|
||||||
|
*/
|
||||||
|
void put(NodeRef nodeToLock, LockInfo lockInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves LockInfo for as given nodeRef. The LockInfo may specify that a node is
|
||||||
|
* <strong>NOT</strong> locked, so the LockInfo should always be checked for validity.
|
||||||
|
* <p>
|
||||||
|
* The presence of LockInfo does not imply that a node is locked.
|
||||||
|
*
|
||||||
|
* @param nodeRef
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
LockInfo get(NodeRef nodeRef);
|
||||||
|
|
||||||
|
|
||||||
|
void remove(NodeRef nodeRef);
|
||||||
|
}
|
30
source/java/org/alfresco/repo/webdav/LockStoreFactory.java
Normal file
30
source/java/org/alfresco/repo/webdav/LockStoreFactory.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.webdav;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory to create {@link LockStore} instances.
|
||||||
|
*
|
||||||
|
* @author Matt Ward
|
||||||
|
*/
|
||||||
|
public interface LockStoreFactory
|
||||||
|
{
|
||||||
|
LockStore getLockStore();
|
||||||
|
}
|
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.webdav;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
|
||||||
|
import com.hazelcast.core.HazelcastInstance;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of the {@link LockStoreFactory} interface. Creates {@link LockStore}s
|
||||||
|
* backed by a Hazelcast distributed {@link java.util.Map Map}.
|
||||||
|
*
|
||||||
|
* @see LockStoreFactory
|
||||||
|
* @see LockStoreImpl
|
||||||
|
* @author Matt Ward
|
||||||
|
*/
|
||||||
|
public class LockStoreFactoryImpl implements LockStoreFactory
|
||||||
|
{
|
||||||
|
private HazelcastInstance hazelcast;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LockStore getLockStore()
|
||||||
|
{
|
||||||
|
ConcurrentMap<NodeRef, LockInfo> map = hazelcast.getMap("webdav-locks");
|
||||||
|
return new LockStoreImpl(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param hazelcast the hazelcast to set
|
||||||
|
*/
|
||||||
|
public void setHazelcast(HazelcastInstance hazelcast)
|
||||||
|
{
|
||||||
|
this.hazelcast = hazelcast;
|
||||||
|
}
|
||||||
|
}
|
59
source/java/org/alfresco/repo/webdav/LockStoreImpl.java
Normal file
59
source/java/org/alfresco/repo/webdav/LockStoreImpl.java
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.webdav;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default {@link LockStore} implementation. This is based upon a ConcurrentMap intended to be supplied by
|
||||||
|
* Hazelcast (or a similar, distributed data structure library).
|
||||||
|
*
|
||||||
|
* @see LockStore
|
||||||
|
* @author Matt Ward
|
||||||
|
*/
|
||||||
|
public class LockStoreImpl implements LockStore
|
||||||
|
{
|
||||||
|
private final ConcurrentMap<NodeRef, LockInfo> lockInfoMap;
|
||||||
|
|
||||||
|
public LockStoreImpl(ConcurrentMap<NodeRef, LockInfo> lockInfoMap)
|
||||||
|
{
|
||||||
|
this.lockInfoMap = lockInfoMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void put(NodeRef nodeToLock, LockInfo lockInfo)
|
||||||
|
{
|
||||||
|
lockInfoMap.put(nodeToLock, lockInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LockInfo get(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
return lockInfoMap.get(nodeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
lockInfoMap.remove(nodeRef);
|
||||||
|
}
|
||||||
|
}
|
@@ -24,10 +24,9 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.action.executer.ContentMetadataExtracter;
|
import org.alfresco.repo.action.executer.ContentMetadataExtracter;
|
||||||
import org.alfresco.service.cmr.action.Action;
|
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
import org.alfresco.service.cmr.lock.LockStatus;
|
import org.alfresco.service.cmr.action.Action;
|
||||||
import org.alfresco.service.cmr.model.FileExistsException;
|
import org.alfresco.service.cmr.model.FileExistsException;
|
||||||
import org.alfresco.service.cmr.model.FileFolderService;
|
import org.alfresco.service.cmr.model.FileFolderService;
|
||||||
import org.alfresco.service.cmr.model.FileInfo;
|
import org.alfresco.service.cmr.model.FileInfo;
|
||||||
@@ -44,7 +43,6 @@ import org.springframework.dao.ConcurrencyFailureException;
|
|||||||
public class PutMethod extends WebDAVMethod
|
public class PutMethod extends WebDAVMethod
|
||||||
{
|
{
|
||||||
// Request parameters
|
// Request parameters
|
||||||
private String m_strLockToken = null;
|
|
||||||
private String m_strContentType = null;
|
private String m_strContentType = null;
|
||||||
private boolean m_expectHeaderPresent = false;
|
private boolean m_expectHeaderPresent = false;
|
||||||
|
|
||||||
@@ -189,12 +187,17 @@ public class PutMethod extends WebDAVMethod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LockStatus lockSts = getLockService().getLockStatus(contentNodeInfo.getNodeRef());
|
|
||||||
String userName = getDAVHelper().getAuthenticationService().getCurrentUserName();
|
String userName = getDAVHelper().getAuthenticationService().getCurrentUserName();
|
||||||
String owner = (String) getNodeService().getProperty(contentNodeInfo.getNodeRef(), ContentModel.PROP_LOCK_OWNER);
|
LockInfo lockInfo = getLockStore().get(contentNodeInfo.getNodeRef());
|
||||||
|
|
||||||
if (lockSts == LockStatus.LOCKED || (lockSts == LockStatus.LOCK_OWNER && !userName.equals(owner)))
|
if (lockInfo != null && 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
|
// Indicate that the resource is locked
|
||||||
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
|
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
|
||||||
}
|
}
|
||||||
|
@@ -23,13 +23,9 @@ import java.util.Set;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.model.WebDAVModel;
|
|
||||||
import org.alfresco.service.cmr.lock.LockService;
|
|
||||||
import org.alfresco.service.cmr.lock.LockStatus;
|
|
||||||
import org.alfresco.service.cmr.model.FileInfo;
|
import org.alfresco.service.cmr.model.FileInfo;
|
||||||
import org.alfresco.service.cmr.model.FileNotFoundException;
|
import org.alfresco.service.cmr.model.FileNotFoundException;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the WebDAV UNLOCK method
|
* Implements the WebDAV UNLOCK method
|
||||||
@@ -126,37 +122,42 @@ public class UnlockMethod extends WebDAVMethod
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse the lock token
|
// Parse the lock token
|
||||||
String[] lockInfo = WebDAV.parseLockToken(getLockToken());
|
String[] lockInfoFromRequest = WebDAV.parseLockToken(getLockToken());
|
||||||
if (lockInfo == null)
|
if (lockInfoFromRequest == null)
|
||||||
{
|
{
|
||||||
// Bad lock token
|
// Bad lock token
|
||||||
throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
|
throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the lock status for the node
|
|
||||||
LockService lockService = getDAVHelper().getLockService();
|
|
||||||
NodeService nodeService = getNodeService();
|
|
||||||
// String nodeId = lockInfo[0];
|
|
||||||
// String userName = lockInfo[1];
|
|
||||||
|
|
||||||
NodeRef nodeRef = lockNodeInfo.getNodeRef();
|
NodeRef nodeRef = lockNodeInfo.getNodeRef();
|
||||||
LockStatus lockSts = lockService.getLockStatus(nodeRef);
|
LockInfo lockInfo = getLockStore().get(nodeRef);
|
||||||
if (lockSts == LockStatus.LOCK_OWNER)
|
|
||||||
{
|
|
||||||
if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY))
|
|
||||||
lockService.unlock(nodeRef);
|
|
||||||
nodeService.removeProperty(nodeRef, WebDAVModel.PROP_OPAQUE_LOCK_TOKEN);
|
|
||||||
nodeService.removeProperty(nodeRef, WebDAVModel.PROP_LOCK_DEPTH);
|
|
||||||
nodeService.removeProperty(nodeRef, WebDAVModel.PROP_LOCK_SCOPE);
|
|
||||||
|
|
||||||
// Return the cm:lockable aspect to working copy (ALF-4479, ALF-7079)
|
if (lockInfo == null || !lockInfo.isLocked())
|
||||||
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY))
|
{
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
nodeService.addAspect(nodeRef, ContentModel.ASPECT_LOCKABLE, null);
|
logger.debug("Unlock token=" + getLockToken() + " Not locked");
|
||||||
}
|
}
|
||||||
|
// Node is not locked
|
||||||
|
throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
|
||||||
|
}
|
||||||
|
else if (lockInfo.isExpired())
|
||||||
|
{
|
||||||
|
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() /* && user is lock-owner */)
|
||||||
|
{
|
||||||
|
getLockStore().remove(nodeRef);
|
||||||
|
|
||||||
// Indicate that the unlock was successful
|
// Indicate that the unlock was successful
|
||||||
m_response.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
m_response.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
||||||
|
|
||||||
removeNoContentAspect(nodeRef);
|
removeNoContentAspect(nodeRef);
|
||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
@@ -165,57 +166,27 @@ public class UnlockMethod extends WebDAVMethod
|
|||||||
logger.debug("Unlock token=" + getLockToken() + " Successful");
|
logger.debug("Unlock token=" + getLockToken() + " Successful");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (lockSts == LockStatus.NO_LOCK)
|
else if (lockInfo.isShared())
|
||||||
{
|
{
|
||||||
String sharedLocks = (String) nodeService.getProperty(nodeRef, WebDAVModel.PROP_SHARED_LOCK_TOKENS);
|
Set<String> sharedLocks = lockInfo.getSharedLockTokens();
|
||||||
if (sharedLocks != null)
|
if (sharedLocks.contains(m_strLockToken))
|
||||||
{
|
{
|
||||||
Set<String> locks = LockInfo.parseSharedLockTokens(sharedLocks);
|
sharedLocks.remove(m_strLockToken);
|
||||||
|
|
||||||
if (locks != null && locks.contains(m_strLockToken))
|
// Indicate that the unlock was successful
|
||||||
{
|
m_response.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
||||||
locks.remove(m_strLockToken);
|
removeNoContentAspect(nodeRef);
|
||||||
nodeService.setProperty(nodeRef, WebDAVModel.PROP_SHARED_LOCK_TOKENS, LockInfo.makeSharedLockTokensString(locks));
|
|
||||||
|
|
||||||
// Indicate that the unlock was successful
|
|
||||||
m_response.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
|
||||||
removeNoContentAspect(nodeRef);
|
|
||||||
|
|
||||||
// DEBUG
|
|
||||||
if (logger.isDebugEnabled())
|
|
||||||
{
|
|
||||||
logger.debug("Unlock token=" + getLockToken() + " Successful");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
logger.debug("Unlock token=" + getLockToken() + " Not locked");
|
{
|
||||||
|
logger.debug("Unlock token=" + getLockToken() + " Successful");
|
||||||
// Node is not locked
|
}
|
||||||
throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (lockSts == LockStatus.LOCKED)
|
else
|
||||||
{
|
{
|
||||||
// DEBUG
|
throw new IllegalStateException("Invalid LockInfo state: " + lockInfo);
|
||||||
if (logger.isDebugEnabled())
|
|
||||||
logger.debug("Unlock token=" + getLockToken() + " Not lock owner");
|
|
||||||
|
|
||||||
// Node is locked but not by this user
|
|
||||||
throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
|
|
||||||
}
|
|
||||||
else if (lockSts == LockStatus.LOCK_EXPIRED)
|
|
||||||
{
|
|
||||||
// DEBUG
|
|
||||||
if (logger.isDebugEnabled())
|
|
||||||
logger.debug("Unlock token=" + getLockToken() + " Lock expired");
|
|
||||||
|
|
||||||
// Return a success status
|
|
||||||
m_response.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
|
||||||
removeNoContentAspect(nodeRef);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -77,6 +77,7 @@ public class WebDAVHelper
|
|||||||
private DictionaryService m_dictionaryService;
|
private DictionaryService m_dictionaryService;
|
||||||
private MimetypeService m_mimetypeService;
|
private MimetypeService m_mimetypeService;
|
||||||
private LockService m_lockService;
|
private LockService m_lockService;
|
||||||
|
private LockStore m_lockStore;
|
||||||
private ActionService m_actionService;
|
private ActionService m_actionService;
|
||||||
private AuthenticationService m_authService;
|
private AuthenticationService m_authService;
|
||||||
private PermissionService m_permissionService;
|
private PermissionService m_permissionService;
|
||||||
@@ -88,7 +89,7 @@ public class WebDAVHelper
|
|||||||
/**
|
/**
|
||||||
* Class constructor
|
* Class constructor
|
||||||
*/
|
*/
|
||||||
protected WebDAVHelper(ServiceRegistry serviceRegistry, AuthenticationService authService)
|
protected WebDAVHelper(ServiceRegistry serviceRegistry, LockStore lockStore, AuthenticationService authService)
|
||||||
{
|
{
|
||||||
m_serviceRegistry = serviceRegistry;
|
m_serviceRegistry = serviceRegistry;
|
||||||
|
|
||||||
@@ -103,6 +104,8 @@ public class WebDAVHelper
|
|||||||
m_permissionService = m_serviceRegistry.getPermissionService();
|
m_permissionService = m_serviceRegistry.getPermissionService();
|
||||||
|
|
||||||
m_authService = authService;
|
m_authService = authService;
|
||||||
|
|
||||||
|
m_lockStore = lockStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -174,6 +177,14 @@ public class WebDAVHelper
|
|||||||
return m_lockService;
|
return m_lockService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Return the {@link LockStore lock store}.
|
||||||
|
*/
|
||||||
|
public final LockStore getLockStore()
|
||||||
|
{
|
||||||
|
return m_lockStore;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Return the action service
|
* @return Return the action service
|
||||||
*/
|
*/
|
||||||
|
@@ -25,15 +25,14 @@ import java.io.FileOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.servlet.ServletInputStream;
|
import javax.servlet.ServletInputStream;
|
||||||
@@ -45,16 +44,12 @@ import javax.xml.parsers.DocumentBuilderFactory;
|
|||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.model.WebDAVModel;
|
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||||
import org.alfresco.repo.security.permissions.AccessDeniedException;
|
import org.alfresco.repo.security.permissions.AccessDeniedException;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
import org.alfresco.service.ServiceRegistry;
|
import org.alfresco.service.ServiceRegistry;
|
||||||
import org.alfresco.service.cmr.action.ActionService;
|
import org.alfresco.service.cmr.action.ActionService;
|
||||||
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.model.FileFolderService;
|
import org.alfresco.service.cmr.model.FileFolderService;
|
||||||
import org.alfresco.service.cmr.model.FileInfo;
|
import org.alfresco.service.cmr.model.FileInfo;
|
||||||
import org.alfresco.service.cmr.model.FileNotFoundException;
|
import org.alfresco.service.cmr.model.FileNotFoundException;
|
||||||
@@ -654,13 +649,13 @@ public abstract class WebDAVMethod
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method to return the lock service
|
* Retrieve the (WebDAV protocol-level) {@link LockStore lock store}.
|
||||||
*
|
*
|
||||||
* @return LockService
|
* @return LockStore
|
||||||
*/
|
*/
|
||||||
protected final LockService getLockService()
|
protected final LockStore getLockStore()
|
||||||
{
|
{
|
||||||
return m_davHelper.getLockService();
|
return m_davHelper.getLockStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -747,16 +742,19 @@ public abstract class WebDAVMethod
|
|||||||
return writer;
|
return writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the lock discovery XML response
|
* Generates the lock discovery XML response
|
||||||
*
|
*
|
||||||
* @param xml XMLWriter
|
* @param xml XMLWriter
|
||||||
* @param lockNode NodeRef
|
* @param lockNode NodeRef
|
||||||
|
* @param lockInfo
|
||||||
*/
|
*/
|
||||||
protected void generateLockDiscoveryXML(XMLWriter xml, FileInfo lockNodeInfo, LockInfo lockInfo) throws Exception
|
protected void generateLockDiscoveryXML(XMLWriter xml, FileInfo lockNodeInfo, LockInfo lockInfo) throws Exception
|
||||||
{
|
{
|
||||||
String owner = (String) lockNodeInfo.getProperties().get(ContentModel.PROP_LOCK_OWNER);
|
String owner = lockInfo.getOwner();
|
||||||
generateLockDiscoveryXML(xml, lockNodeInfo, false, lockInfo.getScope(), lockInfo.getDepth(), WebDAV.makeLockToken(lockNodeInfo.getNodeRef(), owner), owner);
|
Date expiry = lockInfo.getExpires();
|
||||||
|
generateLockDiscoveryXML(xml, lockNodeInfo, false, lockInfo.getScope(), lockInfo.getDepth(),
|
||||||
|
WebDAV.makeLockToken(lockNodeInfo.getNodeRef(), owner), owner, expiry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -769,17 +767,15 @@ public abstract class WebDAVMethod
|
|||||||
* @param depth String lock depth
|
* @param depth String lock depth
|
||||||
* @param lToken String locktoken
|
* @param lToken String locktoken
|
||||||
* @param owner String lock owner
|
* @param owner String lock owner
|
||||||
*
|
* @param expiryDate the date/time the lock should expire
|
||||||
*/
|
*/
|
||||||
protected void generateLockDiscoveryXML(XMLWriter xml, FileInfo lockNodeInfo, boolean emptyNamespace, String scope, String depth, String lToken, String owner) throws Exception
|
protected void generateLockDiscoveryXML(XMLWriter xml, FileInfo lockNodeInfo, boolean emptyNamespace,
|
||||||
|
String scope, String depth, String lToken, String owner, Date expiryDate) throws Exception
|
||||||
{
|
{
|
||||||
Attributes nullAttr= getDAVHelper().getNullAttributes();
|
Attributes nullAttr= getDAVHelper().getNullAttributes();
|
||||||
String ns = emptyNamespace ? "" : WebDAV.DAV_NS;
|
String ns = emptyNamespace ? "" : WebDAV.DAV_NS;
|
||||||
if (lockNodeInfo != null)
|
if (lockNodeInfo != null)
|
||||||
{
|
{
|
||||||
// Get the lock details
|
|
||||||
Date expiryDate = (Date) lockNodeInfo.getProperties().get(ContentModel.PROP_EXPIRY_DATE);
|
|
||||||
|
|
||||||
// Output the XML response
|
// Output the XML response
|
||||||
|
|
||||||
xml.startElement(ns, WebDAV.XML_LOCK_DISCOVERY, emptyNamespace ? WebDAV.XML_LOCK_DISCOVERY : WebDAV.XML_NS_LOCK_DISCOVERY, nullAttr);
|
xml.startElement(ns, WebDAV.XML_LOCK_DISCOVERY, emptyNamespace ? WebDAV.XML_LOCK_DISCOVERY : WebDAV.XML_NS_LOCK_DISCOVERY, nullAttr);
|
||||||
@@ -882,15 +878,22 @@ public abstract class WebDAVMethod
|
|||||||
|
|
||||||
if (m_conditions == null)
|
if (m_conditions == null)
|
||||||
{
|
{
|
||||||
if (nodeLockInfo.getToken() == null)
|
if (!nodeLockInfo.isLocked())
|
||||||
{
|
{
|
||||||
CheckOutCheckInService checkOutCheckInService = m_davHelper.getServiceRegistry().getCheckOutCheckInService();
|
// Not locked
|
||||||
if (nodeLockInfo.getSharedLockTokens() == null && checkOutCheckInService.getWorkingCopy(fileInfo.getNodeRef()) == null)
|
return nodeLockInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeLockInfo.isShared())
|
||||||
|
{
|
||||||
|
if (nodeLockInfo.getSharedLockTokens().isEmpty())
|
||||||
{
|
{
|
||||||
|
// Although flagged as shared - no shared locks.
|
||||||
return nodeLockInfo;
|
return nodeLockInfo;
|
||||||
}
|
}
|
||||||
if (!ignoreShared)
|
if (!ignoreShared)
|
||||||
{
|
{
|
||||||
|
// Shared locks exist
|
||||||
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
|
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -900,15 +903,16 @@ public abstract class WebDAVMethod
|
|||||||
if (m_userAgent != null && m_userAgent.equals(WebDAV.AGENT_MICROSOFT_DATA_ACCESS_INTERNET_PUBLISHING_PROVIDER_DAV))
|
if (m_userAgent != null && m_userAgent.equals(WebDAV.AGENT_MICROSOFT_DATA_ACCESS_INTERNET_PUBLISHING_PROVIDER_DAV))
|
||||||
{
|
{
|
||||||
String currentUser = getAuthenticationService().getCurrentUserName();
|
String currentUser = getAuthenticationService().getCurrentUserName();
|
||||||
Serializable lockOwner = fileInfo.getProperties().get(ContentModel.PROP_LOCK_OWNER);
|
String lockOwner = nodeLockInfo.getOwner();
|
||||||
|
|
||||||
if (lockOwner.equals(currentUser))
|
if (lockOwner.equals(currentUser))
|
||||||
{
|
{
|
||||||
|
// OK to write - lock is owned by current user.
|
||||||
return nodeLockInfo;
|
return nodeLockInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Exclusive lock, owned by someone else
|
||||||
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
|
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -920,7 +924,7 @@ public abstract class WebDAVMethod
|
|||||||
// but request must fail with 423 Locked response because node is locked.
|
// but request must fail with 423 Locked response because node is locked.
|
||||||
// 2. Check if ANY of the conditions in If header true.
|
// 2. Check if ANY of the conditions in If header true.
|
||||||
checkLockToken(nodeLockInfo, ignoreShared, lockMethod);
|
checkLockToken(nodeLockInfo, ignoreShared, lockMethod);
|
||||||
checkConditions(nodeLockInfo.getToken(), nodeETag);
|
checkConditions(nodeLockInfo.getExclusiveLockToken(), nodeETag);
|
||||||
|
|
||||||
return nodeLockInfo;
|
return nodeLockInfo;
|
||||||
}
|
}
|
||||||
@@ -949,7 +953,7 @@ public abstract class WebDAVMethod
|
|||||||
*/
|
*/
|
||||||
private void checkLockToken(LockInfo lockInfo, boolean ignoreShared, boolean lockMethod) throws WebDAVServerException
|
private void checkLockToken(LockInfo lockInfo, boolean ignoreShared, boolean lockMethod) throws WebDAVServerException
|
||||||
{
|
{
|
||||||
String nodeLockToken = lockInfo.getToken();
|
String nodeLockToken = lockInfo.getExclusiveLockToken();
|
||||||
Set<String> sharedLockTokens = lockInfo.getSharedLockTokens();
|
Set<String> sharedLockTokens = lockInfo.getSharedLockTokens();
|
||||||
|
|
||||||
if (m_conditions != null)
|
if (m_conditions != null)
|
||||||
@@ -959,7 +963,7 @@ public abstract class WebDAVMethod
|
|||||||
{
|
{
|
||||||
// Node has shared lock. Check if conditions contains lock token of the node.
|
// Node has shared lock. Check if conditions contains lock token of the node.
|
||||||
// If not throw exception
|
// If not throw exception
|
||||||
if (sharedLockTokens != null)
|
if (!sharedLockTokens.isEmpty())
|
||||||
{
|
{
|
||||||
if (!ignoreShared)
|
if (!ignoreShared)
|
||||||
{
|
{
|
||||||
@@ -1096,44 +1100,15 @@ public abstract class WebDAVMethod
|
|||||||
private LockInfo getNodeLockInfoImpl(FileInfo nodeInfo)
|
private LockInfo getNodeLockInfoImpl(FileInfo nodeInfo)
|
||||||
{
|
{
|
||||||
// Check if node is locked directly.
|
// Check if node is locked directly.
|
||||||
|
|
||||||
LockInfo lockInfo = getNodeLockInfoDirect(nodeInfo);
|
LockInfo lockInfo = getNodeLockInfoDirect(nodeInfo);
|
||||||
if (lockInfo != null)
|
if (lockInfo != null)
|
||||||
{
|
{
|
||||||
return lockInfo;
|
return lockInfo;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// No has no exclusive lock but can be locked with shared lock
|
|
||||||
String sharedLocks = (String) nodeInfo.getProperties().get(WebDAVModel.PROP_SHARED_LOCK_TOKENS);
|
|
||||||
if (sharedLocks != null)
|
|
||||||
{
|
|
||||||
// Get lock depth
|
|
||||||
String depth = (String) nodeInfo.getProperties().get(WebDAVModel.PROP_LOCK_DEPTH);
|
|
||||||
//Get lock scope
|
|
||||||
String scope = (String) nodeInfo.getProperties().get(WebDAVModel.PROP_LOCK_SCOPE);
|
|
||||||
|
|
||||||
// Node has it's own Lock token.
|
|
||||||
// Store lock information to the lockInfo object
|
|
||||||
|
|
||||||
lockInfo = new LockInfo();
|
|
||||||
|
|
||||||
lockInfo.setDepth(depth);
|
|
||||||
lockInfo.setScope(scope);
|
|
||||||
lockInfo.setSharedLockTokens(LockInfo.parseSharedLockTokens(sharedLocks));
|
|
||||||
lockInfo.setShared(true);
|
|
||||||
|
|
||||||
return lockInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Node isn't locked directly and has no it's own Lock token.
|
|
||||||
// Try to search indirect lock.
|
|
||||||
|
|
||||||
|
// Node isn't locked directly, try to search for an indirect lock.
|
||||||
NodeService nodeService = getNodeService();
|
NodeService nodeService = getNodeService();
|
||||||
|
NodeRef node = nodeInfo.getNodeRef();
|
||||||
NodeRef nodeRef = nodeInfo.getNodeRef();
|
|
||||||
NodeRef node = nodeRef;
|
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@@ -1175,41 +1150,13 @@ public abstract class WebDAVMethod
|
|||||||
{
|
{
|
||||||
return lockInfo;
|
return lockInfo;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// TODO - I assume this should be parent not nodeRef (see ALF-6224) ?
|
|
||||||
|
|
||||||
lockInfo = new LockInfo();
|
|
||||||
|
|
||||||
// Node has no exclusive lock but can be locked with shared lock
|
|
||||||
String sharedLocks = (String) nodeService.getProperty(parent, WebDAVModel.PROP_SHARED_LOCK_TOKENS);
|
|
||||||
if (sharedLocks != null)
|
|
||||||
{
|
|
||||||
// Check node lock depth.
|
|
||||||
// If depth is WebDAV.INFINITY then return this node's Lock token.
|
|
||||||
String depth = (String) nodeService.getProperty(parent, WebDAVModel.PROP_LOCK_DEPTH);
|
|
||||||
if (WebDAV.INFINITY.equals(depth))
|
|
||||||
{
|
|
||||||
// In this case node is locked indirectly.
|
|
||||||
|
|
||||||
//Get lock scope
|
|
||||||
String scope = (String) nodeService.getProperty(parent, WebDAVModel.PROP_LOCK_SCOPE);
|
|
||||||
|
|
||||||
// Node has it's own Lock token.
|
|
||||||
|
|
||||||
lockInfo.setDepth(depth);
|
|
||||||
lockInfo.setScope(scope);
|
|
||||||
lockInfo.setSharedLockTokens(LockInfo.parseSharedLockTokens(sharedLocks));
|
|
||||||
|
|
||||||
lockInfo.setShared(true);
|
|
||||||
|
|
||||||
return lockInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
if (lockInfo == null)
|
||||||
|
{
|
||||||
|
lockInfo = new LockInfo();
|
||||||
|
}
|
||||||
// temporarily cache - for this request
|
// temporarily cache - for this request
|
||||||
m_parentLockInfo.put(parent, lockInfo);
|
m_parentLockInfo.put(parent, lockInfo);
|
||||||
}
|
}
|
||||||
@@ -1221,32 +1168,11 @@ public abstract class WebDAVMethod
|
|||||||
|
|
||||||
private LockInfo getNodeLockInfoDirect(FileInfo nodeInfo)
|
private LockInfo getNodeLockInfoDirect(FileInfo nodeInfo)
|
||||||
{
|
{
|
||||||
String propOpaqueLockToken = (String) nodeInfo.getProperties().get(WebDAVModel.PROP_OPAQUE_LOCK_TOKEN);
|
LockInfo lock = getLockStore().get(nodeInfo.getNodeRef());
|
||||||
if (propOpaqueLockToken != null)
|
|
||||||
|
if (lock != null && lock.isLocked())
|
||||||
{
|
{
|
||||||
// now check for lock status ...
|
return lock;
|
||||||
LockStatus lockSts = getLockService().getLockStatus(nodeInfo.getNodeRef());
|
|
||||||
if (lockSts == LockStatus.LOCKED || lockSts == LockStatus.LOCK_OWNER)
|
|
||||||
{
|
|
||||||
// Get lock depth
|
|
||||||
String depth = (String) nodeInfo.getProperties().get(WebDAVModel.PROP_LOCK_DEPTH);
|
|
||||||
//Get lock scope
|
|
||||||
String scope = (String) nodeInfo.getProperties().get(WebDAVModel.PROP_LOCK_SCOPE);
|
|
||||||
// Get shared lock tokens
|
|
||||||
String sharedLocks = (String) nodeInfo.getProperties().get(WebDAVModel.PROP_SHARED_LOCK_TOKENS);
|
|
||||||
|
|
||||||
// Node has it's own Lock token.
|
|
||||||
// Store lock information to the lockInfo object
|
|
||||||
|
|
||||||
LockInfo lockInfo = new LockInfo();
|
|
||||||
|
|
||||||
lockInfo.setToken(propOpaqueLockToken);
|
|
||||||
lockInfo.setDepth(depth);
|
|
||||||
lockInfo.setScope(scope);
|
|
||||||
lockInfo.setSharedLockTokens(LockInfo.parseSharedLockTokens(sharedLocks));
|
|
||||||
|
|
||||||
return lockInfo;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -1254,37 +1180,15 @@ public abstract class WebDAVMethod
|
|||||||
|
|
||||||
private LockInfo getNodeLockInfoIndirect(FileInfo nodeInfo, NodeRef parent)
|
private LockInfo getNodeLockInfoIndirect(FileInfo nodeInfo, NodeRef parent)
|
||||||
{
|
{
|
||||||
NodeService nodeService = getNodeService();
|
LockInfo parentLock = getLockStore().get(parent);
|
||||||
|
|
||||||
// Check node lock depth.
|
if (parentLock != null && WebDAV.INFINITY.equals(parentLock.getDepth()))
|
||||||
// If depth is WebDAV.INFINITY then return this node's Lock token.
|
|
||||||
String depth = (String) nodeService.getProperty(parent, WebDAVModel.PROP_LOCK_DEPTH);
|
|
||||||
if (WebDAV.INFINITY.equals(depth))
|
|
||||||
{
|
{
|
||||||
// now check for lock status ...
|
// now check for lock status ...
|
||||||
LockStatus lockSts = getLockService().getLockStatus(parent);
|
if (parentLock.isLocked())
|
||||||
if (lockSts == LockStatus.LOCKED || lockSts == LockStatus.LOCK_OWNER)
|
|
||||||
{
|
{
|
||||||
// In this case node is locked indirectly.
|
// In this case node is locked indirectly.
|
||||||
|
return parentLock;
|
||||||
//Get lock scope
|
|
||||||
String scope = (String) nodeInfo.getProperties().get(WebDAVModel.PROP_LOCK_SCOPE);
|
|
||||||
// Get shared lock tokens
|
|
||||||
String sharedLocks = (String) nodeInfo.getProperties().get(WebDAVModel.PROP_SHARED_LOCK_TOKENS);
|
|
||||||
|
|
||||||
// Store lock information to the lockInfo object
|
|
||||||
|
|
||||||
// Get lock token of the locked node - this is indirect lock token.
|
|
||||||
String propOpaqueLockToken = (String) nodeService.getProperty(parent, WebDAVModel.PROP_OPAQUE_LOCK_TOKEN);
|
|
||||||
|
|
||||||
LockInfo lockInfo = new LockInfo();
|
|
||||||
|
|
||||||
lockInfo.setToken(propOpaqueLockToken);
|
|
||||||
lockInfo.setDepth(depth);
|
|
||||||
lockInfo.setScope(scope);
|
|
||||||
lockInfo.setSharedLockTokens(LockInfo.parseSharedLockTokens(sharedLocks));
|
|
||||||
|
|
||||||
return lockInfo;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -280,9 +280,11 @@ public class WebDAVServlet extends HttpServlet
|
|||||||
NodeService nodeService = (NodeService) context.getBean("NodeService");
|
NodeService nodeService = (NodeService) context.getBean("NodeService");
|
||||||
SearchService searchService = (SearchService) context.getBean("SearchService");
|
SearchService searchService = (SearchService) context.getBean("SearchService");
|
||||||
NamespaceService namespaceService = (NamespaceService) context.getBean("NamespaceService");
|
NamespaceService namespaceService = (NamespaceService) context.getBean("NamespaceService");
|
||||||
|
LockStoreFactory lockStoreFactory = (LockStoreFactory) context.getBean("webdavLockStoreFactory");
|
||||||
|
LockStore lockStore = lockStoreFactory.getLockStore();
|
||||||
|
|
||||||
// Create the WebDAV helper
|
// Create the WebDAV helper
|
||||||
m_davHelper = new WebDAVHelper(m_serviceRegistry, authService);
|
m_davHelper = new WebDAVHelper(m_serviceRegistry, lockStore, authService);
|
||||||
|
|
||||||
// Initialize the root node
|
// Initialize the root node
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user