/** * */ package org.alfresco.repo.transaction; import java.util.Random; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.apache.log4j.Logger; import org.hibernate.StaleObjectStateException; import org.hibernate.exception.LockAcquisitionException; import org.springframework.aop.framework.ReflectiveMethodInvocation; import org.springframework.dao.ConcurrencyFailureException; import org.springframework.dao.DeadlockLoserDataAccessException; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; /** * * @author britt */ public class RetryingTransactionAdvice implements MethodInterceptor { private static Logger fgLogger = Logger.getLogger(RetryingTransactionAdvice.class); /** * The transaction manager instance. */ private PlatformTransactionManager fTxnManager; /** * The TransactionDefinition. */ private TransactionDefinition fDefinition; /** * The maximum number of retries. */ private int fMaxRetries; /** * A Random number generator for getting retry intervals. */ private Random fRandom; public RetryingTransactionAdvice() { fRandom = new Random(System.currentTimeMillis()); } /** * Setter. */ public void setTransactionManager(PlatformTransactionManager manager) { fTxnManager = manager; } /** * Setter. */ public void setTransactionDefinition(TransactionDefinition def) { fDefinition = def; } /** * Setter. */ public void setMaxRetries(int maxRetries) { fMaxRetries = maxRetries; } /* (non-Javadoc) * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) */ public Object invoke(MethodInvocation methodInvocation) throws Throwable { RuntimeException lastException = null; for (int count = 0; fMaxRetries < -1 || count < fMaxRetries; count++) { TransactionStatus txn = null; boolean isNewTxn = false; try { txn = fTxnManager.getTransaction(fDefinition); isNewTxn = txn.isNewTransaction(); MethodInvocation clone = ((ReflectiveMethodInvocation)methodInvocation).invocableClone(); Object result = clone.proceed(); if (isNewTxn) { fTxnManager.commit(txn); } if (fgLogger.isDebugEnabled()) { if (count != 0) { fgLogger.debug("Transaction succeeded after " + count + " retries."); } } return result; } catch (RuntimeException e) { if (txn != null && isNewTxn && !txn.isCompleted()) { fTxnManager.rollback(txn); } if (!isNewTxn) { throw e; } lastException = e; Throwable t = e; boolean shouldRetry = false; while (t != null) { if (t instanceof ConcurrencyFailureException || t instanceof DeadlockLoserDataAccessException || t instanceof StaleObjectStateException || t instanceof LockAcquisitionException) { shouldRetry = true; try { Thread.sleep(fRandom.nextInt(500 * count + 500)); } catch (InterruptedException ie) { // Do nothing. } break; } // Apparently java.lang.Throwable default the cause as 'this'. if (t == t.getCause()) { break; } t = t.getCause(); } if (shouldRetry) { fgLogger.warn("Retry #" + (count + 1)); continue; } throw e; } } fgLogger.error("Txn Failed after " + fMaxRetries + " retries:", lastException); throw lastException; } }