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:
Matt Ward
2012-02-22 15:28:47 +00:00
parent a2bce3ac9a
commit bb1ffbd967
11 changed files with 468 additions and 347 deletions

View File

@@ -18,6 +18,8 @@
*/
package org.alfresco.repo.webdav;
import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
@@ -27,10 +29,12 @@ import java.util.Set;
* @author Ivan Rybnikov
*
*/
public class LockInfo
public final class LockInfo implements Serializable
{
private static final long serialVersionUID = 1L;
// Exclusive lock token
private String token = null;
private String exclusiveLockToken = null;
// Lock scope
private String scope = null;
@@ -38,14 +42,14 @@ public class LockInfo
// Lock depth
private String depth = null;
// If lock is shared
private boolean shared = false;
// Shared lock tokens
private Set<String> sharedLockTokens = null;
private final Set<String> sharedLockTokens = new HashSet<String>(3);
// Shared lock token separator
private static final String SHARED_LOCK_TOKEN_SEPARATOR = ",";
// User name of the lock's owner
private String owner;
// When does the lock expire?
private Date expires;
/**
* Default constructor
@@ -64,7 +68,7 @@ public class LockInfo
*/
public LockInfo(String token, String scope, String depth)
{
this.token = token;
this.exclusiveLockToken = token;
this.scope = scope;
this.depth = depth;
}
@@ -76,11 +80,7 @@ public class LockInfo
*/
public boolean isLocked()
{
if (token != null || (sharedLockTokens != null && !sharedLockTokens.isEmpty()))
{
return true;
}
return false;
return (isExclusive() || isShared());
}
/**
@@ -88,18 +88,20 @@ public class LockInfo
*
* @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
* @return
* Getter for exclusive lock token.
*
* @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.
*
* @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
* Getter for sharedLockTokens list.
*
* @return LinkedList<String>
*/
public Set<String> getSharedLockTokens()
{
checkLockState();
return sharedLockTokens;
}
/**
* Setter for sharedLockTokens list
* Setter for sharedLockTokens list.
*
* @param 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)
{
if (sharedLockTokens == null)
{
sharedLockTokens = new HashSet<String>(3);
}
sharedLockTokens.add(token);
}
/**
* Transforms list of shared locks to string.
* Lock tokens separated with SHARED_LOCK_TOKEN_SEPARATOR value.
* Is it a shared lock?
*
* @param lockTokens list of shared locks
* @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
* @return true if shared.
*/
public boolean isShared()
{
return shared;
return (!sharedLockTokens.isEmpty());
}
/**
* Return the lock info as a string
*
@@ -256,20 +196,113 @@ public class LockInfo
{
StringBuilder str = new StringBuilder();
str.append("[");
str.append("LockInfo[");
str.append("token=");
str.append(getToken());
str.append(",scope=");
str.append("exclusiveLockToken=");
str.append(getExclusiveLockToken());
str.append(", scope=");
str.append(getScope());
str.append(",depth=");
str.append(", depth=");
str.append(getDepth());
str.append(",shared locks=");
str.append(", sharedLockTokens=");
str.append(getSharedLockTokens());
str.append(", owner=");
str.append(owner);
str.append(", expires=");
str.append(expires);
str.append("]");
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);
}
}

View File

@@ -18,6 +18,7 @@
*/
package org.alfresco.repo.webdav;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -27,12 +28,9 @@ import java.util.TimerTask;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.model.ContentModel;
import org.alfresco.model.WebDAVModel;
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.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.LockType;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileFolderUtil;
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
{
LockService lockService = getLockService();
// Create Lock token
lockToken = WebDAV.makeLockToken(lockNode.getNodeRef(), userName);
if (this.createExclusive)
if (createExclusive)
{
// Lock the node
lockService.lock(lockNode.getNodeRef(), LockType.WRITE_LOCK, getLockTimeout());
// 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);
lockInfo.setTimeoutSeconds(getLockTimeout());
lockInfo.setExclusiveLockToken(lockToken);
}
else
{
this.lockInfo.addSharedLockToken(lockToken);
String sharedLockTokens = LockInfo.makeSharedLockTokensString(this.lockInfo.getSharedLockTokens());
getNodeService().setProperty(lockNode.getNodeRef(), WebDAVModel.PROP_SHARED_LOCK_TOKENS, sharedLockTokens);
lockInfo.addSharedLockToken(lockToken);
}
// 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)
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)
{
// 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
scope = this.lockInfo.getScope();
// 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();
@@ -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());
// 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
xml.endElement(WebDAV.DAV_NS, WebDAV.XML_PROP, WebDAV.XML_NS_PROP);

View 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);
}

