Details
Description
To reproduce:
1. Create EJB class and JSF page from the source pasted below.
2. Clicking JSF page button "Test EJB business method transaction" gives correct output:
TEST TIMER BEGIN: timerFired(): txKey = [Xid:globalId=15000000047544d4944000000000000000000000000000000000000000000000000000,length=64,branchId=0000000000000000000000000000000000000000000000000000000000000000,length=64] TEST TIMER END afterCompletion(): txKey = [Xid:globalId=15000000047544d4944000000000000000000000000000000000000000000000000000,length=64,branchId=0000000000000000000000000000000000000000000000000000000000000000,length=64]
3. Clicking JSF page button "Test EJB timer transaction" shows that in method "afterCompletion()" TransactionSynchronizationRegistry is not found in JNDI:
TEST TIMER BEGIN:
timerFired(): txKey = [Xid:globalId=e000000047544d4944000000000000000000000000000000000000000000000000000,length=64,branchId=0000000000000000000000000000000000000000000000000000000000000000,length=64]
TEST TIMER END
afterCompletion(): errorMessage = Name [TransactionSynchronizationRegistry] is not bound in this Context. Unable to find [TransactionSynchronizationRegistry].
WHY I need all this I have implemented TransactionScoped CDI context, that listens for afterCompletion() events and destroyes TransactionScoped beans. Well, I have memory leak currently for transactions started by EJB timers...
Source code:
JSF:
<h:form> <h:commandButton value="Test EJB Timer transaction" actionListener="#{testEjbTimerTx.setTimerNow}"/> <h:commandButton value="Test EJB business method transaction" actionListener="#{testEjbTimerTx.timerFired}"/> </h:form>
EJB class:
import org.joda.time.LocalTime; import javax.annotation.Resource; import javax.ejb.*; import javax.inject.Named; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.transaction.Synchronization; import javax.transaction.TransactionSynchronizationRegistry; @Named @Singleton public class TestEjbTimerTx { @Resource private TimerService timerService; public static final String TRANSACTION_SYNCHRONIZATION_REGISTRY_JNDI_NAME = "java:comp/TransactionSynchronizationRegistry"; @Timeout public void timerFired() { System.out.println("TEST TIMER BEGIN:"); try { TransactionSynchronizationRegistry txRegistry = (TransactionSynchronizationRegistry) new InitialContext().lookup( TRANSACTION_SYNCHRONIZATION_REGISTRY_JNDI_NAME ); System.out.println("timerFired(): txKey = " + txRegistry.getTransactionKey()); txRegistry.registerInterposedSynchronization(new TxListener()); } catch (NamingException e) { System.out.println("timerFired(): errorMessage = " + e.getMessage()); } System.out.println("TEST TIMER END"); } public void setTimerNow() { LocalTime localTime = new LocalTime().plusSeconds(3); ScheduleExpression schedule = new ScheduleExpression().second(localTime.getSecondOfMinute()) .minute(localTime.getMinuteOfHour()).hour(localTime.getHourOfDay()); TimerConfig timerConfig = new TimerConfig(); timerConfig.setPersistent(false); timerService.createCalendarTimer(schedule, timerConfig); } public static class TxListener implements Synchronization { @Override public void beforeCompletion() {} @Override public void afterCompletion(int i) { try { TransactionSynchronizationRegistry txRegistry = (TransactionSynchronizationRegistry) new InitialContext().lookup( TRANSACTION_SYNCHRONIZATION_REGISTRY_JNDI_NAME ); System.out.println("afterCompletion(): txKey = " + txRegistry.getTransactionKey()); } catch (NamingException e) { System.out.println("afterCompletion(): errorMessage = " + e.getMessage()); } } } }