{
- public Object execute();
+ /**
+ * Perform a unit of transactional work.
+ *
+ * @return Return the result of the unit of work
+ * @throws Throwable This can be anything and will guarantee either a retry or a rollback
+ */
+ public Result execute() throws Throwable;
};
/**
@@ -81,11 +124,53 @@ public class RetryingTransactionHelper
* because of an error not the result of an optimistic locking failure,
* or a deadlock loser failure, or until a maximum number of retries have
* been attempted.
- * @param cb The callback containing the unit of work.
- * @param readOnly Whether this is a read only transaction.
- * @return The result of the unit of work.
+ *
+ * If there is already an active transaction, then the callback is merely
+ * executed and any retry logic is left to the caller. The transaction
+ * will attempt to be read-write.
+ *
+ * @param cb The callback containing the unit of work.
+ * @return Returns the result of the unit of work.
*/
- public Object doInTransaction(Callback cb, boolean readOnly)
+ public R doInTransaction(RetryingTransactionCallback cb)
+ {
+ return doInTransaction(cb, false, false);
+ }
+
+ /**
+ * Execute a callback in a transaction until it succeeds, fails
+ * because of an error not the result of an optimistic locking failure,
+ * or a deadlock loser failure, or until a maximum number of retries have
+ * been attempted.
+ *
+ * If there is already an active transaction, then the callback is merely
+ * executed and any retry logic is left to the caller.
+ *
+ * @param cb The callback containing the unit of work.
+ * @param readOnly Whether this is a read only transaction.
+ * @return Returns the result of the unit of work.
+ */
+ public R doInTransaction(RetryingTransactionCallback cb, boolean readOnly)
+ {
+ return doInTransaction(cb, readOnly, false);
+ }
+
+ /**
+ * Execute a callback in a transaction until it succeeds, fails
+ * because of an error not the result of an optimistic locking failure,
+ * or a deadlock loser failure, or until a maximum number of retries have
+ * been attempted.
+ *
+ * It is possible to force a new transaction to be created or to partake in
+ * any existing transaction.
+ *
+ * @param cb The callback containing the unit of work.
+ * @param readOnly Whether this is a read only transaction.
+ * @param newTransaction true to force a new transaction or
+ * false to partake in any existing transaction.
+ * @return Returns the result of the unit of work.
+ */
+ public R doInTransaction(RetryingTransactionCallback cb, boolean readOnly, boolean newTransaction)
{
// Track the last exception caught, so that we
// can throw it if we run out of retries.
@@ -96,7 +181,14 @@ public class RetryingTransactionHelper
boolean isNew = false;
try
{
- txn = fTxnService.getUserTransaction(readOnly);
+ if (newTransaction)
+ {
+ txn = fTxnService.getNonPropagatingUserTransaction();
+ }
+ else
+ {
+ txn = fTxnService.getUserTransaction(readOnly);
+ }
// Do we need to handle transaction demarcation. If
// no, we cannot do retries, that will be up to the containing
// transaction.
@@ -106,7 +198,7 @@ public class RetryingTransactionHelper
txn.begin();
}
// Do the work.
- Object result = cb.execute();
+ R result = cb.execute();
// Only commit if we 'own' the transaction.
if (isNew)
{
@@ -160,45 +252,28 @@ public class RetryingTransactionHelper
}
lastException = (e instanceof RuntimeException) ?
(RuntimeException)e : new AlfrescoRuntimeException("Unknown Exception in Transaction.", e);
- Throwable t = e;
- boolean shouldRetry = false;
- while (t != null)
+ // Check if there is a cause for retrying
+ Throwable retryCause = ExceptionStackUtil.getCause(e, RETRY_EXCEPTIONS);
+ if (retryCause != null)
{
- // These are the 'OK' exceptions. These mean we can retry.
- if (t instanceof ConcurrencyFailureException ||
- t instanceof DeadlockLoserDataAccessException ||
- t instanceof StaleObjectStateException ||
- t instanceof LockAcquisitionException)
+ // Sleep a random amount of time before retrying.
+ // The sleep interval increases with the number of retries.
+ try
{
- shouldRetry = true;
- // Sleep a random amount of time before retrying.
- // The sleep interval increases with the number of retries.
- try
- {
- Thread.sleep(fRandom.nextInt(500 * count + 500));
- }
- catch (InterruptedException ie)
- {
- // Do nothing.
- }
- break;
+ Thread.sleep(fRandom.nextInt(500 * count + 500));
}
- if (t == t.getCause())
+ catch (InterruptedException ie)
{
- break;
+ // Do nothing.
}
- t = t.getCause();
- }
- if (shouldRetry)
- {
+ // Try again
continue;
}
- // It was a 'bad' exception.
- if (e instanceof RuntimeException)
+ else
{
- throw (RuntimeException)e;
+ // It was a 'bad' exception.
+ throw lastException;
}
- throw new AlfrescoRuntimeException("Exception in Transaction.", e);
}
}
// We've worn out our welcome and retried the maximum number of times.
diff --git a/source/java/org/alfresco/repo/transaction/TransactionUtil.java b/source/java/org/alfresco/repo/transaction/TransactionUtil.java
index f0c6f512a6..3bb5c22f7e 100644
--- a/source/java/org/alfresco/repo/transaction/TransactionUtil.java
+++ b/source/java/org/alfresco/repo/transaction/TransactionUtil.java
@@ -37,7 +37,9 @@ import org.apache.commons.logging.LogFactory;
/**
* Class containing transactions helper methods and interfaces.
*
- * @author Roy Wetherall
+ * @deprecated Use a {@link RetryingTransactionHelper} instance
+ *
+ * @author Derek Hulley
*/
public class TransactionUtil
{
@@ -48,6 +50,9 @@ public class TransactionUtil
*
* This interface encapsulates a unit of work that should be done within a
* transaction.
+ *
+ * @deprecated
+ * @see RetryingTransactionHelper.RetryingTransactionCallback
*/
public interface TransactionWork
{
@@ -74,6 +79,8 @@ public class TransactionUtil
* @param transactionWork the transaction work
*
* @throws java.lang.RuntimeException if the transaction was rolled back
+ *
+ * @deprecated Use a {@link RetryingTransactionHelper} instance
*/
public static R executeInUserTransaction(
TransactionService transactionService,
@@ -91,6 +98,8 @@ public class TransactionUtil
* @param readOnly true if the transaction should be read-only
*
* @throws java.lang.RuntimeException if the transaction was rolled back
+ *
+ * @deprecated Use a {@link RetryingTransactionHelper} instance
*/
public static R executeInUserTransaction(
TransactionService transactionService,
@@ -108,6 +117,8 @@ public class TransactionUtil
* @param transactionWork the transaction work
*
* @throws java.lang.RuntimeException if the transaction was rolled back
+ *
+ * @deprecated Use a {@link RetryingTransactionHelper} instance
*/
public static R executeInNonPropagatingUserTransaction(
TransactionService transactionService,
@@ -125,6 +136,8 @@ public class TransactionUtil
* @param readOnly true if the transaction should be read-only
*
* @throws java.lang.RuntimeException if the transaction was rolled back
+ *
+ * @deprecated Use a {@link RetryingTransactionHelper} instance
*/
public static R executeInNonPropagatingUserTransaction(
TransactionService transactionService,