diff --git a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionConfiguration.java b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionConfiguration.java index 197668742f743..4b0ad5df6dd59 100644 --- a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionConfiguration.java +++ b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionConfiguration.java @@ -31,4 +31,19 @@ * @return The transaction timeout in seconds. */ int timeout() default UNSET_TIMEOUT; + + String UNSET_TIMEOUT_CONFIG_PROPERTY = "<>"; + + /** + * The configuration property to use in order to determine the value of the timeout in seconds. + * If the property exists, it must be an integer value representing the transaction timeout in seconds. + * + * An example configuration in {@code application.properties} could be: {@code my-transaction.timeout=5}. + * + * If both {@code timeoutFromConfigProperty} and {@code timeout} are set, then Quarkus will attempt to resolve + * {@code timeoutFromConfigProperty} and if a value for it has been provided, the timeout is set to that value. + * If no value has been provided at runtime for the property, then the value of {@code timeout} will be used + * as the fallback. + */ + String timeoutFromConfigProperty() default UNSET_TIMEOUT_CONFIG_PROPERTY; } 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 6910628258eaa..22e9554c3982e 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 @@ -15,6 +15,8 @@ import javax.transaction.TransactionManager; import javax.transaction.Transactional; +import org.eclipse.microprofile.config.ConfigProvider; +import org.jboss.logging.Logger; import org.jboss.tm.usertx.client.ServerVMClientUserTransaction; import org.reactivestreams.Publisher; @@ -34,6 +36,7 @@ public abstract class TransactionalInterceptorBase implements Serializable { private static final long serialVersionUID = 1L; + private static final Logger log = Logger.getLogger(TransactionalInterceptorBase.class); @Inject TransactionManager transactionManager; @@ -106,16 +109,33 @@ protected Object invokeInOurTx(InvocationContext ic, TransactionManager tm, Runn TransactionConfiguration configAnnotation = getTransactionConfiguration(ic); int currentTmTimeout = ((CDIDelegatingTransactionManager) transactionManager).getTransactionTimeout(); - if (configAnnotation != null && configAnnotation.timeout() != TransactionConfiguration.UNSET_TIMEOUT) { - tm.setTransactionTimeout(configAnnotation.timeout()); + 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; + } } Transaction tx; try { tm.begin(); tx = tm.getTransaction(); } finally { - if (configAnnotation != null && configAnnotation.timeout() != TransactionConfiguration.UNSET_TIMEOUT) { - //restore the default behaviour + if (restoreTimeout) { tm.setTransactionTimeout(currentTmTimeout); } } @@ -263,7 +283,9 @@ protected Object invokeInNoTx(InvocationContext ic) throws Exception { private void checkConfiguration(InvocationContext ic) { TransactionConfiguration configAnnotation = getTransactionConfiguration(ic); - if (configAnnotation != null && configAnnotation.timeout() != TransactionConfiguration.UNSET_TIMEOUT) { + if (configAnnotation != null && ((configAnnotation.timeout() != TransactionConfiguration.UNSET_TIMEOUT) + || !TransactionConfiguration.UNSET_TIMEOUT_CONFIG_PROPERTY + .equals(configAnnotation.timeoutFromConfigProperty()))) { throw new RuntimeException("Changing timeout via @TransactionConfiguration can only be done " + "at the entry level of a transaction"); } diff --git a/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/proxy/ProxyTestEndpoint.java b/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/proxy/ProxyTestEndpoint.java index d870b85879101..f9d23b81c847b 100644 --- a/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/proxy/ProxyTestEndpoint.java +++ b/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/proxy/ProxyTestEndpoint.java @@ -13,6 +13,7 @@ import javax.servlet.http.HttpServletResponse; import javax.transaction.Transactional; +import io.quarkus.narayana.jta.runtime.TransactionConfiguration; import io.quarkus.runtime.StartupEvent; @WebServlet(urlPatterns = "/jpa-h2/testproxy") @@ -23,6 +24,7 @@ public class ProxyTestEndpoint extends HttpServlet { EntityManager entityManager; @Transactional + @TransactionConfiguration(timeoutFromConfigProperty = "dummy.transaction.timeout") public void setup(@Observes StartupEvent startupEvent) { Pet pet = new Pet(); pet.setId(1); diff --git a/integration-tests/jpa-h2/src/main/resources/application.properties b/integration-tests/jpa-h2/src/main/resources/application.properties index bf4226d2762fd..34e4a396fab1f 100644 --- a/integration-tests/jpa-h2/src/main/resources/application.properties +++ b/integration-tests/jpa-h2/src/main/resources/application.properties @@ -1 +1,2 @@ quarkus.datasource.jdbc.max-size=8 +dummy.transaction.timeout=30