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

Merge branch 'develop' into stable

parents 85ec7aa3 f636b22c
Loading
Loading
Loading
Loading
+20 −2
Original line number Diff line number Diff line
@@ -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>

+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");
		}
	}

}
+21 −17
Original line number Diff line number Diff line
@@ -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
@@ -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());
@@ -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:
@@ -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();
		}
@@ -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:
@@ -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:
@@ -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 {
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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-*