From 3e6468bd02161d6452a6b50ebfb7d36b64ec2e2b Mon Sep 17 00:00:00 2001 From: Britt Park Date: Tue, 12 Dec 2006 04:01:25 +0000 Subject: [PATCH] Added RetryingTransactionAdvice and wired it up for AVMService. The tests pass and the app still works. Added new convenience target to common.xml, run-junit. It syntactically like run-test but doesn't use the junit ant task, which has the unfortunate characteristic of buffering stdout and stderr in memory. This may be fine in many cases but wreaks havoc with memory use if you're test spews lots of diagnostics. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4577 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/core-services-context.xml | 42 +++++ config/alfresco/public-services-context.xml | 101 +++++++++++++ .../RetryingTransactionAdvice.java | 143 ++++++++++++++++++ 3 files changed, 286 insertions(+) create mode 100644 source/java/org/alfresco/repo/transaction/RetryingTransactionAdvice.java diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index bb03702f7a..bb062a3df6 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -158,6 +158,48 @@ + + + PROPAGATION_REQUIRED + + + false + + + + + + PROPAGATION_REQUIRED + + + true + + + + + + + + + + + + 10 + + + + + + + + + + + + 10 + + + diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index 25b2581599..d9a3e9e2d6 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -1023,6 +1023,106 @@ + + + + + + + getFileInputStream + getContentReader + getDirectoryListing + getDirectoryListingDirect + getDirectoryListingArray + getDeleted + getNextVersionID + getLatestSnapshotID + getAVMStoreVersions + getAVMStores + getAVMStoreRoot + lookup + getPaths + getHeadPaths + getPathsInStoreHead + getIndirectionPath + getHistory + getCommonAncestor + getLayereingInfo + getNodeProperty + getNodeProperties + getStoreProperty + getStoreProperties + queryStorePropertyKey + queryStoresPropertyKeys + getContentDataForRead + getAspects + hasAspect + + + + + + + + + + + getFileOutputStream + createContentWriter + createFile + createDirectory + createLayeredFile + createLayeredDirectory + retargetLayeredDirectory + createAVMStore + createBranch + removeNode + rename + uncover + flatten + createSnapshot + purgeAVMStore + purgeVersion + makePrimary + setOpacity + setNodeProperty + setNodeProperties + deleteNodeProperty + deleteNodeProperties + setStoreProperty + setStoreProperties + deleteStoreProperty + getContentDataForWrite + setContentData + setMetaDataFrom + addAspect + removeAspect + link + forceCopy + copy + renameStore + + + + + + + + org.alfresco.service.cmr.avm.AVMService + + + + avmService + + + + avmServiceWriteTxnAdvisor + avmServiceReadTxnAdvisor + + + + + diff --git a/source/java/org/alfresco/repo/transaction/RetryingTransactionAdvice.java b/source/java/org/alfresco/repo/transaction/RetryingTransactionAdvice.java new file mode 100644 index 0000000000..2032648874 --- /dev/null +++ b/source/java/org/alfresco/repo/transaction/RetryingTransactionAdvice.java @@ -0,0 +1,143 @@ +/** + * + */ +package org.alfresco.repo.transaction; + +import java.util.Random; + +import org.alfresco.error.AlfrescoRuntimeException; +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 (Throwable e) + { + if (txn != null && isNewTxn && !txn.isCompleted()) + { + fTxnManager.rollback(txn); + } + if (e instanceof ConcurrencyFailureException || + e instanceof DeadlockLoserDataAccessException || + e instanceof StaleObjectStateException || + e instanceof LockAcquisitionException) + { + if (!isNewTxn) + { + throw (RuntimeException)e; + } + lastException = (RuntimeException)e; + try + { + Thread.sleep(fRandom.nextInt(500 * count + 500)); + } + catch (InterruptedException ie) + { + // Do nothing. + } + continue; + } + if (e instanceof RuntimeException) + { + throw (RuntimeException)e; + } + throw new AlfrescoRuntimeException("Failure in Transaction.", e); + } + } + fgLogger.error("Txn Failed after " + fMaxRetries + " retries:", lastException); + throw lastException; + } +}