Added monitoring of node service to generate CIFS change notifications and update file server state cache, ALFCOM-1205. Should also fix ALFCOM-1021, ALFCOM-1052 and JLAN-9.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@10287 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gary Spencer
2008-08-07 10:17:19 +00:00
parent 73d195b65e
commit 12f45da03a
10 changed files with 1561 additions and 1 deletions

View File

@@ -26,8 +26,11 @@ package org.alfresco.filesys.repo;
import org.alfresco.filesys.alfresco.AlfrescoContext; import org.alfresco.filesys.alfresco.AlfrescoContext;
import org.alfresco.filesys.alfresco.IOControlHandler; import org.alfresco.filesys.alfresco.IOControlHandler;
import org.alfresco.jlan.server.core.DeviceContextException;
import org.alfresco.jlan.server.filesys.DiskInterface; import org.alfresco.jlan.server.filesys.DiskInterface;
import org.alfresco.jlan.server.filesys.DiskSharedDevice;
import org.alfresco.jlan.server.filesys.FileSystem; import org.alfresco.jlan.server.filesys.FileSystem;
import org.alfresco.jlan.smb.server.notify.NotifyChangeHandler;
import org.alfresco.service.cmr.repository.*; import org.alfresco.service.cmr.repository.*;
/** /**
@@ -48,6 +51,10 @@ public class ContentContext extends AlfrescoContext
private NodeRef m_rootNodeRef; private NodeRef m_rootNodeRef;
// Node monitor
private NodeMonitor m_nodeMonitor;
/** /**
* Class constructor * Class constructor
* *
@@ -115,6 +122,11 @@ public class ContentContext extends AlfrescoContext
*/ */
public void CloseContext() { public void CloseContext() {
// Stop the node monitor, if enabled
if ( m_nodeMonitor != null)
m_nodeMonitor.shutdownRequest();
// Call the base class // Call the base class
super.CloseContext(); super.CloseContext();
@@ -130,4 +142,32 @@ public class ContentContext extends AlfrescoContext
{ {
return new ContentIOControlHandler(); return new ContentIOControlHandler();
} }
/**
* Create the node monitor
*
* @param filesysDriver ContentDiskDriver
*/
protected void createNodeMonitor( ContentDiskDriver filesysDriver) {
m_nodeMonitor = new NodeMonitor( filesysDriver, this);
}
/**
* Start the filesystem
*
* @param share DiskSharedDevice
* @exception DeviceContextException
*/
public void startFilesystem(DiskSharedDevice share)
throws DeviceContextException {
// Call the base class
super.startFilesystem(share);
// Start the node monitor, if enabled
if ( m_nodeMonitor != null)
m_nodeMonitor.startMonitor();
}
} }

View File

