Loading pom.xml +20 −2 Original line number Diff line number Diff line Loading @@ -72,8 +72,26 @@ <dependency> <groupId>com.inteligr8.alfresco</groupId> <artifactId>aspectj-platform-module</artifactId> <version>1.0.0</version> <scope>provided</scope> <version>1.0.1</version> <type>amp</type> </dependency> <!-- AMP resources are included in the WAR, not the extension directory; this makes aspectjweaver available to javaagent --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> <scope>test</scope> </dependency> <!-- Removes startup warning regarding annotations that aren't annotations --> <dependency> <groupId>jakarta.transaction</groupId> <artifactId>jakarta.transaction-api</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency> </dependencies> Loading src/main/java/com/inteligr8/alfresco/annotations/TransactionalWrapper.java 0 → 100644 +113 −0 Original line number Diff line number Diff line package com.inteligr8.alfresco.annotations; import java.lang.reflect.Method; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; public class TransactionalWrapper { private Transactional stxl = null; private javax.transaction.Transactional jtxl = null; public TransactionalWrapper(Transactional txl) { this.stxl = txl; } public TransactionalWrapper(javax.transaction.Transactional txl) { this.jtxl = txl; } public static TransactionalWrapper wrap(Method method) { Transactional stxl = method.getAnnotation(Transactional.class); javax.transaction.Transactional jtxl = method.getAnnotation(javax.transaction.Transactional.class); if (stxl == null && jtxl == null) { return null; } else if (stxl != null) { return new TransactionalWrapper(stxl); } else if (jtxl != null) { return new TransactionalWrapper(jtxl); } else { throw new IllegalStateException("This should never happen"); } } public boolean isReadOnly() { if (this.stxl != null) { return this.stxl.readOnly(); } else if (this.jtxl != null) { return false; } else { throw new IllegalStateException("This should never happen"); } } public Propagation getPropagation() { if (this.stxl != null) { return this.stxl.propagation(); } else if (this.jtxl != null) { switch (this.jtxl.value()) { case MANDATORY: return Propagation.MANDATORY; case REQUIRED: return Propagation.REQUIRED; case REQUIRES_NEW: return Propagation.REQUIRES_NEW; case SUPPORTS: return Propagation.SUPPORTS; case NOT_SUPPORTED: return Propagation.NOT_SUPPORTED; case NEVER: return Propagation.NEVER; default: throw new IllegalStateException("This should never happen"); } } else { throw new IllegalStateException("This should never happen"); } } public Isolation getIsolation() { if (this.stxl != null) { return this.stxl.isolation(); } else if (this.jtxl != null) { return Isolation.DEFAULT; } else { throw new IllegalStateException("This should never happen"); } } public int getTimeoutInSeconds() { if (this.stxl != null) { return this.stxl.timeout(); } else if (this.jtxl != null) { return 0; } else { throw new IllegalStateException("This should never happen"); } } @SuppressWarnings("unchecked") public Class<? extends Throwable>[] getRollbackFor() { if (this.stxl != null) { return this.stxl.rollbackFor(); } else if (this.jtxl != null) { return this.jtxl.rollbackOn(); } else { throw new IllegalStateException("This should never happen"); } } @SuppressWarnings("unchecked") public Class<? extends Throwable>[] getNoRollbackFor() { if (this.stxl != null) { return this.stxl.noRollbackFor(); } else if (this.jtxl != null) { return this.jtxl.dontRollbackOn(); } else { throw new IllegalStateException("This should never happen"); } } } src/main/java/com/inteligr8/alfresco/annotations/aspect/RetryingTransactionAspect.java +21 −17 Original line number Diff line number Diff line Loading @@ -17,9 +17,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.IllegalTransactionStateException; import org.springframework.transaction.annotation.Transactional; import com.inteligr8.alfresco.annotations.TransactionalRetryable; import com.inteligr8.alfresco.annotations.TransactionalWrapper; /** * This aspect implements the @Transactional and @TransactionalRetryable Loading Loading @@ -50,20 +50,24 @@ public class RetryingTransactionAspect { public void isTransactionalAnnotated() { } @Pointcut("@annotation(javax.transaction.Transactional) && execution(* *(..))") public void isJtaTransactionalAnnotated() { } @Pointcut("@annotation(com.inteligr8.alfresco.annotations.TransactionalRetryable) && execution(* *(..))") public void isTransactionalRetryableAnnotated() { } @Around("isTransactionalAnnotated() || isTransactionalRetryableAnnotated()") @Around("isTransactionalAnnotated() || isJtaTransactionalAnnotated() || isTransactionalRetryableAnnotated()") public Object retryingTransactional(ProceedingJoinPoint joinPoint) throws Throwable { this.logger.trace("retryingTransactional({})", joinPoint); Method method = this.getMethod(joinPoint); Transactional txl = method.getAnnotation(Transactional.class); TransactionalWrapper txl = TransactionalWrapper.wrap(method); TransactionalRetryable txtry = method.getAnnotation(TransactionalRetryable.class); if (this.doCreateNewTxContext(txl) || this.isReadStateChange(txl)) { this.logger.debug("Changing TX context: {} => [ro: {}, new: {}]", AlfrescoTransactionSupport.getTransactionReadState(), txl.readOnly(), txl.propagation()); this.logger.debug("Changing TX context: {} => [ro: {}, new: {}]", AlfrescoTransactionSupport.getTransactionReadState(), txl.isReadOnly(), txl.getPropagation()); return this.execute(joinPoint, txl, txtry); } else if (this.doCreateNewTxRetryContext(txtry)) { this.logger.debug("Changing TX context: retries: {}", txtry.maxRetries()); Loading @@ -81,11 +85,11 @@ public class RetryingTransactionAspect { return methodSig.getMethod(); } private boolean isReadStateChange(Transactional txl) { private boolean isReadStateChange(TransactionalWrapper txl) { if (txl == null) return false; switch (txl.propagation()) { switch (txl.getPropagation()) { case NEVER: case NOT_SUPPORTED: case SUPPORTS: Loading @@ -98,9 +102,9 @@ public class RetryingTransactionAspect { case TXN_NONE: return true; case TXN_READ_ONLY: return !txl.readOnly(); return !txl.isReadOnly(); case TXN_READ_WRITE: return txl.readOnly(); return txl.isReadOnly(); default: throw new IllegalStateException(); } Loading @@ -110,10 +114,10 @@ public class RetryingTransactionAspect { return txtry != null; } private boolean doCreateNewTxContext(Transactional txl) { private boolean doCreateNewTxContext(TransactionalWrapper txl) { if (txl == null) { return false; } else switch (txl.propagation()) { } else switch (txl.getPropagation()) { case NEVER: switch (AlfrescoTransactionSupport.getTransactionReadState()) { case TXN_NONE: Loading @@ -126,10 +130,10 @@ public class RetryingTransactionAspect { case TXN_NONE: throw new IllegalTransactionStateException("A transaction does not exist where one is mandatory"); case TXN_READ_ONLY: if (!txl.readOnly()) if (!txl.isReadOnly()) throw new IllegalTransactionStateException("A read-only transaction exists where a read/write one is mandatory"); case TXN_READ_WRITE: if (txl.readOnly()) if (txl.isReadOnly()) throw new IllegalTransactionStateException("A read/write transaction exists where a read-only one is mandatory"); } case SUPPORTS: Loading @@ -145,11 +149,11 @@ public class RetryingTransactionAspect { case REQUIRES_NEW: return true; default: throw new IllegalTransactionStateException("The transactional propagation is not supported: " + txl.propagation()); throw new IllegalTransactionStateException("The transactional propagation is not supported: " + txl.getPropagation()); } } private Object execute(final ProceedingJoinPoint joinPoint, Transactional txl, TransactionalRetryable txtry) throws Throwable { private Object execute(final ProceedingJoinPoint joinPoint, TransactionalWrapper txl, TransactionalRetryable txtry) throws Throwable { RetryingTransactionCallback<Object> rtcallback = new RetryingTransactionCallback<Object>() { @Override public Object execute() throws Throwable { Loading Loading @@ -179,12 +183,12 @@ public class RetryingTransactionAspect { if (txtry.incRetryWaitInMillis() > 0) rthelper.setRetryWaitIncrementMs(txtry.incRetryWaitInMillis()); } if (txl != null && txl.timeout() > 0) rthelper.setMaxExecutionMs(txl.timeout() * 1000L); if (txl != null && txl.getTimeoutInSeconds() > 0) rthelper.setMaxExecutionMs(txl.getTimeoutInSeconds() * 1000L); try { this.logger.trace("source tx: {}", AlfrescoTransactionSupport.getTransactionId()); boolean readonly = txl != null && txl.readOnly() || txl == null && AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY; boolean readonly = txl != null && txl.isReadOnly() || txl == null && AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY; return rthelper.doInTransaction(rtcallback, readonly, txl != null); } catch (RuntimeException re) { // attempt to unwrap the exception Loading src/main/resources/alfresco/module/com.inteligr8.alfresco.annotations-platform-module/module.properties +1 −1 Original line number Diff line number Diff line Loading @@ -6,4 +6,4 @@ module.version=${project.version} module.repo.version.min=6.0 #module.repo.version.max= module.depends.aspectj-platform-module=1.0-* module.depends.com.inteligr8.alfresco.aspectj-platform-module=1.0-* Loading
pom.xml +20 −2 Original line number Diff line number Diff line Loading @@ -72,8 +72,26 @@ <dependency> <groupId>com.inteligr8.alfresco</groupId> <artifactId>aspectj-platform-module</artifactId> <version>1.0.0</version> <scope>provided</scope> <version>1.0.1</version> <type>amp</type> </dependency> <!-- AMP resources are included in the WAR, not the extension directory; this makes aspectjweaver available to javaagent --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> <scope>test</scope> </dependency> <!-- Removes startup warning regarding annotations that aren't annotations --> <dependency> <groupId>jakarta.transaction</groupId> <artifactId>jakarta.transaction-api</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency> </dependencies> Loading
src/main/java/com/inteligr8/alfresco/annotations/TransactionalWrapper.java 0 → 100644 +113 −0 Original line number Diff line number Diff line package com.inteligr8.alfresco.annotations; import java.lang.reflect.Method; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; public class TransactionalWrapper { private Transactional stxl = null; private javax.transaction.Transactional jtxl = null; public TransactionalWrapper(Transactional txl) { this.stxl = txl; } public TransactionalWrapper(javax.transaction.Transactional txl) { this.jtxl = txl; } public static TransactionalWrapper wrap(Method method) { Transactional stxl = method.getAnnotation(Transactional.class); javax.transaction.Transactional jtxl = method.getAnnotation(javax.transaction.Transactional.class); if (stxl == null && jtxl == null) { return null; } else if (stxl != null) { return new TransactionalWrapper(stxl); } else if (jtxl != null) { return new TransactionalWrapper(jtxl); } else { throw new IllegalStateException("This should never happen"); } } public boolean isReadOnly() { if (this.stxl != null) { return this.stxl.readOnly(); } else if (this.jtxl != null) { return false; } else { throw new IllegalStateException("This should never happen"); } } public Propagation getPropagation() { if (this.stxl != null) { return this.stxl.propagation(); } else if (this.jtxl != null) { switch (this.jtxl.value()) { case MANDATORY: return Propagation.MANDATORY; case REQUIRED: return Propagation.REQUIRED; case REQUIRES_NEW: return Propagation.REQUIRES_NEW; case SUPPORTS: return Propagation.SUPPORTS; case NOT_SUPPORTED: return Propagation.NOT_SUPPORTED; case NEVER: return Propagation.NEVER; default: throw new IllegalStateException("This should never happen"); } } else { throw new IllegalStateException("This should never happen"); } } public Isolation getIsolation() { if (this.stxl != null) { return this.stxl.isolation(); } else if (this.jtxl != null) { return Isolation.DEFAULT; } else { throw new IllegalStateException("This should never happen"); } } public int getTimeoutInSeconds() { if (this.stxl != null) { return this.stxl.timeout(); } else if (this.jtxl != null) { return 0; } else { throw new IllegalStateException("This should never happen"); } } @SuppressWarnings("unchecked") public Class<? extends Throwable>[] getRollbackFor() { if (this.stxl != null) { return this.stxl.rollbackFor(); } else if (this.jtxl != null) { return this.jtxl.rollbackOn(); } else { throw new IllegalStateException("This should never happen"); } } @SuppressWarnings("unchecked") public Class<? extends Throwable>[] getNoRollbackFor() { if (this.stxl != null) { return this.stxl.noRollbackFor(); } else if (this.jtxl != null) { return this.jtxl.dontRollbackOn(); } else { throw new IllegalStateException("This should never happen"); } } }
src/main/java/com/inteligr8/alfresco/annotations/aspect/RetryingTransactionAspect.java +21 −17 Original line number Diff line number Diff line Loading @@ -17,9 +17,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.IllegalTransactionStateException; import org.springframework.transaction.annotation.Transactional; import com.inteligr8.alfresco.annotations.TransactionalRetryable; import com.inteligr8.alfresco.annotations.TransactionalWrapper; /** * This aspect implements the @Transactional and @TransactionalRetryable Loading Loading @@ -50,20 +50,24 @@ public class RetryingTransactionAspect { public void isTransactionalAnnotated() { } @Pointcut("@annotation(javax.transaction.Transactional) && execution(* *(..))") public void isJtaTransactionalAnnotated() { } @Pointcut("@annotation(com.inteligr8.alfresco.annotations.TransactionalRetryable) && execution(* *(..))") public void isTransactionalRetryableAnnotated() { } @Around("isTransactionalAnnotated() || isTransactionalRetryableAnnotated()") @Around("isTransactionalAnnotated() || isJtaTransactionalAnnotated() || isTransactionalRetryableAnnotated()") public Object retryingTransactional(ProceedingJoinPoint joinPoint) throws Throwable { this.logger.trace("retryingTransactional({})", joinPoint); Method method = this.getMethod(joinPoint); Transactional txl = method.getAnnotation(Transactional.class); TransactionalWrapper txl = TransactionalWrapper.wrap(method); TransactionalRetryable txtry = method.getAnnotation(TransactionalRetryable.class); if (this.doCreateNewTxContext(txl) || this.isReadStateChange(txl)) { this.logger.debug("Changing TX context: {} => [ro: {}, new: {}]", AlfrescoTransactionSupport.getTransactionReadState(), txl.readOnly(), txl.propagation()); this.logger.debug("Changing TX context: {} => [ro: {}, new: {}]", AlfrescoTransactionSupport.getTransactionReadState(), txl.isReadOnly(), txl.getPropagation()); return this.execute(joinPoint, txl, txtry); } else if (this.doCreateNewTxRetryContext(txtry)) { this.logger.debug("Changing TX context: retries: {}", txtry.maxRetries()); Loading @@ -81,11 +85,11 @@ public class RetryingTransactionAspect { return methodSig.getMethod(); } private boolean isReadStateChange(Transactional txl) { private boolean isReadStateChange(TransactionalWrapper txl) { if (txl == null) return false; switch (txl.propagation()) { switch (txl.getPropagation()) { case NEVER: case NOT_SUPPORTED: case SUPPORTS: Loading @@ -98,9 +102,9 @@ public class RetryingTransactionAspect { case TXN_NONE: return true; case TXN_READ_ONLY: return !txl.readOnly(); return !txl.isReadOnly(); case TXN_READ_WRITE: return txl.readOnly(); return txl.isReadOnly(); default: throw new IllegalStateException(); } Loading @@ -110,10 +114,10 @@ public class RetryingTransactionAspect { return txtry != null; } private boolean doCreateNewTxContext(Transactional txl) { private boolean doCreateNewTxContext(TransactionalWrapper txl) { if (txl == null) { return false; } else switch (txl.propagation()) { } else switch (txl.getPropagation()) { case NEVER: switch (AlfrescoTransactionSupport.getTransactionReadState()) { case TXN_NONE: Loading @@ -126,10 +130,10 @@ public class RetryingTransactionAspect { case TXN_NONE: throw new IllegalTransactionStateException("A transaction does not exist where one is mandatory"); case TXN_READ_ONLY: if (!txl.readOnly()) if (!txl.isReadOnly()) throw new IllegalTransactionStateException("A read-only transaction exists where a read/write one is mandatory"); case TXN_READ_WRITE: if (txl.readOnly()) if (txl.isReadOnly()) throw new IllegalTransactionStateException("A read/write transaction exists where a read-only one is mandatory"); } case SUPPORTS: Loading @@ -145,11 +149,11 @@ public class RetryingTransactionAspect { case REQUIRES_NEW: return true; default: throw new IllegalTransactionStateException("The transactional propagation is not supported: " + txl.propagation()); throw new IllegalTransactionStateException("The transactional propagation is not supported: " + txl.getPropagation()); } } private Object execute(final ProceedingJoinPoint joinPoint, Transactional txl, TransactionalRetryable txtry) throws Throwable { private Object execute(final ProceedingJoinPoint joinPoint, TransactionalWrapper txl, TransactionalRetryable txtry) throws Throwable { RetryingTransactionCallback<Object> rtcallback = new RetryingTransactionCallback<Object>() { @Override public Object execute() throws Throwable { Loading Loading @@ -179,12 +183,12 @@ public class RetryingTransactionAspect { if (txtry.incRetryWaitInMillis() > 0) rthelper.setRetryWaitIncrementMs(txtry.incRetryWaitInMillis()); } if (txl != null && txl.timeout() > 0) rthelper.setMaxExecutionMs(txl.timeout() * 1000L); if (txl != null && txl.getTimeoutInSeconds() > 0) rthelper.setMaxExecutionMs(txl.getTimeoutInSeconds() * 1000L); try { this.logger.trace("source tx: {}", AlfrescoTransactionSupport.getTransactionId()); boolean readonly = txl != null && txl.readOnly() || txl == null && AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY; boolean readonly = txl != null && txl.isReadOnly() || txl == null && AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY; return rthelper.doInTransaction(rtcallback, readonly, txl != null); } catch (RuntimeException re) { // attempt to unwrap the exception Loading
src/main/resources/alfresco/module/com.inteligr8.alfresco.annotations-platform-module/module.properties +1 −1 Original line number Diff line number Diff line Loading @@ -6,4 +6,4 @@ module.version=${project.version} module.repo.version.min=6.0 #module.repo.version.max= module.depends.aspectj-platform-module=1.0-* module.depends.com.inteligr8.alfresco.aspectj-platform-module=1.0-*