diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index f3d98b8d79..d0cb9c2586 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -484,6 +484,11 @@ + + + + + diff --git a/config/alfresco/caches.properties b/config/alfresco/caches.properties index 6d8c105eb0..1c36f418a3 100644 --- a/config/alfresco/caches.properties +++ b/config/alfresco/caches.properties @@ -528,6 +528,17 @@ cache.publicapi.webScriptsRegistryCache.eviction-policy=LRU cache.publicapi.webScriptsRegistryCache.eviction-percentage=25 cache.publicapi.webScriptsRegistryCache.merge-policy=hz.ADD_NEW_ENTRY +cache.deletePseudoFileCache.tx.maxItems=50000 +cache.deletePseudoFileCache.maxItems=50000 +cache.deletePseudoFileCache.timeToLiveSeconds=180 +cache.deletePseudoFileCache.maxIdleSeconds=0 +cache.deletePseudoFileCache.cluster.type=fully-distributed +cache.deletePseudoFileCache.backup-count=1 +cache.deletePseudoFileCache.eviction-policy=LRU +cache.deletePseudoFileCache.eviction-percentage=25 +cache.deletePseudoFileCache.merge-policy=hz.ADD_NEW_ENTRY + + # # RM Caveat cache diff --git a/config/alfresco/subsystems/fileServers/default/network-protocol-context.xml b/config/alfresco/subsystems/fileServers/default/network-protocol-context.xml index 0946bc192a..6aa52d18a5 100644 --- a/config/alfresco/subsystems/fileServers/default/network-protocol-context.xml +++ b/config/alfresco/subsystems/fileServers/default/network-protocol-context.xml @@ -408,6 +408,7 @@ + diff --git a/source/java/org/alfresco/filesys/alfresco/PseudoFileOverlay.java b/source/java/org/alfresco/filesys/alfresco/PseudoFileOverlay.java index 60d961a373..1808f31356 100644 --- a/source/java/org/alfresco/filesys/alfresco/PseudoFileOverlay.java +++ b/source/java/org/alfresco/filesys/alfresco/PseudoFileOverlay.java @@ -44,5 +44,16 @@ public interface PseudoFileOverlay * @return list of pseudo files. */ public PseudoFileList searchPseudoFiles(NodeRef parentDir, String name); + + /** + * Delete a pseudo file. + * + * Pseudo files may need to be deleted for delete folder operations to work + * correctly. + * + * A pseudo file can be deleted for a short time. However it may re-appear at some point + * later since there is no permanent persistence of pseudo files which are ephemeral! + */ + public void delete(NodeRef parentDir, String name); } diff --git a/source/java/org/alfresco/filesys/alfresco/PseudoFileOverlayImpl.java b/source/java/org/alfresco/filesys/alfresco/PseudoFileOverlayImpl.java index 84b10822f6..2671ae793c 100644 --- a/source/java/org/alfresco/filesys/alfresco/PseudoFileOverlayImpl.java +++ b/source/java/org/alfresco/filesys/alfresco/PseudoFileOverlayImpl.java @@ -18,9 +18,12 @@ */ package org.alfresco.filesys.alfresco; +import java.io.Serializable; import java.util.Enumeration; +import java.util.Map; import org.alfresco.filesys.repo.ContentDiskDriver2; +import org.alfresco.jlan.server.filesys.FileInfo; import org.alfresco.jlan.server.filesys.FileName; import org.alfresco.jlan.server.filesys.pseudo.MemoryPseudoFile; import org.alfresco.jlan.server.filesys.pseudo.PseudoFile; @@ -28,6 +31,7 @@ import org.alfresco.jlan.server.filesys.pseudo.PseudoFileList; import org.alfresco.jlan.util.WildCard; import org.alfresco.model.ContentModel; import org.alfresco.repo.admin.SysAdminParams; +import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.site.SiteModel; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -47,6 +51,7 @@ public class PseudoFileOverlayImpl implements PseudoFileOverlay private SysAdminParams sysAdminParams; private AlfrescoContext context; private NodeService nodeService; + private SimpleCache deletePseudoFileCache; private static final Log logger = LogFactory.getLog(PseudoFileOverlayImpl.class); @@ -57,6 +62,7 @@ public class PseudoFileOverlayImpl implements PseudoFileOverlay PropertyCheck.mandatory(this, "nodeService", getNodeService()); PropertyCheck.mandatory(this, "context", context); PropertyCheck.mandatory(this, "sysAdminParams", sysAdminParams); + PropertyCheck.mandatory(this, "deletePseudoFileCache", deletePseudoFileCache); DesktopActionTable actions = context.getDesktopActions(); @@ -322,23 +328,39 @@ public class PseudoFileOverlayImpl implements PseudoFileOverlay for ( int i = 0; i < pl.numberOfFiles(); i++) { PseudoFile pseudoFile = pl.getFileAt(i); - filterList.addFile(pseudoFile); + if(!isDeleted(parentDir, pseudoFile.getFileName())) + { + // File is not deleted + filterList.addFile(pseudoFile); + } } // The URL file is dependent upon the parent dir if(context.isAlfrescoURLEnabled()) { - filterList.addFile(generateAlfrescoURLShortcut(parentDir)); + if(!isDeleted(parentDir, context.getURLFileName())) + { + filterList.addFile(generateAlfrescoURLShortcut(parentDir)); + } + else + { + if(logger.isDebugEnabled()) + { + logger.debug("alfresco URL pseudo file deleted"); + } + } } if(context.isShareURLEnabled()) { - PseudoFile sharePseudoFile = generateShareURLShortcut(parentDir); - - if(sharePseudoFile != null) + if(!isDeleted(parentDir, context.getShareURLFileName())) { - filterList.addFile(sharePseudoFile); - } + PseudoFile sharePseudoFile = generateShareURLShortcut(parentDir); + if(sharePseudoFile != null) + { + filterList.addFile(sharePseudoFile); + } + } } return filterList; @@ -355,8 +377,11 @@ public class PseudoFileOverlayImpl implements PseudoFileOverlay PseudoFile pseudoFile = pl.getFileAt( i); if ( wildCard.matchesPattern( pseudoFile.getFileName())) { - // Add the pseudo file to the filtered list - filterList.addFile( pseudoFile); + if(!isDeleted(parentDir, pseudoFile.getFileName())) + { + // Add the pseudo file to the filtered list + filterList.addFile( pseudoFile); + } } } @@ -365,7 +390,10 @@ public class PseudoFileOverlayImpl implements PseudoFileOverlay { if(wildCard.matchesPattern(context.getURLFileName())) { - filterList.addFile(generateAlfrescoURLShortcut(parentDir)); + if(!isDeleted(parentDir, context.getURLFileName())) + { + filterList.addFile(generateAlfrescoURLShortcut(parentDir)); + } } } @@ -373,12 +401,15 @@ public class PseudoFileOverlayImpl implements PseudoFileOverlay { if(wildCard.matchesPattern(context.getShareURLFileName())) { - PseudoFile sharePseudoFile = generateShareURLShortcut(parentDir); + if(!isDeleted(parentDir, context.getShareURLFileName())) + { + PseudoFile sharePseudoFile = generateShareURLShortcut(parentDir); - if(sharePseudoFile != null) - { - filterList.addFile(sharePseudoFile); - } + if(sharePseudoFile != null) + { + filterList.addFile(sharePseudoFile); + } + } } } @@ -392,7 +423,7 @@ public class PseudoFileOverlayImpl implements PseudoFileOverlay PseudoFileList filterList = new PseudoFileList(); PseudoFile file = getPseudoFile(parentDir, fname); - if(file != null) + if(file != null && !isDeleted(parentDir, fname)) { filterList.addFile(file); } @@ -400,6 +431,32 @@ public class PseudoFileOverlayImpl implements PseudoFileOverlay return filterList; } } + + @Override + public void delete(NodeRef parentDir, String name) + { + if(logger.isDebugEnabled()) + { + logger.debug("delete pseudo file parentDir:" + parentDir + ", name: " + name); + } + getDeletePseudoFileCache().put(toDeleteKey(parentDir, name), "Deleted"); + } + + private String toDeleteKey(NodeRef parentNoderef, String name) + { + return (parentNoderef.toString() + "/" + name + ":" + context.getDeviceName()).toLowerCase(); + } + + private boolean isDeleted(NodeRef parentDir, String name) + { + String key = toDeleteKey(parentDir, name); + boolean isDeleted = getDeletePseudoFileCache().contains(key); + if(logger.isDebugEnabled()) + { + logger.debug("pseudoFile isDeleted: " + isDeleted + ", for name:" + name); + } + return isDeleted; + } // public void setNodeService(NodeService nodeService) @@ -440,4 +497,13 @@ public class PseudoFileOverlayImpl implements PseudoFileOverlay { return sysAdminParams; } + + public SimpleCache getDeletePseudoFileCache() + { + return deletePseudoFileCache; + } + + public void setDeletePseudoFileCache(SimpleCache deletePseudoFileCache) { + this.deletePseudoFileCache = deletePseudoFileCache; + } } diff --git a/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java b/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java index c0c8df770a..2a71959024 100644 --- a/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java +++ b/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java @@ -96,7 +96,7 @@ public interface RepositoryDiskInterface * @throws FileNotFoundException * @return node ref of deleted file or null if no file deleted */ - public NodeRef closeFile(NodeRef rootNode, String Path, NetworkFile file) throws IOException; + public NodeRef closeFile(TreeConnection tree, NodeRef rootNode, String Path, NetworkFile file) throws IOException; /** diff --git a/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java b/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java index bd1e3e71ad..8c23756931 100644 --- a/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java +++ b/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java @@ -220,7 +220,7 @@ public class CommandExecutorImpl implements CommandExecutor { logger.debug("close file command"); CloseFileCommand c = (CloseFileCommand)command; - return repositoryDiskInterface.closeFile(c.getRootNodeRef(), c.getPath(), c.getNetworkFile()); + return repositoryDiskInterface.closeFile(tree, c.getRootNodeRef(), c.getPath(), c.getNetworkFile()); } else if(command instanceof ReduceQuotaCommand) { diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java index e17169a786..af43d9882d 100644 --- a/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java +++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java @@ -59,6 +59,7 @@ import org.alfresco.jlan.server.filesys.SearchContext; import org.alfresco.jlan.server.filesys.SrvDiskInfo; import org.alfresco.jlan.server.filesys.TreeConnection; import org.alfresco.jlan.server.filesys.cache.FileState; +import org.alfresco.jlan.server.filesys.pseudo.MemoryNetworkFile; import org.alfresco.jlan.server.filesys.pseudo.PseudoFile; import org.alfresco.jlan.server.filesys.pseudo.PseudoFileList; import org.alfresco.jlan.server.filesys.pseudo.PseudoNetworkFile; @@ -74,6 +75,7 @@ import org.alfresco.jlan.smb.server.SMBSrvSession; import org.alfresco.jlan.util.DataBuffer; import org.alfresco.jlan.util.MemorySize; import org.alfresco.model.ContentModel; +import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.encoding.ContentCharsetFinder; import org.alfresco.repo.content.filestore.FileContentReader; @@ -178,6 +180,7 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD PropertyCheck.mandatory(this, "nodeArchiveService", nodeArchiveService); PropertyCheck.mandatory(this, "hiddenAspect", hiddenAspect); PropertyCheck.mandatory(this, "lockKeeper", lockKeeper); + PropertyCheck.mandatory(this, "deletePseudoFileCache", deletePseudoFileCache); } /** @@ -601,6 +604,7 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD ps.setContext(context); ps.setNodeService(nodeService); ps.setSysAdminParams(context.getSysAdminParams()); + ps.setDeletePseudoFileCache(deletePseudoFileCache); context.setPseudoFileOverlay(ps); ps.init(); } @@ -1231,9 +1235,11 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD // lookup parent directory NodeRef dirNodeRef = getNodeForPath(tree, paths[0]); - // Check whether we are opening a pseudo file + // Check whether we are closing a pseudo file if(ctx.getPseudoFileOverlay().isPseudoFile(dirNodeRef, paths[1])) { + // pseudo delete a pseudo file + ctx.getPseudoFileOverlay().delete(dirNodeRef, paths[1]); return null; } } @@ -2863,16 +2869,34 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD * @exception java.io.IOException If an error occurs. * @return node ref of deleted file */ - public NodeRef closeFile(NodeRef rootNode, String path, NetworkFile file) throws IOException + public NodeRef closeFile(TreeConnection tree, NodeRef rootNode, String path, NetworkFile file) throws IOException { if ( logger.isDebugEnabled()) { logger.debug("Close file:" + path + ", readOnly=" + file.isReadOnly() ); } - if( file instanceof PseudoNetworkFile) + if( file instanceof PseudoNetworkFile || file instanceof MemoryNetworkFile) { file.close(); + + if(file.hasDeleteOnClose()) + { + if(logger.isDebugEnabled()) + { + logger.debug("delete on close a pseudo file"); + } + final ContentContext ctx = (ContentContext) tree.getContext(); + + String[] paths = FileName.splitPath(path); + + if (paths[0] != null && paths[0].length() > 1) + { + // lookup parent directory + NodeRef dirNodeRef = getNodeForPath(tree, paths[0]); + ctx.getPseudoFileOverlay().delete(dirNodeRef, paths[1]); + } + } return null; } @@ -3205,4 +3229,12 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD { return nodeArchiveService; } + + private SimpleCache deletePseudoFileCache; + + public void setDeletePseudoFileCache(SimpleCache deletePseudoFileCache) + { + this.deletePseudoFileCache = deletePseudoFileCache; + } + }