Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use CDI when accessing UserTransaction/TransactionManager in QuarkusTransaction #31036

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
yrodiere marked this conversation as resolved.
Show resolved Hide resolved
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();
}

}