Fixed ALF-4551: DeletedNodeCleanupWorker will throw UnsupportedOperationException

- Included unit test to start NodeCleanupRegistry (sanity check only)
 - DAO call to 'purgeNodes'
 - Add job locking to cleanup tasks


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@22255 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2010-09-03 22:55:23 +00:00
parent 10d8672fa9
commit 5f9da921d7
9 changed files with 201 additions and 170 deletions

View File

@@ -20,16 +20,20 @@ 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.domain.node.NodeDAO;
import org.alfresco.repo.lock.JobLockService;
import org.alfresco.repo.lock.LockAcquisitionException;
import org.alfresco.repo.node.db.DbNodeServiceImpl;
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.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.VmShutdownListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -43,18 +47,28 @@ import org.apache.commons.logging.LogFactory;
*/
public abstract class AbstractNodeCleanupWorker implements NodeCleanupWorker
{
/** Lock key: system:NodeCleanup */
private static final QName LOCK = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "NodeCleanup");
/** Default Lock time to live: 1 minute */
private static final long LOCK_TTL = 60*1000L;
protected final Log logger;
private final ReentrantLock cleanupLock;
private NodeCleanupRegistry registry;
protected TransactionService transactionService;
protected JobLockService jobLockService;
protected DbNodeServiceImpl dbNodeService;
protected NodeDAO nodeDAO;
private ThreadLocal<String> lockToken = new ThreadLocal<String>();
private VmShutdownListener shutdownListener = new VmShutdownListener("NodeCleanup");
/**
* Default constructor
*/
public AbstractNodeCleanupWorker()
{
logger = LogFactory.getLog(this.getClass());
cleanupLock = new ReentrantLock();
}
public void setRegistry(NodeCleanupRegistry registry)
@@ -67,6 +81,11 @@ public abstract class AbstractNodeCleanupWorker implements NodeCleanupWorker
this.transactionService = transactionService;
}
public void setJobLockService(JobLockService jobLockService)
{
this.jobLockService = jobLockService;
}
public void setDbNodeService(DbNodeServiceImpl dbNodeService)
{
this.dbNodeService = dbNodeService;
@@ -81,6 +100,7 @@ public abstract class AbstractNodeCleanupWorker implements NodeCleanupWorker
{
PropertyCheck.mandatory(this, "registry", registry);
PropertyCheck.mandatory(this, "transactionService", transactionService);
PropertyCheck.mandatory(this, "jobLockService", jobLockService);
PropertyCheck.mandatory(this, "dbNodeService", dbNodeService);
PropertyCheck.mandatory(this, "nodeDAO", nodeDAO);
@@ -94,46 +114,52 @@ public abstract class AbstractNodeCleanupWorker implements NodeCleanupWorker
*/
public List<String> doClean()
{
/** Prevent multiple executions of the implementation method */
boolean locked = cleanupLock.tryLock();
if (locked)
try
{
try
// Get a lock
lockToken.set(null);
String token = jobLockService.getLock(LOCK, LOCK_TTL);
lockToken.set(token);
// Do the work
return doCleanWithTxn();
}
catch (LockAcquisitionException e)
{
// Some other process was busy
return Collections.singletonList("Node cleanup in process: " + e.getMessage());
}
catch (Throwable e)
{
if (logger.isDebugEnabled())
{
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.getMessage(),
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.getMessage(),
e.getStackTrace(),
sb,
20);
return Collections.singletonList(sb.toString());
}
finally
{
cleanupLock.unlock();
"Node cleanup failed: " +
" Worker: " + this.getClass().getName() + "\n" +
" Error: " + e.getMessage(),
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.getMessage(),
e.getStackTrace(),
sb,
20);
return Collections.singletonList(sb.toString());
}
else
finally
{
return Collections.emptyList();
String token = this.lockToken.get();
if (token != null)
{
jobLockService.releaseLock(token, LOCK);
}
}
}
@@ -156,6 +182,24 @@ public abstract class AbstractNodeCleanupWorker implements NodeCleanupWorker
return AuthenticationUtil.runAs(doCleanRunAs, AuthenticationUtil.getSystemUserName());
}
/**
* Helper method to refresh the current job's lock token
*/
protected void refreshLock() throws LockAcquisitionException
{
String token = this.lockToken.get();
if (token != null && !shutdownListener.isVmShuttingDown())
{
// We had a lock token AND the VM is still going
jobLockService.refreshLock(token, LOCK, LOCK_TTL);
}
else
{
// There is no lock token on this thread, so we trigger a deliberate failure
jobLockService.refreshLock("lock token not available", LOCK, LOCK_TTL);
}
}
/**
* Do the actual cleanup. Any errors are handled by this base class.
*