mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-30 18:15:39 +00:00
20025: Created Enterprise branch V3.3 20026: ALF-2597 : IMAP : permissions on home space. - now, by default, people can't read other's mail. 20030: Merged BRANCHES/V3.2 to BRANCHES/V3.3: 19919: Merged BRANCHES/V3.1 to BRANCHES/V3.2: 19766: Fixed ALF-2351: Oracle upgrade scripts need enhancements from 2.2SP7 20027: Merged BRANCHES/V3.1 to BRANCHES/V3.2: 19983: Changes for ALF-2545: Cannot upgrade from 2.1.2a (b 209) to the 3.1.2 (.a3 458) on Oracle 20008: ALF-2351: Oracle upgrade scripts need enhancements from 2.2SP7 20032: Merged HEAD to BRANCHES/V3.3 (RECORD ONLY) 20031: Fix ALF-2626 - Share Repository browser broken 20035: Enterprise branding for Share & Explorer - DO NOT MERGE (RECORD ONLY) Also: SAIL-282: Update the Help URLs for 3.3 Enterprise 20039: Fix ALF-2393 - Alfresco Comunity 3.3 deployment error on JBoss v6 20044: Fix ALF-750 (versioning does not persist node associations) - TODO: review version migrator (if upgrading directly from Ent 2.x to Ent 3.3) 20049: Merged PATCHES/V3.2.r to BRANCHES/V3.3 20047: Fix for ALF-2640: Share: Edit Offline and Upload New Version fails with HTML uploader on FF3.5, works on IE 20054: Fix ALF-750 (versioning does not persist node associations) - update version migrator (only applies if not already run, ie. upgrading directly from Ent 2.x to Ent 3.3) 20057: Merged HEAD to BRANCHES/V3.3: (RECORD ONLY) 20033: Accordion example was broken when FDK is deployed as a JAR 20064: Fix for ALF-2623: Alfresco 3.3G's Share site is prone to cross site scripting attacks - Bug is actually in the wiki components 20065: Fix unreported issue (auto-versioning for metadata-only updates stops working after checkin) & additional improvements to LockService - explicitly remove lockable aspect (rather than nullifying properties) for unlock / checkin - use txn resource to track ignorable nodes (for lockable aspect behaviours) - note: currently affects Alfresco Explorer only (since Alfresco Share explicitly disables autoVersionOnUpdateProps) 20066: Increased PermGen space for tests to 256M from 128M 20071: AVM - check for circular layered directories (ALF-813 / ALF-910) 20073: Fix LockService tests - fix typo (introduced in r20065) - TODO: review LockOwnerDynamicAuthorityTest.testCheckOutCheckInAuthorities 20076: Fix LockOwnerDynamicAuthorityTest.testCheckOutCheckInAuthorities 20078: Fixed ALF-2464 "Missing i18n labels when rules fail to run" 20081: Fixed ALF-1626 "The position:absolute behaviour of the Flash preview container needs a re-think" - Now handles long file names (resize was already fixed) 20083: Fix for ALF-2708: Unmodifiable exception thrown when Web Script f/w attempts to report error (latest Spring Surf webscripts libraries) 20084: Fixed ALF-253 "Unfriendly message appears when trying to login with username which contains symbol '\'" - also fixed bug whereerror messages for illegal characters was displayed as undefined for FF on Mac 20085: Merging HEAD into BRANCHES/V3.3: 20074: ALF-959 The invitation email 'subject' can now be set as a localizable property in invitation-services.properties: 20080: Fixing failing test InvitationServiceImplTest. 20087: ALF-1498: RM web script puts Alfresco in endless loop This was a general issue with the onUpdateProperties behaviour in the versionable aspect. This code now disables the behaviour whilst it is executing to prevent the endless loop occuring. 20088: Fixed an issue when uploading 2 or more documents for a new site. - A failure occured since it asynchronously tried to create the documentLibrary container twice and the second attempt failed since it already existed. 20089: SAIL-356: Action label changes 20090: Dynamic models: minor improvements to DictionaryModelType - add (optional) concurrency locking - remove duplicate bean def - bind remaining class behaviours (onCreateNode, onRemoveAspect) based on type 20091: Fixed ALF-1046 "Leave button is displayed for admin on Site Finder page near private site where admin is not invited" 20092: Merged DEV/BELARUS/V3.2-2010_03_17 to V3.3 20043: ALF-928: Upgrade from 2.1.7 to 3.2 with lots of content items - GC overhead limit exceeded exception Call getChildAssocs(NodeRef, QNamePattern, QNamePattern, boolean) with a value of 'false' for the preload argument to avoid preloading all the child nodes 20093: Fix for ALF-2721: Upgrade clean 2.2.current + 20k users to 3.3.current fails in CalendarModelUriPatch updating URI that does not exist 20094: ALF-2630: LDAP differential sync was failing to sync group memberships of users who themselves hadn't changed - New post process deals with group associations of unprocessed users - Modified unit test to properly simulate differential sync 20095: Fix for ALF-2715: Rule creation in Alfresco Share 3.3G leads to an "Internal Server Error" failure message 20096: Fix webview and wiki dashlet titles in yellow and gdocs themes. 20097: Follow-up fix to cross-browser WebView dashlet (iframe) resizing 20098: Workaround for ALF-2211: Share - Accessing User homes from Share/JSF integration freezes the browser. - The tree control has been given a configurable maximum folder count setting for both Site and Repository working modes. By default these are "unlimited" in Site mode and 500 in Repository mode. These values can be overridden in share-config-custom.xml - see the sample configuration file for details. - The workaround is to display a "Too many sub-folders to display" message when the maximum number of folders has been reached. - To aid users to select their User Home space (or sub-folder thereof) for Copy and Move actions, a new "My User Home" button is provided on the folder picker control. 20099: Fix for ALF-2606: Manage Permissions on multiple nodes. - Toolbar action removed when in Repository Browser, as the fine-grained permissions page does not support multiple nodes. 20100: Merged Outlook Meeting Workspace integration from BRANCHES/DEV/BELARUS/V3.2-2010_01_11 20102: Fix for ALF-478: Authority CRC calculations must use UTF-8 20103: Follow up from ALF-253 (Unfriendly message appears when trying to login with username which contains symbol '\') - Making lastName mandatory in Share ui since service otherwise complains 20106: Fixed ALF-1041 "Revert action is available for SiteContributor and SiteConsumer" (and added a missing msg key for blogs) 20108: ALF-2235: Permission exception when creating non-electronic records by Power User with Read and File permssions 20109: Fix for ALF-2706 "ConcurrentModificationException in AsynchronousActionExecutionQueueImpl" 20110: Merge Dev to V3.3 ALF-1980 - Huge UIDVALIDITY giving IMAP client problems 20111: Latest webeditor JAR containing change to orientation strings in WEF 20113: Fix Share DocLib copy/move actions from recent refactor. Picker now appears with correct Site/Repository mode set upon opening. 20114: Fix for ALF-2726: 'Transform and Copy content' action causes error. 20115: Fix for ALF-2697 - File encoding is hard-coded for upload.post.js (Webscript API) 20116: Fix for ALF-1090 20119: ALF-2734 - Incorrect behaviour on creating google docs in Repository Browser 20120: Enterprise build fix for Index check tests - disable user usage updates - this should not be required 20121: ALF-959 The site name/title should now correctly appear in the invite email subject, replacing '{0}'. 20123: Merged HEAD to V3.3 (RECORD ONLY) 20122: First part of fix for ALF-2718: DOD5015 module breaks CMIS Atom DiscoveryService webscripts 20126: Fix rule rest api json so numbers are not incorrectly formatted. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@20565 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
690 lines
23 KiB
Java
690 lines
23 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.Collections;
|
|
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.DefaultCopyBehaviourCallback;
|
|
import org.alfresco.repo.node.NodeServicePolicies;
|
|
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.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.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.cmr.security.OwnableService;
|
|
import org.alfresco.service.namespace.NamespaceService;
|
|
import org.alfresco.service.namespace.QName;
|
|
|
|
/**
|
|
* Simple Lock service implementation
|
|
*
|
|
* @author Roy Wetherall
|
|
*/
|
|
public class LockServiceImpl implements LockService,
|
|
NodeServicePolicies.BeforeCreateChildAssociationPolicy,
|
|
NodeServicePolicies.BeforeUpdateNodePolicy,
|
|
NodeServicePolicies.BeforeDeleteNodePolicy,
|
|
CopyServicePolicies.OnCopyNodePolicy,
|
|
VersionServicePolicies.BeforeCreateVersionPolicy,
|
|
VersionServicePolicies.OnCreateVersionPolicy
|
|
{
|
|
/**
|
|
* The node service
|
|
*/
|
|
private NodeService nodeService;
|
|
|
|
/**
|
|
* The tenant service
|
|
*/
|
|
private TenantService tenantService;
|
|
|
|
/**
|
|
* The policy component
|
|
*/
|
|
private PolicyComponent policyComponent;
|
|
|
|
/** Key to the nodes ref's to ignore when checking for locks */
|
|
private static final String KEY_IGNORE_NODES = "lockService.ignoreNodes";
|
|
|
|
/**
|
|
* The authentication service
|
|
*/
|
|
private AuthenticationService authenticationService;
|
|
|
|
/**
|
|
* The ownable service
|
|
*
|
|
*/
|
|
private OwnableService ownableService;
|
|
|
|
/**
|
|
* The search service
|
|
*/
|
|
private SearchService searchService;
|
|
|
|
/**
|
|
* Set the node service
|
|
*
|
|
* @param nodeService
|
|
* the node service
|
|
*/
|
|
public void setNodeService(NodeService nodeService)
|
|
{
|
|
this.nodeService = nodeService;
|
|
}
|
|
|
|
/**
|
|
* Set the tenant service
|
|
*
|
|
* @param tenantService
|
|
* the tenant service
|
|
*/
|
|
public void setTenantService(TenantService tenantService)
|
|
{
|
|
this.tenantService = tenantService;
|
|
}
|
|
|
|
/**
|
|
* Sets the policy component
|
|
*
|
|
* @param policyComponent
|
|
* the policy componentO
|
|
*/
|
|
public void setPolicyComponent(PolicyComponent policyComponent)
|
|
{
|
|
this.policyComponent = policyComponent;
|
|
}
|
|
|
|
/**
|
|
* Sets the authentication service
|
|
*
|
|
* @param authenticationService
|
|
* the authentication service
|
|
*/
|
|
public void setAuthenticationService(AuthenticationService authenticationService)
|
|
{
|
|
this.authenticationService = authenticationService;
|
|
}
|
|
|
|
/**
|
|
* Sets the ownable service
|
|
*
|
|
* @param ownableService
|
|
* the ownable service
|
|
*/
|
|
public void setOwnableService(OwnableService ownableService)
|
|
{
|
|
this.ownableService = ownableService;
|
|
}
|
|
|
|
/**
|
|
* Set the search service
|
|
*
|
|
* @param searchService the search service
|
|
*/
|
|
public void setSearchService(SearchService searchService)
|
|
{
|
|
this.searchService = searchService;
|
|
}
|
|
|
|
/**
|
|
* Initialise methods called by Spring framework
|
|
*/
|
|
public void init()
|
|
{
|
|
// Register the various class behaviours to enable lock checking
|
|
this.policyComponent.bindAssociationBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "beforeCreateChildAssociation"),
|
|
ContentModel.ASPECT_LOCKABLE,
|
|
new JavaBehaviour(this, "beforeCreateChildAssociation"));
|
|
this.policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "beforeUpdateNode"),
|
|
ContentModel.ASPECT_LOCKABLE,
|
|
new JavaBehaviour(this, "beforeUpdateNode"));
|
|
this.policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"),
|
|
ContentModel.ASPECT_LOCKABLE,
|
|
new JavaBehaviour(this, "beforeDeleteNode"));
|
|
|
|
// Register copy class behaviour
|
|
this.policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
|
ContentModel.ASPECT_LOCKABLE,
|
|
new JavaBehaviour(this, "getCopyCallback"));
|
|
|
|
// Register the onCreateVersion behavior for the version aspect
|
|
this.policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "beforeCreateVersion"),
|
|
ContentModel.ASPECT_LOCKABLE,
|
|
new JavaBehaviour(this, "beforeCreateVersion"));
|
|
this.policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateVersion"),
|
|
ContentModel.ASPECT_LOCKABLE,
|
|
new JavaBehaviour(this, "onCreateVersion"));
|
|
}
|
|
|
|
@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 synchronized 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)
|
|
*/
|
|
public synchronized void lock(NodeRef nodeRef, LockType lockType, int timeToExpire)
|
|
{
|
|
nodeRef = tenantService.getName(nodeRef);
|
|
|
|
// Check for lock aspect
|
|
checkForLockApsect(nodeRef);
|
|
|
|
// Get the current user name
|
|
String userName = getUserName();
|
|
|
|
// Set a default value
|
|
if (lockType == null)
|
|
{
|
|
lockType = LockType.WRITE_LOCK;
|
|
}
|
|
|
|
LockStatus currentLockStatus = getLockStatus(nodeRef, userName);
|
|
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)
|
|
{
|
|
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());
|
|
setExpiryDate(nodeRef, timeToExpire);
|
|
}
|
|
finally
|
|
{
|
|
removeFromIgnoreSet(nodeRef);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper method to set the expiry date based on the time to expire provided
|
|
*
|
|
* @param nodeRef the node reference
|
|
* @param timeToExpire the time to expire (in seconds)
|
|
*/
|
|
private void setExpiryDate(NodeRef nodeRef, 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();
|
|
}
|
|
|
|
this.nodeService.setProperty(nodeRef, ContentModel.PROP_EXPIRY_DATE, 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 synchronized 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 synchronized 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)
|
|
*/
|
|
public synchronized void unlock(NodeRef nodeRef) throws UnableToReleaseLockException
|
|
{
|
|
nodeRef = tenantService.getName(nodeRef);
|
|
|
|
// Check for lock aspect
|
|
checkForLockApsect(nodeRef);
|
|
|
|
addToIgnoreSet(nodeRef);
|
|
try
|
|
{
|
|
// Clear the lock
|
|
this.nodeService.removeAspect(nodeRef, ContentModel.ASPECT_LOCKABLE);
|
|
}
|
|
finally
|
|
{
|
|
removeFromIgnoreSet(nodeRef);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.lock.LockService#unlock(NodeRef, String,
|
|
* boolean)
|
|
*/
|
|
public synchronized void unlock(NodeRef nodeRef, boolean unlockChildren)
|
|
throws UnableToReleaseLockException
|
|
{
|
|
// Unlock the parent
|
|
unlock(nodeRef);
|
|
|
|
if (unlockChildren == true)
|
|
{
|
|
// 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 synchronized 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 statuc 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)
|
|
{
|
|
LockStatus result = LockStatus.NO_LOCK;
|
|
|
|
if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE) == true)
|
|
{
|
|
// Get the current lock owner
|
|
String currentUserRef = (String) this.nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_OWNER);
|
|
String owner = ownableService.getOwner(nodeRef);
|
|
if (currentUserRef != null)
|
|
{
|
|
Date expiryDate = (Date)this.nodeService.getProperty(nodeRef, ContentModel.PROP_EXPIRY_DATE);
|
|
if (expiryDate != null && expiryDate.before(new Date()) == true)
|
|
{
|
|
// Indicate that the lock has expired
|
|
result = LockStatus.LOCK_EXPIRED;
|
|
}
|
|
else
|
|
{
|
|
if (currentUserRef.equals(userName) == true)
|
|
{
|
|
result = LockStatus.LOCK_OWNER;
|
|
}
|
|
else if ((owner != null) && owner.equals(userName))
|
|
{
|
|
result = LockStatus.LOCK_OWNER;
|
|
}
|
|
else
|
|
{
|
|
result = LockStatus.LOCKED;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
return result;
|
|
|
|
}
|
|
|
|
/**
|
|
* @see LockService#getLockType(NodeRef)
|
|
*/
|
|
public LockType getLockType(NodeRef nodeRef)
|
|
{
|
|
LockType result = null;
|
|
|
|
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 checkForLockApsect(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)
|
|
{
|
|
// Error since we are trying to preform an operation on a locked node
|
|
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);
|
|
}
|
|
}
|
|
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 beforeCreateChildAssociation(
|
|
NodeRef parentNodeRef,
|
|
NodeRef childNodeRef,
|
|
QName assocTypeQName,
|
|
QName assocQName,
|
|
boolean isNewNode)
|
|
{
|
|
checkForLock(parentNodeRef);
|
|
}
|
|
|
|
/**
|
|
* 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 LockableAspectCopyBehaviourCallback}
|
|
*/
|
|
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
|
{
|
|
return LockableAspectCopyBehaviourCallback.INSTANCE;
|
|
}
|
|
|
|
/**
|
|
* Extends the default copy behaviour to prevent copying of lockable properties.
|
|
*
|
|
* @author Derek Hulley
|
|
* @since 3.2
|
|
*/
|
|
private static class LockableAspectCopyBehaviourCallback extends DefaultCopyBehaviourCallback
|
|
{
|
|
private static final CopyBehaviourCallback INSTANCE = new LockableAspectCopyBehaviourCallback();
|
|
|
|
/**
|
|
* @return Returns an empty map
|
|
*/
|
|
@Override
|
|
public Map<QName, Serializable> getCopyProperties(
|
|
QName classQName, CopyDetails copyDetails, Map<QName, Serializable> properties)
|
|
{
|
|
return Collections.emptyMap();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensures that node is not locked.
|
|
*
|
|
* @see #checkForLock(NodeRef)
|
|
*/
|
|
public void beforeCreateVersion(NodeRef versionableNode)
|
|
{
|
|
checkForLock(versionableNode);
|
|
}
|
|
|
|
/**
|
|
* OnCreateVersion behaviour for the lock aspect
|
|
* <p>
|
|
* Ensures that the property valies 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
|
|
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()
|
|
*/
|
|
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
|
|
*/
|
|
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)
|
|
*/
|
|
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() + "\"");
|
|
}
|
|
}
|