@@ -67,6 +67,7 @@ import org.alfresco.jlan.smb.server.SMBServer;
import org.alfresco.jlan.smb.server.SMBSrvSession; import org.alfresco.jlan.smb.server.SMBSrvSession;
import org.alfresco.jlan.util.WildCard; import org.alfresco.jlan.util.WildCard;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.service.cmr.lock.NodeLockedException; import org.alfresco.service.cmr.lock.NodeLockedException;
import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileFolderService;
@@ -120,6 +121,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
private AuthenticationComponent authComponent; private AuthenticationComponent authComponent;
private AuthenticationService authService; private AuthenticationService authService;
private PolicyComponent policyComponent;
// Lock manager // Lock manager
private static LockManager _lockManager = new FileStateLockManager(); private static LockManager _lockManager = new FileStateLockManager();
@@ -154,6 +157,15 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
return authService; return authService;
} }
/**
* Return the authentication component
*
* @return AuthenticationComponent
*/
public final AuthenticationComponent getAuthComponent() {
return authComponent;
}
/** /**
* Return the node service * Return the node service
* *
@@ -193,6 +205,33 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
return this.searchService; return this.searchService;
} }
/**
* Return the file folder service
*
* @return FileFolderService
*/
public final FileFolderService getFileFolderService() {
return this.fileFolderService;
}
/**
* Return the permission service
*
* @return PermissionService
*/
public final PermissionService getPermissionService() {
return this.permissionService;
}
/**
* Return the policy component
*
* @return PolicyComponent
*/
public final PolicyComponent getPolicyComponent() {
return this.policyComponent;
}
/** /**
* @param contentService the content service * @param contentService the content service
*/ */
@@ -273,6 +312,15 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
this.mimetypeService = mimetypeService; this.mimetypeService = mimetypeService;
} }
/**
* Set the policy component
*
* @param policyComponent PolicyComponent
*/
public void setPolicyComponent(PolicyComponent policyComponent) {
this.policyComponent = policyComponent;
}
/** /**
* Parse and validate the parameter string and create a device context object for this instance * Parse and validate the parameter string and create a device context object for this instance
* of the shared device. The same DeviceInterface implementation may be used for multiple * of the shared device. The same DeviceInterface implementation may be used for multiple
@@ -498,6 +546,15 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
if ( context.hasIOHandler()) if ( context.hasIOHandler())
context.getIOHandler().initialize( this, context); context.getIOHandler().initialize( this, context);
// Install the node service monitor
if ( cfg.getChild("disableNodeMonitor") == null) {
// Create the node monitor
context.createNodeMonitor( this);
}
// Return the context for this shared filesystem // Return the context for this shared filesystem
return context; return context;

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2006-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.filesys.repo;
import org.alfresco.service.cmr.model.FileFolderServiceType;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Create Node Event Class
*
* @author gkspencer
*/
public class CreateNodeEvent extends NodeEvent {
/**
* Class constructor
*
* @param fType FileFolderServiceTtype
* @param nodeRef NodeRef
*/
public CreateNodeEvent( FileFolderServiceType fType, NodeRef nodeRef) {
super( fType, nodeRef);
}
/**
* Return the node event as a string
*
* @return String
*/
public String toString() {
StringBuilder str = new StringBuilder();
str.append("[Create:fType=");
str.append(getFileType());
str.append(",nodeRef=");
str.append(getNodeRef());
str.append("]");
return str.toString();
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2006-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.filesys.repo;
import org.alfresco.service.cmr.model.FileFolderServiceType;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Delete Node Event Class
*
* @author gkspencer
*/
public class DeleteNodeEvent extends NodeEvent {
// Deleted node path and confirmation
private String m_path;
private boolean m_deleteConfirm;
/**
* Class constructor
*
* @param fType FileFolderServiceTtype
* @param nodeRef NodeRef
* @param path String
*/
public DeleteNodeEvent( FileFolderServiceType fType, NodeRef nodeRef, String path) {
super( fType, nodeRef);
m_path = path;
}
/**
* Return the relative path of the target node
*
* @return String
*/
public final String getPath() {
return m_path;
}
/**
* Check if the delete confirm flag is set
*
* @return boolean
*/
public final boolean hasDeleteConfirm() {
return m_deleteConfirm;
}
/**
* Set/clear the delete confirm flag
*
* @param delConfirm boolean
*/
public final void setDeleteConfirm( boolean delConfirm) {
m_deleteConfirm = delConfirm;
}
/**
* Return the node event as a string
*
* @return String
*/
public String toString() {
StringBuilder str = new StringBuilder();
str.append("[Delete:fType=");
str.append(getFileType());
str.append(",nodeRef=");
str.append(getNodeRef());
str.append(",path=");
str.append(getPath());
str.append(",confirm=");
str.append(hasDeleteConfirm());
str.append("]");
return str.toString();
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2006-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.filesys.repo;
import org.alfresco.service.cmr.model.FileFolderServiceType;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Lock Node Event Class
*
* @author gkspencer
*/
public class LockNodeEvent extends NodeEvent {
// Before and after lock types
private String m_lockBefore;
private String m_lockAfter;
/**
* Class constructor
*
* @param fType FileFolderServiceTtype
* @param nodeRef NodeRef
* @param lockBefore String
* @param lockAfter String
*/
public LockNodeEvent( FileFolderServiceType fType, NodeRef nodeRef, String lockBefore, String lockAfter) {
super( fType, nodeRef);
m_lockAfter = lockAfter;
m_lockBefore = lockBefore;
}
/**
* Return the previous type
*
* @return String
*/
public final String getBeforeLockType() {
return m_lockBefore;
}
/**
* Return the new lock type
*
* @return String
*/
public final String getAfterLockType() {
return m_lockAfter;
}
/**
* Return the node event as a string
*
* @return String
*/
public String toString() {
StringBuilder str = new StringBuilder();
str.append("[Lock:fType=");
str.append(getFileType());
str.append(",nodeRef=");
str.append(getNodeRef());
str.append(",lockBefore=");
str.append(getBeforeLockType());
str.append(",lockAfter=");
str.append(getAfterLockType());
str.append("]");
return str.toString();
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright (C) 2006-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.filesys.repo;
import org.alfresco.service.cmr.model.FileFolderServiceType;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Move Node Event Class
*
* @author gkspencer
*/
public class MoveNodeEvent extends NodeEvent {
// Moved node path and destination node
private String m_path;
private NodeRef m_moveToNode;
/**
* Class constructor
*
* @param fType FileFolderServiceTtype
* @param nodeRef NodeRef
* @param fromPath String
* @param toNodeRef NodeRef
*/
public MoveNodeEvent( FileFolderServiceType fType, NodeRef nodeRef, String fromPath, NodeRef toNodeRef) {
super( fType, nodeRef);
m_path = fromPath;
m_moveToNode = toNodeRef;
}
/**
* Return the relative path of the target node
*
* @return String
*/
public final String getPath() {
return m_path;
}
/**
* Return the target node for a move
*
* @return NodeRef
*/
public final NodeRef getMoveToNodeRef() {
return m_moveToNode;
}
/**
* Return the node event as a string
*
* @return String
*/
public String toString() {
StringBuilder str = new StringBuilder();
str.append("[Move:fType=");
str.append(getFileType());
str.append(",nodeRef=");
str.append(getNodeRef());
str.append(",path=");
str.append(getPath());
str.append(",toNodeRef=");
str.append(getMoveToNodeRef());
str.append("]");
return str.toString();
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2006-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.filesys.repo;
import org.alfresco.service.cmr.model.FileFolderServiceType;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Node Event Base Class
*
* <p>Contains the details of a file/folder node event to be processed by a node monitor thread.
*
* @author gkspencer
*/
public class NodeEvent {
// Target node
private NodeRef m_nodeRef;
// File/folder node type
private FileFolderServiceType m_fileType;
/**
* Class constructor
*
* @param fType FileFolderServiceTtype
* @param nodeRef NodeRef
*/
protected NodeEvent( FileFolderServiceType fType, NodeRef nodeRef) {
m_fileType = fType;
m_nodeRef = nodeRef;
}
/**
* Return the target node
*
* @return NodeRef
*/
public final NodeRef getNodeRef() {
return m_nodeRef;
}
/**
* Return the node file/folder type
*
* @return FileFolderServiceType
*/
public final FileFolderServiceType getFileType() {
return m_fileType;
}
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright (C) 2006-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.filesys.repo;
import java.util.LinkedList;
/**
* Node Event Queue Class
*
* @author gkspencer
*/
public class NodeEventQueue {
// List of node events
private LinkedList<NodeEvent> m_queue;
/**
* Class constructor
*/
public NodeEventQueue() {
m_queue = new LinkedList<NodeEvent>();
}
/**
* Return the number of events in the queue
*
* @return int
*/
public final synchronized int numberOfEvents() {
return m_queue.size();
}
/**
* Add an event to the queue
*
* @param evetn NodeEvent
*/
public final synchronized void addEvent(NodeEvent event) {
// Add the event to the queue
m_queue.add( event);
// Notify a listener that there is an event to process
notify();
}
/**
* Remove an event from the head of the queue
*
* @return NodeEvent
* @exception InterruptedException
*/
public final synchronized NodeEvent removeEvent()
throws InterruptedException {
// Wait until there is an event
waitWhileEmpty();
// Get the event from the head of the queue
return m_queue.removeFirst();
}
/**
* Remove an event from the queue, without waiting if there are no events in the queue
*
* @return NodeEvent
*/
public final synchronized NodeEvent removeSessionNoWait() {
NodeEvent event = null;
if ( m_queue.size() > 0)
event = m_queue.removeFirst();
return event;
}
/**
* Wait for an event to be added to the queue
*
* @exception InterruptedException
*/
public final synchronized void waitWhileEmpty()
throws InterruptedException {
// Wait until an event arrives on the queue
while (m_queue.size() == 0)
wait();
}
/**
* Wait for the event queue to be emptied
*
* @exception InterruptedException
*/
public final synchronized void waitUntilEmpty()
throws InterruptedException {
// Wait until the event queue is empty
while (m_queue.size() != 0)
wait();
}
}

View File

@@ -0,0 +1,892 @@
/*
* Copyright (C) 2006-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.filesys.repo;
import java.io.Serializable;
import java.util.Map;
import javax.transaction.UserTransaction;
import org.alfresco.filesys.state.FileState;
import org.alfresco.filesys.state.FileStateTable;
import org.alfresco.jlan.server.filesys.FileStatus;
import org.alfresco.jlan.server.filesys.NotifyChange;
import org.alfresco.jlan.smb.server.notify.NotifyChangeHandler;
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.AuthenticationComponent;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionListenerAdapter;
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.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
private ContentDiskDriver m_filesysDriver;
private ContentContext m_filesysCtx;
// File state table and change notification handler
private FileStateTable m_stateTable;
private NotifyChangeHandler m_changeHandler;
// 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 filesysDriver ContentDiskDriver
* @param filesysCtx ContentContext
*/
public NodeMonitor( ContentDiskDriver filesysDriver, ContentContext filesysCtx) {
m_filesysDriver = filesysDriver;
m_filesysCtx = filesysCtx;
// Initialize the node monitor
init();
}
/**
* Initialize the node monitor
*/
public final void init() {
// Get various services via the filesystem driver
m_nodeService = m_filesysDriver.getNodeService();
m_policyComponent = m_filesysDriver.getPolicyComponent();
m_fileFolderService = m_filesysDriver.getFileFolderService();
m_permissionService = m_filesysDriver.getPermissionService();
m_transService = m_filesysDriver.getTransactionService();
// 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, "onDeleteNode"),
this, new JavaBehaviour(this, "onDeleteNode"));
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
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.getStateTable();
m_changeHandler = m_filesysCtx.getChangeHandler();
// 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)
return;
QName nodeType = m_nodeService.getType( nodeRef);
FileFolderServiceType fType = m_fileFolderService.getType( nodeType);
if ( fType != FileFolderServiceType.INVALID) {
// DEBUG
if ( logger.isDebugEnabled()) {
// 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);
logger.debug("OnCreateNode: nodeRef=" + nodeRef + ", name=" + fName + ", path=" + nodePath.toDisplayPath(m_nodeService, m_permissionService));
}
// Create an event to process the node creation
NodeEvent nodeEvent = new CreateNodeEvent( fType, nodeRef);
// Store the event in the transaction until committed, and register the transaction listener
AlfrescoTransactionSupport.bindListener( this);
AlfrescoTransactionSupport.bindResource( FileSysNodeEvent, nodeEvent);
}
}
/**
* Update properties event
*
* @param nodeRef NodeRef
* @param before Map<QName, Serializable>
* @param after Map<QName, Serializable>
*/
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
NodeEvent nodeEvent = null;
String beforeLock = (String) before.get( ContentModel.PROP_LOCK_TYPE);
String afterLock = (String) after.get( ContentModel.PROP_LOCK_TYPE);
if (( beforeLock != null && afterLock == null) ||
( beforeLock == null && afterLock != null)) {
// Process the update
nodeEvent = new LockNodeEvent( fType, nodeRef, beforeLock, afterLock);
}
// Check if a node event has been created
if ( nodeEvent != null) {
// Check for an existing event
String eventKey = FileSysNodeEvent;
if ( AlfrescoTransactionSupport.getResource( FileSysNodeEvent) != null)
eventKey = FileSysNodeEvent2;
// Store the event in the transaction until committed, and register the transaction listener
AlfrescoTransactionSupport.bindListener( this);
AlfrescoTransactionSupport.bindResource( eventKey, nodeEvent);
}
}
}
/**
* Delete node event
*
* @param childAssocRef ChildAssociationRef
* @param isArchiveNode boolean
*/
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isArchivedNode) {
// Check if there is a node event stored in the transaction
NodeEvent nodeEvent = (NodeEvent) AlfrescoTransactionSupport.getResource( FileSysNodeEvent);
if ( nodeEvent != null && nodeEvent instanceof DeleteNodeEvent) {
// Should be the same node id
DeleteNodeEvent deleteEvent = (DeleteNodeEvent) nodeEvent;
NodeRef nodeRef = childAssocRef.getChildRef();
if ( nodeRef.equals( deleteEvent.getNodeRef())) {
// Confirm the node delete
deleteEvent.setDeleteConfirm( true);
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("OnDeleteNode: confirm delete nodeRef=" + nodeRef);
}
}
}
/**
* 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 nodeRef = oldChildAssocRef.getChildRef();
if ( nodeRef.getStoreRef().equals( m_storeRef) == false)
return;
QName nodeType = m_nodeService.getType( nodeRef);
FileFolderServiceType fType = m_fileFolderService.getType( nodeType);
if ( fType != FileFolderServiceType.INVALID) {
// 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 pathStr = new StringBuilder();
pathStr.append( nodePath.toDisplayPath(m_nodeService, m_permissionService));
if ( pathStr.charAt(pathStr.length() - 1) != '/' && pathStr.charAt(pathStr.length() - 1) != '\\')
pathStr.append("\\");
pathStr.append( fName);
String relPath = pathStr.toString();
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("OnMoveNode: nodeRef=" + nodeRef + ", relPath=" + relPath);
// Queue an event to process the node move
if ( relPath.startsWith( m_rootPath)) {
// Create a move event
NodeEvent nodeEvent = new MoveNodeEvent( fType, nodeRef, relPath, newChildAssocRef.getChildRef());
// Store the event in the transaction until committed, and register the transaction listener
AlfrescoTransactionSupport.bindListener( this);
AlfrescoTransactionSupport.bindResource( FileSysNodeEvent, 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) {
// 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 pathStr = new StringBuilder();
pathStr.append( nodePath.toDisplayPath(m_nodeService, m_permissionService));
if ( pathStr.charAt(pathStr.length() - 1) != '/' && pathStr.charAt(pathStr.length() - 1) != '\\')
pathStr.append("\\");
pathStr.append( fName);
String relPath = 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);
// Store the event in the transaction until committed, and register the transaction listener
AlfrescoTransactionSupport.bindListener( this);
AlfrescoTransactionSupport.bindResource( FileSysNodeEvent, nodeEvent);
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("BeforeDeleteNode: nodeRef=" + nodeRef + ", relPath=" + relPath);
}
}
}
/**
* 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
NodeEvent nodeEvent = (NodeEvent) AlfrescoTransactionSupport.getResource( FileSysNodeEvent);
if ( nodeEvent != null) {
// Queue the primary event for processing
m_eventQueue.addEvent( nodeEvent);
// Check for a secondary event
nodeEvent = (NodeEvent) AlfrescoTransactionSupport.getResource(FileSysNodeEvent2);
if ( nodeEvent != null)
m_eventQueue.addEvent( nodeEvent);
}
}
/**
* 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
AuthenticationComponent authComponent = m_filesysDriver.getAuthComponent();
authComponent.setCurrentUser( authComponent.getSystemUserName());
// Loop until shutdown
NodeEvent nodeEvent = null;
UserTransaction tx = null;
while ( m_shutdown == false) {
// Wait for an event to process
try {
nodeEvent = m_eventQueue.removeEvent();
}
catch ( InterruptedException ex) {
}
// Check for a shutdown
if ( m_shutdown == true)
continue;
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Processing event " + nodeEvent);
// Create a transaction
tx = m_transService.getUserTransaction( true);
try {
// Start the transaction
tx.begin();
// Process the event
if ( nodeEvent instanceof CreateNodeEvent) {
// Node created
processCreateNode((CreateNodeEvent) nodeEvent);
}
else if ( nodeEvent instanceof DeleteNodeEvent) {
// Node deleted
processDeleteNode((DeleteNodeEvent) nodeEvent);
}
else if ( nodeEvent instanceof MoveNodeEvent) {
// Node moved
processMoveNode((MoveNodeEvent) nodeEvent);
}
else if ( nodeEvent instanceof LockNodeEvent) {
// Node locked/unlocked
processLockNode(( LockNodeEvent) nodeEvent);
}
// Commit the transaction
tx.commit();
tx = null;
}
catch ( Exception ex) {
logger.error( ex);
}
finally
{
// If there is an active transaction then roll it back
if ( tx != null)
{
try
{
tx.rollback();
}
catch (Exception ex)
{
logger.warn("Failed to rollback transaction", ex);
}
}
}
}
}
/**
* Process a create node event
*
* @param createEvent CreateNodeEvent
*/
private final void processCreateNode(NodeEvent createEvent) {
// Get the full path to the file/folder node
Path nodePath = m_nodeService.getPath( createEvent.getNodeRef());
String relPath = nodePath.toDisplayPath(m_nodeService, m_permissionService);
String fName = (String) m_nodeService.getProperty( createEvent.getNodeRef(), ContentModel.PROP_NAME);
// 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=" + fName + ", path=" + relPath);
// Build the full file path
StringBuilder fullPath = new StringBuilder();
fullPath.append( relPath.substring( m_rootPath.length()));
fullPath.append( "/");
fullPath.append( fName);
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_changeHandler != null) {
// Check if there are any active notifications
if ( m_changeHandler.getGlobalNotifyMask() != 0) {
// Send a file created event to the change notification handler
if ( createEvent.getFileType() == FileFolderServiceType.FILE)
m_changeHandler.notifyFileChanged(NotifyChange.ActionAdded, relPath);
else
m_changeHandler.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);
// 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_changeHandler != null) {
// Check if there are any active notifications
if ( m_changeHandler.getGlobalNotifyMask() != 0) {
// Send a file created event to the change notification handler
if ( deleteEvent.getFileType() == FileFolderServiceType.FILE)
m_changeHandler.notifyFileChanged(NotifyChange.ActionRemoved, relPath);
else
m_changeHandler.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.getPath().substring( m_rootPath.length()).replace( '/', '\\');
// Get the destination relative path
Path nodePath = m_nodeService.getPath( moveEvent.getMoveToNodeRef());
String fName = (String) m_nodeService.getProperty( moveEvent.getMoveToNodeRef(), ContentModel.PROP_NAME);
// Build the share relative path to the destination
StringBuilder pathStr = new StringBuilder();
pathStr.append( nodePath.toDisplayPath(m_nodeService, m_permissionService));
if ( pathStr.charAt(pathStr.length() - 1) != '/' && pathStr.charAt(pathStr.length() - 1) != '\\')
pathStr.append("\\");
pathStr.append( fName);
String toPath = pathStr.toString().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_changeHandler != null) {
// Check if there are any active notifications
if ( m_changeHandler.getGlobalNotifyMask() != 0) {
// Send a file renamed event to the change notification handler
m_changeHandler.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
Path nodePath = m_nodeService.getPath( lockEvent.getNodeRef());
String relPath = nodePath.toDisplayPath(m_nodeService, m_permissionService);
String fName = (String) m_nodeService.getProperty( lockEvent.getNodeRef(), ContentModel.PROP_NAME);
// Check if the path is within the filesystem view
if ( relPath.startsWith( m_rootPath)) {
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("LockNode nodeRef=" + lockEvent.getNodeRef() + ", fName=" + fName + ", path=" + relPath);
// Build the full file path
StringBuilder fullPath = new StringBuilder();
fullPath.append( relPath.substring( m_rootPath.length()));
fullPath.append( "/");
fullPath.append( fName);
relPath = fullPath.toString().replace( '/', '\\');
// Node has been locked or unlocked, send a change notification to indicate the file attributes have changed
if ( m_changeHandler != null) {
// Send out a change of attributes notification
m_changeHandler.notifyAttributesChanged( relPath, lockEvent.getFileType() == FileFolderServiceType.FILE ? false : true);
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("LockNode queued change notification");
}
}
}
}

View File

@@ -114,7 +114,7 @@ public class CheckInOutDesktopAction extends DesktopAction {
// Check if there are any file/directory change notify requests active // Check if there are any file/directory change notify requests active
if ( getContext().hasChangeHandler()) { if ( getContext().hasFileServerNotifications()) {
// Build the relative path to the checked in file // Build the relative path to the checked in file