Skip to content

Commit

Permalink
Merge pull request #14053 from msfm/master_TransactionScoped_PreDestroy
Browse files Browse the repository at this point in the history
@TransactionScoped Context does not call @Predestroy on TransactionScoped Beans
  • Loading branch information
gsmet authored Jan 6, 2021
2 parents be1f01b + 2f5b288 commit 164c817
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import javax.enterprise.context.ContextNotActiveException;
import javax.enterprise.context.spi.Contextual;
import javax.enterprise.context.spi.CreationalContext;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
Expand Down Expand Up @@ -70,7 +72,7 @@ public ContextState getState() {
TransactionContextState contextState = (TransactionContextState) transactionSynchronizationRegistry
.getResource(TRANSACTION_CONTEXT_MARKER);
if (contextState == null) {
result = new TransactionContextState();
result = new TransactionContextState(getCurrentTransaction());
} else {
result = contextState;
}
Expand All @@ -97,7 +99,7 @@ public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContex
.getResource(TRANSACTION_CONTEXT_MARKER);

if (contextState == null) {
contextState = new TransactionContextState();
contextState = new TransactionContextState(getCurrentTransaction());
transactionSynchronizationRegistry.putResource(TRANSACTION_CONTEXT_MARKER, contextState);
}

Expand Down Expand Up @@ -158,10 +160,18 @@ private Transaction getCurrentTransaction() {
* Representing of the context state. It's a container for all available beans in the context.
* It's filled during bean usage and cleared on destroy.
*/
private static class TransactionContextState implements ContextState {
private static class TransactionContextState implements ContextState, Synchronization {

private final ConcurrentMap<Contextual<?>, ContextInstanceHandle<?>> mapBeanToInstanceHandle = new ConcurrentHashMap<>();

TransactionContextState(Transaction transaction) {
try {
transaction.registerSynchronization(this);
} catch (RollbackException | SystemException e) {
throw new RuntimeException("Cannot register synchronization", e);
}
}

/**
* Put the contextual bean and its handle to the container.
*
Expand All @@ -178,7 +188,10 @@ <T> void put(Contextual<T> bean, ContextInstanceHandle<T> handle) {
* @param bean contextual bean instance
*/
<T> void remove(Contextual<T> bean) {
mapBeanToInstanceHandle.remove(bean);
ContextInstanceHandle<?> instance = mapBeanToInstanceHandle.remove(bean);
if (instance != null) {
instance.destroy();
}
}

/**
Expand Down Expand Up @@ -212,5 +225,13 @@ public Map<InjectableBean<?>, Object> getContextualInstances() {
.collect(Collectors.toMap(ContextInstanceHandle::getBean, ContextInstanceHandle::get));
}

@Override
public void beforeCompletion() {
}

@Override
public void afterCompletion(int status) {
this.destroy();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
package io.quarkus.narayana.jta;

import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.transaction.TransactionScoped;

@TransactionScoped
public class TransactionScopedBean {

private static final AtomicInteger initializedCount = new AtomicInteger(0);
private static final AtomicInteger destroyedCount = new AtomicInteger(0);

private int value = 0;

public int getValue() {
Expand All @@ -13,4 +21,28 @@ public int getValue() {
public void setValue(int value) {
this.value = value;
}

static void resetCounters() {
initializedCount.set(0);
destroyedCount.set(0);
}

static int getInitializedCount() {
return initializedCount.get();
}

static int getPreDestroyCount() {
return destroyedCount.get();
}

@PostConstruct
void postConstruct() {
initializedCount.incrementAndGet();
}

@PreDestroy
void preDestroy() {
destroyedCount.incrementAndGet();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ class TransactionScopedTest {

@Test
void transactionScopedInTransaction() throws Exception {
TransactionScopedBean.resetCounters();

tx.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();

Expand All @@ -40,8 +43,10 @@ void transactionScopedInTransaction() throws Exception {

tx.begin();
beanTransactional.setValue(1);
assertEquals(2, TransactionScopedBean.getInitializedCount(), "Expected @PostConstruct to be invoked");
assertEquals(1, beanTransactional.getValue(), "Transaction scope did not save the value");
tx.commit();
assertEquals(1, TransactionScopedBean.getPreDestroyCount(), "Expected @PreDestroy to be invoked");

assertThrows(ContextNotActiveException.class, () -> {
beanTransactional.getValue();
Expand All @@ -50,6 +55,7 @@ void transactionScopedInTransaction() throws Exception {
tm.resume(suspendedTransaction);
assertEquals(42, beanTransactional.getValue(), "Transaction scope did not resumed correctly");
tx.rollback();
assertEquals(2, TransactionScopedBean.getPreDestroyCount(), "Expected @PreDestroy to be invoked");
}

@Test
Expand Down

0 comments on commit 164c817

Please sign in to comment.