View 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();
}

View File

@@ -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;
}
}

View 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);
}
}

View File

@@ -24,10 +24,9 @@ import javax.servlet.http.HttpServletResponse;
import org.alfresco.model.ContentModel;
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.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.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
@@ -44,7 +43,6 @@ import org.springframework.dao.ConcurrencyFailureException;
public class PutMethod extends WebDAVMethod
{
// Request parameters
private String m_strLockToken = null;
private String m_strContentType = null;
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 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
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
}

View File

@@ -23,13 +23,9 @@ import java.util.Set;
import javax.servlet.http.HttpServletResponse;
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.FileNotFoundException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
/**
* Implements the WebDAV UNLOCK method
@@ -126,37 +122,42 @@ public class UnlockMethod extends WebDAVMethod
}
// Parse the lock token
String[] lockInfo = WebDAV.parseLockToken(getLockToken());
if (lockInfo == null)
String[] lockInfoFromRequest = WebDAV.parseLockToken(getLockToken());
if (lockInfoFromRequest == null)
{
// Bad lock token
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();
LockStatus lockSts = lockService.getLockStatus(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);
LockInfo lockInfo = getLockStore().get(nodeRef);
// Return the cm:lockable aspect to working copy (ALF-4479, ALF-7079)
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY))
if (lockInfo == null || !lockInfo.isLocked())
{
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
m_response.setStatus(HttpServletResponse.SC_NO_CONTENT);
removeNoContentAspect(nodeRef);
// DEBUG
@@ -165,57 +166,27 @@ public class UnlockMethod extends WebDAVMethod
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);
if (sharedLocks != null)
Set<String> sharedLocks = lockInfo.getSharedLockTokens();
if (sharedLocks.contains(m_strLockToken))
{
Set<String> locks = LockInfo.parseSharedLockTokens(sharedLocks);
sharedLocks.remove(m_strLockToken);
if (locks != null && locks.contains(m_strLockToken))
{
locks.remove(m_strLockToken);
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);
// 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
if (logger.isDebugEnabled())
logger.debug("Unlock token=" + getLockToken() + " Not locked");
// Node is not locked
throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
{
logger.debug("Unlock token=" + getLockToken() + " Successful");
}
}
}
else if (lockSts == LockStatus.LOCKED)
else
{
// DEBUG
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);
throw new IllegalStateException("Invalid LockInfo state: " + lockInfo);
}
}

View File

@@ -77,6 +77,7 @@ public class WebDAVHelper
private DictionaryService m_dictionaryService;
private MimetypeService m_mimetypeService;
private LockService m_lockService;
private LockStore m_lockStore;
private ActionService m_actionService;
private AuthenticationService m_authService;
private PermissionService m_permissionService;
@@ -88,7 +89,7 @@ public class WebDAVHelper
/**
* Class constructor
*/
protected WebDAVHelper(ServiceRegistry serviceRegistry, AuthenticationService authService)
protected WebDAVHelper(ServiceRegistry serviceRegistry, LockStore lockStore, AuthenticationService authService)
{
m_serviceRegistry = serviceRegistry;
@@ -103,6 +104,8 @@ public class WebDAVHelper
m_permissionService = m_serviceRegistry.getPermissionService();
m_authService = authService;
m_lockStore = lockStore;
}
/**
@@ -174,6 +177,14 @@ 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
*/

View File

