Skip to content

Commit

Permalink
Merge pull request #31036 from yrodiere/i28725-quarkustransaction-ini…
Browse files Browse the repository at this point in the history
…tialized

Use CDI when accessing UserTransaction/TransactionManager in QuarkusTransaction
  • Loading branch information
yrodiere authored Feb 10, 2023
2 parents 372bcbd + eed0241 commit fadf451
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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;
}
Expand Down
5 changes: 5 additions & 0 deletions integration-tests/narayana-jta/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>

<!-- Minimal test dependencies to *-deployment artifacts for consistent build order -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}

}
Original file line number Diff line number Diff line change
@@ -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();
}

}
Original file line number Diff line number Diff line change
@@ -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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
class TransactionScopedTest {
class TransactionScopeUserTransactionTest {
@Inject
UserTransaction tx;

Expand Down Expand Up @@ -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();
}

}

0 comments on commit fadf451

Please sign in to comment.