diff --git a/source/java/org/alfresco/repo/domain/locks/AbstractLockDAOImpl.java b/source/java/org/alfresco/repo/domain/locks/AbstractLockDAOImpl.java index 4eb8d0548b..e40e90c1b2 100644 --- a/source/java/org/alfresco/repo/domain/locks/AbstractLockDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/locks/AbstractLockDAOImpl.java @@ -175,6 +175,22 @@ public abstract class AbstractLockDAOImpl implements LockDAO return updateLocks(lockQName, lockToken, LOCK_TOKEN_RELEASED, 0L, optimistic); } + @Override + public boolean releaseLockQuiet(QName lockQName, String lockToken) + { + try + { + updateLocks(lockQName, lockToken, LOCK_TOKEN_RELEASED, 0L, false); + // It worked + return true; + } + catch (LockAcquisitionException e) + { + // We absorb this + return false; + } + } + /** * Put new values against the given exclusive lock. This works against the related locks as well. * @param optimistic true if a mismatch in the number of locked rows should diff --git a/source/java/org/alfresco/repo/domain/locks/LockDAO.java b/source/java/org/alfresco/repo/domain/locks/LockDAO.java index 70b6896ae5..a386728093 100644 --- a/source/java/org/alfresco/repo/domain/locks/LockDAO.java +++ b/source/java/org/alfresco/repo/domain/locks/LockDAO.java @@ -71,11 +71,22 @@ public interface LockDAO * @param lockToken the current lock token * @param optimistic true if the release attempt is enough even * if the number of released locks was incorrect. - * @return true if the lock was successfully (and completely) - * released or false if the lock was no longer valid - * and the method was being called optimistically. + * @return Returns true if all the required locks were + * (still) held under the lock token and were + * valid at the time of release, otherwise false * @throws LockAcquisitionException if the number of locks released was incorrect * and pessimistic release is requested. */ boolean releaseLock(QName lockQName, String lockToken, boolean optimistic); + + /** + * Release a lock without throwing any exceptions if the lock was not updated. + * + * @param lockQName the unique name of the lock to release + * @param lockToken the current lock token + * @return Returns true if all the required locks were + * (still) held under the lock token and were + * valid at the time of release, otherwise false + */ + boolean releaseLockQuiet(QName lockQName, String lockToken); } diff --git a/source/java/org/alfresco/repo/lock/JobLockServiceImpl.java b/source/java/org/alfresco/repo/lock/JobLockServiceImpl.java index be94db3738..796c8d968d 100644 --- a/source/java/org/alfresco/repo/lock/JobLockServiceImpl.java +++ b/source/java/org/alfresco/repo/lock/JobLockServiceImpl.java @@ -136,6 +136,7 @@ public class JobLockServiceImpl implements JobLockService /** * {@inheritDoc} */ + @Override public void getTransactionalLock(QName lockQName, long timeToLive) { getTransactionalLock(lockQName, timeToLive, defaultRetryWait, defaultRetryCount); @@ -144,6 +145,7 @@ public class JobLockServiceImpl implements JobLockService /** * {@inheritDoc} */ + @Override public void getTransactionalLock(QName lockQName, long timeToLive, long retryWait, int retryCount) { // Check that transaction is present @@ -204,6 +206,7 @@ public class JobLockServiceImpl implements JobLockService * * @see #getLock(QName, long, long, int) */ + @Override public String getLock(QName lockQName, long timeToLive) { return getLock(lockQName, timeToLive, defaultRetryWait, defaultRetryCount); @@ -212,6 +215,7 @@ public class JobLockServiceImpl implements JobLockService /** * {@inheritDoc} */ + @Override public String getLock(QName lockQName, long timeToLive, long retryWait, int retryCount) { String lockToken = GUID.generate(); @@ -225,6 +229,7 @@ public class JobLockServiceImpl implements JobLockService * * @throws LockAcquisitionException on failure */ + @Override public void refreshLock(final String lockToken, final QName lockQName, final long timeToLive) { RetryingTransactionCallback refreshLockCallback = new RetryingTransactionCallback() @@ -347,16 +352,13 @@ public class JobLockServiceImpl implements JobLockService } // The callback is no longer active, so we don't need to refresh. // Release the lock in case the initiator did not do it. - try + // We just want to release and don't care if the lock was already released + // or taken by another process + if (releaseLockQuiet(lockToken, lockQName)) { - releaseLock(lockToken, lockQName); // The callback must be informed as we released the lock automatically callLockReleased(callback); } - catch (LockAcquisitionException e) - { - // The lock is already gone: job done - } } else { @@ -419,6 +421,7 @@ public class JobLockServiceImpl implements JobLockService /** * {@inheritDoc} */ + @Override public void releaseLock(final String lockToken, final QName lockQName) { releaseLockVerify(lockToken, lockQName); @@ -439,6 +442,27 @@ public class JobLockServiceImpl implements JobLockService return retryingTransactionHelper.doInTransaction(releaseCallback, false, true); } + /** + * Attempt to release a lock but do not worry about not being able to update the lock. + * If the lock was taken by another process, then it will not matter. Any other database-related + * conditions will still trigger a retry. + * + * @param lockToken the unique lock token to release (expired or not) + * @param lockQName the name of the lock + * @return true if the lock was released or false if not + */ + private boolean releaseLockQuiet(final String lockToken, final QName lockQName) + { + RetryingTransactionCallback releaseCallback = new RetryingTransactionCallback() + { + public Boolean execute() throws Throwable + { + return lockDAO.releaseLockQuiet(lockQName, lockToken); + } + }; + return retryingTransactionHelper.doInTransaction(releaseCallback, false, true); + } + /** * @throws LockAcquisitionException on failure */ diff --git a/source/test-java/org/alfresco/repo/domain/locks/LockDAOTest.java b/source/test-java/org/alfresco/repo/domain/locks/LockDAOTest.java index d16e6e4526..fa0a7c9590 100644 --- a/source/test-java/org/alfresco/repo/domain/locks/LockDAOTest.java +++ b/source/test-java/org/alfresco/repo/domain/locks/LockDAOTest.java @@ -238,6 +238,12 @@ public class LockDAOTest extends TestCase String token = lock(lockAAA, 500000L, true); release(lockAAA, token, true); token = lock(lockAAA, 0L, true); + + // Check that the lock cannot be release when not held + release(lockAAA, "Invalid-Token", false); + assertFalse(lockDAO.releaseLockQuiet(lockAAA, "invalidToken")); + assertTrue(lockDAO.releaseLockQuiet(lockAAA, token)); + assertFalse(lockDAO.releaseLockQuiet(lockAAA, token)); } public void testReleaseLockRepeated() throws Exception