mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
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
This commit is contained in:
@@ -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");
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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<FileInfo> results = new ArrayList<FileInfo>(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(
|
||||
|
@@ -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<NodeRef> createFolderWork = new TransactionWork<NodeRef>()
|
||||
RetryingTransactionCallback<NodeRef> createFolderCallback = new RetryingTransactionCallback<NodeRef>()
|
||||
{
|
||||
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
|
||||
|
@@ -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
|
||||
* <b>sys:deleted</b> 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<StoreRef> 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)
|
||||
{
|
||||
}
|
||||
}
|
@@ -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<String> INBOUND_FIRST_ARG = new HashSet<String>(17);
|
||||
private static final Set<String> INBOUND_SECOND_ARG = new HashSet<String>(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<Boolean> deleting = new ThreadLocal<Boolean>();
|
||||
|
||||
/** 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 <b>sys:deleted</b> 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 <tt>true</tt> if the node has the <b>sys:deleted</b> 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 <tt>true</tt> 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<BackgroundDeleteRunner> getDeleteWorkers()
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
List<BackgroundDeleteRunner> deleteRunners =
|
||||
(List<BackgroundDeleteRunner>) AlfrescoTransactionSupport.getResource(KEY_DELETE_WORKERS);
|
||||
if (deleteRunners == null)
|
||||
{
|
||||
// It is not bound, yet
|
||||
deleteRunners = new ArrayList<BackgroundDeleteRunner>(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<BackgroundDeleteRunner> 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<Object> deleteTxnCallback = new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
public Object execute() throws Throwable
|
||||
{
|
||||
// Determine if the execution should proceed
|
||||
RunAsWork<Boolean> getContinueAuthCallback = new RunAsWork<Boolean>()
|
||||
{
|
||||
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<String> getUserAuthCallback = new RunAsWork<String>()
|
||||
{
|
||||
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<Object> deleteAuthCallback = new RunAsWork<Object>()
|
||||
{
|
||||
/**
|
||||
* 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<ChildAssociationRef> 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<Object> callback = new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
public Object execute() throws Throwable
|
||||
{
|
||||
RunAsWork<Object> authCallback = new RunAsWork<Object>()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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<QName, Serializable> 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<QName, Serializable> propertiesBefore = getPropertiesImpl(node);
|
||||
Map<QName, Serializable> 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<QName, Serializable> 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);
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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.
|
||||
* <p>
|
||||
* 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 <tt>true</tt> if the node was completely removed, otherwise
|
||||
* <tt>false</tt> 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.
|
||||
|
Reference in New Issue
Block a user