- 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:
Derek Hulley
2009-04-15 13:52:12 +00:00
parent 6a20eda27f
commit 5bda334c22
6 changed files with 180 additions and 57 deletions

View File

@@ -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.

View File

@@ -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
}
/**

View File

@@ -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);
}

View File

@@ -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) {}
}

View File

@@ -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);

View File

@@ -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);
}
}