@@ -25,15 +25,14 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Pattern;
import javax.servlet.ServletInputStream;
@@ -45,16 +44,12 @@ import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.alfresco.model.ContentModel;
import org.alfresco.model.WebDAVModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
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.FileInfo;
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;
}
/**
/**
* Generates the lock discovery XML response
*
* @param xml XMLWriter
* @param lockNode NodeRef
* @param lockInfo
*/
protected void generateLockDiscoveryXML(XMLWriter xml, FileInfo lockNodeInfo, LockInfo lockInfo) throws Exception
{
String owner = (String) lockNodeInfo.getProperties().get(ContentModel.PROP_LOCK_OWNER);
generateLockDiscoveryXML(xml, lockNodeInfo, false, lockInfo.getScope(), lockInfo.getDepth(), WebDAV.makeLockToken(lockNodeInfo.getNodeRef(), owner), owner);
String owner = lockInfo.getOwner();
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 lToken String locktoken
* @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();
String ns = emptyNamespace ? "" : WebDAV.DAV_NS;
if (lockNodeInfo != null)
{
// Get the lock details
Date expiryDate = (Date) lockNodeInfo.getProperties().get(ContentModel.PROP_EXPIRY_DATE);
// Output the XML response
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 (nodeLockInfo.getToken() == null)
if (!nodeLockInfo.isLocked())
{
CheckOutCheckInService checkOutCheckInService = m_davHelper.getServiceRegistry().getCheckOutCheckInService();
if (nodeLockInfo.getSharedLockTokens() == null && checkOutCheckInService.getWorkingCopy(fileInfo.getNodeRef()) == null)
// Not locked
return nodeLockInfo;
}
if (nodeLockInfo.isShared())
{
if (nodeLockInfo.getSharedLockTokens().isEmpty())
{
// Although flagged as shared - no shared locks.
return nodeLockInfo;
}
if (!ignoreShared)
{
// Shared locks exist
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))
{
String currentUser = getAuthenticationService().getCurrentUserName();
Serializable lockOwner = fileInfo.getProperties().get(ContentModel.PROP_LOCK_OWNER);
String lockOwner = nodeLockInfo.getOwner();
if (lockOwner.equals(currentUser))
{
// OK to write - lock is owned by current user.
return nodeLockInfo;
}
}
else
{
// Exclusive lock, owned by someone else
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.
// 2. Check if ANY of the conditions in If header true.
checkLockToken(nodeLockInfo, ignoreShared, lockMethod);
checkConditions(nodeLockInfo.getToken(), nodeETag);
checkConditions(nodeLockInfo.getExclusiveLockToken(), nodeETag);
return nodeLockInfo;
}
@@ -949,7 +953,7 @@ public abstract class WebDAVMethod
*/
private void checkLockToken(LockInfo lockInfo, boolean ignoreShared, boolean lockMethod) throws WebDAVServerException
{
String nodeLockToken = lockInfo.getToken();
String nodeLockToken = lockInfo.getExclusiveLockToken();
Set<String> sharedLockTokens = lockInfo.getSharedLockTokens();
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.
// If not throw exception
if (sharedLockTokens != null)
if (!sharedLockTokens.isEmpty())
{
if (!ignoreShared)
{
@@ -1096,44 +1100,15 @@ public abstract class WebDAVMethod
private LockInfo getNodeLockInfoImpl(FileInfo nodeInfo)
{
// Check if node is locked directly.
LockInfo lockInfo = getNodeLockInfoDirect(nodeInfo);
if (lockInfo != null)
{
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();
NodeRef nodeRef = nodeInfo.getNodeRef();
NodeRef node = nodeRef;
NodeRef node = nodeInfo.getNodeRef();
while (true)
{
@@ -1175,41 +1150,13 @@ public abstract class WebDAVMethod
{
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
{
if (lockInfo == null)
{
lockInfo = new LockInfo();
}
// temporarily cache - for this request
m_parentLockInfo.put(parent, lockInfo);
}
@@ -1221,32 +1168,11 @@ public abstract class WebDAVMethod
private LockInfo getNodeLockInfoDirect(FileInfo nodeInfo)
{
String propOpaqueLockToken = (String) nodeInfo.getProperties().get(WebDAVModel.PROP_OPAQUE_LOCK_TOKEN);
if (propOpaqueLockToken != null)
LockInfo lock = getLockStore().get(nodeInfo.getNodeRef());
if (lock != null && lock.isLocked())
{
// now check for lock status ...
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 lock;
}
return null;
@@ -1254,37 +1180,15 @@ public abstract class WebDAVMethod
private LockInfo getNodeLockInfoIndirect(FileInfo nodeInfo, NodeRef parent)
{
NodeService nodeService = getNodeService();
LockInfo parentLock = getLockStore().get(parent);
// 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))
if (parentLock != null && WebDAV.INFINITY.equals(parentLock.getDepth()))
{
// now check for lock status ...
LockStatus lockSts = getLockService().getLockStatus(parent);
if (lockSts == LockStatus.LOCKED || lockSts == LockStatus.LOCK_OWNER)
if (parentLock.isLocked())
{
// In this case node is locked indirectly.
//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;
return parentLock;
}
}

View File

@@ -280,9 +280,11 @@ public class WebDAVServlet extends HttpServlet
NodeService nodeService = (NodeService) context.getBean("NodeService");
SearchService searchService = (SearchService) context.getBean("SearchService");
NamespaceService namespaceService = (NamespaceService) context.getBean("NamespaceService");
LockStoreFactory lockStoreFactory = (LockStoreFactory) context.getBean("webdavLockStoreFactory");
LockStore lockStore = lockStoreFactory.getLockStore();
// Create the WebDAV helper
m_davHelper = new WebDAVHelper(m_serviceRegistry, authService);
m_davHelper = new WebDAVHelper(m_serviceRegistry, lockStore, authService);
// Initialize the root node