mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Lock DAO
- Use LockAcquisitionException instead of boolean for failures - Lock update with TTL=0 sets the start and expiry time to 0 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@13956 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -19,3 +19,9 @@ system.openoffice.info.connection_verified=The connection to OpenOffice has been
|
||||
system.openoffice.err.connection_failed=An initial OpenOffice connection could not be established.
|
||||
system.openoffice.err.connection_lost=The OpenOffice connection has been lost.
|
||||
system.openoffice.err.connection_remade=The OpenOffice connection was re-established.
|
||||
|
||||
# Locks
|
||||
system.locks.err.failed_to_acquire_lock=Failed to get lock ''{0}'' using token ''{1}''.
|
||||
system.locks.err.lock_resource_missing=Failed to manipulate lock ''{0}'' using token ''{1}''. The lock resource no longer exists.
|
||||
system.locks.err.lock_update_count=Failed to update lock ''{0}'' using token ''{1}''. {2} locks were updated when {3} should have been.
|
||||
system.locks.err.excl_lock_exists=Failed to get lock ''{0}'' using token ''{1}''. An exclusive lock in the hierarchy exists.
|
@@ -31,6 +31,7 @@ import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.alfresco.repo.domain.QNameDAO;
|
||||
import org.alfresco.repo.lock.LockAcquisitionException;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.EqualsHelper;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
@@ -63,7 +64,7 @@ public abstract class AbstractLockDAOImpl implements LockDAO
|
||||
this.qnameDAO = qnameDAO;
|
||||
}
|
||||
|
||||
public boolean getLock(QName lockQName, String lockToken, long timeToLive)
|
||||
public void getLock(QName lockQName, String lockToken, long timeToLive)
|
||||
{
|
||||
String qnameNamespaceUri = lockQName.getNamespaceURI();
|
||||
String qnameLocalName = lockQName.getLocalName();
|
||||
@@ -114,7 +115,9 @@ public abstract class AbstractLockDAOImpl implements LockDAO
|
||||
boolean canTakeLock = canTakeLock(existingLock, lockToken, requiredExclusiveLockResourceId);
|
||||
if (!canTakeLock)
|
||||
{
|
||||
return false;
|
||||
throw new LockAcquisitionException(
|
||||
LockAcquisitionException.ERR_EXCLUSIVE_LOCK_EXISTS,
|
||||
lockQName, lockToken);
|
||||
}
|
||||
existingLocksMap.put(existingLock, existingLock);
|
||||
}
|
||||
@@ -142,24 +145,25 @@ public abstract class AbstractLockDAOImpl implements LockDAO
|
||||
timeToLive);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
// Done
|
||||
}
|
||||
|
||||
public boolean refreshLock(QName lockQName, String lockToken, long timeToLive)
|
||||
public void refreshLock(QName lockQName, String lockToken, long timeToLive)
|
||||
{
|
||||
return updateLocks(lockQName, lockToken, lockToken, timeToLive);
|
||||
updateLocks(lockQName, lockToken, lockToken, timeToLive);
|
||||
}
|
||||
|
||||
public boolean releaseLock(QName lockQName, String lockToken)
|
||||
public void releaseLock(QName lockQName, String lockToken)
|
||||
{
|
||||
return updateLocks(lockQName, lockToken, LOCK_TOKEN_RELEASED, 0L);
|
||||
updateLocks(lockQName, lockToken, LOCK_TOKEN_RELEASED, 0L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put new values against the given exclusive lock. This works against the related locks as
|
||||
* well.
|
||||
* @throws LockAcquisitionException on failure
|
||||
*/
|
||||
private boolean updateLocks(QName lockQName, String lockToken, String newLockToken, long timeToLive)
|
||||
private void updateLocks(QName lockQName, String lockToken, String newLockToken, long timeToLive)
|
||||
{
|
||||
String qnameNamespaceUri = lockQName.getNamespaceURI();
|
||||
String qnameLocalName = lockQName.getLocalName();
|
||||
@@ -181,7 +185,9 @@ public abstract class AbstractLockDAOImpl implements LockDAO
|
||||
if (exclusiveLockResource == null)
|
||||
{
|
||||
// If the exclusive lock doesn't exist, the locks don't exist
|
||||
return false;
|
||||
throw new LockAcquisitionException(
|
||||
LockAcquisitionException.ERR_LOCK_RESOURCE_MISSING,
|
||||
lockQName, lockToken);
|
||||
}
|
||||
Long exclusiveLockResourceId = exclusiveLockResource.getId();
|
||||
// Split the lock name
|
||||
@@ -194,12 +200,11 @@ public abstract class AbstractLockDAOImpl implements LockDAO
|
||||
// Check
|
||||
if (updateCount != requiredUpdateCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
throw new LockAcquisitionException(
|
||||
LockAcquisitionException.ERR_LOCK_UPDATE_COUNT,
|
||||
lockQName, lockToken, new Integer(updateCount), new Integer(requiredUpdateCount));
|
||||
}
|
||||
// Done
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
package org.alfresco.repo.domain.locks;
|
||||
|
||||
import org.alfresco.repo.lock.LockAcquisitionException;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
@@ -45,8 +46,9 @@ public interface LockDAO
|
||||
* @param timeToLive the time (in milliseconds) that the lock must remain
|
||||
* @return Returns <tt>true</tt> if the lock was taken,
|
||||
* otherwise <tt>false</tt>
|
||||
* @throws LockAcquisitionException on failure
|
||||
*/
|
||||
boolean getLock(QName lockQName, String lockToken, long timeToLive);
|
||||
void getLock(QName lockQName, String lockToken, long timeToLive);
|
||||
|
||||
/**
|
||||
* Refresh a held lock. This is successful if the lock in question still exists
|
||||
@@ -58,8 +60,9 @@ public interface LockDAO
|
||||
* @param timeToLive the new time to live (in milliseconds)
|
||||
* @return Returns <tt>true</tt> if the lock was updated,
|
||||
* otherwise <tt>false</tt>
|
||||
* @throws LockAcquisitionException on failure
|
||||
*/
|
||||
boolean refreshLock(QName lockQName, String lockToken, long timeToLive);
|
||||
void refreshLock(QName lockQName, String lockToken, long timeToLive);
|
||||
|
||||
/**
|
||||
* Release a lock. The lock token must still apply and all the shared and exclusive
|
||||
@@ -75,5 +78,5 @@ public interface LockDAO
|
||||
* (still) held under the lock token and were
|
||||
* valid at the time of release, otherwise <tt>false</tt>
|
||||
*/
|
||||
boolean releaseLock(QName lockQName, String lockToken);
|
||||
void releaseLock(QName lockQName, String lockToken);
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.alfresco.repo.lock.LockAcquisitionException;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
@@ -91,24 +92,32 @@ public class LockDAOTest extends TestCase
|
||||
|
||||
private String lock(final QName lockName, final long timeToLive, boolean expectSuccess)
|
||||
{
|
||||
String token = lock(lockName, timeToLive);
|
||||
if (expectSuccess)
|
||||
try
|
||||
{
|
||||
assertNotNull(
|
||||
"Expected to get lock " + lockName + " with TTL of " + timeToLive,
|
||||
token);
|
||||
String token = lock(lockName, timeToLive);
|
||||
if (!expectSuccess)
|
||||
{
|
||||
fail("Expected lock " + lockName + " to have been denied");
|
||||
}
|
||||
return token;
|
||||
}
|
||||
else
|
||||
catch (LockAcquisitionException e)
|
||||
{
|
||||
assertNull(
|
||||
"Expected lock " + lockName + " to have been denied",
|
||||
token);
|
||||
if (expectSuccess)
|
||||
{
|
||||
// oops
|
||||
throw new RuntimeException("Expected to get lock " + lockName + " with TTL of " + timeToLive, e);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return token;
|
||||
}
|
||||
/**
|
||||
* Do the lock in a new transaction
|
||||
* @return Returns the lock token or <tt>null</tt> if it didn't work
|
||||
* @throws LockAcquisitionException on failure
|
||||
*/
|
||||
private String lock(final QName lockName, final long timeToLive)
|
||||
{
|
||||
@@ -117,8 +126,8 @@ public class LockDAOTest extends TestCase
|
||||
public String execute() throws Throwable
|
||||
{
|
||||
String txnId = AlfrescoTransactionSupport.getTransactionId();
|
||||
boolean locked = lockDAO.getLock(lockName, txnId, timeToLive);
|
||||
return locked ? txnId : null;
|
||||
lockDAO.getLock(lockName, txnId, timeToLive);
|
||||
return txnId;
|
||||
}
|
||||
};
|
||||
return txnHelper.doInTransaction(callback);
|
||||
@@ -130,21 +139,24 @@ public class LockDAOTest extends TestCase
|
||||
{
|
||||
public Boolean execute() throws Throwable
|
||||
{
|
||||
return lockDAO.refreshLock(lockName, lockToken, timeToLive);
|
||||
lockDAO.refreshLock(lockName, lockToken, timeToLive);
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
};
|
||||
Boolean released = txnHelper.doInTransaction(callback);
|
||||
if (expectSuccess)
|
||||
try
|
||||
{
|
||||
assertTrue(
|
||||
"Expected to have refreshed lock " + lockName,
|
||||
released.booleanValue());
|
||||
txnHelper.doInTransaction(callback);
|
||||
if (!expectSuccess)
|
||||
{
|
||||
fail("Expected to have failed to refresh lock " + lockName);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (LockAcquisitionException e)
|
||||
{
|
||||
assertFalse(
|
||||
"Expected to have failed to refresh lock " + lockName,
|
||||
released.booleanValue());
|
||||
if (expectSuccess)
|
||||
{
|
||||
throw new RuntimeException("Expected to have refreshed lock " + lockName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,21 +166,24 @@ public class LockDAOTest extends TestCase
|
||||
{
|
||||
public Boolean execute() throws Throwable
|
||||
{
|
||||
return lockDAO.releaseLock(lockName, lockToken);
|
||||
lockDAO.releaseLock(lockName, lockToken);
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
};
|
||||
Boolean released = txnHelper.doInTransaction(callback);
|
||||
if (expectSuccess)
|
||||
try
|
||||
{
|
||||
assertTrue(
|
||||
"Expected to have released lock " + lockName,
|
||||
released.booleanValue());
|
||||
txnHelper.doInTransaction(callback);
|
||||
if (!expectSuccess)
|
||||
{
|
||||
fail("Expected to have failed to release lock " + lockName);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (LockAcquisitionException e)
|
||||
{
|
||||
assertFalse(
|
||||
"Expected to have failed to release lock " + lockName,
|
||||
released.booleanValue());
|
||||
if (expectSuccess)
|
||||
{
|
||||
throw new RuntimeException("Expected to have released lock " + lockName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,10 +402,15 @@ public class LockDAOTest extends TestCase
|
||||
String tokenAAA = null;
|
||||
while (true)
|
||||
{
|
||||
tokenAAA = lock(lockAAA, 100000L); // Lock for a long time
|
||||
if (tokenAAA != null)
|
||||
try
|
||||
{
|
||||
break; // Got the lock
|
||||
tokenAAA = lock(lockAAA, 100000L); // Lock for a long time
|
||||
// Success
|
||||
break;
|
||||
}
|
||||
catch (LockAcquisitionException e)
|
||||
{
|
||||
// OK. Keep trying.
|
||||
}
|
||||
try { wait(20L); } catch (InterruptedException e) {}
|
||||
}
|
||||
|
@@ -144,8 +144,8 @@ public class LockDAOImpl extends AbstractLockDAOImpl
|
||||
updateLockEntity.setSharedResourceId(lockEntity.getSharedResourceId());
|
||||
updateLockEntity.setExclusiveResourceId(lockEntity.getExclusiveResourceId());
|
||||
updateLockEntity.setLockToken(lockToken);
|
||||
long now = System.currentTimeMillis();
|
||||
long exp = now + timeToLive;
|
||||
long now = (timeToLive > 0) ? System.currentTimeMillis() : 0L;
|
||||
long exp = (timeToLive > 0) ? (now + timeToLive) : 0L;
|
||||
updateLockEntity.setStartTime(new Long(now));
|
||||
updateLockEntity.setExpiryTime(new Long(exp));
|
||||
template.update(UPDATE_LOCK, updateLockEntity, 1);
|
||||
@@ -164,8 +164,8 @@ public class LockDAOImpl extends AbstractLockDAOImpl
|
||||
params.put("exclusiveLockResourceId", exclusiveLockResourceId);
|
||||
params.put("oldLockToken", oldLockToken);
|
||||
params.put("newLockToken", newLockToken);
|
||||
long now = System.currentTimeMillis();
|
||||
long exp = now + timeToLive;
|
||||
long now = (timeToLive > 0) ? System.currentTimeMillis() : 0L;
|
||||
long exp = (timeToLive > 0) ? (now + timeToLive) : 0L;
|
||||
params.put("newStartTime", new Long(now));
|
||||
params.put("newExpiryTime", new Long(exp));
|
||||
int updateCount = template.update(UPDATE_EXCLUSIVE_LOCK, params);
|
||||
|
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 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.lock;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* Exception generated when a lock cannot be acquired.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public class LockAcquisitionException extends AlfrescoRuntimeException
|
||||
{
|
||||
private static final long serialVersionUID = 8215858379509645862L;
|
||||
|
||||
/**
|
||||
* <ul>
|
||||
* <li>1: the qname</li>
|
||||
* <li>2: the lock token</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final String ERR_FAILED_TO_ACQUIRE_LOCK = "system.locks.err.failed_to_acquire_lock";
|
||||
/**
|
||||
* <ul>
|
||||
* <li>1: the qname</li>
|
||||
* <li>2: the lock token</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final String ERR_LOCK_RESOURCE_MISSING = "system.locks.err.lock_resource_missing";
|
||||
/**
|
||||
* <ul>
|
||||
* <li>1: the qname</li>
|
||||
* <li>2: the lock token</li>
|
||||
* <li>3: the actual update count</li>
|
||||
* <li>4: the expected update count</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final String ERR_LOCK_UPDATE_COUNT = "system.locks.err.lock_update_count";
|
||||
/**
|
||||
* <ul>
|
||||
* <li>1: the qname</li>
|
||||
* <li>2: the lock token</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final String ERR_EXCLUSIVE_LOCK_EXISTS = "system.locks.err.excl_lock_exists";
|
||||
|
||||
/**
|
||||
* @param lockQName the lock that was sought
|
||||
* @param lockToken the lock token being used
|
||||
*/
|
||||
public LockAcquisitionException(QName lockQName, String lockToken)
|
||||
{
|
||||
super(ERR_FAILED_TO_ACQUIRE_LOCK, new Object[] {lockQName, lockToken});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param msgId one of the message IDs defined
|
||||
* @param args the arguments that apply
|
||||
*/
|
||||
public LockAcquisitionException(String msgId, Object ... args)
|
||||
{
|
||||
super(msgId, args);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user