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.