Alan Davis 68f7ce770e Merged HEAD-BUG-FIX (5.1/Cloud) to HEAD (5.1/Cloud)
91057: Merged V4.2-BUG-FIX (4.2.5) to HEAD-BUG-FIX (5.0/Cloud)
      90986: MNT-11732: ephemeral locks have configurable threshold over which locks will be automatically made persistent instead.
      By setting the new property, administrators may control how ephemeral locks are created, for example:
      property alfresco.ephemeralLock.expiryThresh=30
      This will mean that when a LockService client requests that an ephemeral lock is created with a timeout of greater than 30 seconds, the lock will be created as a persistent lock instead (the ephemeral lifetime request is vetoed).
      By setting this property to -1, ALL locks will be created as persistent locks (giving the old, pre-ephemeral locking behaviour). By leaving this at the default (48 hours) the newer locking behaviour is completely unaffected, e.g. ephemeral locks may be created with up to 48 hour expiry time and over this limit an exception is thrown (and the lock is not created).
      By setting the value to something in between these settings it is possible to have quite a nice balance of behaviour. Using the example of 30 seconds, as above, will mean that using Share's "Edit Online" to edit a document in MS Word will result in a persistent lock (as MS Word requests approx 1 hour for its locks). After Solr has performed its next incremental index, then the Word document may be seen in the "Document's I'm Editing" filter in Share, since this is a persistent (in-DB) lock.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@94765 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2015-01-31 11:11:51 +00:00

932 lines
34 KiB
Java

