From 240adf865da314752e10fa175c6b1344a2f2aada Mon Sep 17 00:00:00 2001 From: "Brian M. Long" Date: Mon, 16 Sep 2024 15:28:32 -0400 Subject: [PATCH] added jakarta tx support --- pom.xml | 12 ++ .../annotations/TransactionalWrapper.java | 113 ------------------ .../aspect/RetryingTransactionAspect.java | 48 +++++++- ...JakartaTransactionalAnnotationAdapter.java | 63 ++++++++++ .../JtaTransactionalAnnotationAdapter.java | 63 ++++++++++ .../SpringTransactionalAnnotationAdapter.java | 45 +++++++ .../util/TransactionalAnnotationAdapter.java | 20 ++++ 7 files changed, 245 insertions(+), 119 deletions(-) delete mode 100644 src/main/java/com/inteligr8/alfresco/annotations/TransactionalWrapper.java create mode 100644 src/main/java/com/inteligr8/alfresco/annotations/util/JakartaTransactionalAnnotationAdapter.java create mode 100644 src/main/java/com/inteligr8/alfresco/annotations/util/JtaTransactionalAnnotationAdapter.java create mode 100644 src/main/java/com/inteligr8/alfresco/annotations/util/SpringTransactionalAnnotationAdapter.java create mode 100644 src/main/java/com/inteligr8/alfresco/annotations/util/TransactionalAnnotationAdapter.java diff --git a/pom.xml b/pom.xml index 69cf7c0..df92505 100644 --- a/pom.xml +++ b/pom.xml @@ -75,6 +75,18 @@ 1.0.1 amp + + javax.transaction + javax.transaction-api + 1.3 + provided + + + jakarta.transaction + jakarta.transaction-api + 2.0.1 + provided + diff --git a/src/main/java/com/inteligr8/alfresco/annotations/TransactionalWrapper.java b/src/main/java/com/inteligr8/alfresco/annotations/TransactionalWrapper.java deleted file mode 100644 index 41f0397..0000000 --- a/src/main/java/com/inteligr8/alfresco/annotations/TransactionalWrapper.java +++ /dev/null @@ -1,113 +0,0 @@ -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[] 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[] 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"); - } - } - -} diff --git a/src/main/java/com/inteligr8/alfresco/annotations/aspect/RetryingTransactionAspect.java b/src/main/java/com/inteligr8/alfresco/annotations/aspect/RetryingTransactionAspect.java index 820c0c9..1be4758 100644 --- a/src/main/java/com/inteligr8/alfresco/annotations/aspect/RetryingTransactionAspect.java +++ b/src/main/java/com/inteligr8/alfresco/annotations/aspect/RetryingTransactionAspect.java @@ -1,5 +1,6 @@ package com.inteligr8.alfresco.annotations.aspect; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; @@ -17,9 +18,13 @@ 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; +import com.inteligr8.alfresco.annotations.util.JakartaTransactionalAnnotationAdapter; +import com.inteligr8.alfresco.annotations.util.JtaTransactionalAnnotationAdapter; +import com.inteligr8.alfresco.annotations.util.SpringTransactionalAnnotationAdapter; +import com.inteligr8.alfresco.annotations.util.TransactionalAnnotationAdapter; /** * This aspect implements the @Transactional and @TransactionalRetryable @@ -54,16 +59,20 @@ public class RetryingTransactionAspect { public void isJtaTransactionalAnnotated() { } + @Pointcut("@annotation(jakarta.transaction.Transactional) && execution(* *(..))") + public void isJakartaTransactionalAnnotated() { + } + @Pointcut("@annotation(com.inteligr8.alfresco.annotations.TransactionalRetryable) && execution(* *(..))") public void isTransactionalRetryableAnnotated() { } - @Around("isTransactionalAnnotated() || isJtaTransactionalAnnotated() || isTransactionalRetryableAnnotated()") + @Around("isTransactionalAnnotated() || isJtaTransactionalAnnotated() || isJakartaTransactionalAnnotated() || isTransactionalRetryableAnnotated()") public Object retryingTransactional(ProceedingJoinPoint joinPoint) throws Throwable { this.logger.trace("retryingTransactional({})", joinPoint); Method method = this.getMethod(joinPoint); - TransactionalWrapper txl = TransactionalWrapper.wrap(method); + TransactionalAnnotationAdapter txl = this.wrapTransactionalAnnotation(method); TransactionalRetryable txtry = method.getAnnotation(TransactionalRetryable.class); if (this.doCreateNewTxContext(txl) || this.isReadStateChange(txl)) { @@ -77,6 +86,33 @@ public class RetryingTransactionAspect { } } + private TransactionalAnnotationAdapter wrapTransactionalAnnotation(Method method) { + Annotation txl = method.getAnnotation(Transactional.class); + if (txl != null) + return new SpringTransactionalAnnotationAdapter((Transactional) txl); + + txl = this.getOptionalAnnotation(method, "javax.transaction.Transactional"); + if (txl != null) + return new JtaTransactionalAnnotationAdapter((javax.transaction.Transactional) txl); + + txl = this.getOptionalAnnotation(method, "jakarta.transaction.Transactional"); + if (txl != null) + return new JakartaTransactionalAnnotationAdapter((jakarta.transaction.Transactional) txl); + + return null; + } + + private A getOptionalAnnotation(Method method, String fullyQualifiedAnnotationName) { + try { + @SuppressWarnings("unchecked") + Class annotationClass = (Class) Class.forName(fullyQualifiedAnnotationName); + return method.getAnnotation(annotationClass); + } catch (ClassNotFoundException cnfe) { + this.logger.trace("The {} annotation is not available in the classpath; assuming not set", fullyQualifiedAnnotationName); + return null; + } + } + private Method getMethod(ProceedingJoinPoint joinPoint) { if (!(joinPoint.getSignature() instanceof MethodSignature)) throw new IllegalStateException("The @Transactional or @TransactionalRetryable annotations must be on methods"); @@ -85,7 +121,7 @@ public class RetryingTransactionAspect { return methodSig.getMethod(); } - private boolean isReadStateChange(TransactionalWrapper txl) { + private boolean isReadStateChange(TransactionalAnnotationAdapter txl) { if (txl == null) return false; @@ -114,7 +150,7 @@ public class RetryingTransactionAspect { return txtry != null; } - private boolean doCreateNewTxContext(TransactionalWrapper txl) { + private boolean doCreateNewTxContext(TransactionalAnnotationAdapter txl) { if (txl == null) { return false; } else switch (txl.getPropagation()) { @@ -159,7 +195,7 @@ public class RetryingTransactionAspect { } } - private Object execute(final ProceedingJoinPoint joinPoint, TransactionalWrapper txl, TransactionalRetryable txtry) throws Throwable { + private Object execute(final ProceedingJoinPoint joinPoint, TransactionalAnnotationAdapter txl, TransactionalRetryable txtry) throws Throwable { RetryingTransactionCallback rtcallback = new RetryingTransactionCallback() { @Override public Object execute() throws Throwable { diff --git a/src/main/java/com/inteligr8/alfresco/annotations/util/JakartaTransactionalAnnotationAdapter.java b/src/main/java/com/inteligr8/alfresco/annotations/util/JakartaTransactionalAnnotationAdapter.java new file mode 100644 index 0000000..c591091 --- /dev/null +++ b/src/main/java/com/inteligr8/alfresco/annotations/util/JakartaTransactionalAnnotationAdapter.java @@ -0,0 +1,63 @@ +package com.inteligr8.alfresco.annotations.util; + +import jakarta.transaction.Transactional; + +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; + +public class JakartaTransactionalAnnotationAdapter implements TransactionalAnnotationAdapter { + + private final Transactional txl; + + public JakartaTransactionalAnnotationAdapter(Transactional txl) { + this.txl = txl; + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public Propagation getPropagation() { + switch (this.txl.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"); + } + } + + @Override + public Isolation getIsolation() { + return Isolation.DEFAULT; + } + + @Override + public int getTimeoutInSeconds() { + return 0; + } + + @SuppressWarnings("unchecked") + @Override + public Class[] getRollbackFor() { + return this.txl.rollbackOn(); + } + + @SuppressWarnings("unchecked") + @Override + public Class[] getNoRollbackFor() { + return this.txl.dontRollbackOn(); + } + +} diff --git a/src/main/java/com/inteligr8/alfresco/annotations/util/JtaTransactionalAnnotationAdapter.java b/src/main/java/com/inteligr8/alfresco/annotations/util/JtaTransactionalAnnotationAdapter.java new file mode 100644 index 0000000..594fd0e --- /dev/null +++ b/src/main/java/com/inteligr8/alfresco/annotations/util/JtaTransactionalAnnotationAdapter.java @@ -0,0 +1,63 @@ +package com.inteligr8.alfresco.annotations.util; + +import javax.transaction.Transactional; + +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; + +public class JtaTransactionalAnnotationAdapter implements TransactionalAnnotationAdapter { + + private final Transactional txl; + + public JtaTransactionalAnnotationAdapter(Transactional txl) { + this.txl = txl; + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public Propagation getPropagation() { + switch (this.txl.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"); + } + } + + @Override + public Isolation getIsolation() { + return Isolation.DEFAULT; + } + + @Override + public int getTimeoutInSeconds() { + return 0; + } + + @SuppressWarnings("unchecked") + @Override + public Class[] getRollbackFor() { + return this.txl.rollbackOn(); + } + + @SuppressWarnings("unchecked") + @Override + public Class[] getNoRollbackFor() { + return this.txl.dontRollbackOn(); + } + +} diff --git a/src/main/java/com/inteligr8/alfresco/annotations/util/SpringTransactionalAnnotationAdapter.java b/src/main/java/com/inteligr8/alfresco/annotations/util/SpringTransactionalAnnotationAdapter.java new file mode 100644 index 0000000..acc4fcb --- /dev/null +++ b/src/main/java/com/inteligr8/alfresco/annotations/util/SpringTransactionalAnnotationAdapter.java @@ -0,0 +1,45 @@ +package com.inteligr8.alfresco.annotations.util; + +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +public class SpringTransactionalAnnotationAdapter implements TransactionalAnnotationAdapter { + + private final Transactional txl; + + public SpringTransactionalAnnotationAdapter(Transactional txl) { + this.txl = txl; + } + + @Override + public boolean isReadOnly() { + return this.txl.readOnly(); + } + + @Override + public Propagation getPropagation() { + return this.txl.propagation(); + } + + @Override + public Isolation getIsolation() { + return this.txl.isolation(); + } + + @Override + public int getTimeoutInSeconds() { + return this.txl.timeout(); + } + + @Override + public Class[] getRollbackFor() { + return this.txl.rollbackFor(); + } + + @Override + public Class[] getNoRollbackFor() { + return this.txl.noRollbackFor(); + } + +} diff --git a/src/main/java/com/inteligr8/alfresco/annotations/util/TransactionalAnnotationAdapter.java b/src/main/java/com/inteligr8/alfresco/annotations/util/TransactionalAnnotationAdapter.java new file mode 100644 index 0000000..349251a --- /dev/null +++ b/src/main/java/com/inteligr8/alfresco/annotations/util/TransactionalAnnotationAdapter.java @@ -0,0 +1,20 @@ +package com.inteligr8.alfresco.annotations.util; + +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; + +public interface TransactionalAnnotationAdapter { + + boolean isReadOnly(); + + Propagation getPropagation(); + + Isolation getIsolation(); + + int getTimeoutInSeconds(); + + Class[] getRollbackFor(); + + Class[] getNoRollbackFor(); + +}