+
+ = :minTxnId and
+ txn.commitTimeMs <= :maxCommitTime
+ order by
+ txn.id asc
+ ]]>
+
+
diff --git a/source/java/org/alfresco/repo/jscript/People.java b/source/java/org/alfresco/repo/jscript/People.java
index ce3d6c607f..5f26f6f328 100644
--- a/source/java/org/alfresco/repo/jscript/People.java
+++ b/source/java/org/alfresco/repo/jscript/People.java
@@ -413,7 +413,7 @@ public final class People extends BaseScopableProcessorExtension
ScriptNode group = null;
String actualName = services.getAuthorityService().getName(AuthorityType.GROUP, groupName);
- if (authorityService.authorityExists(groupName) == false)
+ if (authorityService.authorityExists(actualName) == false)
{
String parentGroupName = null;
if (parentGroup != null)
diff --git a/source/java/org/alfresco/repo/jscript/ScriptNode.java b/source/java/org/alfresco/repo/jscript/ScriptNode.java
index e2a137b6d9..49451d5550 100644
--- a/source/java/org/alfresco/repo/jscript/ScriptNode.java
+++ b/source/java/org/alfresco/repo/jscript/ScriptNode.java
@@ -1409,7 +1409,7 @@ public class ScriptNode implements Serializable, Scopeable
if (destination.getNodeRef().getStoreRef().getProtocol().equals(StoreRef.PROTOCOL_WORKSPACE))
{
NodeRef copyRef = this.services.getCopyService().copyAndRename(this.nodeRef, destination.getNodeRef(),
- ContentModel.ASSOC_CONTAINS, getPrimaryParentAssoc().getQName(), deepCopy);
+ ContentModel.ASSOC_CONTAINS, null, deepCopy);
copy = newInstance(copyRef, this.services, this.scope);
}
else
diff --git a/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java b/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java
index b4188cc8a5..41f87a98df 100644
--- a/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java
+++ b/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java
@@ -30,7 +30,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.locks.ReentrantLock;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.node.NodeServicePolicies.BeforeAddAspectPolicy;
@@ -59,8 +58,6 @@ import org.alfresco.repo.policy.AssociationPolicyDelegate;
import org.alfresco.repo.policy.ClassPolicyDelegate;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.search.Indexer;
-import org.alfresco.repo.security.authentication.AuthenticationUtil;
-import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
@@ -648,47 +645,4 @@ public abstract class AbstractNodeServiceImpl implements NodeService
}
return properties;
}
-
- /**
- * Override to implement cleanup processes. The default does nothing.
- *
- * This method will be called as the system user but without any
- * additional transactions.
- */
- protected List cleanupImpl()
- {
- // No operation
- return Collections.emptyList();
- }
-
- /** Prevent multiple executions of the implementation method */
- private ReentrantLock cleanupLock = new ReentrantLock();
- public final List cleanup()
- {
- boolean locked = cleanupLock.tryLock();
- if (locked)
- {
- try
- {
- // Authenticate as system
- RunAsWork> cleanupWork = new RunAsWork>()
- {
- public List doWork() throws Exception
- {
- // The current thread got the lock
- return cleanupImpl();
- }
- };
- return AuthenticationUtil.runAs(cleanupWork, AuthenticationUtil.SYSTEM_USER_NAME);
- }
- finally
- {
- cleanupLock.unlock();
- }
- }
- else
- {
- return Collections.emptyList();
- }
- }
}
diff --git a/source/java/org/alfresco/repo/node/cleanup/AbstractNodeCleanupWorker.java b/source/java/org/alfresco/repo/node/cleanup/AbstractNodeCleanupWorker.java
new file mode 100644
index 0000000000..ca94e17aa5
--- /dev/null
+++ b/source/java/org/alfresco/repo/node/cleanup/AbstractNodeCleanupWorker.java
@@ -0,0 +1,147 @@
+package org.alfresco.repo.node.cleanup;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.alfresco.error.StackTraceUtil;
+import org.alfresco.repo.node.db.DbNodeServiceImpl;
+import org.alfresco.repo.node.db.NodeDaoService;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.PropertyCheck;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Base class for Node cleaners. This class ensures calls through
+ * after having created a read-write transaction that is authenticated
+ * as system.
+ *
+ * @author Derek Hulley
+ * @since 2.2 SP2
+ */
+public abstract class AbstractNodeCleanupWorker implements NodeCleanupWorker
+{
+ protected final Log logger;
+ private final ReentrantLock cleanupLock;
+
+ private NodeCleanupRegistry registry;
+ protected TransactionService transactionService;
+ protected DbNodeServiceImpl dbNodeService;
+ protected NodeDaoService nodeDaoService;
+
+ public AbstractNodeCleanupWorker()
+ {
+ logger = LogFactory.getLog(this.getClass());
+ cleanupLock = new ReentrantLock();
+ }
+
+ public void setRegistry(NodeCleanupRegistry registry)
+ {
+ this.registry = registry;
+ }
+
+ public void setTransactionService(TransactionService transactionService)
+ {
+ this.transactionService = transactionService;
+ }
+
+ public void setDbNodeService(DbNodeServiceImpl dbNodeService)
+ {
+ this.dbNodeService = dbNodeService;
+ }
+
+ public void setNodeDaoService(NodeDaoService nodeDaoService)
+ {
+ this.nodeDaoService = nodeDaoService;
+ }
+
+ public void register()
+ {
+ PropertyCheck.mandatory(this, "registry", registry);
+ PropertyCheck.mandatory(this, "transactionService", transactionService);
+ PropertyCheck.mandatory(this, "dbNodeService", dbNodeService);
+ PropertyCheck.mandatory(this, "nodeDaoService", nodeDaoService);
+
+ registry.register(this);
+ }
+
+ /**
+ * Calls {@link #doCleanInternal()} in a System-user authenticated read-write transaction.
+ * This method is non-blocking but passes all second and subsequent concurrent invocations
+ * straight through.
+ */
+ public List doClean()
+ {
+ /** Prevent multiple executions of the implementation method */
+ boolean locked = cleanupLock.tryLock();
+ if (locked)
+ {
+ try
+ {
+ return doCleanWithTxn();
+ }
+ catch (Throwable e)
+ {
+ if (logger.isDebugEnabled())
+ {
+ StringBuilder sb = new StringBuilder(1024);
+ StackTraceUtil.buildStackTrace(
+ "Node cleanup failed: " +
+ " Worker: " + this.getClass().getName() + "\n" +
+ " Error: ",
+ e.getStackTrace(),
+ sb,
+ Integer.MAX_VALUE);
+ logger.debug(sb.toString());
+ }
+ StringBuilder sb = new StringBuilder(1024);
+ StackTraceUtil.buildStackTrace(
+ "Node cleanup failed: " +
+ " Worker: " + this.getClass().getName() + "\n" +
+ " Error: ",
+ e.getStackTrace(),
+ sb,
+ 20);
+ return Collections.singletonList(sb.toString());
+ }
+ finally
+ {
+ cleanupLock.unlock();
+ }
+ }
+ else
+ {
+ return Collections.emptyList();
+ }
+ }
+
+ private List doCleanWithTxn()
+ {
+ final RetryingTransactionCallback> doCleanCallback = new RetryingTransactionCallback>()
+ {
+ public List execute() throws Throwable
+ {
+ return doCleanInternal();
+ }
+ };
+ final RunAsWork> doCleanRunAs = new RunAsWork>()
+ {
+ public List doWork() throws Exception
+ {
+ return transactionService.getRetryingTransactionHelper().doInTransaction(doCleanCallback, false, true);
+ }
+ };
+ return AuthenticationUtil.runAs(doCleanRunAs, AuthenticationUtil.getSystemUserName());
+ }
+
+ /**
+ * Do the actual cleanup. Any errors are handled by this base class.
+ *
+ * @return Returns the cleanup messages.
+ */
+ protected abstract List doCleanInternal() throws Throwable;
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/node/db/NodeServiceCleanupJob.java b/source/java/org/alfresco/repo/node/cleanup/NodeCleanupJob.java
similarity index 58%
rename from source/java/org/alfresco/repo/node/db/NodeServiceCleanupJob.java
rename to source/java/org/alfresco/repo/node/cleanup/NodeCleanupJob.java
index 727e159462..aee4476893 100644
--- a/source/java/org/alfresco/repo/node/db/NodeServiceCleanupJob.java
+++ b/source/java/org/alfresco/repo/node/cleanup/NodeCleanupJob.java
@@ -22,36 +22,50 @@
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
-package org.alfresco.repo.node.db;
+package org.alfresco.repo.node.cleanup;
+
+import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException;
-import org.alfresco.service.cmr.repository.NodeService;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
- * Prompts the Node Service to perform regular cleanup operations.
- *
- * @see NodeService#cleanup()
+ * Scheduled job to call a {@link NodeCleanupWorker}.
+ *
+ * Job data is: nodeCleanupWorker
*
* @author Derek Hulley
- * @since 2.1.6
+ * @since 2.2SP2
*/
-public class NodeServiceCleanupJob implements Job
+public class NodeCleanupJob implements Job
{
+ private static Log logger = LogFactory.getLog(NodeCleanupJob.class);
+
public void execute(JobExecutionContext context) throws JobExecutionException
{
JobDataMap jobData = context.getJobDetail().getJobDataMap();
- // extract the content cleaner to use
- Object nodeServiceObj = jobData.get("nodeService");
- if (nodeServiceObj == null || !(nodeServiceObj instanceof NodeService))
+ // extract the content Cleanup to use
+ Object nodeCleanupWorkerObj = jobData.get("nodeCleanupWorker");
+ if (nodeCleanupWorkerObj == null || !(nodeCleanupWorkerObj instanceof NodeCleanupWorker))
{
throw new AlfrescoRuntimeException(
- "NodeServiceCleanupJob data must contain valid 'nodeService' reference");
+ "NodeCleanupJob data must contain valid 'nodeCleanupWorker' reference");
+ }
+ NodeCleanupWorker nodeCleanupWorker = (NodeCleanupWorker) nodeCleanupWorkerObj;
+ List cleanupLog = nodeCleanupWorker.doClean();
+ // Done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Node cleanup log:");
+ for (String log : cleanupLog)
+ {
+ logger.debug(log);
+ }
}
- NodeService nodeService = (NodeService) nodeServiceObj;
- nodeService.cleanup();
}
}
diff --git a/source/java/org/alfresco/repo/node/cleanup/NodeCleanupRegistry.java b/source/java/org/alfresco/repo/node/cleanup/NodeCleanupRegistry.java
new file mode 100644
index 0000000000..91a4c7c1a6
--- /dev/null
+++ b/source/java/org/alfresco/repo/node/cleanup/NodeCleanupRegistry.java
@@ -0,0 +1,65 @@
+package org.alfresco.repo.node.cleanup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.alfresco.error.StackTraceUtil;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A {@link NodeCleanupWorker worker} that aggregates any number of
+ * {@link #register(NodeCleanupWorker) registered} workers.
+ *
+ * @author Derek Hulley
+ * @since 2.2 SP2
+ */
+public class NodeCleanupRegistry implements NodeCleanupWorker
+{
+ private static Log logger = LogFactory.getLog(NodeCleanupRegistry.class);
+
+ private List cleanupWorkers;
+
+ public NodeCleanupRegistry()
+ {
+ cleanupWorkers = new ArrayList(5);
+ }
+
+ public void register(NodeCleanupWorker cleanupWorker)
+ {
+ cleanupWorkers.add(cleanupWorker);
+ }
+
+ /**
+ * Calls all registered cleaners in order, without transactions or authentication.
+ * The return messages are aggregated.
+ */
+ public List doClean()
+ {
+ List results = new ArrayList(100);
+ for (NodeCleanupWorker cleanupWorker : cleanupWorkers)
+ {
+ try
+ {
+ results.addAll(cleanupWorker.doClean());
+ }
+ catch (Throwable e)
+ {
+ // This failed. The cleaner should be handling this, but we can't guarantee it.
+ logger.error(
+ "NodeCleanupWork doesn't handle all exception conditions: " +
+ cleanupWorker.getClass().getName());
+ StringBuilder sb = new StringBuilder(1024);
+ StackTraceUtil.buildStackTrace(
+ "Node cleanup failed: " +
+ " Worker: " + cleanupWorker.getClass().getName() + "\n" +
+ " Error: ",
+ e.getStackTrace(),
+ sb,
+ 20);
+ results.add(sb.toString());
+ }
+ }
+ return results;
+ }
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/node/cleanup/NodeCleanupWorker.java b/source/java/org/alfresco/repo/node/cleanup/NodeCleanupWorker.java
new file mode 100644
index 0000000000..567b90e9e9
--- /dev/null
+++ b/source/java/org/alfresco/repo/node/cleanup/NodeCleanupWorker.java
@@ -0,0 +1,20 @@
+package org.alfresco.repo.node.cleanup;
+
+import java.util.List;
+
+/**
+ * Interface for classes that implement a snippet of node cleanup.
+ *
+ * @author Derek Hulley
+ * @since 2.2 SP2
+ */
+public interface NodeCleanupWorker
+{
+ /**
+ * Perform some work to clean up data. All errors must be handled and converted
+ * to error messages.
+ *
+ * @return Returns a list of informational messages.
+ */
+ List doClean();
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
index 00930054a8..3e941fc321 100644
--- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
+++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
@@ -42,6 +42,7 @@ import org.alfresco.model.ContentModel;
import org.alfresco.repo.domain.Node;
import org.alfresco.repo.node.AbstractNodeServiceImpl;
import org.alfresco.repo.node.StoreArchiveMap;
+import org.alfresco.repo.node.cleanup.AbstractNodeCleanupWorker;
import org.alfresco.repo.node.db.NodeDaoService.NodeRefQueryCallback;
import org.alfresco.repo.node.index.NodeIndexer;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
@@ -185,7 +186,12 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
public List getStores()
{
// Get the ADM stores
- List storeRefs = nodeDaoService.getStoreRefs();
+ List> stores = nodeDaoService.getStores();
+ List storeRefs = new ArrayList(50);
+ for (Pair pair : stores)
+ {
+ storeRefs.add(pair.getSecond());
+ }
// Now get the AVMStores.
List avmStores = avmNodeService.getStores();
storeRefs.addAll(avmStores);
@@ -2059,7 +2065,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
}
}
- private void indexChildren(Pair nodePair, boolean cascade)
+ public void indexChildren(Pair nodePair, boolean cascade)
{
Long nodeId = nodePair.getFirst();
// Get the node's children, but only one's that aren't in the same store
@@ -2162,21 +2168,29 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
}
}
- @Override
- protected List cleanupImpl()
+ public static class MoveChildrenToCorrectStore extends AbstractNodeCleanupWorker
{
- List moveChildrenResults = moveChildrenToCorrectStore();
- List indexChildrenResults = indexChildrenWhereRequired();
-
- List allResults = new ArrayList(100);
- allResults.addAll(moveChildrenResults);
- allResults.addAll(indexChildrenResults);
-
- // Done
- return allResults;
- }
+ @Override
+ protected List doCleanInternal() throws Throwable
+ {
+ return dbNodeService.moveChildrenToCorrectStore();
+ }
+ };
private List moveChildrenToCorrectStore()
+ {
+ List results = new ArrayList(1000);
+ // Repeat the process for each store
+ List> storePairs = nodeDaoService.getStores();
+ for (Pair storePair : storePairs)
+ {
+ List storeResults = moveChildrenToCorrectStore(storePair.getFirst());
+ results.addAll(storeResults);
+ }
+ return results;
+ }
+
+ private List moveChildrenToCorrectStore(final Long storeId)
{
final List> parentNodePairs = new ArrayList>(100);
final NodeRefQueryCallback callback = new NodeRefQueryCallback()
@@ -2191,7 +2205,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
{
public Object execute() throws Throwable
{
- nodeDaoService.getNodesWithChildrenInDifferentStores(Long.MIN_VALUE, 100, callback);
+ nodeDaoService.getNodesWithChildrenInDifferentStore(storeId, Long.MIN_VALUE, 100, callback);
// Done
return null;
}
@@ -2226,11 +2240,19 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
catch (Throwable e)
{
String msg =
- "Failed to move child nodes to parent node's store: \n" +
+ "Failed to move child nodes to parent node's store." +
+ " Set log level to WARN for this class to get exception log: \n" +
" Parent node: " + parentNodePair.getFirst() + "\n" +
" Error: " + e.getMessage();
- // It failed, which is not an error to consider here
- logger.warn(msg, e);
+ // It failed; do a full log in WARN mode
+ if (logger.isWarnEnabled())
+ {
+ logger.warn(msg, e);
+ }
+ else
+ {
+ logger.error(msg);
+ }
results.add(msg);
}
}
@@ -2248,88 +2270,4 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
}
return results;
}
-
- private List indexChildrenWhereRequired()
- {
- final List> parentNodePairs = new ArrayList>(100);
- final NodeRefQueryCallback callback = new NodeRefQueryCallback()
- {
- public boolean handle(Pair nodePair)
- {
- parentNodePairs.add(nodePair);
- return true;
- }
- };
- RetryingTransactionCallback