mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Fix for CIFS/CheckInOut.exe save of working copy breaks lock on original file. ALF-2028.
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@19784 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -65,6 +65,7 @@
|
|||||||
<property name="stateReaper"><ref bean="fileStateReaper"/></property>
|
<property name="stateReaper"><ref bean="fileStateReaper"/></property>
|
||||||
<property name="nodeMonitorFactory"><ref bean="nodeMonitorFactory"/></property>
|
<property name="nodeMonitorFactory"><ref bean="nodeMonitorFactory"/></property>
|
||||||
<property name="nodeArchiveService"><ref bean="nodeArchiveService" /></property>
|
<property name="nodeArchiveService"><ref bean="nodeArchiveService" /></property>
|
||||||
|
<property name="lockService"><ref bean="lockService" /></property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="nodeMonitorFactory" class="org.alfresco.filesys.repo.NodeMonitorFactory">
|
<bean id="nodeMonitorFactory" class="org.alfresco.filesys.repo.NodeMonitorFactory">
|
||||||
|
@@ -20,9 +20,12 @@ package org.alfresco.filesys.repo;
|
|||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.transaction.UserTransaction;
|
import javax.transaction.UserTransaction;
|
||||||
|
|
||||||
@@ -69,6 +72,8 @@ import org.alfresco.repo.admin.SysAdminParams;
|
|||||||
import org.alfresco.repo.node.archive.NodeArchiveService;
|
import org.alfresco.repo.node.archive.NodeArchiveService;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationContext;
|
import org.alfresco.repo.security.authentication.AuthenticationContext;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
|
import org.alfresco.service.cmr.lock.LockService;
|
||||||
|
import org.alfresco.service.cmr.lock.LockType;
|
||||||
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;
|
||||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||||
@@ -84,6 +89,7 @@ import org.alfresco.service.cmr.security.AccessStatus;
|
|||||||
import org.alfresco.service.cmr.security.AuthenticationService;
|
import org.alfresco.service.cmr.security.AuthenticationService;
|
||||||
import org.alfresco.service.cmr.security.PermissionService;
|
import org.alfresco.service.cmr.security.PermissionService;
|
||||||
import org.alfresco.service.namespace.NamespaceService;
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.extensions.config.ConfigElement;
|
import org.springframework.extensions.config.ConfigElement;
|
||||||
@@ -118,6 +124,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
|||||||
private PermissionService permissionService;
|
private PermissionService permissionService;
|
||||||
private FileFolderService fileFolderService;
|
private FileFolderService fileFolderService;
|
||||||
private NodeArchiveService nodeArchiveService;
|
private NodeArchiveService nodeArchiveService;
|
||||||
|
private LockService lockService;
|
||||||
|
|
||||||
private AuthenticationContext authContext;
|
private AuthenticationContext authContext;
|
||||||
private AuthenticationService authService;
|
private AuthenticationService authService;
|
||||||
@@ -236,6 +243,15 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
|||||||
return nodeArchiveService;
|
return nodeArchiveService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the lock service
|
||||||
|
*
|
||||||
|
* @return LockService
|
||||||
|
*/
|
||||||
|
public final LockService getLockService() {
|
||||||
|
return lockService;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param contentService the content service
|
* @param contentService the content service
|
||||||
*/
|
*/
|
||||||
@@ -345,6 +361,15 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
|||||||
this.nodeArchiveService = nodeArchiveService;
|
this.nodeArchiveService = nodeArchiveService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the lock service
|
||||||
|
*
|
||||||
|
* @param lockService LockService
|
||||||
|
*/
|
||||||
|
public void setLockService(LockService lockService) {
|
||||||
|
this.lockService = lockService;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
@@ -2327,7 +2352,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
|||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
|
|
||||||
if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
|
if (logger.isDebugEnabled() && (ctx.hasDebug(AlfrescoContext.DBG_FILE) || ctx.hasDebug(AlfrescoContext.DBG_RENAME)))
|
||||||
logger.debug("Closed file: network file=" + file + " delete on close=" + file.hasDeleteOnClose());
|
logger.debug("Closed file: network file=" + file + " delete on close=" + file.hasDeleteOnClose());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2406,7 +2431,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
|||||||
|
|
||||||
// Debug
|
// Debug
|
||||||
|
|
||||||
if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
|
if (logger.isDebugEnabled() && (ctx.hasDebug(AlfrescoContext.DBG_FILE) || ctx.hasDebug(AlfrescoContext.DBG_RENAME)))
|
||||||
logger.debug("Deleted file: " + name + ", node=" + nodeRef);
|
logger.debug("Deleted file: " + name + ", node=" + nodeRef);
|
||||||
}
|
}
|
||||||
catch (NodeLockedException ex)
|
catch (NodeLockedException ex)
|
||||||
@@ -2554,9 +2579,9 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
|||||||
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
||||||
logger.debug(" Using renamed node, " + newState);
|
logger.debug(" Using renamed node, " + newState);
|
||||||
|
|
||||||
// Use the renamed node as the target
|
// Use the renamed node to clone aspects/state
|
||||||
|
|
||||||
targetNodeRef = newState.getNodeRef();
|
cloneNodeAspects( name, newState.getNodeRef(), nodeToMoveRef, ctx);
|
||||||
}
|
}
|
||||||
else if ( newState.getFileStatus() == FileStateStatus.DeleteOnClose) {
|
else if ( newState.getFileStatus() == FileStateStatus.DeleteOnClose) {
|
||||||
|
|
||||||
@@ -2572,7 +2597,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
|||||||
// DEBUG
|
// DEBUG
|
||||||
|
|
||||||
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
||||||
logger.debug(" Found archived node + " + archivedNode);
|
logger.debug(" Found archived node " + archivedNode);
|
||||||
|
|
||||||
if ( archivedNode != null && getNodeService().exists( archivedNode))
|
if ( archivedNode != null && getNodeService().exists( archivedNode))
|
||||||
{
|
{
|
||||||
@@ -2584,6 +2609,34 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
|||||||
|
|
||||||
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
||||||
logger.debug(" Restored node " + targetNodeRef);
|
logger.debug(" Restored node " + targetNodeRef);
|
||||||
|
|
||||||
|
// Check if the deleted file had a linked node, due to a rename
|
||||||
|
|
||||||
|
if ( newState.hasLinkNode() && nodeService.exists( newState.getLinkNode())) {
|
||||||
|
|
||||||
|
// Clone aspects from the linked node onto the restored node
|
||||||
|
|
||||||
|
cloneNodeAspects( name, newState.getLinkNode(), targetNodeRef, ctx);
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
|
||||||
|
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) {
|
||||||
|
logger.debug(" Moved aspects from linked node " + newState.getLinkNode());
|
||||||
|
|
||||||
|
// Check if the node is a working copy
|
||||||
|
|
||||||
|
if ( nodeService.hasAspect( targetNodeRef, ContentModel.ASPECT_WORKING_COPY)) {
|
||||||
|
|
||||||
|
// Check if the main document is still locked
|
||||||
|
|
||||||
|
NodeRef mainNodeRef = (NodeRef) nodeService.getProperty( targetNodeRef, ContentModel.PROP_COPY_REFERENCE);
|
||||||
|
if ( mainNodeRef != null) {
|
||||||
|
LockType lockTyp = lockService.getLockType( mainNodeRef);
|
||||||
|
logger.debug(" Main node ref lock type = " + lockTyp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2600,21 +2653,9 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
|||||||
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
||||||
logger.debug(" Created new node for " + newName);
|
logger.debug(" Created new node for " + newName);
|
||||||
|
|
||||||
// Check if the new file name is a temporary file name
|
// Copy aspects from the original file
|
||||||
|
|
||||||
String newNameNorm = newName.toLowerCase();
|
cloneNodeAspects( name, nodeToMoveRef, targetNodeRef, ctx);
|
||||||
|
|
||||||
if ( newNameNorm.endsWith(".tmp") || newNameNorm.endsWith(".temp")) {
|
|
||||||
|
|
||||||
// Add the temporary aspect, also prevents versioning
|
|
||||||
|
|
||||||
nodeService.addAspect(targetNodeRef, ContentModel.ASPECT_TEMPORARY, null);
|
|
||||||
|
|
||||||
// DEBUG
|
|
||||||
|
|
||||||
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
|
||||||
logger.debug(" Added Temporary aspect to renamed file " + newName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2627,14 +2668,25 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
|||||||
|
|
||||||
cifsHelper.rename(nodeToMoveRef, name);
|
cifsHelper.rename(nodeToMoveRef, name);
|
||||||
|
|
||||||
// Remove the file state for the old file name
|
// Mark the new file as existing
|
||||||
|
|
||||||
ctx.getStateTable().renameFileState(newName, oldState);
|
newState.setFileStatus( FileStatus.FileExists);
|
||||||
|
newState.setNodeRef( nodeToMoveRef);
|
||||||
|
|
||||||
|
// Make sure the old file state is cached for a short while, the file may not be open so the
|
||||||
|
// file state could be expired
|
||||||
|
|
||||||
|
oldState.setExpiryTime(System.currentTimeMillis() + FileState.DeleteTimeout);
|
||||||
|
|
||||||
|
// Indicate that this is a renamed file state, set the node ref of the file that was renamed
|
||||||
|
|
||||||
|
oldState.setFileStatus(FileStateStatus.Renamed);
|
||||||
|
oldState.setNodeRef(nodeToMoveRef);
|
||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
|
|
||||||
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
||||||
logger.debug(" User standard rename for " + name + "(versionable=" + isFromVersionable + ", targetNodeRef=" + targetNodeRef + ")");
|
logger.debug(" Use standard rename for " + name + "(versionable=" + isFromVersionable + ", targetNodeRef=" + targetNodeRef + ")");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
@@ -2675,6 +2727,10 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
|||||||
oldState.setFileStatus(FileStateStatus.DeleteOnClose);
|
oldState.setFileStatus(FileStateStatus.DeleteOnClose);
|
||||||
oldState.setNodeRef(nodeToMoveRef);
|
oldState.setNodeRef(nodeToMoveRef);
|
||||||
|
|
||||||
|
// Link to the new node, a new file may be renamed into place, we need to transfer aspect/locks
|
||||||
|
|
||||||
|
oldState.setLinkNode( targetNodeRef);
|
||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
|
|
||||||
if ( logger.isDebugEnabled() && ctx.hasDebug( AlfrescoContext.DBG_RENAME))
|
if ( logger.isDebugEnabled() && ctx.hasDebug( AlfrescoContext.DBG_RENAME))
|
||||||
@@ -3228,4 +3284,111 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
|||||||
throw new IOException("Failed to copy content");
|
throw new IOException("Failed to copy content");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone/move aspects/properties between nodes
|
||||||
|
*
|
||||||
|
* @param newName String
|
||||||
|
* @param fromNode NodeRef
|
||||||
|
* @param toNode NodeRef
|
||||||
|
* @param ctx ContentContext
|
||||||
|
*/
|
||||||
|
private void cloneNodeAspects( String newName, NodeRef fromNode, NodeRef toNode, ContentContext ctx)
|
||||||
|
{
|
||||||
|
// We need to remove various aspects/properties from the original file, and move them to the new file
|
||||||
|
//
|
||||||
|
// Check for the lockable aspect
|
||||||
|
|
||||||
|
if ( nodeService.hasAspect( fromNode, ContentModel.ASPECT_LOCKABLE)) {
|
||||||
|
|
||||||
|
// Remove the lockable aspect from the old working copy, add it to the new file
|
||||||
|
|
||||||
|
nodeService.removeAspect( fromNode, ContentModel.ASPECT_LOCKABLE);
|
||||||
|
nodeService.addAspect( toNode, ContentModel.ASPECT_LOCKABLE, null);
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
|
||||||
|
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
||||||
|
logger.debug(" Moved aspect " + ContentModel.ASPECT_LOCKABLE + " to new document");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for the working copy aspect
|
||||||
|
|
||||||
|
if ( nodeService.hasAspect( fromNode, ContentModel.ASPECT_WORKING_COPY)) {
|
||||||
|
|
||||||
|
// Add the working copy aspect to the new file
|
||||||
|
|
||||||
|
Map<QName, Serializable> workingCopyProperties = new HashMap<QName, Serializable>(1);
|
||||||
|
workingCopyProperties.put(ContentModel.PROP_WORKING_COPY_OWNER, nodeService.getProperty( fromNode, ContentModel.PROP_WORKING_COPY_OWNER));
|
||||||
|
|
||||||
|
nodeService.addAspect( toNode, ContentModel.ASPECT_WORKING_COPY, workingCopyProperties);
|
||||||
|
|
||||||
|
// Remove the working copy aspect from old working copy file
|
||||||
|
|
||||||
|
nodeService.removeAspect( fromNode, ContentModel.ASPECT_WORKING_COPY);
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
|
||||||
|
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
||||||
|
logger.debug(" Moved aspect " + ContentModel.ASPECT_WORKING_COPY + " to new document");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for the copied from aspect
|
||||||
|
|
||||||
|
if ( nodeService.hasAspect( fromNode, ContentModel.ASPECT_COPIEDFROM)) {
|
||||||
|
|
||||||
|
// Add the copied from aspect to the new file
|
||||||
|
|
||||||
|
NodeRef copiedFromNode = (NodeRef) nodeService.getProperty( fromNode, ContentModel.PROP_COPY_REFERENCE);
|
||||||
|
Map<QName, Serializable> copiedFromProperties = new HashMap<QName, Serializable>(1);
|
||||||
|
copiedFromProperties.put(ContentModel.PROP_COPY_REFERENCE, copiedFromNode);
|
||||||
|
|
||||||
|
nodeService.addAspect( toNode, ContentModel.ASPECT_COPIEDFROM, copiedFromProperties);
|
||||||
|
|
||||||
|
// Remove the copied from aspect from old working copy file
|
||||||
|
|
||||||
|
nodeService.removeAspect( fromNode, ContentModel.ASPECT_COPIEDFROM);
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
|
||||||
|
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
||||||
|
logger.debug(" Moved aspect " + ContentModel.ASPECT_COPIEDFROM + " to new document");
|
||||||
|
|
||||||
|
// Check if the original node is locked
|
||||||
|
|
||||||
|
if ( lockService.getLockType( copiedFromNode) == null) {
|
||||||
|
|
||||||
|
// Add the lock back onto the original file
|
||||||
|
|
||||||
|
lockService.lock( copiedFromNode, LockType.READ_ONLY_LOCK);
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
|
||||||
|
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
||||||
|
logger.debug(" Re-locked copied from node " + copiedFromNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the new file name is a temporary file, remove any versionable aspect from it
|
||||||
|
|
||||||
|
String newNameNorm = newName.toLowerCase();
|
||||||
|
|
||||||
|
if ( newNameNorm.endsWith( ".tmp") || newNameNorm.endsWith( ".temp")) {
|
||||||
|
|
||||||
|
// Remove the versionable aspect
|
||||||
|
|
||||||
|
if ( nodeService.hasAspect( toNode, ContentModel.ASPECT_VERSIONABLE))
|
||||||
|
nodeService.removeAspect( toNode, ContentModel.ASPECT_VERSIONABLE);
|
||||||
|
|
||||||
|
// Add the temporary aspect, also prevents versioning
|
||||||
|
|
||||||
|
nodeService.addAspect( toNode, ContentModel.ASPECT_TEMPORARY, null);
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
|
||||||
|
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
|
||||||
|
logger.debug(" Removed versionable aspect from temp file");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -103,6 +103,10 @@ public class FileState
|
|||||||
private long m_modifyDate;
|
private long m_modifyDate;
|
||||||
private long m_changeDate;
|
private long m_changeDate;
|
||||||
|
|
||||||
|
// Keep track of the node we are linked to, when deleted
|
||||||
|
|
||||||
|
private NodeRef m_linkNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor
|
* Class constructor
|
||||||
*
|
*
|
||||||
@@ -639,6 +643,33 @@ public class FileState
|
|||||||
m_modifyDate = modTime;
|
m_modifyDate = modTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the file is linked to another node
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public final boolean hasLinkNode() {
|
||||||
|
return m_linkNode != null ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the node that the file is linked to
|
||||||
|
*
|
||||||
|
* @return NodeRef
|
||||||
|
*/
|
||||||
|
public final NodeRef getLinkNode() {
|
||||||
|
return m_linkNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the node that this file is linked to
|
||||||
|
*
|
||||||
|
* @param node NodeRef
|
||||||
|
*/
|
||||||
|
public final void setLinkNode( NodeRef node) {
|
||||||
|
m_linkNode = node;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the file is readable for the specified section of the file and process id
|
* Check if the file is readable for the specified section of the file and process id
|
||||||
*
|
*
|
||||||
|
Reference in New Issue
Block a user