mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@128510 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
		
			
				
	
	
		
			871 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			871 lines
		
	
	
		
			27 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.filesys.repo;
 | |
| 
 | |
| import java.io.Serializable;
 | |
| import java.util.List;
 | |
| import java.util.Map;
 | |
| 
 | |
| import org.alfresco.jlan.server.filesys.FileStatus;
 | |
| import org.alfresco.jlan.server.filesys.NotifyChange;
 | |
| import org.alfresco.jlan.server.filesys.cache.FileState;
 | |
| import org.alfresco.jlan.server.filesys.cache.FileStateCache;
 | |
| import org.alfresco.model.ContentModel;
 | |
| import org.alfresco.repo.node.NodeServicePolicies;
 | |
| import org.alfresco.repo.policy.JavaBehaviour;
 | |
| import org.alfresco.repo.policy.PolicyComponent;
 | |
| import org.alfresco.repo.security.authentication.AuthenticationUtil;
 | |
| import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
 | |
| import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
 | |
| import org.alfresco.repo.transaction.TransactionalResourceHelper;
 | |
| import org.alfresco.service.cmr.model.FileFolderService;
 | |
| import org.alfresco.service.cmr.model.FileFolderServiceType;
 | |
| 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.Path;
 | |
| import org.alfresco.service.cmr.repository.StoreRef;
 | |
| import org.alfresco.service.cmr.security.PermissionService;
 | |
| import org.alfresco.service.namespace.NamespaceService;
 | |
| import org.alfresco.service.namespace.QName;
 | |
| import org.alfresco.service.transaction.TransactionService;
 | |
| import org.alfresco.util.PropertyCheck;
 | |
| import org.alfresco.util.transaction.TransactionListenerAdapter;
 | |
| import org.apache.commons.logging.Log;
 | |
| import org.apache.commons.logging.LogFactory;
 | |
| 
 | |
| /**
 | |
|  * Node Monitor Class
 | |
|  * 
 | |
|  * <p>Monitor node events from the node service to update the file state cache and feed notification events into
 | |
|  * the file server change notification handler.
 | |
|  * 
 | |
|  * @author gkspencer
 | |
|  */
 | |
| public class NodeMonitor extends TransactionListenerAdapter 
 | |
| 						 implements  NodeServicePolicies.OnCreateNodePolicy,
 | |
| 									 NodeServicePolicies.OnUpdatePropertiesPolicy,
 | |
| 									 NodeServicePolicies.BeforeDeleteNodePolicy,
 | |
| 									 NodeServicePolicies.OnMoveNodePolicy,
 | |
| 									 Runnable
 | |
