From 096729effcc3ba0df6f9acd4ff7b7a9fa21f555c Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Wed, 4 Jul 2007 11:05:15 +0000 Subject: [PATCH] Unfix AR-822 and defer to AR-1573. The in-transaction work has to align with the work that will be done by the actual background archival, but node archival doesn't fully support all model constructs and associated behaviour. Instead of continuing to hack away at each issue that comes up, a complete archive rethink is in order. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@6154 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .externalToolBuilders/JibX.launch | 42 +- config/alfresco/bootstrap-context.xml | 11 - config/alfresco/model/systemModel.xml | 15 - config/alfresco/node-services-context.xml | 22 - config/alfresco/repository.properties | 6 - .../java/org/alfresco/model/ContentModel.java | 5 - .../org/alfresco/repo/avm/AVMNodeService.java | 3 +- .../filefolder/FileFolderServiceImpl.java | 18 +- .../tools/AbstractMultilingualTestCases.java | 9 +- .../node/archive/DeletedTagBootstrap.java | 105 --- .../node/archive/NodeArchiveInterceptor.java | 631 ------------------ .../repo/node/db/DbNodeServiceImpl.java | 50 +- .../repo/version/NodeServiceImpl.java | 2 +- .../service/cmr/repository/NodeService.java | 9 +- 14 files changed, 50 insertions(+), 878 deletions(-) delete mode 100644 source/java/org/alfresco/repo/node/archive/DeletedTagBootstrap.java delete mode 100644 source/java/org/alfresco/repo/node/archive/NodeArchiveInterceptor.java diff --git a/.externalToolBuilders/JibX.launch b/.externalToolBuilders/JibX.launch index 12cc9fa8d6..eb79f7cf20 100644 --- a/.externalToolBuilders/JibX.launch +++ b/.externalToolBuilders/JibX.launch @@ -1,25 +1,24 @@ - + - - - - - - - - - - - + + + + + - - - - - - + + + + + + + + + + + @@ -29,8 +28,9 @@ - - - + + + + diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index 5d996c9b51..49e63ebfa5 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -344,17 +344,6 @@ - - - - - - - - - - - diff --git a/config/alfresco/model/systemModel.xml b/config/alfresco/model/systemModel.xml index 5d1065b39e..fbd6f98adc 100644 --- a/config/alfresco/model/systemModel.xml +++ b/config/alfresco/model/systemModel.xml @@ -141,21 +141,6 @@ Temporary - - - Deleted Node - - - d:text - true - - - d:text - true - - - - Archived diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml index 0262c30704..250c540929 100644 --- a/config/alfresco/node-services-context.xml +++ b/config/alfresco/node-services-context.xml @@ -4,27 +4,6 @@ - - - - - - - - - - - - - - - - - - ${system.deleted-items.mode} - - - @@ -46,7 +25,6 @@ mlPropertyInterceptor - nodeArchiveInterceptor diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 9b4df8779c..2a45770328 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -27,12 +27,6 @@ system.acl.maxPermissionCheckTimeMillis=10000 # The maximum number of results to perform permission checks against system.acl.maxPermissionChecks=1000 -# -# Control the transfer of nodes to 'Deleted Items' -# EAGER - Transfer nodes to 'Deleted Items' in the same transaction. (Slower) -# LAZY - Move nodes away but transfer to 'Deleted Items' separately. (Faster) -system.deleted-items.mode=LAZY - # #################### # # Lucene configuration # # #################### # diff --git a/source/java/org/alfresco/model/ContentModel.java b/source/java/org/alfresco/model/ContentModel.java index c3d9b771c3..92b1d9f254 100644 --- a/source/java/org/alfresco/model/ContentModel.java +++ b/source/java/org/alfresco/model/ContentModel.java @@ -55,11 +55,6 @@ public interface ContentModel static final QName ASPECT_LOCALIZED = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "localized"); static final QName PROP_LOCALE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "locale"); - // Deleted nodes constants - static final QName ASPECT_DELETED_NODE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "deletedNode"); - static final QName PROP_DELETED_NODE_ORIGINAL_NAME = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "deletedNodeOriginalName"); - static final QName PROP_DELETED_NODE_USER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "deletedNodeUser"); - // archived nodes aspect constants static final QName ASPECT_ARCHIVED = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archived"); static final QName PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedOriginalParentAssoc"); diff --git a/source/java/org/alfresco/repo/avm/AVMNodeService.java b/source/java/org/alfresco/repo/avm/AVMNodeService.java index 15f7b08ded..5b50ef7e88 100644 --- a/source/java/org/alfresco/repo/avm/AVMNodeService.java +++ b/source/java/org/alfresco/repo/avm/AVMNodeService.java @@ -764,7 +764,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi * @param nodeRef reference to a node within a store * @throws InvalidNodeRefException if the reference given is invalid */ - public boolean deleteNode(NodeRef nodeRef) throws InvalidNodeRefException + public void deleteNode(NodeRef nodeRef) throws InvalidNodeRefException { // Invoke policy behaviors. // invokeBeforeDeleteNode(nodeRef); @@ -795,7 +795,6 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi { throw new InvalidNodeRefException(avmVersionPath.getSecond() +" not found.", nodeRef); } - return true; } /** diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java index 7c51417c59..b65f35c7c3 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java @@ -76,7 +76,6 @@ public class FileFolderServiceImpl implements FileFolderService "./*" + "[like(@cm:name, $cm:name, false)" + " and not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" + - " and not (hasAspect('" + ContentModel.ASPECT_DELETED_NODE + "'))" + " and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "')" + " or subtypeOf('" + ContentModel.TYPE_LINK + "'))]"; @@ -84,7 +83,6 @@ public class FileFolderServiceImpl implements FileFolderService private static final String LUCENE_QUERY_SHALLOW_ALL = "+PARENT:\"${cm:parent}\"" + "-TYPE:\"" + ContentModel.TYPE_SYSTEM_FOLDER + "\" " + - "-ASPECT:\"" + ContentModel.ASPECT_DELETED_NODE + "\" " + "+(" + "TYPE:\"" + ContentModel.TYPE_CONTENT + "\" " + "TYPE:\"" + ContentModel.TYPE_FOLDER + "\" " + @@ -95,14 +93,12 @@ public class FileFolderServiceImpl implements FileFolderService private static final String LUCENE_QUERY_SHALLOW_FOLDERS = "+PARENT:\"${cm:parent}\"" + "-TYPE:\"" + ContentModel.TYPE_SYSTEM_FOLDER + "\" " + - "-ASPECT:\"" + ContentModel.ASPECT_DELETED_NODE + "\" " + "+TYPE:\"" + ContentModel.TYPE_FOLDER + "\" "; /** Shallow search for all files and folders */ private static final String LUCENE_QUERY_SHALLOW_FILES = "+PARENT:\"${cm:parent}\"" + "-TYPE:\"" + ContentModel.TYPE_SYSTEM_FOLDER + "\" " + - "-ASPECT:\"" + ContentModel.ASPECT_DELETED_NODE + "\" " + "+TYPE:\"" + ContentModel.TYPE_CONTENT + "\" "; /** Deep search for files and folders with a name pattern */ @@ -110,7 +106,6 @@ public class FileFolderServiceImpl implements FileFolderService ".//*" + "[like(@cm:name, $cm:name, false)" + " and not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" + - " and not (hasAspect('" + ContentModel.ASPECT_DELETED_NODE + "'))" + " and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "')" + " or subtypeOf('" + ContentModel.TYPE_LINK + "'))]"; @@ -203,14 +198,11 @@ public class FileFolderServiceImpl implements FileFolderService List results = new ArrayList(nodeRefs.size()); for (NodeRef nodeRef : nodeRefs) { - // Ignore missing nodes - if (!nodeService.exists(nodeRef)) + if (nodeService.exists(nodeRef)) { - continue; + FileInfo fileInfo = toFileInfo(nodeRef, true); + results.add(fileInfo); } - // It's good - FileInfo fileInfo = toFileInfo(nodeRef, true); - results.add(fileInfo); } return results; } @@ -332,10 +324,6 @@ public class FileFolderServiceImpl implements FileFolderService public NodeRef searchSimple(NodeRef contextNodeRef, String name) { NodeRef childNodeRef = nodeService.getChildByName(contextNodeRef, ContentModel.ASSOC_CONTAINS, name); - if (childNodeRef != null && nodeService.hasAspect(childNodeRef, ContentModel.ASPECT_DELETED_NODE)) - { - childNodeRef = null; - } if (logger.isDebugEnabled()) { logger.debug( diff --git a/source/java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java b/source/java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java index 392c5dbc50..538d0f9428 100644 --- a/source/java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java +++ b/source/java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java @@ -29,8 +29,7 @@ import junit.framework.TestCase; import org.alfresco.model.ContentModel; import org.alfresco.repo.node.archive.NodeArchiveService; import org.alfresco.repo.security.authentication.AuthenticationComponent; -import org.alfresco.repo.transaction.TransactionUtil; -import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; import org.alfresco.service.cmr.ml.EditionService; @@ -87,9 +86,9 @@ public abstract class AbstractMultilingualTestCases extends TestCase authenticationComponent.setCurrentUser("admin"); // Create a folder to work in - TransactionWork createFolderWork = new TransactionWork() + RetryingTransactionCallback createFolderCallback = new RetryingTransactionCallback() { - public NodeRef doWork() throws Exception + public NodeRef execute() throws Exception { StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); NodeRef rootNodeRef = nodeService.getRootNode(storeRef); @@ -103,7 +102,7 @@ public abstract class AbstractMultilingualTestCases extends TestCase return folderNodeRef; } }; - folderNodeRef = TransactionUtil.executeInUserTransaction(transactionService, createFolderWork); + folderNodeRef = transactionService.getRetryingTransactionHelper().doInTransaction(createFolderCallback); } @Override diff --git a/source/java/org/alfresco/repo/node/archive/DeletedTagBootstrap.java b/source/java/org/alfresco/repo/node/archive/DeletedTagBootstrap.java deleted file mode 100644 index 26a2a70dda..0000000000 --- a/source/java/org/alfresco/repo/node/archive/DeletedTagBootstrap.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2005-2007 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.repo.node.archive; - -import java.util.List; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.cmr.search.ResultSetRow; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.util.AbstractLifecycleBean; -import org.springframework.context.ApplicationEvent; - -/** - * Bootstrap component that component that ensures that any nodes tagged with the - * sys:deleted aspects are removed as the archival process was probably interrupted. - * - * @since 2.1 - * @author Derek Hulley - */ -public class DeletedTagBootstrap extends AbstractLifecycleBean -{ - private static final String LUCENE_QUERY = - "+ASPECT:\"" + ContentModel.ASPECT_DELETED_NODE + "\""; - - private NodeService nodeService; - private SearchService searchService; - - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - public void setSearchService(SearchService searchService) - { - this.searchService = searchService; - } - - @Override - protected void onBootstrap(ApplicationEvent event) - { - AuthenticationUtil.setSystemUserAsCurrentUser(); - removeAspects(); - } - - private void removeAspects() - { - // Get all stores - List storeRefs = nodeService.getStores(); - for (StoreRef storeRef : storeRefs) - { - SearchParameters params = new SearchParameters(); - params.setLanguage(SearchService.LANGUAGE_LUCENE); - params.addStore(storeRef); - params.setQuery(LUCENE_QUERY); - // Search - ResultSet rs = searchService.query(params); - try - { - for (ResultSetRow row : rs) - { - NodeRef nodeRef = row.getNodeRef(); - // Delete it - nodeService.deleteNode(nodeRef); - } - } - finally - { - rs.close(); - } - } - } - - @Override - protected void onShutdown(ApplicationEvent event) - { - } -} diff --git a/source/java/org/alfresco/repo/node/archive/NodeArchiveInterceptor.java b/source/java/org/alfresco/repo/node/archive/NodeArchiveInterceptor.java deleted file mode 100644 index 5ce62a4782..0000000000 --- a/source/java/org/alfresco/repo/node/archive/NodeArchiveInterceptor.java +++ /dev/null @@ -1,631 +0,0 @@ -/* - * Copyright (C) 2005-2007 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.repo.node.archive; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ThreadPoolExecutor; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.node.StoreArchiveMap; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport; -import org.alfresco.repo.transaction.TransactionListenerAdapter; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.dictionary.TypeDefinition; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.GUID; -import org.alfresco.util.PropertyMap; -import org.alfresco.util.VmShutdownListener; -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * An interceptor to handle handle the deletion of nodes. This allows - * deletion and archival process to be pushed into the background. - * - * @since 2.1 - * @author Derek Hulley - */ -public class NodeArchiveInterceptor extends TransactionListenerAdapter implements MethodInterceptor -{ - private static VmShutdownListener shutdownListener = new VmShutdownListener("NodeArchiveInterceptor"); - - private static final Set INBOUND_FIRST_ARG = new HashSet(17); - private static final Set INBOUND_SECOND_ARG = new HashSet(17); - static - { - // First arguments - INBOUND_FIRST_ARG.add("getNodeStatus"); - INBOUND_FIRST_ARG.add("createNode"); - INBOUND_FIRST_ARG.add("moveNode"); - INBOUND_FIRST_ARG.add("getType"); - INBOUND_FIRST_ARG.add("setType"); - INBOUND_FIRST_ARG.add("addAspect"); - INBOUND_FIRST_ARG.add("removeAspect"); - INBOUND_FIRST_ARG.add("hasAspect"); - INBOUND_FIRST_ARG.add("getAspects"); - INBOUND_FIRST_ARG.add("addChild"); - INBOUND_FIRST_ARG.add("removeChild"); - INBOUND_FIRST_ARG.add("getProperties"); - INBOUND_FIRST_ARG.add("getProperty"); - INBOUND_FIRST_ARG.add("setProperties"); - INBOUND_FIRST_ARG.add("setProperty"); - INBOUND_FIRST_ARG.add("removeProperty"); - INBOUND_FIRST_ARG.add("getParentAssocs"); - INBOUND_FIRST_ARG.add("getChildAssocs"); - INBOUND_FIRST_ARG.add("getChildByName"); - INBOUND_FIRST_ARG.add("getPrimaryParent"); - INBOUND_FIRST_ARG.add("createAssociation"); - INBOUND_FIRST_ARG.add("removeAssociation"); - INBOUND_FIRST_ARG.add("getTargetAssocs"); - INBOUND_FIRST_ARG.add("getSourceAssocs"); - INBOUND_FIRST_ARG.add("getPath"); - INBOUND_FIRST_ARG.add("getPaths"); - INBOUND_FIRST_ARG.add("restoreNode"); - // Second arguments - INBOUND_SECOND_ARG.add("moveNode"); - INBOUND_SECOND_ARG.add("addChild"); - INBOUND_SECOND_ARG.add("removeChild"); - INBOUND_SECOND_ARG.add("createAssociation"); - INBOUND_SECOND_ARG.add("removeAssociation"); - INBOUND_SECOND_ARG.add("restoreNode"); - } - - /** A key for storing in-transaction values */ - private static final String KEY_DELETE_WORKERS = "NodeArchiveInterceptor.DeleteNodeWorkers"; - - private static Log logger = LogFactory.getLog(NodeArchiveInterceptor.class); - private static boolean isDebugEnabled = logger.isDebugEnabled(); - - /** - * An archival strategy to follow. - * @since 2.1 - * @author Derek Hulley - */ - public static enum ArchiveMode - { - /** - * Node archival will be done immediately within the current transaction. - */ - EAGER, - /** - * Node archival, where archival is going to occur, will be pushed onto a background - * process. - */ - LAZY - } - - /** Used to ensure that the interceptor isn't in a configuration endless loop */ - private ThreadLocal deleting = new ThreadLocal(); - - /** Used for running background deletes */ - private TransactionService transactionService; - /** Direct access to the NodeService */ - private NodeService nodeService; - /** Used to access property definitions */ - private DictionaryService dictionaryService; - /** A map of stores to send archived nodes to */ - private StoreArchiveMap storeArchiveMap; - /** Helper to perform background deletes */ - private ThreadPoolExecutor threadPoolExecutor; - /** The archival timing */ - private ArchiveMode archiveMode; - - /** - * Default constructor - */ - public NodeArchiveInterceptor() - { - } - - @Override - public boolean equals(Object obj) - { - return super.equals(obj); // Just to be explicit - } - - @Override - public int hashCode() - { - return super.hashCode(); // Just to be explicit - } - - public void setTransactionService(TransactionService transactionService) - { - this.transactionService = transactionService; - } - - /** - * @param nodeService the NodeService that doesn't include this interceptor - */ - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - public void setDictionaryService(DictionaryService dictionaryService) - { - this.dictionaryService = dictionaryService; - } - - public void setStoreArchiveMap(StoreArchiveMap storeArchiveMap) - { - this.storeArchiveMap = storeArchiveMap; - } - - public void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor) - { - this.threadPoolExecutor = threadPoolExecutor; - } - - public void setArchiveMode(ArchiveMode archiveMode) - { - this.archiveMode = archiveMode; - } - - @SuppressWarnings("unchecked") - public Object invoke(MethodInvocation invocation) throws Throwable - { - Object ret = null; - - String methodName = invocation.getMethod().getName(); - Object[] args = invocation.getArguments(); - - if (methodName.equals("deleteNode")) - { - NodeRef nodeRef = (NodeRef) args[0]; - // Handle the deletion - boolean deleted = handleDeleteNode(nodeRef); - ret = Boolean.valueOf(deleted); - } -// else if (methodName.equals("exists")) -// { -// if (args[0] instanceof NodeRef) -// { -// NodeRef nodeRef = (NodeRef) args[0]; -// if (nodeService.exists(nodeRef)) -// { -// if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_DELETED_NODE)) -// { -// // It really exists, but shouldn't be visible -// ret = Boolean.FALSE; -// } -// else -// { -// ret = Boolean.TRUE; -// } -// } -// else -// { -// ret = Boolean.FALSE; -// } -// } -// else -// { -// ret = invocation.proceed(); -// } -// } - else - { - // All other methods will be checked for 'real' deletion. We post-process - // the successful methods as required so that we don't unnecessarily check - // for missing nodes when it would be picked up anyway. - ret = invocation.proceed(); - - } -// // Check first argument -// if (INBOUND_FIRST_ARG.contains(methodName)) -// { -// checkNodeForDeleteMarker((NodeRef)args[0]); -// } -// // Check seconds argument -// if (INBOUND_SECOND_ARG.contains(methodName)) -// { -// checkNodeForDeleteMarker((NodeRef)args[1]); -// } - - // done - return ret; - } - -// /** -// * Check if the node should be treated as invalid due to a deletion -// * -// * @param nodeRef the node to check -// * @throws InvalidNodeRefException -// * if the node has the sys:deleted aspect -// */ -// private void checkNodeForDeleteMarker(NodeRef nodeRef) -// { -// if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_DELETED_NODE)) -// { -// throw new InvalidNodeRefException("Node has been deleted: " + nodeRef, nodeRef); -// } -// } -// -// /** -// * -// * @param nodeRef the node to check -// * @return Returns true if the node has the sys:deleted aspect -// */ -// private boolean isDeleted(NodeRef nodeRef) -// { -// return nodeService.hasAspect(nodeRef, ContentModel.ASPECT_DELETED_NODE); -// } -// - /** - * Determines whether the node can be archived. - * - * @param nodeRef the node to check - * @return Returns true if the node can be archived - */ - private boolean isArchivable(NodeRef nodeRef) - { - // Temporary nodes can't be archived - boolean isTemporary = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TEMPORARY); - if (isTemporary) - { - return false; - } - // Check that the store has an associated archive store - StoreRef storeRef = nodeRef.getStoreRef(); - if (!storeArchiveMap.getArchiveMap().containsKey(storeRef)) - { - // There is no mapping for the store - return false; - } - // Check the type - QName nodeTypeQName = nodeService.getType(nodeRef); - TypeDefinition typeDef = dictionaryService.getType(nodeTypeQName); - if (typeDef == null || !typeDef.isArchive()) - { - // It is not an archivable type - return false; - } - // Otherwise it can be archived - return true; - } - - /** - * Performs a real delete, whilst ensuring that the interceptor doesn't get into an - * infinite loop in the case of a configuration error. - * - * @param nodeRef the node to delete - */ - private boolean deleteNodeDirectly(NodeRef nodeRef) - { - // Catch the infinite loop - if (deleting.get() == Boolean.TRUE) // Handles null and TRUE - { - throw new AlfrescoRuntimeException( - "The NodeArchiveInterceptor must be given a " + - "NodeService that is not similarly intercepted."); - } - try - { - deleting.set(Boolean.TRUE); - // It can really be deleted - return nodeService.deleteNode(nodeRef); - } - finally - { - deleting.set(Boolean.FALSE); - } - } - - /** - * Get the worker runnables that need to be executed after the current transaction has committed. - * - * @return Returns a list of delete node workers - */ - private List getDeleteWorkers() - { - @SuppressWarnings("unchecked") - List deleteRunners = - (List) AlfrescoTransactionSupport.getResource(KEY_DELETE_WORKERS); - if (deleteRunners == null) - { - // It is not bound, yet - deleteRunners = new ArrayList(20); - AlfrescoTransactionSupport.bindResource(KEY_DELETE_WORKERS, deleteRunners); - } - return deleteRunners; - } - - private boolean handleDeleteNode(NodeRef nodeRef) throws Throwable - { - boolean deleteDirect = false; - // If the node is not archivable, then we delete it inline - boolean isArchivable = isArchivable(nodeRef); - if (!isArchivable) - { - deleteDirect = true; - if (isDebugEnabled) - { - logger.debug("\n" + - "Deleted node directly as it is not archivable: \n" + - " Node: " + nodeRef + "\n" + - " Type: " + nodeService.getType(nodeRef)); - } - } - // Check the archive mode - if (archiveMode == ArchiveMode.EAGER) - { - deleteDirect = true; - if (isDebugEnabled) - { - logger.debug("\n" + - "Deleted node directly due to archive mode: \n" + - " Node: " + nodeRef + "\n" + - " Mode: " + archiveMode); - } - } - // When must we the nodes? - if (deleteDirect) - { - return deleteNodeDirectly(nodeRef); - } - else - { - try - { - if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_DELETED_NODE)) - { - // We need to keep the node's original name for later use - Serializable name = nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); - String currentUser = AuthenticationUtil.getCurrentUserName(); - // Add the sys:deletedNode aspect - PropertyMap properties = new PropertyMap(); - properties.put(ContentModel.PROP_DELETED_NODE_ORIGINAL_NAME, name); - properties.put(ContentModel.PROP_DELETED_NODE_USER, currentUser); - nodeService.addAspect(nodeRef, ContentModel.ASPECT_DELETED_NODE, properties); - // Now rename the node to a random name - String guid = GUID.generate(); - nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, guid); - } - // Store it for later deletion - BackgroundDeleteRunner backgroundDeleteRunner = new BackgroundDeleteRunner(nodeRef); - getDeleteWorkers().add(backgroundDeleteRunner); - - // Register this instance as a listener on the transaction - AlfrescoTransactionSupport.bindListener(this); - } - catch(Throwable e) - { - e.printStackTrace(); - throw e; - } - - if (isDebugEnabled) - { - logger.debug("\n" + - "Queued node deletion for post-transaction processing: \n" + - " Node: " + nodeRef); - } - - return false; - } - } - - /** - * Checks if there are any nodes that were earmarked for deletion. These are then - * pushed onto an execution queue to be handled in the background. What we are sure - * of is that any nodes that were created have been committed by the transaction - * that has just ended. - */ - public void afterCommit() - { - // Get the list of nodes - List deleteWorkers = getDeleteWorkers(); - for (BackgroundDeleteRunner deleteWorker : deleteWorkers) - { - // Push the node onto the execution queue - threadPoolExecutor.submit(deleteWorker); - } - } - - /** - * A worker class that is able to delete a node, on behalf of a particular user, as a background - * task. - * - * @since 2.1 - * @author Derek Hulley - */ - private class BackgroundDeleteRunner implements Runnable - { - private NodeRef nodeRef; - - /** - * @param nodeRef the node to delete - */ - public BackgroundDeleteRunner(NodeRef nodeRef) - { - this.nodeRef = nodeRef; - } - public void run() - { - // Transaction wrapper - RetryingTransactionCallback deleteTxnCallback = new RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Determine if the execution should proceed - RunAsWork getContinueAuthCallback = new RunAsWork() - { - public Boolean doWork() throws Exception - { - if (nodeService.exists(nodeRef) && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_DELETED_NODE)) - { - return Boolean.TRUE; - } - else - { - return Boolean.FALSE; - } - } - }; - Boolean mustContinue = AuthenticationUtil.runAs(getContinueAuthCallback, AuthenticationUtil.SYSTEM_USER_NAME); - if (mustContinue == Boolean.FALSE) - { - if (isDebugEnabled) - { - logger.debug("\n" + - "Queued deletion stopped. The node is no longer marked for deletion or no longer exists. \n" + - " Node: " + nodeRef); - } - return null; - } - // Get the user that initiated the delete - RunAsWork getUserAuthCallback = new RunAsWork() - { - public String doWork() throws Exception - { - return (String) nodeService.getProperty(nodeRef, ContentModel.PROP_DELETED_NODE_USER); - } - }; - String runAs = AuthenticationUtil.runAs(getUserAuthCallback, AuthenticationUtil.SYSTEM_USER_NAME); - // Authentication wrapper - RunAsWork deleteAuthCallback = new RunAsWork() - { - /** - * Recursive method that removes the aspect from all children of the given node - */ - private void removeAspectFromHierarchy(NodeRef nodeRef) - { - if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_DELETED_NODE)) - { - // Restore the original name - Serializable originalName = nodeService.getProperty(nodeRef, ContentModel.PROP_DELETED_NODE_ORIGINAL_NAME); - nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, originalName); - // Remove the aspect to stake our claim - nodeService.removeAspect(nodeRef, ContentModel.ASPECT_DELETED_NODE); - } - // Make sure that nothing in the hierarchy has the aspect, either - List childAssocRefs = nodeService.getChildAssocs(nodeRef); - for (ChildAssociationRef assocRef : childAssocRefs) - { - // Ignore non-primary assocs - if (!assocRef.isPrimary()) - { - continue; - } - removeAspectFromHierarchy(assocRef.getChildRef()); - } - } - public Object doWork() throws Exception - { - deleteNodeDirectly(nodeRef); - // If the node went into an archive, then follow it and remove the sys:deleted aspect - // and revert the cm:name property - NodeRef archivedRootNodeRef = nodeService.getStoreArchiveNode(nodeRef.getStoreRef()); - if (archivedRootNodeRef != null) - { - StoreRef archiveStoreRef = archivedRootNodeRef.getStoreRef(); - NodeRef archivedNodeRef = new NodeRef(archiveStoreRef, nodeRef.getId()); - if (nodeService.exists(archivedNodeRef)) - { - removeAspectFromHierarchy(archivedNodeRef); - } - } - // Success - if (isDebugEnabled) - { - logger.debug("\n" + - "Successfully deleted node.\n" + - " Node: " + nodeRef); - } - // Done - return null; - } - }; - return AuthenticationUtil.runAs(deleteAuthCallback, runAs); - } - }; - try - { - transactionService.getRetryingTransactionHelper().doInTransaction(deleteTxnCallback); - // Done - } - catch (Throwable e) - { - // We can ignore all errors if the VM is shutting down - if (NodeArchiveInterceptor.shutdownListener.isVmShuttingDown()) - { - return; - } - - // It failed, so just ensure that the sys:deleted aspect has been removed - RetryingTransactionCallback callback = new RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - RunAsWork authCallback = new RunAsWork() - { - public Object doWork() throws Exception - { - nodeService.removeAspect(nodeRef, ContentModel.ASPECT_DELETED_NODE); - return null; - } - }; - return AuthenticationUtil.runAs(authCallback, AuthenticationUtil.SYSTEM_USER_NAME); - } - }; - try - { - transactionService.getRetryingTransactionHelper().doInTransaction(callback); - } - catch (Throwable ee) - { - // This is bad, but the original exception is the one that really needs to get out. - // We dump this error. - logger.info("\n" + - "Failed to remove sys:deletedNode aspect from node: \n" + - " Node: " + nodeRef + "\n" + - " After Error: " + e.getMessage(), - e); - } - // Rethrow the original error - throw new AlfrescoRuntimeException("\n" + - "Failed to delete node: \n" + - " Node: " + nodeRef, - e); - } - } - } -} diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index 25a3498032..d33dbfdf6b 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -285,9 +285,6 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl Assert.notNull(assocTypeQName); Assert.notNull(assocQName); - // Get the parent node - Node parentNode = getNodeNotNull(parentRef); - // null property map is allowed if (properties == null) { @@ -327,6 +324,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // We now have enough to declare the child association creation invokeBeforeCreateChildAssociation(parentRef, childNodeRef, assocTypeQName, assocQName, true); + // Get the parent node + Node parentNode = getNodeNotNull(parentRef); // Create the association ChildAssoc childAssoc = nodeDaoService.newChildAssoc( parentNode, @@ -521,12 +520,12 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl { throw new InvalidTypeException(typeQName); } - Node node = getNodeNotNull(nodeRef); // Invoke policies invokeBeforeUpdateNode(nodeRef); // Get the node and set the new type + Node node = getNodeNotNull(nodeRef); node.setTypeQName(typeQName); // Add the default aspects to the node (update the properties with any new default values) @@ -554,12 +553,12 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl throw new InvalidAspectException("The aspect is invalid: " + aspectTypeQName, aspectTypeQName); } - Node node = getNodeNotNull(nodeRef); - // Invoke policy behaviours invokeBeforeUpdateNode(nodeRef); invokeBeforeAddAspect(nodeRef, aspectTypeQName); + Node node = getNodeNotNull(nodeRef); + // attach the properties to the current node properties Map nodeProperties = getPropertiesImpl(node); @@ -688,14 +687,11 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl return ret; } - /** - * {@inheritDoc} - */ - public boolean deleteNode(NodeRef nodeRef) + public void deleteNode(NodeRef nodeRef) { // First get the node to ensure that it exists Node node = getNodeNotNull(nodeRef); - + boolean requiresDelete = false; // Invoke policy behaviours @@ -740,20 +736,17 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl archiveNode(nodeRef, archiveStoreRef); // The archive performs a move, which will fire the appropriate OnDeleteNode } - // Done - return true; } public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName assocQName) { + // Invoke policy behaviours + invokeBeforeCreateChildAssociation(parentRef, childRef, assocTypeQName, assocQName, false); + // get the parent node and ensure that it is a container node Node parentNode = getNodeNotNull(parentRef); // get the child node Node childNode = getNodeNotNull(childRef); - - // Invoke policy behaviours - invokeBeforeCreateChildAssociation(parentRef, childRef, assocTypeQName, assocQName, false); - // make the association ChildAssoc assoc = nodeDaoService.newChildAssoc( parentNode, @@ -931,9 +924,6 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl public Serializable getProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException { - // get the property from the node - Node node = getNodeNotNull(nodeRef); - // spoof referencable properties if (qname.equals(ContentModel.PROP_STORE_PROTOCOL)) { @@ -948,6 +938,9 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl return nodeRef.getId(); } + // get the property from the node + Node node = getNodeNotNull(nodeRef); + if (qname.equals(ContentModel.PROP_NODE_DBID)) { return node.getId(); @@ -1048,12 +1041,12 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl { Assert.notNull(qname); - // get the node - Node node = getNodeNotNull(nodeRef); - // Invoke policy behaviours invokeBeforeUpdateNode(nodeRef); + // get the node + Node node = getNodeNotNull(nodeRef); + // Do the set operation Map propertiesBefore = getPropertiesImpl(node); Map propertiesAfter = setPropertyImpl(node, qname, value); @@ -1101,12 +1094,12 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl throw new UnsupportedOperationException("The property " + qname + " may not be removed individually"); } - // Get the node - Node node = getNodeNotNull(nodeRef); - // Invoke policy behaviours invokeBeforeUpdateNode(nodeRef); + // Get the node + Node node = getNodeNotNull(nodeRef); + // Get the values before Map propertiesBefore = getPropertiesImpl(node); // Remove the property @@ -1617,7 +1610,6 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl for (NodeStatus oldNodeStatus : nodeStatusesById.values()) { Node nodeToMove = oldNodeStatus.getNode(); - NodeRef oldNodeRef = nodeToMove.getNodeRef(); nodeToMove.setStore(store); NodeRef newNodeRef = nodeToMove.getNodeRef(); @@ -1627,10 +1619,6 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl NodeStatus newNodeStatus = nodeDaoService.getNodeStatus(newNodeRef, true); newNodeStatus.setNode(nodeToMove); - // Record change IDs - nodeDaoService.recordChangeId(oldNodeRef); - nodeDaoService.recordChangeId(newNodeRef); - invokeOnUpdateNode(newNodeRef); } } diff --git a/source/java/org/alfresco/repo/version/NodeServiceImpl.java b/source/java/org/alfresco/repo/version/NodeServiceImpl.java index 739fd5ddc6..9795d388e0 100644 --- a/source/java/org/alfresco/repo/version/NodeServiceImpl.java +++ b/source/java/org/alfresco/repo/version/NodeServiceImpl.java @@ -199,7 +199,7 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * @throws UnsupportedOperationException always */ - public boolean deleteNode(NodeRef nodeRef) throws InvalidNodeRefException + public void deleteNode(NodeRef nodeRef) throws InvalidNodeRefException { // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); diff --git a/source/java/org/alfresco/service/cmr/repository/NodeService.java b/source/java/org/alfresco/service/cmr/repository/NodeService.java index 35cc4d6104..fa59c93f48 100644 --- a/source/java/org/alfresco/service/cmr/repository/NodeService.java +++ b/source/java/org/alfresco/service/cmr/repository/NodeService.java @@ -287,19 +287,12 @@ public interface NodeService * All associations (both children and regular node associations) * will be deleted, and where the given node is the primary parent, * the children will also be cascade deleted. - *

- * Depending on the node's type, the presence of certain aspects, the - * node's store or the any other factors determined by the implementation, - * the node may not actually disappear immediately. It may be lined up for - * archival or later deletion. * * @param nodeRef reference to a node within a store - * @return Returns true if the node was completely removed, otherwise - * false if the node will still exist after the call. * @throws InvalidNodeRefException if the reference given is invalid */ @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) - public boolean deleteNode(NodeRef nodeRef) throws InvalidNodeRefException; + public void deleteNode(NodeRef nodeRef) throws InvalidNodeRefException; /** * Makes a parent-child association between the given nodes. Both nodes must belong to the same store.