mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-08 14:51:49 +00:00
Merged 5.1-MNT1 (5.1.0) to HEAD (5.1)
115548 adavis: Merged 5.1.N (5.1.1) to 5.1-MNT1 (5.1.0) 112342 adavis: Merged 5.0.N (5.0.3) to 5.1.N (5.1.0) 112260: MNT-14526 : JobLockService add getLock with refresh callback variant git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@115685 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -141,10 +141,25 @@ public interface JobLockService
|
|||||||
*/
|
*/
|
||||||
String getLock(QName lockQName, long timeToLive, long retryWait, int retryCount);
|
String getLock(QName lockQName, long timeToLive, long retryWait, int retryCount);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take a manually-managed lock and provide a callback to refresh it periodically.
|
||||||
|
* A convenience wrapper around {@link #getLock(QName,long)}
|
||||||
|
* and {@link #refreshLock(String,QName,long,JobLockRefreshCallback)}.
|
||||||
|
*
|
||||||
|
* @param lockQName the name of the lock to acquire
|
||||||
|
* @param timeToLive the time (in milliseconds) for the lock to remain valid.
|
||||||
|
* This value <b>must not</b> be larger than either the anticipated
|
||||||
|
* operation time or a server startup time. Typically, it should be
|
||||||
|
* a few seconds.
|
||||||
|
* @param callback the object that will be called at intervals of timeToLive/2 (about)
|
||||||
|
* @return Returns the newly-created lock token, or null if callback not active.
|
||||||
|
* @throws LockAcquisitionException if the lock could not be acquired
|
||||||
|
*/
|
||||||
|
String getLock(QName lockQName, long timeToLive, JobLockRefreshCallback callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh the lock using a valid lock token.
|
* Refresh the lock using a valid lock token.
|
||||||
*
|
*
|
||||||
* @param lockToken the lock token returned when the lock was acquired
|
|
||||||
* @param lockQName the name of the previously-acquired lock
|
* @param lockQName the name of the previously-acquired lock
|
||||||
* @param timeToLive the time (in milliseconds) for the lock to remain valid
|
* @param timeToLive the time (in milliseconds) for the lock to remain valid
|
||||||
* @throws LockAcquisitionException if the lock could not be refreshed or acquired
|
* @throws LockAcquisitionException if the lock could not be refreshed or acquired
|
||||||
@@ -159,8 +174,9 @@ public interface JobLockService
|
|||||||
* Since the lock is not actually refreshed by this method, there will be no LockAcquisitionException.
|
* Since the lock is not actually refreshed by this method, there will be no LockAcquisitionException.
|
||||||
* <p/>
|
* <p/>
|
||||||
* The TTL (time to live) will be divided by two and the result used to trigger a timer thread
|
* The TTL (time to live) will be divided by two and the result used to trigger a timer thread
|
||||||
* to initiate the callback.
|
* to initiate the callback. The first refresh will occur after TTL/2 and no significant work
|
||||||
*
|
* should be done between acquiring a lock and calling this method, to prevent expiration.
|
||||||
|
*
|
||||||
* @param lockToken the lock token returned when the lock was acquired
|
* @param lockToken the lock token returned when the lock was acquired
|
||||||
* @param lockQName the name of the previously-acquired lock
|
* @param lockQName the name of the previously-acquired lock
|
||||||
* @param timeToLive the time (in milliseconds) for the lock to remain valid
|
* @param timeToLive the time (in milliseconds) for the lock to remain valid
|
||||||
|
@@ -222,7 +222,7 @@ public class JobLockServiceImpl implements JobLockService
|
|||||||
// Done
|
// Done
|
||||||
return lockToken;
|
return lockToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
@@ -269,6 +269,28 @@ public class JobLockServiceImpl implements JobLockService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getLock(QName lockQName, long timeToLive, JobLockRefreshCallback callback)
|
||||||
|
{
|
||||||
|
if (lockQName == null) throw new IllegalArgumentException("lock name null");
|
||||||
|
if (callback == null) throw new IllegalArgumentException("callback null");
|
||||||
|
|
||||||
|
String lockToken = getLock(lockQName, timeToLive);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
refreshLock(lockToken, lockQName, timeToLive, callback);
|
||||||
|
return lockToken;
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException|LockAcquisitionException e)
|
||||||
|
{
|
||||||
|
this.releaseLockVerify(lockToken, lockQName);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@@ -29,6 +29,8 @@ import org.alfresco.service.namespace.QName;
|
|||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.alfresco.test_category.OwnJVMTestsCategory;
|
import org.alfresco.test_category.OwnJVMTestsCategory;
|
||||||
import org.alfresco.util.ApplicationContextHelper;
|
import org.alfresco.util.ApplicationContextHelper;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.apache.log4j.Level;
|
import org.apache.log4j.Level;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.junit.experimental.categories.Category;
|
import org.junit.experimental.categories.Category;
|
||||||
@@ -50,6 +52,8 @@ public class JobLockServiceTest extends TestCase
|
|||||||
{
|
{
|
||||||
public static final String NAMESPACE = "http://www.alfresco.org/test/JobLockServiceTest";
|
public static final String NAMESPACE = "http://www.alfresco.org/test/JobLockServiceTest";
|
||||||
|
|
||||||
|
private static final Log logger = LogFactory.getLog(JobLockServiceTest.class);
|
||||||
|
|
||||||
private ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
|
private ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
|
||||||
|
|
||||||
private TransactionService transactionService;
|
private TransactionService transactionService;
|
||||||
@@ -473,4 +477,128 @@ public class JobLockServiceTest extends TestCase
|
|||||||
assertEquals("Lock callback timer was not terminated", checkedCount, checked[0]);
|
assertEquals("Lock callback timer was not terminated", checkedCount, checked[0]);
|
||||||
assertEquals("Lock callback timer was not terminated", releasedCount, released[0]);
|
assertEquals("Lock callback timer was not terminated", releasedCount, released[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testGetLockWithCallbackNullLock() { runGetLockWithCallback(0); }
|
||||||
|
public void testGetLockWithCallbackNullCallback() { runGetLockWithCallback(1); }
|
||||||
|
public void testGetLockWithCallbackShortTTL() { runGetLockWithCallback(2); }
|
||||||
|
public void testGetLockWithCallbackLocked() { runGetLockWithCallback(3); }
|
||||||
|
public void testGetLockWithCallbackNormal() { runGetLockWithCallback(4); }
|
||||||
|
|
||||||
|
public void runGetLockWithCallback(int t)
|
||||||
|
{
|
||||||
|
logger.debug("runGetLockWithCallback "+t+
|
||||||
|
"\n----------------------------------------"+
|
||||||
|
"\n"+Thread.currentThread().getStackTrace()[2].getMethodName()+
|
||||||
|
"\n----------------------------------------");
|
||||||
|
|
||||||
|
String token = null;
|
||||||
|
String tokenB = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
QName lockName = t==0 ? null : lockA;
|
||||||
|
TestCallback callback = t==1 ? null : new TestCallback();
|
||||||
|
long timeToLive = t==2 ? 1 : 50;
|
||||||
|
|
||||||
|
if (t==3)
|
||||||
|
{
|
||||||
|
long ttlLongerThanDefaultRetry = 300;
|
||||||
|
tokenB = jobLockService.getLock(lockA, ttlLongerThanDefaultRetry);
|
||||||
|
}
|
||||||
|
|
||||||
|
token = jobLockService.getLock(lockName, timeToLive, callback);
|
||||||
|
|
||||||
|
if (t<4) fail("expected getLock to fail");
|
||||||
|
|
||||||
|
if (callback == null) throw new IllegalStateException();
|
||||||
|
|
||||||
|
assertEquals(false,callback.released);
|
||||||
|
assertEquals(0,callback.isActiveCount);
|
||||||
|
|
||||||
|
Thread.sleep(40);
|
||||||
|
|
||||||
|
assertEquals(false,callback.released);
|
||||||
|
assertEquals(1,callback.isActiveCount);
|
||||||
|
|
||||||
|
callback.isActive = false;
|
||||||
|
|
||||||
|
Thread.sleep(40);
|
||||||
|
|
||||||
|
assertEquals(true,callback.released);
|
||||||
|
assertEquals(2,callback.isActiveCount);
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e)
|
||||||
|
{
|
||||||
|
switch (t)
|
||||||
|
{
|
||||||
|
case 0: logger.debug("null lock => exception as expected: "+e); break;
|
||||||
|
case 1: logger.debug("null callback => exception as expected: "+e); break;
|
||||||
|
case 2: logger.debug("short ttl => exception as expected: "+e); break;
|
||||||
|
default: fail("exception not expected: "+e); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (LockAcquisitionException e)
|
||||||
|
{
|
||||||
|
switch (t)
|
||||||
|
{
|
||||||
|
case 3: logger.debug("already locked => exception as expected: "+e); break;
|
||||||
|
default: fail("exception not expected: "+e); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
fail("exception not expected: "+e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (token != null)
|
||||||
|
{
|
||||||
|
logger.debug("token should have been released");
|
||||||
|
if (jobLockService.releaseLockVerify(token, lockA))
|
||||||
|
{
|
||||||
|
fail("token not released");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenB != null)
|
||||||
|
{
|
||||||
|
logger.debug("tokenB should be released");
|
||||||
|
jobLockService.releaseLockVerify(tokenB, lockA);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
logger.debug("lock should have been released so check can acquire");
|
||||||
|
String tokenC = jobLockService.getLock(lockA, 50);
|
||||||
|
jobLockService.releaseLock(tokenC, lockA);
|
||||||
|
}
|
||||||
|
catch (LockAcquisitionException e)
|
||||||
|
{
|
||||||
|
fail("lock not released");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("runGetLockWithCallback\n----------------------------------------");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestCallback implements JobLockRefreshCallback
|
||||||
|
{
|
||||||
|
public volatile long isActiveCount;
|
||||||
|
public volatile boolean released;
|
||||||
|
public volatile boolean isActive = true;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive()
|
||||||
|
{
|
||||||
|
isActiveCount++;
|
||||||
|
logger.debug("TestCallback.isActive => "+isActive+" ("+isActiveCount+")");
|
||||||
|
return isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lockReleased()
|
||||||
|
{
|
||||||
|
logger.debug("TestCallback.lockReleased");
|
||||||
|
released = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user