| {
 | |
|     // Logging
 | |
|     
 | |
|     private static final Log logger = LogFactory.getLog(NodeMonitor.class);
 | |
| 
 | |
|     // Transaction object binding keys
 | |
|     
 | |
|     public static final String FileSysNodeEvent  = "FileSysNodeEvent";
 | |
|     public static final String FileSysNodeEvent2 = "FileSysNodeEvent2";
 | |
|     
 | |
|     // Services/components
 | |
| 	
 | |
| 	private PolicyComponent m_policyComponent;
 | |
| 	private NodeService m_nodeService;
 | |
| 	private FileFolderService m_fileFolderService;
 | |
| 	private PermissionService m_permissionService;
 | |
| 	private TransactionService m_transService;
 | |
| 	
 | |
| 	// Filesystem driver and context
 | |
| 
 | |
| 	// TODO needs to be configured with many filesystem contexts.
 | |
| 	private ContentContext m_filesysCtx;
 | |
| 	
 | |
| 	// File state table 
 | |
| 	private FileStateCache m_stateTable;
 | |
| 	
 | |
| 	// Root node path and store
 | |
| 	private String m_rootPath;
 | |
| 	private StoreRef m_storeRef;
 | |
| 	
 | |
| 	// Queue of node update events
 | |
| 	
 | |
| 	private NodeEventQueue m_eventQueue;
 | |
| 
 | |
| 	// Thread for the main event processing
 | |
| 	
 | |
| 	private Thread m_thread;
 | |
| 	private boolean m_shutdown;
 | |
| 	
 | |
| 	/**
 | |
| 	 * Class constructor
 | |
| 	 * 
 | |
| 	 * @param filesysCtx ContentContext
 | |
| 	 * @param nodeService NodeService
 | |
| 	 * @param policyComponent PolicyComponent
 | |
| 	 * @param fileFolderService FileFolderService
 | |
| 	 * @param permissionService PermissionService
 | |
| 	 * @param transService TransactionService
 | |
| 	 */
 | |
| 	protected NodeMonitor(ContentContext filesysCtx, NodeService nodeService, PolicyComponent policyComponent,
 | |
| 			FileFolderService fileFolderService, PermissionService permissionService, TransactionService transService) {
 | |
| 		m_filesysCtx    = filesysCtx;
 | |
| 		
 | |
| 		// Set various services
 | |
| 
 | |
| 		m_nodeService       = nodeService;
 | |
| 		m_policyComponent   = policyComponent;
 | |
| 		m_fileFolderService = fileFolderService;
 | |
| 		m_permissionService = permissionService;
 | |
| 		m_transService      = transService;
 | |
| 		
 | |
| 		// Initialize the node monitor
 | |
| 		
 | |
| 		init();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Initialize the node monitor
 | |
| 	 */
 | |
| 	public final void init() 
 | |
| 	{
 | |
| 	    PropertyCheck.mandatory(this, "nodeService", m_nodeService);
 | |
| 	    PropertyCheck.mandatory(this, "filesysCtx",  m_filesysCtx);
 | |
| 	    PropertyCheck.mandatory(this, "policyComponent", m_policyComponent);
 | |
| 	    PropertyCheck.mandatory(this, "fileFolderService",  m_fileFolderService);
 | |
| 	    PropertyCheck.mandatory(this, "permissionService", m_permissionService);
 | |
| 	    PropertyCheck.mandatory(this, "transService", m_transService);
 | |
| 
 | |
| 		// Disable change notifications from the file server
 | |
| 		
 | |
| 		m_filesysCtx.setFileServerNotifications( false);
 | |
| 		
 | |
|         // Register for node service events
 | |
| 		
 | |
|         m_policyComponent.bindClassBehaviour( QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"),
 | |
|                 this, new JavaBehaviour(this, "onCreateNode"));   
 | |
|         m_policyComponent.bindClassBehaviour( QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"),
 | |
|                 this, new JavaBehaviour(this, "beforeDeleteNode"));     
 | |
|         m_policyComponent.bindClassBehaviour( QName.createQName(NamespaceService.ALFRESCO_URI, "onMoveNode"),
 | |
|                 this, new JavaBehaviour(this, "onMoveNode"));
 | |
|         m_policyComponent.bindClassBehaviour( QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
 | |
|     		  	this, new JavaBehaviour(this, "onUpdateProperties"));
 | |
| 
 | |
|         // Get the store for the root node.
 | |
|         
 | |
|         m_storeRef = m_filesysCtx.getRootNode().getStoreRef();
 | |
|         
 | |
|         // Get the root node path
 | |
|         
 | |
|         String rootPath = (String) m_nodeService.getProperty( m_filesysCtx.getRootNode(), ContentModel.PROP_NAME);
 | |
|         
 | |
|         StringBuilder pathBuilder= new StringBuilder();
 | |
|         pathBuilder.append("/");
 | |
|         if ( rootPath != null && rootPath.length() > 0)
 | |
|         	pathBuilder.append( rootPath);
 | |
|         
 | |
|         m_rootPath = pathBuilder.toString();
 | |
|         
 | |
|         // DEBUG
 | |
|         
 | |
|         if ( logger.isDebugEnabled())
 | |
|         	logger.debug("Node monitor filesystem=" + m_filesysCtx.getDeviceName() + ", rootPath=" + m_rootPath);
 | |
|         
 | |
|         // Create the node event queue
 | |
|         
 | |
|         m_eventQueue = new NodeEventQueue();
 | |
|         
 | |
|         // DEBUG
 | |
| 
 | |
|         if ( logger.isDebugEnabled())
 | |
|         	logger.debug("Node monitor installed for " + m_filesysCtx.getDeviceName());
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Start the node monitor thread
 | |
| 	 */
 | |
| 	public void startMonitor() {
 | |
| 		
 | |
|         // Get the file state table and change notification handler, if enabled
 | |
|         m_stateTable = m_filesysCtx.getStateCache();
 | |
|         
 | |
|         // Start the event processing thread
 | |
|         
 | |
|         m_thread = new Thread( this);
 | |
|         m_thread.setName( "NodeMonitor_" + m_filesysCtx.getDeviceName());
 | |
|         m_thread.setDaemon( true);
 | |
|         
 | |
|         m_thread.start();
 | |
|         
 | |
|         // DEBUG
 | |
|         
 | |
|         if ( logger.isDebugEnabled())
 | |
|         {
 | |
|         	logger.debug("NodeMonitor started, " + m_thread.getName());
 | |
| 	
 | |
|         }
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Create node event
 | |
| 	 * 
 | |
| 	 * @param childAssocRef ChildAssociationRef
 | |
| 	 */
 | |
|     public void onCreateNode(ChildAssociationRef childAssocRef) {
 | |
|     	
 | |
|     	// Check if the node is a file/folder
 | |
|     	
 | |
|     	NodeRef nodeRef = childAssocRef.getChildRef();
 | |
|     	if ( nodeRef.getStoreRef().equals( m_storeRef) == false)
 | |
|     	{
 | |
|     	    // different store so irrelevant
 | |
|     		return;
 | |
|     	}
 | |
|     	
 | |
|     	QName nodeType = m_nodeService.getType( nodeRef);
 | |
|     	FileFolderServiceType fType = m_fileFolderService.getType( nodeType);
 | |
|     	    	
 | |
|     	if ( fType != FileFolderServiceType.INVALID) 
 | |
|     	{
 | |
|     		// Node is not INVALID - therefore its VALID
 | |
|     	    Path nodePath = m_nodeService.getPath( nodeRef);
 | |
|             String relPath = nodePath.toDisplayPath(m_nodeService, m_permissionService);
 | |
|             String fName = (String) m_nodeService.getProperty( nodeRef, ContentModel.PROP_NAME);
 | |
| 
 | |
|     		if ( logger.isDebugEnabled()) 
 | |
|     		{
 | |
|     			logger.debug("OnCreateNode: nodeRef=" + nodeRef + ", name=" + fName + ", path=" + relPath);
 | |
|     		}
 | |
|     	
 | |
|     		// Create an event to process the node creation
 | |
|     		NodeEvent nodeEvent = new CreateNodeEvent( fType, nodeRef, relPath, fName);
 | |
|     		
 | |
|     		// Store the event in the transaction until committed, and register the transaction listener
 | |
|     		fireNodeEvent(nodeEvent);
 | |
|     	}
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Update properties event
 | |
|      * 
 | |
|      * @param nodeRef NodeRef
 | |
|      */
 | |
|     public void onUpdateProperties( NodeRef nodeRef, Map<QName, Serializable> before, Map<QName, Serializable> after) {
 | |
|     	
 | |
|     	// Check that the node is in our store
 | |
|     	
 | |
|     	if ( nodeRef.getStoreRef().equals( m_storeRef) == false)
 | |
|     	{
 | |
|     		return;
 | |
|     	}
 | |
| 
 | |
|     	// Check if the node is a file/folder
 | |
|     	
 | |
|     	QName nodeType = m_nodeService.getType( nodeRef);
 | |
|     	FileFolderServiceType fType = m_fileFolderService.getType( nodeType);
 | |
|     	
 | |
|     	if ( fType != FileFolderServiceType.INVALID) 
 | |
|     	{
 | |
|     		// Check if there has been a lock change
 | |
|     		
 | |
|     		String beforeLock = (String) before.get( ContentModel.PROP_LOCK_TYPE);
 | |
|     		String afterLock  = (String) after.get( ContentModel.PROP_LOCK_TYPE);
 | |
|             String beforeName = (String) before.get(ContentModel.PROP_NAME);
 | |
|             String afterName = (String) after.get(ContentModel.PROP_NAME);
 | |
|             
 | |
|     		if (( beforeLock != null && afterLock == null) ||
 | |
|     				( beforeLock == null && afterLock != null)) 
 | |
|     		{
 | |
|                 Path nodePath = m_nodeService.getPath(nodeRef);
 | |
|                 String relPath = nodePath.toDisplayPath(m_nodeService, m_permissionService);
 | |
| 
 | |
|     			// Process the lock update first
 | |
|         		fireNodeEvent(new LockNodeEvent( fType, nodeRef, relPath, beforeName, beforeLock, afterLock));
 | |
|     		}
 | |
|     		
 | |
|     		// Check if node has been renamed
 | |
|     		if (beforeName != null && !beforeName.equals(afterName)) 
 | |
|     		{
 | |
|     		    // Yes Node has been renamed in the same folder
 | |
|     			ChildAssociationRef childAssocRef = m_nodeService.getPrimaryParent(nodeRef);
 | |
|     			String relPath2 = buildRelativePathString(childAssocRef.getParentRef(), beforeName);
 | |
|     			String relPath3 = buildRelativePathString(childAssocRef.getParentRef(), afterName);
 | |
|     			fireNodeEvent(new MoveNodeEvent( fType, nodeRef, relPath2 , relPath3));
 | |
|     		}
 | |
|     	}
 | |
|     }    
 | |
| 
 | |
| 	/**
 | |
| 	 * Move node event
 | |
| 	 * 
 | |
| 	 * @param oldChildAssocRef ChildAssociationRef
 | |
| 	 * @param newChildAssocRef ChildAssociationRef
 | |
| 	 */
 | |
| 	public void onMoveNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef) {
 | |
| 		
 | |
|     	// Check if the node is a file/folder, and for our store
 | |
|     	
 | |
|     	NodeRef oldNodeRef = oldChildAssocRef.getChildRef();
 | |
|     	if ( oldNodeRef.getStoreRef().equals( m_storeRef) == false)
 | |
|     	{
 | |
|     		return;
 | |
|     	}
 | |
|     	
 | |
|     	QName nodeType = m_nodeService.getType( oldNodeRef);
 | |
|     	FileFolderServiceType fType = m_fileFolderService.getType( nodeType);
 | |
|     	
 | |
|     	if ( fType != FileFolderServiceType.INVALID) 
 | |
|     	{
 | |
|     		
 | |
|     		// Get the full path to the file/folder node
 | |
| 
 | |
|     		String fName = (String) m_nodeService.getProperty( oldNodeRef, ContentModel.PROP_NAME);
 | |
|     		
 | |
|     		// Build the share relative path to the node
 | |
|     		String relPath = buildRelativePathString(oldChildAssocRef.getParentRef(), fName);
 | |
|     		String relPath2 = buildRelativePathString(newChildAssocRef.getParentRef(), fName);
 | |
|     		
 | |
|     		// DEBUG
 | |
|     		
 | |
|     		if ( logger.isDebugEnabled())
 | |
|     			logger.debug("OnMoveNode: nodeRef=" + oldNodeRef + ", relPath=" + relPath);
 | |
| 
 | |
|     		// Queue an event to process the node move
 | |
|     		
 | |
|     		if ( relPath.startsWith( m_rootPath)) {
 | |
| 
 | |
| 	    		// Create a move event
 | |
| 		 		
 | |
| 				NodeEvent nodeEvent = new MoveNodeEvent( fType, oldNodeRef, relPath, relPath2);
 | |
| 			
 | |
| 	    		// Store the event in the transaction until committed, and register the transaction listener
 | |
| 	    		fireNodeEvent(nodeEvent);
 | |
|     		}
 | |
|     	}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Before delete node event
 | |
| 	 * 
 | |
| 	 * @param nodeRef NodeRef
 | |
| 	 */
 | |
| 	public void beforeDeleteNode(NodeRef nodeRef) {
 | |
| 
 | |
| 		// Check if the node is in the filesystem store
 | |
| 		
 | |
|     	if ( nodeRef.getStoreRef().equals( m_storeRef) == false)
 | |
|     		return;
 | |
|     	
 | |
|     	// Check if the node is a file/folder
 | |
|     	
 | |
|     	QName nodeType = m_nodeService.getType( nodeRef);
 | |
|     	FileFolderServiceType fType = m_fileFolderService.getType( nodeType);
 | |
|     	
 | |
|     	if ( fType != FileFolderServiceType.INVALID) {
 | |
|     		
 | |
|             StringBuilder pathStr = calculateDisplayPath(nodeRef);
 | |
|             String relPath = (null != pathStr) ? (pathStr.toString()):("");
 | |
|     		
 | |
|     		// Create an event to process the node deletion
 | |
|     		
 | |
|     	 	if ( relPath.startsWith( m_rootPath)) {
 | |
|     	 		
 | |
|     	 		// Create a delete event
 | |
|     	 		
 | |
|     			NodeEvent nodeEvent = new DeleteNodeEvent( fType, nodeRef, relPath);
 | |
|     			
 | |
|     			fireNodeEvent(nodeEvent);
 | |
|     		
 | |
| 	    		// DEBUG
 | |
| 	    		
 | |
| 	    		if ( logger.isDebugEnabled())
 | |
| 	    			logger.debug("BeforeDeleteNode: nodeRef=" + nodeRef + ", relPath=" + relPath);
 | |
|     	 	}
 | |
|     	}
 | |
| 	}
 | |
| 
 | |
|     private StringBuilder calculateDisplayPath(final NodeRef nodeRef)
 | |
|     {
 | |
|         return AuthenticationUtil.runAs(new RunAsWork<StringBuilder>()
 | |
|         {
 | |
|             @Override
 | |
|             public StringBuilder doWork() throws Exception
 | |
|             {
 | |
|                 // Get the full path to the file/folder node
 | |
|                 Path nodePath = m_nodeService.getPath(nodeRef);
 | |
|                 String fName = (String) m_nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
 | |
| 
 | |
|                 // Build the share relative path to the node
 | |
|                 StringBuilder result = new StringBuilder();
 | |
|                 result.append(nodePath.toDisplayPath(m_nodeService, m_permissionService));
 | |
|                 if ((0 == result.length()) || ('/' != (result.charAt(result.length() - 1)) && ('\\' != result.charAt(result.length() - 1))))
 | |
|                 {
 | |
|                     result.append("\\");
 | |
|                 }
 | |
|                 return result.append(fName);
 | |
|             }
 | |
|         }, AuthenticationUtil.SYSTEM_USER_NAME);
 | |
|     }
 | |
| 
 | |
| 	/**
 | |
| 	 * The relative path of a renamed/moved node
 | |
| 	 * 
 | |
| 	 * ALF-2309: construct the path from the old parent of the moved
 | |
| 	 * node (parentNodeRef) - this will have the correct path
 | |
| 	 * 
 | |
| 	 * @param parentNodeRef the old parent of the node
 | |
| 	 * @param nodeName		the old name of the childs
 | |
| 	 * @return String
 | |
| 	 */
 | |
| 	private String buildRelativePathString(NodeRef parentNodeRef, String nodeName) {
 | |
| 		Path nodePath = m_nodeService.getPath(parentNodeRef);
 | |
| 		
 | |
| 		StringBuilder pathStr = new StringBuilder();
 | |
| 		pathStr.append(nodePath.toDisplayPath(m_nodeService, m_permissionService));
 | |
| 		if (pathStr.length() == 0 
 | |
| 				||  pathStr.charAt(pathStr.length() - 1) != '/' && pathStr.charAt(pathStr.length() - 1) != '\\')
 | |
| 			pathStr.append("/");
 | |
| 
 | |
| 		pathStr.append((String) m_nodeService.getProperty( parentNodeRef, ContentModel.PROP_NAME))
 | |
| 			.append("\\")
 | |
| 			.append( nodeName);
 | |
| 
 | |
| 		return pathStr.toString();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Queues a node event for execution post-commit.
 | |
| 	 *  
 | |
| 	 * @param nodeEvent the event to queue
 | |
| 	 */
 | |
| 	private void fireNodeEvent(NodeEvent nodeEvent) {
 | |
|     	
 | |
| 	    List<NodeEvent> events = TransactionalResourceHelper.getList(FileSysNodeEvent);
 | |
| 	    events.add(nodeEvent);
 | |
| 	    		
 | |
| 		// Store the event in the transaction until committed, and register the transaction listener
 | |
|     }
 | |
| 	
 | |
| 	/**
 | |
| 	 * Request the node monitor thread to shut down
 | |
| 	 */
 | |
| 	public final void shutdownRequest() {
 | |
| 		
 | |
| 		if ( m_thread != null) {
 | |
| 			
 | |
| 			// Set the shutdown request flag
 | |
| 			
 | |
| 			m_shutdown = true;
 | |
| 			
 | |
| 			// Interrupt the event processing thread
 | |
| 			
 | |
| 			try 
 | |
| 			{
 | |
| 				m_thread.interrupt();
 | |
| 			}
 | |
| 			catch ( Exception ex) 
 | |
| 			{
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Transaction processing hook 
 | |
| 	 */
 | |
| 	public void afterCommit() {
 | |
| 		
 | |
| 		// Get the node event that was stored in the transaction
 | |
| 		List<NodeEvent>events = TransactionalResourceHelper.getList( FileSysNodeEvent);
 | |
| 		for(NodeEvent event: events )
 | |
| 		{
 | |
| 		    // Queue the primary event for processing
 | |
| 		    m_eventQueue.addEvent(event);
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Post Commit Event queue processing
 | |
| 	 */
 | |
| 	public void run() {
 | |
| 		
 | |
| 		// Clear the shutdown flag
 | |
| 		
 | |
| 		m_shutdown = false;
 | |
| 		
 | |
|         // Use the system user as the authenticated context for the node monitor
 | |
|         
 | |
|         AuthenticationUtil.setRunAsUserSystem();
 | |
| 
 | |
| 		// Loop until shutdown
 | |
| 		
 | |
| 		while ( m_shutdown == false)
 | |
| 		{
 | |
| 			try
 | |
| 			{
 | |
| 	            // Wait for an event to process
 | |
| 				
 | |
| 			    final NodeEvent nodeEvent = m_eventQueue.removeEvent();
 | |
| 
 | |
| 			    if ( logger.isDebugEnabled())
 | |
|                 {
 | |
|                     logger.debug("Processing event " + nodeEvent);
 | |
|                 }
 | |
|                 
 | |
|                 // Check for a shutdown
 | |
|                 
 | |
|                 if ( m_shutdown == true)
 | |
|                     continue;
 | |
|                 
 | |
|                 RetryingTransactionCallback<Object> processEventCallback = new RetryingTransactionCallback<Object>()
 | |
|                 {
 | |
|                     public Object execute() throws Throwable
 | |
|                     {
 | |
|                         // Process the event
 | |
|                     	
 | |
|                         if (nodeEvent == null)
 | |
|                         {
 | |
|                             return null;
 | |
|                         }
 | |
|                         
 | |
|                         // check for a node delete
 | |
|                         
 | |
|                         if ( nodeEvent instanceof DeleteNodeEvent) 
 | |
|                         {
 | |
|                             // Node deleted   
 | |
|                             processDeleteNode((DeleteNodeEvent) nodeEvent);
 | |
|                         }
 | |
|          
 | |
|                         // Process the node event, for an existing node
 | |
|                         else if ( nodeEvent instanceof CreateNodeEvent) 
 | |
|                         {    
 | |
|                             // Node created
 | |
|                             processCreateNode((CreateNodeEvent) nodeEvent);
 | |
|                         }
 | |
|                         else if ( nodeEvent instanceof MoveNodeEvent) 
 | |
|                         {
 | |
|                             // Node moved        
 | |
|                             processMoveNode((MoveNodeEvent) nodeEvent);
 | |
|                         }
 | |
|                         else if ( nodeEvent instanceof LockNodeEvent) 
 | |
|                         {
 | |
|                             // Node locked/unlocked   
 | |
|                             processLockNode(( LockNodeEvent) nodeEvent);
 | |
|                         }
 | |
|                         
 | |
|                         // Done
 | |
|                         
 | |
|                         return null;
 | |
|                     }
 | |
|                 };
 | |
|                 
 | |
|                 // Execute in a read-only transaction
 | |
|                 
 | |
|                 m_transService.getRetryingTransactionHelper().doInTransaction(processEventCallback, true, true);
 | |
| 			}
 | |
| 			catch ( InterruptedException ex)
 | |
| 			{
 | |
| 			}
 | |
| 			catch (Throwable e)
 | |
| 			{
 | |
| 			    logger.error("Throwable in NodeMonitor thread", e);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Process a create node event
 | |
| 	 * 
 | |
| 	 * @param createEvent CreateNodeEvent
 | |
| 	 */
 | |
| 	private final void processCreateNode(CreateNodeEvent createEvent) {
 | |
| 	    
 | |
| 		// Get the full path to the file/folder node
 | |
| 	    String relPath = createEvent.getRelPath();
 | |
|         String name = createEvent.getName();
 | |
| 		
 | |
| 		// Check if the path is within the filesystem view
 | |
| 		
 | |
| 		if ( relPath.startsWith( m_rootPath)) {
 | |
| 			
 | |
| 			// DEBUG
 | |
| 			
 | |
| 			if ( logger.isDebugEnabled())
 | |
| 				logger.debug("CreateNode nodeRef=" + createEvent.getNodeRef() + ", fName=" + name + ", path=" + relPath);
 | |
| 			
 | |
| 			// Build the full file path
 | |
| 			
 | |
| 			StringBuilder fullPath = new StringBuilder();
 | |
| 			fullPath.append( relPath.substring( m_rootPath.length()));
 | |
| 			fullPath.append( "/");
 | |
| 			fullPath.append( name);
 | |
| 			
 | |
| 			relPath = fullPath.toString();
 | |
| 			
 | |
| 			// Update an existing file state to indicate that the file exists, may have been marked as deleted
 | |
| 			
 | |
| 			if ( m_stateTable != null) {
 | |
| 				
 | |
| 				// Check if there is file state for this file
 | |
| 				
 | |
| 				FileState fState = m_stateTable.findFileState( relPath);
 | |
| 				if ( fState != null && fState.exists() == false) {
 | |
| 
 | |
| 					// Check if the new node is a file or folder
 | |
| 					
 | |
| 					if ( createEvent.getFileType() == FileFolderServiceType.FILE)
 | |
| 						fState.setFileStatus(FileStatus.FileExists);
 | |
| 					else
 | |
| 						fState.setFileStatus(FileStatus.DirectoryExists);
 | |
| 					
 | |
| 					// DEBUG
 | |
| 					
 | |
| 					if ( logger.isDebugEnabled())
 | |
| 						logger.debug("CreateNode updated file state - " + fState);
 | |
| 				}
 | |
| 			}
 | |
| 			
 | |
| 			// If change notifications are enabled then send an event to registered listeners
 | |
| 			
 | |
| 			if ( m_filesysCtx.hasChangeHandler()) {
 | |
| 				
 | |
| 				// Check if there are any active notifications
 | |
| 				
 | |
| 				if ( m_filesysCtx.getChangeHandler().getGlobalNotifyMask() != 0) {
 | |
| 					
 | |
| 					// Send a file created event to the change notification handler
 | |
| 					
 | |
| 					if ( createEvent.getFileType() == FileFolderServiceType.FILE)
 | |
| 					    m_filesysCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionAdded, relPath);
 | |
| 					else
 | |
| 					    m_filesysCtx.getChangeHandler().notifyDirectoryChanged(NotifyChange.ActionAdded, relPath);
 | |
| 
 | |
| 					// DEBUG
 | |
| 					
 | |
| 					if ( logger.isDebugEnabled())
 | |
| 						logger.debug("CreateNode queued change notification");
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		else {
 | |
| 
 | |
| 			// DEBUG
 | |
| 			
 | |
| 			if ( logger.isDebugEnabled())
 | |
| 				logger.debug("CreateNode ignored nodeRef=" + createEvent.getNodeRef() + ", path=" + relPath);
 | |
| 		}
 | |
| 	
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Process a node delete event
 | |
| 	 * 
 | |
| 	 * @param deleteEvent DeleteNodeEvent
 | |
| 	 */
 | |
| 	private final void processDeleteNode(DeleteNodeEvent deleteEvent) {
 | |
| 
 | |
| 		// Check if the delete was confirmed
 | |
| 		
 | |
| //		if ( deleteEvent.hasDeleteConfirm() == false) {
 | |
| //			
 | |
| //			// DEBUG
 | |
| //			
 | |
| //			if ( logger.isDebugEnabled())
 | |
| //				logger.debug("DeleteNode not confirmed, nodeRef=" + deleteEvent.getNodeRef() + ", path=" + deleteEvent.getPath());
 | |
| //		
 | |
| //			return;
 | |
| //		}
 | |
| 		
 | |
| 		// Strip the root path
 | |
| 		
 | |
| 		String relPath = deleteEvent.getPath().substring( m_rootPath.length()).replace( '/', '\\');
 | |
| 		
 | |
| 		// DEBUG
 | |
| 		
 | |
| 		if ( logger.isDebugEnabled())
 | |
| 			logger.debug("DeleteNode nodeRef=" + deleteEvent.getNodeRef() + ", path=" + relPath);
 | |
| 		
 | |
| 		// Update an existing file state to indicate that the file does not exist
 | |
| 		
 | |
| 		if ( m_stateTable != null) {
 | |
| 			
 | |
| 			// Check if there is file state for this file
 | |
| 			
 | |
| 			FileState fState = m_stateTable.findFileState( relPath);
 | |
| 			if ( fState != null && fState.exists() == true) {
 | |
| 
 | |
| 				// Mark the file/folder as no longer existing
 | |
| 				
 | |
| 				fState.setFileStatus(FileStatus.NotExist);
 | |
| 				fState.setAllocationSize(0);
 | |
| 				fState.setOpenCount(0);
 | |
| 				
 | |
| 				// What about oplocks that are on the deleted file?
 | |
| 				
 | |
| 				// DEBUG
 | |
| 				
 | |
| 				if ( logger.isDebugEnabled())
 | |
| 					logger.debug("DeleteNode updated file state - " + fState);
 | |
| 			}
 | |
| 		}
 | |
| 		
 | |
| 		// If change notifications are enabled then send an event to registered listeners
 | |
| 		
 | |
| 		if ( m_filesysCtx.hasChangeHandler()) {
 | |
| 			
 | |
| 			// Check if there are any active notifications
 | |
| 			
 | |
| 			if ( m_filesysCtx.getChangeHandler().getGlobalNotifyMask() != 0) {
 | |
| 				
 | |
| 				// Send a file deleted event to the change notification handler
 | |
| 				
 | |
| 				if ( deleteEvent.getFileType() == FileFolderServiceType.FILE)
 | |
| 				    m_filesysCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionRemoved, relPath);
 | |
| 				else
 | |
| 				    m_filesysCtx.getChangeHandler().notifyDirectoryChanged(NotifyChange.ActionRemoved, relPath);
 | |
| 
 | |
| 				// DEBUG
 | |
| 				
 | |
| 				if ( logger.isDebugEnabled())
 | |
| 					logger.debug("DeleteNode queued change notification");
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Process a node move event
 | |
| 	 * 
 | |
| 	 * @param moveEvent MoveNodeEvent
 | |
| 	 */
 | |
| 	private final void processMoveNode(MoveNodeEvent moveEvent) {
 | |
| 
 | |
| 		// Strip the root path
 | |
| 		
 | |
| 		String fromPath = moveEvent.getFromPath().substring( m_rootPath.length()).replace( '/', '\\');
 | |
| 		String toPath =  moveEvent.getToPath().substring( m_rootPath.length()).replace( '/', '\\');
 | |
| 		
 | |
| 		// DEBUG
 | |
| 		
 | |
| 		if ( logger.isDebugEnabled())
 | |
| 			logger.debug("MoveNode fromPath=" + fromPath + ", toPath=" + toPath);
 | |
| 		
 | |
| 		// Update an existing file state to indicate that the file does not exist
 | |
| 		
 | |
| 		if ( m_stateTable != null) {
 | |
| 			
 | |
| 			// Check if there is file state for the orginal file/folder
 | |
| 			
 | |
| 			FileState fState = m_stateTable.findFileState( fromPath);
 | |
| 			if ( fState != null && fState.exists() == true) {
 | |
| 
 | |
| 				// Mark the file/folder as no longer existing
 | |
| 				
 | |
| 				fState.setFileStatus(FileStatus.NotExist);
 | |
| 				
 | |
| 				// DEBUG
 | |
| 				
 | |
| 				if ( logger.isDebugEnabled())
 | |
| 					logger.debug("MoveNode updated state for fromPath=" + fromPath);
 | |
| 			}
 | |
| 			
 | |
| 			// Check if there is a file state for the destination file/folder
 | |
| 			
 | |
| 			fState = m_stateTable.findFileState( toPath);
 | |
| 			if ( fState != null && fState.exists() == false) {
 | |
| 				
 | |
| 				// Indicate the the file or folder exists
 | |
| 				
 | |
| 				if ( moveEvent.getFileType() == FileFolderServiceType.FILE)
 | |
| 					fState.setFileStatus(FileStatus.FileExists);
 | |
| 				else
 | |
| 					fState.setFileStatus(FileStatus.DirectoryExists);
 | |
| 				
 | |
| 				// DEBUG
 | |
| 				
 | |
| 				if ( logger.isDebugEnabled())
 | |
| 					logger.debug("MoveNode updated state for toPath=" + toPath);
 | |
| 			}
 | |
| 		}
 | |
| 		
 | |
| 		// If change notifications are enabled then send an event to registered listeners
 | |
| 		
 | |
| 		if ( m_filesysCtx.hasChangeHandler()) {
 | |
| 			
 | |
| 			// Check if there are any active notifications
 | |
| 			
 | |
| 			if ( m_filesysCtx.getChangeHandler().getGlobalNotifyMask() != 0) {
 | |
| 				
 | |
| 				// Send a file renamed event to the change notification handler
 | |
| 
 | |
| 			    m_filesysCtx.getChangeHandler().notifyRename( fromPath, toPath);
 | |
| 
 | |
| 				// DEBUG
 | |
| 				
 | |
| 				if ( logger.isDebugEnabled())
 | |
| 					logger.debug("MoveNode queued change notification");
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Process a node lock/unlock event
 | |
| 	 * 
 | |
| 	 * @param lockEvent LockNodeEvent
 | |
| 	 */
 | |
| 	private final void processLockNode(LockNodeEvent lockEvent) {
 | |
| 		
 | |
| 		// Get the full path to the file/folder node
 | |
| 	    
 | |
| 	    String relPath = lockEvent.getRelPath();
 | |
| 		String name = lockEvent.getName();
 | |
| 	    
 | |
| 		// Check if the path is within the filesystem view
 | |
| 		
 | |
| 		if ( relPath.startsWith( m_rootPath)) {
 | |
| 			
 | |
| 			// DEBUG
 | |
| 			
 | |
| 			if ( logger.isDebugEnabled())
 | |
| 			{
 | |
| 				logger.debug("LockNode nodeRef=" + lockEvent.getNodeRef() + ", name=" + name + ", path=" + relPath);
 | |
| 			}
 | |
| 				
 | |
| 			// Build the full file path
 | |
| 			
 | |
| 			StringBuilder fullPath = new StringBuilder();
 | |
| 			fullPath.append( relPath.substring( m_rootPath.length()));
 | |
| 			fullPath.append( "/");
 | |
| 			fullPath.append( name);
 | |
| 			
 | |
| 			relPath = fullPath.toString().replace( '/', '\\');
 | |
| 			
 | |
| 			// Node has been locked or unlocked, send a change notification to indicate the file attributes have changed
 | |
| 
 | |
| 			if ( m_filesysCtx.hasChangeHandler()) {
 | |
| 				
 | |
| 				// Send out a change of attributes notification
 | |
| 				
 | |
| 			    m_filesysCtx.getChangeHandler().notifyAttributesChanged( relPath, lockEvent.getFileType() == FileFolderServiceType.FILE ? false : true);
 | |
| 
 | |
| 				// DEBUG
 | |
| 				
 | |
| 				if ( logger.isDebugEnabled())
 | |
| 					logger.debug("LockNode queued change notification");
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 |