mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	125515 slanglois: MNT-16155 Update source headers - add new Copyrights for Java and JSP source files + automatic check in the build git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.1.N/root@125606 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
		
			
				
	
	
		
			975 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			975 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| /*
 | |
|  * #%L
 | |
|  * Alfresco Repository
 | |
|  * %%
 | |
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited
 | |
|  * %%
 | |
|  * This file is part of the Alfresco software. 
 | |
|  * If the software was purchased under a paid Alfresco license, the terms of 
 | |
|  * the paid license agreement will prevail.  Otherwise, the software is 
 | |
|  * provided under the following open source license terms:
 | |
|  * 
 | |
|  * 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/>.
 | |
|  * #L%
 | |
|  */
 | |
| 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.lock.traitextender.LockServiceExtension;
 | |
| import org.alfresco.repo.lock.traitextender.LockServiceTrait;
 | |
| 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.traitextender.Extend;
 | |
| import org.alfresco.traitextender.ExtendedTrait;
 | |
| import org.alfresco.traitextender.Extensible;
 | |
| import org.alfresco.traitextender.AJProxyTrait;
 | |
| import org.alfresco.traitextender.Trait;
 | |
| 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,
 | |
|                                         Extensible
 | |
| {
 | |
|     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;
 | |
| 
 | |
|     private final ExtendedTrait<LockServiceTrait> lockServiceTrait;
 | |
|     
 | |
|     public LockServiceImpl()
 | |
|     {
 | |
|         this.lockServiceTrait=new ExtendedTrait<LockServiceTrait>(AJProxyTrait.create(this, LockServiceTrait.class));
 | |
|     }
 | |
|     
 | |
|     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, org.alfresco.service.cmr.lock.LockType)
 | |
|      */
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     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, org.alfresco.service.cmr.lock.LockType, int)
 | |
|      */
 | |
|     @Override
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     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, org.alfresco.service.cmr.lock.LockType, int, Lifetime, String)
 | |
|      */
 | |
|     @Override
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     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, org.alfresco.service.cmr.lock.LockType, int, Lifetime, String)
 | |
|      */
 | |
|     @Override
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     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, org.alfresco.service.cmr.lock.LockType, int, boolean)
 | |
|      */
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     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, org.alfresco.service.cmr.lock.LockType, int)
 | |
|      */
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     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)
 | |
|      */
 | |
|     @Override
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     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
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     public void unlock(NodeRef nodeRef, boolean lockChildren) throws UnableToReleaseLockException
 | |
|     {
 | |
|         unlock(nodeRef, lockChildren, false);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @see org.alfresco.service.cmr.lock.LockService#unlock(NodeRef, boolean, boolean)
 | |
|      */
 | |
|     @Override
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     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.service.cmr.lock.LockService#unlock(Collection)
 | |
|      */
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     public void unlock(Collection<NodeRef> nodeRefs) throws UnableToReleaseLockException
 | |
|     {
 | |
|         for (NodeRef nodeRef : nodeRefs)
 | |
|         {
 | |
|             unlock(nodeRef);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @see org.alfresco.service.cmr.lock.LockService#getLockStatus(NodeRef)
 | |
|      */
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     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
 | |
|      */
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     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)
 | |
|      */
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     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}
 | |
|      */
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     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();
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @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;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @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
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     public void suspendLocks()
 | |
|     {
 | |
|        getBehaviourFilter().disableBehaviour(ContentModel.ASPECT_LOCKABLE);
 | |
|        lockableAspectInterceptor.disableForThread();
 | |
|     }
 | |
|     
 | |
|     @Override
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     public void enableLocks()
 | |
|     {
 | |
|        getBehaviourFilter().enableBehaviour(ContentModel.ASPECT_LOCKABLE);
 | |
|        lockableAspectInterceptor.enableForThread();
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     public String getAdditionalInfo(NodeRef nodeRef)
 | |
|     {
 | |
|         LockState lockState = getLockState(nodeRef);
 | |
|         String additionalInfo = lockState.getAdditionalInfo();
 | |
|         return additionalInfo;
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     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
 | |
|     @Extend(traitAPI=LockServiceTrait.class,extensionAPI=LockServiceExtension.class)
 | |
|     public void setEphemeralExpiryThreshold(int threshSecs)
 | |
|     {
 | |
|         ephemeralExpiryThreshold = threshSecs;
 | |
|     }
 | |
|     
 | |
|     public int getEphemeralExpiryThreshold()
 | |
|     {
 | |
|         return ephemeralExpiryThreshold;
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public <M extends Trait> ExtendedTrait<M> getTrait(Class<? extends M> traitAPI)
 | |
|     {
 | |
|         return (ExtendedTrait<M>) lockServiceTrait;
 | |
|     }
 | |
| }
 |