/*
* Copyright (C) 2005-2010 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.lock;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.copy.CopyBehaviourCallback;
import org.alfresco.repo.copy.CopyDetails;
import org.alfresco.repo.copy.CopyServicePolicies;
import org.alfresco.repo.copy.DoNothingCopyBehaviourCallback;
import org.alfresco.repo.lock.LockServicePolicies.BeforeLock;
import org.alfresco.repo.lock.mem.Lifetime;
import org.alfresco.repo.lock.mem.LockState;
import org.alfresco.repo.lock.mem.LockStore;
import org.alfresco.repo.lock.mem.LockableAspectInterceptor;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.node.index.NodeIndexer;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.policy.ClassPolicyDelegate;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.policy.PolicyScope;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionListener;
import org.alfresco.repo.transaction.TransactionalResourceHelper;
import org.alfresco.repo.version.VersionServicePolicies;
import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.LockStatus;
import org.alfresco.service.cmr.lock.LockType;
import org.alfresco.service.cmr.lock.NodeLockedException;
import org.alfresco.service.cmr.lock.UnableToAquireLockException;
import org.alfresco.service.cmr.lock.UnableToReleaseLockException;
import org.alfresco.service.cmr.lock.UnableToReleaseLockException.CAUSE;
import org.alfresco.service.cmr.repository.AspectMissingException;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
import org.springframework.util.Assert;
/**
* Simple Lock service implementation
*
* @author Roy Wetherall
*/
public class LockServiceImpl implements LockService,
NodeServicePolicies.OnCreateChildAssociationPolicy,
NodeServicePolicies.BeforeUpdateNodePolicy,
NodeServicePolicies.BeforeDeleteNodePolicy,
NodeServicePolicies.OnMoveNodePolicy,
CopyServicePolicies.OnCopyNodePolicy,
VersionServicePolicies.OnCreateVersionPolicy, TransactionListener
{
public static final int MAX_EPHEMERAL_LOCK_SECONDS = 2 * 86400;
/** Key to the nodes ref's to ignore when checking for locks */
private static final String KEY_IGNORE_NODES = "lockService.ignoreNodes";
private static final Object KEY_MODIFIED_NODES = "lockService.lockedNode";
private NodeService nodeService;
private TenantService tenantService;
private AuthenticationService authenticationService;
private SearchService searchService;
private BehaviourFilter behaviourFilter;
private LockStore lockStore;
private PolicyComponent policyComponent;
private LockableAspectInterceptor lockableAspectInterceptor;
/** Class policy delegate's */
private ClassPolicyDelegate<BeforeLock> beforeLock;
private NodeIndexer nodeIndexer;
private int ephemeralExpiryThreshold;
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
public void setLockStore(LockStore lockStore)
{
this.lockStore = lockStore;
}
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
}
public void setLockableAspectInterceptor(LockableAspectInterceptor lockableAspectInterceptor)
{
this.lockableAspectInterceptor = lockableAspectInterceptor;
}
public void setAuthenticationService(AuthenticationService authenticationService)
{
this.authenticationService = authenticationService;
}
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* Initialise methods called by Spring framework
*/
public void init()
{
PropertyCheck.mandatory(this, "nodeService", nodeService);
PropertyCheck.mandatory(this, "tenantService", tenantService);
PropertyCheck.mandatory(this, "authenticationService", authenticationService);
PropertyCheck.mandatory(this, "searchService", searchService);
PropertyCheck.mandatory(this, "behaviourFilter", behaviourFilter);
PropertyCheck.mandatory(this, "policyComponent", policyComponent);
// Register the policies
beforeLock = policyComponent.registerClassPolicy(LockServicePolicies.BeforeLock.class);
// Register the various class behaviours to enable lock checking
this.policyComponent.bindAssociationBehaviour(
NodeServicePolicies.OnCreateChildAssociationPolicy.QNAME,
ContentModel.ASPECT_LOCKABLE,
new JavaBehaviour(this, "onCreateChildAssociation"));
this.policyComponent.bindClassBehaviour(
NodeServicePolicies.BeforeUpdateNodePolicy.QNAME,
ContentModel.ASPECT_LOCKABLE,
new JavaBehaviour(this, "beforeUpdateNode"));
this.policyComponent.bindClassBehaviour(
NodeServicePolicies.BeforeDeleteNodePolicy.QNAME,
ContentModel.ASPECT_LOCKABLE,
new JavaBehaviour(this, "beforeDeleteNode"));
this.policyComponent.bindClassBehaviour(
NodeServicePolicies.OnMoveNodePolicy.QNAME,
ContentModel.ASPECT_LOCKABLE,
new JavaBehaviour(this, "onMoveNode"));
// Register copy class behaviour
this.policyComponent.bindClassBehaviour(
CopyServicePolicies.OnCopyNodePolicy.QNAME,
ContentModel.ASPECT_LOCKABLE,
new JavaBehaviour(this, "getCopyCallback"));
// Register the onCreateVersion behavior for the version aspect
// BeforeCreateVersion behavior was removed
// we should be able to version a node regardless of its lock state, see ALF-16540
this.policyComponent.bindClassBehaviour(
VersionServicePolicies.OnCreateVersionPolicy.QNAME,
ContentModel.ASPECT_LOCKABLE,
new JavaBehaviour(this, "onCreateVersion"));
}
/**
* Returns all the classes of a node, including its type and aspects.
*
* @param nodeRef node reference
* @return List<QName> list of classes
*/
private List<QName> getInvokeClasses(NodeRef nodeRef)
{
List<QName> result = new ArrayList<QName>(10);
result.add(nodeService.getType(nodeRef));
Set<QName> aspects = nodeService.getAspects(nodeRef);
for (QName aspect : aspects)
{
result.add(aspect);
}
return result;
}
/**
* Invoke the before log policy
*
* @param nodeRef the node to be locked
* @param lockType the lock type
*/
private void invokeBeforeLock(
NodeRef nodeRef,
LockType lockType)
{
if (!nodeService.exists(nodeRef))
{
return;
}
List<QName> classes = getInvokeClasses(nodeRef);
for (QName invokeClass : classes)
{
Collection<BeforeLock> policies = beforeLock.getList(invokeClass);
for (BeforeLock policy : policies)
{
policy.beforeLock(nodeRef, lockType);
}
}
}
@SuppressWarnings("unchecked")
private void addToIgnoreSet(NodeRef nodeRef)
{
Set<NodeRef> ignoreNodeRefs = (Set<NodeRef>)AlfrescoTransactionSupport.getResource(KEY_IGNORE_NODES);
if (ignoreNodeRefs == null)
{
ignoreNodeRefs = new HashSet<NodeRef>();
AlfrescoTransactionSupport.bindResource(KEY_IGNORE_NODES, ignoreNodeRefs);
}
ignoreNodeRefs.add(nodeRef);
}
@SuppressWarnings("unchecked")
private void removeFromIgnoreSet(NodeRef nodeRef)
{
Set<NodeRef> ignoreNodeRefs = (Set<NodeRef>)AlfrescoTransactionSupport.getResource(KEY_IGNORE_NODES);
if (ignoreNodeRefs != null)
{
ignoreNodeRefs.remove(nodeRef);
}
}
@SuppressWarnings("unchecked")
private boolean ignore(NodeRef nodeRef)
{
Set<NodeRef> ignoreNodeRefs = (Set<NodeRef>)AlfrescoTransactionSupport.getResource(KEY_IGNORE_NODES);
if (ignoreNodeRefs != null)
{
return ignoreNodeRefs.contains(nodeRef);
}
return false;
}
/**
* @see org.alfresco.service.cmr.lock.LockService#lock(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.cmr.lock.LockType)
*/
public void lock(NodeRef nodeRef, LockType lockType)
{
// Lock with no expiration
lock(nodeRef, lockType, 0);
}
/**
* @see org.alfresco.service.cmr.lock.LockService#lock(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.cmr.lock.LockType, int)
*/
@Override
public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire)
{
lock(nodeRef, lockType, timeToExpire, Lifetime.PERSISTENT);
}
/**
* @see org.alfresco.service.cmr.lock.LockService#lock(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.cmr.lock.LockType, int, Lifetime, String)
*/
@Override
public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire, Lifetime lifetime)
{
lock(nodeRef, lockType, timeToExpire, lifetime, null);
}
/**
* @see org.alfresco.service.cmr.lock.LockService#lock(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.cmr.lock.LockType, int, Lifetime, String)
*/
@Override
public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire, Lifetime lifetime, String additionalInfo)
{
invokeBeforeLock(nodeRef, lockType);
if (lifetime.equals(Lifetime.EPHEMERAL) && (timeToExpire > MAX_EPHEMERAL_LOCK_SECONDS))
{
throw new IllegalArgumentException("Attempt to create ephemeral lock for " +
timeToExpire + " seconds - exceeds maximum allowed time.");
}
if (lifetime.equals(Lifetime.EPHEMERAL) && (timeToExpire > ephemeralExpiryThreshold))
{
lifetime = Lifetime.PERSISTENT;
}
nodeRef = tenantService.getName(nodeRef);
// Get the current user name
String userName = getUserName();
// Set a default value
if (lockType == null)
{
lockType = LockType.WRITE_LOCK;
}
// Get the current lock info and status for the node ref.
Pair<LockState, LockStatus> statusAndState = getLockStateAndStatus(nodeRef, userName);
LockState currentLockInfo = statusAndState.getFirst();
LockStatus currentLockStatus = statusAndState.getSecond();
if (LockStatus.LOCKED.equals(currentLockStatus) == true)
{
// Error since we are trying to lock a locked node
throw new UnableToAquireLockException(nodeRef);
}
else if (LockStatus.NO_LOCK.equals(currentLockStatus) == true ||
LockStatus.LOCK_EXPIRED.equals(currentLockStatus) == true ||
LockStatus.LOCK_OWNER.equals(currentLockStatus) == true)
{
final Date expiryDate = makeExpiryDate(timeToExpire);
// Store the lock in the appropriate place.
if (lifetime == Lifetime.PERSISTENT)
{
lockableAspectInterceptor.disableForThread();
try
{
// Add lock aspect if not already present
ensureLockAspect(nodeRef);
persistLockProps(nodeRef, lockType, lifetime, userName, expiryDate, additionalInfo);
}
finally
{
lockableAspectInterceptor.enableForThread();
}
}
else if (lifetime == Lifetime.EPHEMERAL)
{
// Store the lock only in memory.
LockState lock = LockState.createLock(nodeRef, lockType, userName,
expiryDate, lifetime, additionalInfo);
lockStore.set(nodeRef, lock);
// Record the NodeRef being locked and its last known lockstate. This allows
// it to be reverted to this state on rollback.
TransactionalResourceHelper.getMap(KEY_MODIFIED_NODES).put(nodeRef, currentLockInfo);
AlfrescoTransactionSupport.bindListener(this);
nodeIndexer.indexUpdateNode(nodeRef);
}
else
{
throw new IllegalStateException(lifetime.getClass().getSimpleName() +
" is not a valid value: " + lifetime.toString());
}
}
}
private void persistLockProps(NodeRef nodeRef, LockType lockType, Lifetime lifetime, String userName, Date expiryDate, String additionalInfo)
{
addToIgnoreSet(nodeRef);
try
{
// Set the current user as the lock owner
this.nodeService.setProperty(nodeRef, ContentModel.PROP_LOCK_OWNER, userName);
this.nodeService.setProperty(nodeRef, ContentModel.PROP_LOCK_TYPE, lockType.toString());
this.nodeService.setProperty(nodeRef, ContentModel.PROP_LOCK_LIFETIME, lifetime.toString());
this.nodeService.setProperty(nodeRef, ContentModel.PROP_EXPIRY_DATE, expiryDate);
this.nodeService.setProperty(nodeRef, ContentModel.PROP_LOCK_ADDITIONAL_INFO, additionalInfo);
}
finally
{
removeFromIgnoreSet(nodeRef);
}
}
/**
* Calculate expiry date based on the time to expire provided
*
* @param timeToExpire the time to expire (in seconds)
*/
private Date makeExpiryDate(int timeToExpire)
{
// Set the expiry date
Date expiryDate = null;
if (timeToExpire > 0)
{
expiryDate = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(expiryDate);
calendar.add(Calendar.SECOND, timeToExpire);
expiryDate = calendar.getTime();
}
return expiryDate;
}
/**
* @see org.alfresco.service.cmr.lock.LockService#lock(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.cmr.lock.LockType, int, boolean)
*/
public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire, boolean lockChildren)
throws UnableToAquireLockException
{
lock(nodeRef, lockType, timeToExpire);
if (lockChildren == true)
{
Collection<ChildAssociationRef> childAssocRefs = this.nodeService.getChildAssocs(nodeRef);
for (ChildAssociationRef childAssocRef : childAssocRefs)
{
lock(childAssocRef.getChildRef(), lockType, timeToExpire, lockChildren);
}
}
}
/**
* @see org.alfresco.service.cmr.lock.LockService#lock(java.util.Collection, java.lang.String, org.alfresco.service.cmr.lock.LockType, int)
*/
public void lock(Collection<NodeRef> nodeRefs, LockType lockType, int timeToExpire)
throws UnableToAquireLockException
{
// Lock each of the specifed nodes
for (NodeRef nodeRef : nodeRefs)
{
lock(nodeRef, lockType, timeToExpire);
}
}
/**
* @see org.alfresco.service.cmr.lock.LockService#unlock(NodeRef, String)
*/
@Override
public void unlock(NodeRef nodeRef) throws UnableToReleaseLockException
{
unlock(nodeRef, false, false);
}
/**
* @see org.alfresco.service.cmr.lock.LockService#unlock(org.alfresco.service.cmr.repository.NodeRef, boolean)
*/
@Override
public void unlock(NodeRef nodeRef, boolean lockChildren) throws UnableToReleaseLockException
{
unlock(nodeRef, lockChildren, false);
}
/**
* @see org.alfresco.service.cmr.lock.LockService#unlock(NodeRef, String,
* boolean, boolean)
*/
@Override
public void unlock(NodeRef nodeRef, boolean unlockChildren, boolean allowCheckedOut)
throws UnableToReleaseLockException
{
// Unlock the parent
nodeRef = tenantService.getName(nodeRef);
LockState lockState = getLockState(nodeRef);
if (lockState.isLockInfo())
{
// MNT-231: forbidden to unlock a checked out node
if (!allowCheckedOut && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_CHECKED_OUT))
{
throw new UnableToReleaseLockException(nodeRef, CAUSE.CHECKED_OUT);
}
// Remove the lock from persistent storage.
Lifetime lifetime = lockState.getLifetime();
if (lifetime == Lifetime.PERSISTENT)
{
addToIgnoreSet(nodeRef);
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE);
lockableAspectInterceptor.disableForThread();
try
{
// Clear the lock
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE))
{
nodeService.removeAspect(nodeRef, ContentModel.ASPECT_LOCKABLE);
}
}
finally
{
behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE);
lockableAspectInterceptor.enableForThread();
removeFromIgnoreSet(nodeRef);
}
}
else if (lifetime == Lifetime.EPHEMERAL)
{
// Remove the ephemeral lock.
lockStore.set(nodeRef, LockState.createUnlocked(nodeRef));
nodeIndexer.indexUpdateNode(nodeRef);
}
else
{
throw new IllegalStateException("Unhandled Lifetime value: " + lifetime);
}
}
if (unlockChildren)
{
// Get the children and unlock them
Collection<ChildAssociationRef> childAssocRefs = this.nodeService.getChildAssocs(nodeRef);
for (ChildAssociationRef childAssocRef : childAssocRefs)
{
unlock(childAssocRef.getChildRef(), unlockChildren);
}
}
}
/**
* @see org.alfresco.repo.lock.LockService#unlock(Collection<NodeRef>,
* String)
*/
public void unlock(Collection<NodeRef> nodeRefs) throws UnableToReleaseLockException
{
for (NodeRef nodeRef : nodeRefs)
{
unlock(nodeRef);
}
}
/**
* @see org.alfresco.service.cmr.lock.LockService#getLockStatus(NodeRef)
*/
public LockStatus getLockStatus(NodeRef nodeRef)
{
nodeRef = tenantService.getName(nodeRef);
return getLockStatus(nodeRef, getUserName());
}
/**
* Gets the lock status for a node and a user name
*
* @param nodeRef the node reference
* @param userName the user name
* @return the lock status
*/
public LockStatus getLockStatus(NodeRef nodeRef, String userName)
{
Pair<LockState, LockStatus> stateAndStatus = getLockStateAndStatus(nodeRef, userName);
LockStatus lockStatus = stateAndStatus.getSecond();
return lockStatus;
}
private Pair<LockState, LockStatus> getLockStateAndStatus(NodeRef nodeRef, String userName)
{
final LockState lockState = getLockState(nodeRef);
String lockOwner = lockState.getOwner();
Date expiryDate = lockState.getExpires();
LockStatus status = LockUtils.lockStatus(userName, lockOwner, expiryDate);
return new Pair<LockState, LockStatus>(lockState, status);
}
/**
* @see LockService#getLockType(NodeRef)
*/
public LockType getLockType(NodeRef nodeRef)
{
LockType result = null;
// Don't disable the lockable aspect interceptor - allow it to fetch the lock type
// from the correct place (persistent storage or lockStore).
if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE) == true)
{
String lockTypeString = (String) this.nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_TYPE);
if (lockTypeString != null)
{
result = LockType.valueOf(lockTypeString);
}
}
return result;
}
/**
* Checks for the lock aspect. Adds if missing.
*
* @param nodeRef
* the node reference
*/
private void ensureLockAspect(NodeRef nodeRef)
{
if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE) == false)
{
this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_LOCKABLE, null);
}
}
/**
* {@inheritDoc}
*/
public void checkForLock(NodeRef nodeRef) throws NodeLockedException
{
String userName = getUserName();
nodeRef = tenantService.getName(nodeRef);
// Ensure we have found a node reference
if (nodeRef != null && userName != null)
{
String effectiveUserName = AuthenticationUtil.getRunAsUser();
// Check to see if should just ignore this node - note: special MT System due to AuditableAspect
if (! (ignore(nodeRef) || tenantService.getBaseNameUser(effectiveUserName).equals(AuthenticationUtil.getSystemUserName())))
{
try
{
// Get the current lock status on the node ref
LockStatus currentLockStatus = getLockStatus(nodeRef, userName);
LockType lockType = getLockType(nodeRef);
if (LockType.WRITE_LOCK.equals(lockType) == true &&
LockStatus.LOCKED.equals(currentLockStatus) == true)
{
// Lock is of type Write Lock and the node is locked by another owner.
throw new NodeLockedException(nodeRef);
}
else if (LockType.READ_ONLY_LOCK.equals(lockType) == true &&
(LockStatus.LOCKED.equals(currentLockStatus) == true || LockStatus.LOCK_OWNER.equals(currentLockStatus) == true))
{
// Error since there is a read only lock on this object and all
// modifications are prevented
throw new NodeLockedException(nodeRef);
}
else if (LockType.NODE_LOCK.equals(lockType) == true &&
(LockStatus.LOCKED.equals(currentLockStatus) == true || LockStatus.LOCK_OWNER.equals(currentLockStatus) == true))
{
// Error since there is a read only lock on this object and all
// modifications are prevented
throw new NodeLockedException(nodeRef);
}
}
catch (AspectMissingException exception)
{
// Ignore since this indicates that the node does not have the lock aspect applied
}
}
}
}
/**
* Ensures that the parent is not locked.
*
* @see #checkForLock(NodeRef)
*/
public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean isNewNode)
{
LockType lockType = getLockType(childAssocRef.getParentRef());
if(lockType != null)
{
switch (lockType)
{
case WRITE_LOCK:
case READ_ONLY_LOCK:
checkForLock(childAssocRef.getParentRef());
break;
case NODE_LOCK:
// don't check for lock
}
}
}
/**
* Ensures that node is not locked.
*
* @see #checkForLock(NodeRef)
*/
public void beforeUpdateNode(NodeRef nodeRef)
{
checkForLock(nodeRef);
}
/**
* Ensures that node is not locked.
*
* @see #checkForLock(NodeRef)
*/
public void beforeDeleteNode(NodeRef nodeRef)
{
checkForLock(nodeRef);
}
/**
* @return Returns {@link DoNothingCopyBehaviourCallback}
*/
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
{
return DoNothingCopyBehaviourCallback.getInstance();
}
/**
* OnCreateVersion behaviour for the lock aspect
* <p>
* Ensures that the property values of the lock aspect are not 'frozen' in
* the version store.
*/
public void onCreateVersion(
QName classRef,
NodeRef versionableNode,
Map<String, Serializable> versionProperties,
PolicyScope nodeDetails)
{
// Add the lock aspect, but do not version the property values
// TODO: disable the LockAspectInterceptor for this thread, re-enable in finally.
// (we need to add this aspect for real).
nodeDetails.addAspect(ContentModel.ASPECT_LOCKABLE);
}
/**
* Get the current user reference
*
* @return the current user reference
*/
private String getUserName()
{
return this.authenticationService.getCurrentUserName();
}
/**
* @see org.alfresco.service.cmr.lock.LockService#getLocks()
* @deprecated Uses search and does not report on ephemeral locks.
*/
@Deprecated
public List<NodeRef> getLocks(StoreRef storeRef)
{
return getLocks(
storeRef,
"ASPECT:\"" + ContentModel.ASPECT_LOCKABLE.toString() +
"\" +@\\{http\\://www.alfresco.org/model/content/1.0\\}" + ContentModel.PROP_LOCK_OWNER.getLocalName() + ":\"" + getUserName() + "\"");
}
/**
* Get the locks given a store and query string.
*
* @param storeRef the store reference
* @param query the query string
* @return the locked nodes
* @deprecated Uses search and does not report on ephemeral locks.
*/
@Deprecated
private List<NodeRef> getLocks(StoreRef storeRef, String query)
{
List<NodeRef> result = new ArrayList<NodeRef>();
ResultSet resultSet = null;
try
{
resultSet = this.searchService.query(
storeRef,
SearchService.LANGUAGE_LUCENE,
query);
result = resultSet.getNodeRefs();
}
finally
{
if (resultSet != null)
{
resultSet.close();
}
}
return result;
}
/**
* @see org.alfresco.service.cmr.lock.LockService#getLocks(org.alfresco.service.cmr.lock.LockType)
* @deprecated Uses search and does not report on ephemeral locks.
*/
@Deprecated
public List<NodeRef> getLocks(StoreRef storeRef, LockType lockType)
{
return getLocks(
storeRef,
"ASPECT:\"" + ContentModel.ASPECT_LOCKABLE.toString() +
"\" +@\\{http\\://www.alfresco.org/model/content/1.0\\}" + ContentModel.PROP_LOCK_OWNER.getLocalName() + ":\"" + getUserName() + "\"" +
" +@\\{http\\://www.alfresco.org/model/content/1.0\\}" + ContentModel.PROP_LOCK_TYPE.getLocalName() + ":\"" + lockType.toString() + "\"");
}
@Override
public void onMoveNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef)
{
NodeRef nodeRef = oldChildAssocRef.getChildRef();
checkForLock(nodeRef);
}
@Override
public void suspendLocks()
{
getBehaviourFilter().disableBehaviour(ContentModel.ASPECT_LOCKABLE);
lockableAspectInterceptor.disableForThread();
}
@Override
public void enableLocks()
{
getBehaviourFilter().enableBehaviour(ContentModel.ASPECT_LOCKABLE);
lockableAspectInterceptor.enableForThread();
}
@Override
public String getAdditionalInfo(NodeRef nodeRef)
{
LockState lockState = getLockState(nodeRef);
String additionalInfo = lockState.getAdditionalInfo();
return additionalInfo;
}
@Override
public LockState getLockState(NodeRef nodeRef)
{
// Check in-memory for ephemeral locks first.
nodeRef = tenantService.getName(nodeRef);
LockState lockState = lockStore.get(nodeRef);
//ALF-20361: It is possible that a rollback has resulted in a "non-lock" lock state being added to
//the lock store. Because of that, we check both whether the retrieved lockState is null and, if it isn't,
//whether it represents a real lock
if (lockState == null || !lockState.isLockInfo())
{
// No in-memory state, so get from the DB.
if (nodeService.exists(nodeRef) && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE))
{
String lockOwner = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_OWNER);
Date expiryDate = (Date) nodeService.getProperty(nodeRef, ContentModel.PROP_EXPIRY_DATE);
String lockTypeStr = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_TYPE);
LockType lockType = lockTypeStr != null ? LockType.valueOf(lockTypeStr) : null;
String lifetimeStr = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_LIFETIME);
Lifetime lifetime = lifetimeStr != null ? Lifetime.valueOf(lifetimeStr) : null;
String additionalInfo = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_ADDITIONAL_INFO);
// Mark lockstate as PERSISTENT as it was in the persistent storage!
lockState = LockState.createLock(
nodeRef,
lockType,
lockOwner,
expiryDate,
lifetime,
additionalInfo);
}
else
{
// There is no lock information
lockState = LockState.createUnlocked(nodeRef);
}
}
// Never return a null LockState
Assert.notNull(lockState);
return lockState;
}
public void setBehaviourFilter(BehaviourFilter behaviourFilter)
{
this.behaviourFilter = behaviourFilter;
}
public BehaviourFilter getBehaviourFilter()
{
return behaviourFilter;
}
public void setNodeIndexer(NodeIndexer nodeIndexer)
{
this.nodeIndexer = nodeIndexer;
}
@Override
public void flush()
{
}
@Override
public void beforeCommit(boolean readOnly)
{
}
@Override
public void beforeCompletion()
{
}
@Override
public void afterCommit()
{
}
@Override
public void afterRollback()
{
// As rollback has occurred we are unable to keep hold of any ephemeral locks set during this transaction.
Map<NodeRef, LockState> lockedNodes = TransactionalResourceHelper.getMap(KEY_MODIFIED_NODES);
for (LockState lockInfo : lockedNodes.values())
{
lockStore.set(lockInfo.getNodeRef(), lockInfo);
}
}
@Override
public void setEphemeralExpiryThreshold(int threshSecs)
{
ephemeralExpiryThreshold = threshSecs;
}
public int getEphemeralExpiryThreshold()
{
return ephemeralExpiryThreshold;
}
}