io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase Maven / Gradle / Ivy
Show all versions of quarkus-narayana-jta Show documentation
package io.quarkus.narayana.jta.runtime.interceptor;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import javax.inject.Inject;
import javax.interceptor.InvocationContext;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.Transactional;
import org.jboss.tm.usertx.client.ServerVMClientUserTransaction;
import org.reactivestreams.Publisher;
import com.arjuna.ats.jta.logging.jtaLogger;
import io.quarkus.arc.runtime.InterceptorBindings;
import io.quarkus.narayana.jta.runtime.CDIDelegatingTransactionManager;
import io.quarkus.narayana.jta.runtime.TransactionConfiguration;
import io.smallrye.mutiny.Multi;
import io.smallrye.reactive.converters.ReactiveTypeConverter;
import io.smallrye.reactive.converters.Registry;
/**
* @author [email protected] 02/05/2013
*/
public abstract class TransactionalInterceptorBase implements Serializable {
private static final long serialVersionUID = 1L;
@Inject
TransactionManager transactionManager;
private final boolean userTransactionAvailable;
protected TransactionalInterceptorBase(boolean userTransactionAvailable) {
this.userTransactionAvailable = userTransactionAvailable;
}
public Object intercept(InvocationContext ic) throws Exception {
final TransactionManager tm = transactionManager;
final Transaction tx = tm.getTransaction();
boolean previousUserTransactionAvailability = setUserTransactionAvailable(userTransactionAvailable);
try {
return doIntercept(tm, tx, ic);
} finally {
resetUserTransactionAvailability(previousUserTransactionAvailability);
}
}
protected abstract Object doIntercept(TransactionManager tm, Transaction tx, InvocationContext ic) throws Exception;
/**
*
* Looking for the {@link Transactional} annotation first on the method,
* second on the class.
*
* Method handles CDI types to cover cases where extensions are used. In
* case of EE container uses reflection.
*
* @param ic
* invocation context of the interceptor
* @return instance of {@link Transactional} annotation or null
*/
private Transactional getTransactional(InvocationContext ic) {
Set bindings = InterceptorBindings.getInterceptorBindings(ic);
for (Annotation i : bindings) {
if (i.annotationType() == Transactional.class) {
return (Transactional) i;
}
}
throw new RuntimeException(jtaLogger.i18NLogger.get_expected_transactional_annotation());
}
private TransactionConfiguration getTransactionConfiguration(InvocationContext ic) {
TransactionConfiguration configuration = ic.getMethod().getAnnotation(TransactionConfiguration.class);
if (configuration == null) {
Class> clazz;
Object target = ic.getTarget();
if (target != null) {
clazz = target.getClass();
} else {
// Very likely an intercepted static method
clazz = ic.getMethod().getDeclaringClass();
}
return clazz.getAnnotation(TransactionConfiguration.class);
}
return configuration;
}
protected Object invokeInOurTx(InvocationContext ic, TransactionManager tm) throws Exception {
return invokeInOurTx(ic, tm, () -> {
});
}
protected Object invokeInOurTx(InvocationContext ic, TransactionManager tm, RunnableWithException afterEndTransaction)
throws Exception {
TransactionConfiguration configAnnotation = getTransactionConfiguration(ic);
int currentTmTimeout = ((CDIDelegatingTransactionManager) transactionManager).getTransactionTimeout();
if (configAnnotation != null && configAnnotation.timeout() != TransactionConfiguration.UNSET_TIMEOUT) {
tm.setTransactionTimeout(configAnnotation.timeout());
}
Transaction tx;
try {
tm.begin();
tx = tm.getTransaction();
} finally {
if (configAnnotation != null && configAnnotation.timeout() != TransactionConfiguration.UNSET_TIMEOUT) {
//restore the default behaviour
tm.setTransactionTimeout(currentTmTimeout);
}
}
boolean throwing = false;
Object ret = null;
try {
ret = ic.proceed();
} catch (Exception e) {
throwing = true;
handleException(ic, e, tx);
} finally {
// handle asynchronously if not throwing
if (!throwing && ret != null) {
ReactiveTypeConverter