Commit cfcb7fd7 authored by Brian Long's avatar Brian Long
Browse files

Merge branch 'develop' into stable

parents ba7609ec 240adf86
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -75,6 +75,18 @@
			<version>1.0.1</version>
			<type>amp</type>
		</dependency>
		<dependency>
			<groupId>javax.transaction</groupId>
			<artifactId>javax.transaction-api</artifactId>
			<version>1.3</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>jakarta.transaction</groupId>
			<artifactId>jakarta.transaction-api</artifactId>
			<version>2.0.1</version>
			<scope>provided</scope>
		</dependency>
		
		<!-- AMP resources are included in the WAR, not the extension directory; this makes aspectjweaver available to javaagent -->
		<dependency>
+0 −113
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");
		}
	}

}
+42 −6
Original line number Diff line number Diff line
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 extends Annotation> A getOptionalAnnotation(Method method, String fullyQualifiedAnnotationName) {
		try {
			@SuppressWarnings("unchecked")
			Class<A> annotationClass = (Class<A>) 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<Object> rtcallback = new RetryingTransactionCallback<Object>() {
			@Override
			public Object execute() throws Throwable {
+63 −0
Original line number Diff line number Diff line
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<? extends Throwable>[] getRollbackFor() {
		return this.txl.rollbackOn();
	}

	@SuppressWarnings("unchecked")
	@Override
	public Class<? extends Throwable>[] getNoRollbackFor() {
		return this.txl.dontRollbackOn();
	}

}
+63 −0
Original line number Diff line number Diff line
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<? extends Throwable>[] getRollbackFor() {
		return this.txl.rollbackOn();
	}

	@SuppressWarnings("unchecked")
	@Override
	public Class<? extends Throwable>[] getNoRollbackFor() {
		return this.txl.dontRollbackOn();
	}

}
Loading