diff --git a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/QuarkusTransaction.java b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/QuarkusTransaction.java
index 0301cb6cd8049..9329ba9e68e4e 100644
--- a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/QuarkusTransaction.java
+++ b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/QuarkusTransaction.java
@@ -9,8 +9,6 @@
import com.arjuna.ats.jta.UserTransaction;
-import io.quarkus.arc.Arc;
-
/**
* A simplified transaction interface. While broadly covering the same use cases as {@link jakarta.transaction.UserTransaction},
* this class is designed to be easier to use. The main features it offers over {@code UserTransaction} are:
@@ -52,8 +50,7 @@ static void begin() {
* @param options Options that apply to the new transaction
*/
static void begin(BeginOptions options) {
- RequestScopedTransaction tx = Arc.container().instance(RequestScopedTransaction.class).get();
- tx.begin(options);
+ QuarkusTransactionImpl.begin(options);
}
/**
diff --git a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/QuarkusTransactionImpl.java b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/QuarkusTransactionImpl.java
index ebc8e5718dd49..0384ead6f5ccf 100644
--- a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/QuarkusTransactionImpl.java
+++ b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/QuarkusTransactionImpl.java
@@ -218,6 +218,11 @@ private static void begin(RunOptionsBase options) {
}
}
+ static void begin(BeginOptions options) {
+ RequestScopedTransaction tx = Arc.container().instance(RequestScopedTransaction.class).get();
+ tx.begin(options);
+ }
+
static void rollback() {
try {
getUserTransaction().rollback();
@@ -244,15 +249,14 @@ static void setRollbackOnly() {
private static jakarta.transaction.UserTransaction getUserTransaction() {
if (cachedUserTransaction == null) {
- return cachedUserTransaction = com.arjuna.ats.jta.UserTransaction.userTransaction();
+ return cachedUserTransaction = Arc.container().instance(UserTransaction.class).get();
}
return cachedUserTransaction;
}
private static TransactionManager getTransactionManager() {
if (cachedTransactionManager == null) {
- return cachedTransactionManager = com.arjuna.ats.jta.TransactionManager
- .transactionManager();
+ return cachedTransactionManager = Arc.container().instance(TransactionManager.class).get();
}
return cachedTransactionManager;
}
diff --git a/integration-tests/narayana-jta/pom.xml b/integration-tests/narayana-jta/pom.xml
index 87beb6b3687e5..d993384757fb8 100644
--- a/integration-tests/narayana-jta/pom.xml
+++ b/integration-tests/narayana-jta/pom.xml
@@ -30,6 +30,11 @@
rest-assured
test
+
+ org.assertj
+ assertj-core
+ test
+
diff --git a/integration-tests/narayana-jta/src/main/java/io/quarkus/narayana/jta/TransactionBeanWithEvents.java b/integration-tests/narayana-jta/src/main/java/io/quarkus/narayana/jta/TransactionBeanWithEvents.java
index f59bd0134e552..fa350636f2260 100644
--- a/integration-tests/narayana-jta/src/main/java/io/quarkus/narayana/jta/TransactionBeanWithEvents.java
+++ b/integration-tests/narayana-jta/src/main/java/io/quarkus/narayana/jta/TransactionBeanWithEvents.java
@@ -65,6 +65,18 @@ static int getRolledBack() {
void doInTransaction(boolean isCommit) {
log.debug("Running transactional bean method");
+ try {
+ listenToCommitRollback();
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot get transaction to register synchronization on bean call", e);
+ }
+
+ if (!isCommit) {
+ throw new RuntimeException("Rollback here!");
+ }
+ }
+
+ void listenToCommitRollback() {
try {
tm.getTransaction().registerSynchronization(new Synchronization() {
@Override
@@ -85,10 +97,6 @@ public void afterCompletion(int status) {
} catch (Exception e) {
throw new IllegalStateException("Cannot get transaction to register synchronization on bean call", e);
}
-
- if (!isCommit) {
- throw new RuntimeException("Rollback here!");
- }
}
void transactionScopeActivated(@Observes @Initialized(TransactionScoped.class) final Object event,
diff --git a/integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopeQuarkusTransactionBeginCommitTest.java b/integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopeQuarkusTransactionBeginCommitTest.java
new file mode 100644
index 0000000000000..64bfba825274e
--- /dev/null
+++ b/integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopeQuarkusTransactionBeginCommitTest.java
@@ -0,0 +1,85 @@
+package io.quarkus.narayana.jta;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import jakarta.enterprise.context.ContextNotActiveException;
+import jakarta.inject.Inject;
+import jakarta.transaction.Transaction;
+import jakarta.transaction.TransactionManager;
+
+import org.junit.jupiter.api.Test;
+
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+class TransactionScopeQuarkusTransactionBeginCommitTest {
+
+ @Inject
+ TransactionManager tm;
+
+ @Inject
+ TransactionScopedBean beanTransactional;
+
+ @Inject
+ TransactionBeanWithEvents beanEvents;
+
+ @Test
+ void transactionScopedInTransaction() throws Exception {
+ TransactionScopedBean.resetCounters();
+
+ QuarkusTransaction.begin();
+ beanTransactional.setValue(42);
+ assertEquals(1, TransactionScopedBean.getInitializedCount(), "Expected @PostConstruct to be invoked");
+ assertEquals(42, beanTransactional.getValue(), "Transaction scope did not save the value");
+ Transaction suspendedTransaction = tm.suspend();
+
+ assertThrows(ContextNotActiveException.class, () -> {
+ beanTransactional.getValue();
+ }, "Not expecting to have available TransactionScoped bean outside of the transaction");
+
+ QuarkusTransaction.begin();
+ beanTransactional.setValue(1);
+ assertEquals(2, TransactionScopedBean.getInitializedCount(), "Expected @PostConstruct to be invoked");
+ assertEquals(1, beanTransactional.getValue(), "Transaction scope did not save the value");
+ QuarkusTransaction.commit();
+ assertEquals(1, TransactionScopedBean.getPreDestroyCount(), "Expected @PreDestroy to be invoked");
+
+ assertThrows(ContextNotActiveException.class, () -> {
+ beanTransactional.getValue();
+ }, "Not expecting to have available TransactionScoped bean outside of the transaction");
+
+ tm.resume(suspendedTransaction);
+ assertEquals(42, beanTransactional.getValue(), "Transaction scope did not resumed correctly");
+ QuarkusTransaction.rollback();
+ assertEquals(2, TransactionScopedBean.getPreDestroyCount(), "Expected @PreDestroy to be invoked");
+ }
+
+ @Test
+ void scopeEventsAreEmitted() {
+ TransactionBeanWithEvents.cleanCounts();
+
+ QuarkusTransaction.begin();
+ beanEvents.listenToCommitRollback();
+ QuarkusTransaction.commit();
+
+ assertEquals(1, TransactionBeanWithEvents.getInitialized(), "Expected @Initialized to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getBeforeDestroyed(), "Expected @BeforeDestroyed to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getDestroyed(), "Expected @Destroyed to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getCommited(), "Expected commit to be called once");
+ assertEquals(0, TransactionBeanWithEvents.getRolledBack(), "Expected no rollback");
+ TransactionBeanWithEvents.cleanCounts();
+
+ QuarkusTransaction.begin();
+ beanEvents.listenToCommitRollback();
+ QuarkusTransaction.rollback();
+
+ assertEquals(1, TransactionBeanWithEvents.getInitialized(), "Expected @Initialized to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getBeforeDestroyed(), "Expected @BeforeDestroyed to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getDestroyed(), "Expected @Destroyed to be observed");
+ assertEquals(0, TransactionBeanWithEvents.getCommited(), "Expected no commit");
+ assertEquals(1, TransactionBeanWithEvents.getRolledBack(), "Expected rollback to be called once");
+ TransactionBeanWithEvents.cleanCounts();
+ }
+
+}
diff --git a/integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopeQuarkusTransactionRunnerTest.java b/integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopeQuarkusTransactionRunnerTest.java
new file mode 100644
index 0000000000000..fc7fbddf7d0f3
--- /dev/null
+++ b/integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopeQuarkusTransactionRunnerTest.java
@@ -0,0 +1,84 @@
+package io.quarkus.narayana.jta;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import jakarta.enterprise.context.ContextNotActiveException;
+import jakarta.inject.Inject;
+
+import org.junit.jupiter.api.Test;
+
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+class TransactionScopeQuarkusTransactionRunnerTest {
+
+ @Inject
+ TransactionScopedBean beanTransactional;
+
+ @Inject
+ TransactionBeanWithEvents beanEvents;
+
+ @Test
+ void transactionScopedInTransaction() {
+ TransactionScopedBean.resetCounters();
+
+ QuarkusTransaction.requiringNew().run(() -> {
+ beanTransactional.setValue(42);
+ assertEquals(1, TransactionScopedBean.getInitializedCount(), "Expected @PostConstruct to be invoked");
+ assertEquals(42, beanTransactional.getValue(), "Transaction scope did not save the value");
+
+ QuarkusTransaction.suspendingExisting().run(() -> {
+ assertThrows(ContextNotActiveException.class, () -> {
+ beanTransactional.getValue();
+ }, "Not expecting to have available TransactionScoped bean outside of the transaction");
+
+ QuarkusTransaction.requiringNew().run(() -> {
+ beanTransactional.setValue(1);
+ assertEquals(2, TransactionScopedBean.getInitializedCount(), "Expected @PostConstruct to be invoked");
+ assertEquals(1, beanTransactional.getValue(), "Transaction scope did not save the value");
+ });
+ assertEquals(1, TransactionScopedBean.getPreDestroyCount(), "Expected @PreDestroy to be invoked");
+
+ assertThrows(ContextNotActiveException.class, () -> {
+ beanTransactional.getValue();
+ }, "Not expecting to have available TransactionScoped bean outside of the transaction");
+ });
+
+ assertEquals(42, beanTransactional.getValue(), "Transaction scope did not resumed correctly");
+ });
+ assertEquals(2, TransactionScopedBean.getPreDestroyCount(), "Expected @PreDestroy to be invoked");
+ }
+
+ @Test
+ void scopeEventsAreEmitted() {
+ TransactionBeanWithEvents.cleanCounts();
+
+ QuarkusTransaction.requiringNew().run(() -> {
+ beanEvents.listenToCommitRollback();
+ });
+
+ assertEquals(1, TransactionBeanWithEvents.getInitialized(), "Expected @Initialized to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getBeforeDestroyed(), "Expected @BeforeDestroyed to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getDestroyed(), "Expected @Destroyed to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getCommited(), "Expected commit to be called once");
+ assertEquals(0, TransactionBeanWithEvents.getRolledBack(), "Expected no rollback");
+ TransactionBeanWithEvents.cleanCounts();
+
+ assertThatThrownBy(() -> QuarkusTransaction.requiringNew().run(() -> {
+ beanEvents.listenToCommitRollback();
+ throw new RuntimeException();
+ }))
+ // expect runtime exception to rollback the call
+ .isInstanceOf(RuntimeException.class);
+
+ assertEquals(1, TransactionBeanWithEvents.getInitialized(), "Expected @Initialized to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getBeforeDestroyed(), "Expected @BeforeDestroyed to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getDestroyed(), "Expected @Destroyed to be observed");
+ assertEquals(0, TransactionBeanWithEvents.getCommited(), "Expected no commit");
+ assertEquals(1, TransactionBeanWithEvents.getRolledBack(), "Expected rollback to be called once");
+ TransactionBeanWithEvents.cleanCounts();
+ }
+
+}
diff --git a/integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopeTransactionalTest.java b/integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopeTransactionalTest.java
new file mode 100644
index 0000000000000..c853bf2ab1c6e
--- /dev/null
+++ b/integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopeTransactionalTest.java
@@ -0,0 +1,42 @@
+package io.quarkus.narayana.jta;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import jakarta.inject.Inject;
+
+import org.junit.jupiter.api.Test;
+
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+class TransactionScopeTransactionalTest {
+ @Inject
+ TransactionBeanWithEvents beanEvents;
+
+ @Test
+ void scopeEventsAreEmitted() {
+ TransactionBeanWithEvents.cleanCounts();
+
+ beanEvents.doInTransaction(true);
+
+ assertEquals(1, TransactionBeanWithEvents.getInitialized(), "Expected @Initialized to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getBeforeDestroyed(), "Expected @BeforeDestroyed to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getDestroyed(), "Expected @Destroyed to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getCommited(), "Expected commit to be called once");
+ assertEquals(0, TransactionBeanWithEvents.getRolledBack(), "Expected no rollback");
+ TransactionBeanWithEvents.cleanCounts();
+
+ assertThatThrownBy(() -> beanEvents.doInTransaction(false))
+ // expect runtime exception to rollback the call
+ .isInstanceOf(RuntimeException.class);
+
+ assertEquals(1, TransactionBeanWithEvents.getInitialized(), "Expected @Initialized to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getBeforeDestroyed(), "Expected @BeforeDestroyed to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getDestroyed(), "Expected @Destroyed to be observed");
+ assertEquals(0, TransactionBeanWithEvents.getCommited(), "Expected no commit");
+ assertEquals(1, TransactionBeanWithEvents.getRolledBack(), "Expected rollback to be called once");
+ TransactionBeanWithEvents.cleanCounts();
+ }
+
+}
diff --git a/integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopedTest.java b/integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopeUserTransactionTest.java
similarity index 70%
rename from integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopedTest.java
rename to integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopeUserTransactionTest.java
index f726e27c1d851..8a1ffa2555d5c 100644
--- a/integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopedTest.java
+++ b/integration-tests/narayana-jta/src/test/java/io/quarkus/narayana/jta/TransactionScopeUserTransactionTest.java
@@ -14,7 +14,7 @@
import io.quarkus.test.junit.QuarkusTest;
@QuarkusTest
-class TransactionScopedTest {
+class TransactionScopeUserTransactionTest {
@Inject
UserTransaction tx;
@@ -62,22 +62,27 @@ void transactionScopedInTransaction() throws Exception {
void scopeEventsAreEmitted() throws Exception {
TransactionBeanWithEvents.cleanCounts();
- beanEvents.doInTransaction(true);
-
- try {
- beanEvents.doInTransaction(false);
- } catch (RuntimeException expected) {
- // expect runtime exception to rollback the call
- }
-
tx.begin();
+ beanEvents.listenToCommitRollback();
tx.commit();
- assertEquals(3, TransactionBeanWithEvents.getInitialized(), "Expected @Initialized to be observed");
- assertEquals(3, TransactionBeanWithEvents.getBeforeDestroyed(), "Expected @BeforeDestroyed to be observer");
- assertEquals(3, TransactionBeanWithEvents.getDestroyed(), "Expected @Destroyed to be observer");
+ assertEquals(1, TransactionBeanWithEvents.getInitialized(), "Expected @Initialized to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getBeforeDestroyed(), "Expected @BeforeDestroyed to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getDestroyed(), "Expected @Destroyed to be observed");
assertEquals(1, TransactionBeanWithEvents.getCommited(), "Expected commit to be called once");
+ assertEquals(0, TransactionBeanWithEvents.getRolledBack(), "Expected no rollback");
+ TransactionBeanWithEvents.cleanCounts();
+
+ tx.begin();
+ beanEvents.listenToCommitRollback();
+ tx.rollback();
+
+ assertEquals(1, TransactionBeanWithEvents.getInitialized(), "Expected @Initialized to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getBeforeDestroyed(), "Expected @BeforeDestroyed to be observed");
+ assertEquals(1, TransactionBeanWithEvents.getDestroyed(), "Expected @Destroyed to be observed");
+ assertEquals(0, TransactionBeanWithEvents.getCommited(), "Expected no commit");
assertEquals(1, TransactionBeanWithEvents.getRolledBack(), "Expected rollback to be called once");
+ TransactionBeanWithEvents.cleanCounts();
}
}