From ffa831edb4e0231e4f7ffe1bd50a1de6bdba2780 Mon Sep 17 00:00:00 2001 From: Marcel Hanser Date: Wed, 19 May 2021 14:46:13 +0200 Subject: [PATCH] Add method based caching for property based timout configuration Resolves: quarkusio#15752 --- .../TransactionalInterceptorBase.java | 81 ++++++++++++------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/interceptor/TransactionalInterceptorBase.java b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/interceptor/TransactionalInterceptorBase.java index 22e9554c3982e4..61603405607488 100644 --- a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/interceptor/TransactionalInterceptorBase.java +++ b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/interceptor/TransactionalInterceptorBase.java @@ -2,18 +2,19 @@ import java.io.Serializable; import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; 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 javax.transaction.*; import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.logging.Logger; @@ -37,6 +38,7 @@ public abstract class TransactionalInterceptorBase implements Serializable { private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(TransactionalInterceptorBase.class); + private final Map timeoutForMethodCache = new ConcurrentHashMap<>(); @Inject TransactionManager transactionManager; @@ -69,8 +71,7 @@ public Object intercept(InvocationContext ic) throws Exception { * 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 + * @param ic invocation context of the interceptor * @return instance of {@link Transactional} annotation or null */ private Transactional getTransactional(InvocationContext ic) { @@ -107,35 +108,20 @@ protected Object invokeInOurTx(InvocationContext ic, TransactionManager tm) thro protected Object invokeInOurTx(InvocationContext ic, TransactionManager tm, RunnableWithException afterEndTransaction) throws Exception { - TransactionConfiguration configAnnotation = getTransactionConfiguration(ic); + Integer timeoutConfiguredForMethod = getTransactionConfigurationTimeoutFromCache(ic); + int currentTmTimeout = ((CDIDelegatingTransactionManager) transactionManager).getTransactionTimeout(); - boolean restoreTimeout = false; - if (configAnnotation != null) { - Integer newTimeout = null; - if (!configAnnotation.timeoutFromConfigProperty().equals(TransactionConfiguration.UNSET_TIMEOUT_CONFIG_PROPERTY)) { - Optional configTimeout = ConfigProvider.getConfig() - .getOptionalValue(configAnnotation.timeoutFromConfigProperty(), Integer.class); - if (configTimeout.isPresent()) { - newTimeout = configTimeout.get(); - } else if (log.isDebugEnabled()) { - log.debug("Configuration property '" + configAnnotation.timeoutFromConfigProperty() - + "' was not provided, so it will not affect the transaction's timeout."); - } - } - if ((newTimeout == null) && (configAnnotation.timeout() != TransactionConfiguration.UNSET_TIMEOUT)) { - newTimeout = configAnnotation.timeout(); - } - if (newTimeout != null) { - tm.setTransactionTimeout(newTimeout); - restoreTimeout = true; - } + + if (timeoutConfiguredForMethod != null) { + tm.setTransactionTimeout(timeoutConfiguredForMethod); } + Transaction tx; try { tm.begin(); tx = tm.getTransaction(); } finally { - if (restoreTimeout) { + if (timeoutConfiguredForMethod != null) { tm.setTransactionTimeout(currentTmTimeout); } } @@ -187,6 +173,41 @@ protected Object invokeInOurTx(InvocationContext ic, TransactionManager tm, Runn return ret; } + private Integer getTransactionConfigurationTimeoutFromCache(InvocationContext ic) { + Integer timeoutForMethod = timeoutForMethodCache.get(ic.getMethod()); + if (Objects.nonNull(timeoutForMethod)) { + return timeoutForMethod; + } else { + return timeoutForMethodCache.computeIfAbsent(ic.getMethod(), + new Function() { + @Override + public Integer apply(Method m) { + return TransactionalInterceptorBase.this.extractTransactionConfigurationTimeoutFromAnnotation(ic); + } + }); + } + } + + private Integer extractTransactionConfigurationTimeoutFromAnnotation(InvocationContext ic) { + TransactionConfiguration configAnnotation = getTransactionConfiguration(ic); + if (!configAnnotation.timeoutFromConfigProperty().equals(TransactionConfiguration.UNSET_TIMEOUT_CONFIG_PROPERTY)) { + Optional configTimeout = ConfigProvider.getConfig() + .getOptionalValue(configAnnotation.timeoutFromConfigProperty(), Integer.class); + if (configTimeout.isPresent()) { + return configTimeout.get(); + } else if (log.isDebugEnabled()) { + log.debug("Configuration property '" + configAnnotation.timeoutFromConfigProperty() + + "' was not provided, so it will not affect the transaction's timeout."); + } + } + + if ((configAnnotation.timeout() != TransactionConfiguration.UNSET_TIMEOUT)) { + return configAnnotation.timeout(); + } + + return null; + } + protected Object handleAsync(TransactionManager tm, Transaction tx, InvocationContext ic, Object ret, RunnableWithException afterEndTransaction) throws Exception { // Suspend the transaction to remove it from the main request thread @@ -355,7 +376,7 @@ protected void resetUserTransactionAvailability(boolean previousUserTransactionA * An utility method to throw any exception as a {@link RuntimeException}. * We may throw a checked exception (subtype of {@code Throwable} or {@code Exception}) as un-checked exception. * This considers the Java 8 inference rule that states that a {@code throws E} is inferred as {@code RuntimeException}. - * + *

* This method can be used in {@code throw} statement such as: {@code throw sneakyThrow(exception);}. */ @SuppressWarnings("unchecked")