From 04ff67abb51765c4e1aeffe4af337edc4974902a Mon Sep 17 00:00:00 2001 From: Ondra Chaloupka Date: Mon, 12 Apr 2021 23:32:19 +0200 Subject: [PATCH] [#16455] java.lang.Error should rollback the JTA transaction --- .../narayana/jta/runtime/SneakyThrow.java | 21 ++++++++++++++++ .../TransactionalInterceptorBase.java | 24 ++++++++++--------- 2 files changed, 34 insertions(+), 11 deletions(-) create mode 100644 extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/SneakyThrow.java diff --git a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/SneakyThrow.java b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/SneakyThrow.java new file mode 100644 index 00000000000000..12ee21c2465212 --- /dev/null +++ b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/SneakyThrow.java @@ -0,0 +1,21 @@ +package io.quarkus.narayana.jta.runtime; + +/** + * An utility class which makes possible to throw any exception as a {@link RuntimeException}. + * It means to throw checked exception (subtype of Throwable or Exception) as un-checked exception. + * This considers the Java 8 inference rule that states that a {@code throws E} is inferred as {@code RuntimeException}. + */ +public class SneakyThrow { + private SneakyThrow() { + throw new IllegalStateException("utility class, do not create an instance"); + } + + /** + * This method can be used in {@code throw} statement + * such as: {@code throw sneakyThrow(exception);}. + */ + @SuppressWarnings("unchecked") + public static void sneakyThrow(Throwable e) throws E { + throw (E) e; + } +} 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 24b599c3e6e32f..3ed9da83a72a6b 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 @@ -22,6 +22,7 @@ import io.quarkus.arc.runtime.InterceptorBindings; import io.quarkus.narayana.jta.runtime.CDIDelegatingTransactionManager; +import io.quarkus.narayana.jta.runtime.SneakyThrow; import io.quarkus.narayana.jta.runtime.TransactionConfiguration; import io.smallrye.mutiny.Multi; import io.smallrye.reactive.converters.ReactiveTypeConverter; @@ -125,9 +126,9 @@ protected Object invokeInOurTx(InvocationContext ic, TransactionManager tm, Runn try { ret = ic.proceed(); - } catch (Exception e) { + } catch (Throwable t) { throwing = true; - handleException(ic, e, tx); + handleException(ic, t, tx); } finally { // handle asynchronously if not throwing if (!throwing && ret != null) { @@ -250,8 +251,8 @@ protected Object invokeInCallerTx(InvocationContext ic, Transaction tx) throws E try { checkConfiguration(ic); return ic.proceed(); - } catch (Exception e) { - handleException(ic, e, tx); + } catch (Throwable t) { + handleException(ic, t, tx); } throw new RuntimeException("UNREACHABLE"); } @@ -269,34 +270,35 @@ private void checkConfiguration(InvocationContext ic) { } } - protected void handleExceptionNoThrow(InvocationContext ic, Throwable e, Transaction tx) + protected void handleExceptionNoThrow(InvocationContext ic, Throwable t, Transaction tx) throws IllegalStateException, SystemException { Transactional transactional = getTransactional(ic); for (Class dontRollbackOnClass : transactional.dontRollbackOn()) { - if (dontRollbackOnClass.isAssignableFrom(e.getClass())) { + if (dontRollbackOnClass.isAssignableFrom(t.getClass())) { return; } } for (Class rollbackOnClass : transactional.rollbackOn()) { - if (rollbackOnClass.isAssignableFrom(e.getClass())) { + if (rollbackOnClass.isAssignableFrom(t.getClass())) { tx.setRollbackOnly(); return; } } - if (e instanceof RuntimeException) { + // RuntimeException and Error are un-checked exceptions and rollback is expected + if (t instanceof RuntimeException || t instanceof Error) { tx.setRollbackOnly(); return; } } - protected void handleException(InvocationContext ic, Exception e, Transaction tx) throws Exception { + protected void handleException(InvocationContext ic, Throwable t, Transaction tx) throws Exception { - handleExceptionNoThrow(ic, e, tx); - throw e; + handleExceptionNoThrow(ic, t, tx); + SneakyThrow.sneakyThrow(t); } protected void endTransaction(TransactionManager tm, Transaction tx, RunnableWithException afterEndTransaction)