How to receive transaction callbacks for EJB and CDI beans

The JTA API gives a chance to receive callbacks on the event of a finishing transaction. As per specification, the transaction manager announces the even of beforeCompletion and afterCompletion which are defined by the interface javax.transaction.Synchronization. More in detail:

    • beforeCompletion callback is invoked at time the transaction manager starts to commit the global transaction. The invocation is processed in the transaction context.
    • afterCompletion is invoked after the transaction is committed or rolled-back.
public class MyTXSynchronization
        implements javax.transaction.Synchronization {
  @Override
  public void beforeCompletion() {
    System.out.println("Transaction is going to complete"):
  }

  @Override
  public void afterCompletion(int status) {
    System.out.println("Transaction finished with status " + status):
  }
}

In order to register the Class, there are two options. The first one is to inject the javax.transaction.TransactionSynchronizationRegistry and then to register the synchronization instance. The instance is bound to the currently active transaction.

Example:

@Resource
TransactionSynchronizationRegistry transactionSynchronizationRegistry;

public void method() {
  transactionSynchronizationRegistry
    .registerInterposedSynchronization(new MyTXSynchronization());
}

The other option for the user is to use the TransactionManager object to register the synchronization. Example:

@Resource(lookup = "java:/TransactionManager")
TransactionManager tm;

public void method() {
tm.getTransaction().registerSynchronization(new MyTXSynchronization());

Transaction Callbacks for Stateful Session Beans

Besides the generic callbacks provided by the Synchronization interface, it is also possible for Stateful Session Beans to capture transaction callbacks provided that you are implementing the javax.ejb.SessionSynchronization interface or using the @BeforeCompletion and @AfterCompletion annotations.

@Stateful
public class DemoStatefulBean  {
 

  @BeforeCompletion
  public void beforeCompletion() {
    System.out.println("Transaction is about to be finished"):
  }

  @AfterCompletion
  public void afterCompletion(boolean committed) {
    System.out.println("Transaction finished with the outcome "
      + (committed ? "committed" : "rolled-back")):
  }
}

Mind it, as per specification only a stateful session bean with CMT demarcation can receive session synchronization notifications. Other bean types must not implement the SessionSynchronization interface or use the session synchronization annotations.

Transaction Callbacks for CDI Beans

When using CDI Beans, you need to add the @Transactional annotation to demarcate transactions in your beans:

@RequestScope
public class MyCDIBean {
  @Inject
  MyCDIBean myBean;

  @Transactional(TxType.REQUIRED)
  public void mainMethod() {
    myBean.innerFunctionality();
  }

  @Transactional(TxType.REQUIRES_NEW)
  private void innerFunctionality() {
    // some business logic
  }
}

Additionally, you can use the @TransactionScoped annotation. A bean annotated with the @TransactionScoped, when injected, lives in the scope of the currently active transaction. The bean remains bound to the transaction even when it is suspended. On resuming the transaction the scoped data are available again. If a user tries to access the bean out of the scope of the active transaction the javax.enterprise.context.ContextNotActiveException is thrown.

Example:

@TransactionScoped
public class Counter implements Serializable {
    private static final long serialVersionUID = 1L;

    private final AtomicInteger counter = new AtomicInteger();

    public int get() {
        return counter.get();
    }

    public void increment() {
        counter.incrementAndGet();
    }

    @Override
    public String toString() {
        return this.hashCode() + "[value:" + counter.get() + "]";
    }
}

Besides it, Narayana (the Transaction Manager of WildFly and JBoss EAP) brings two new CDI JTA integration capabilities:

The first one is the addition of the transactional scope events. Up to now, Narayana did not fire the scope events for the @TransactionScoped. From now on you can observe the initialization and the destruction of the transaction scope. The code for the observer could be like the following one:

    void transactionScopeActivated(@Observes @Initialized(TransactionScoped.class) final Object event, final BeanManager beanManager) {
        lifeCycle.addEvent(this.getClass().getSimpleName() + "_" + Initialized.class.getSimpleName());
    }

    void transactionScopeDestroyed(@Observes @Destroyed(TransactionScoped.class) final Object event, final BeanManager beanManager) {
        try {
            lifeCycle.addEvent(this.getClass().getSimpleName() + "_" + Destroyed.class.getSimpleName());
        } catch (Exception e) {
            LOG.trace("This is the expected situation."
                    + "The context was destroyed the @Transactional scope is not available at this time.");
        }
}

The event payload for the @Initialized is the javax.transaction.Transaction and for the @Destroyed is just the java.lang.Object.

The second enhancement is the addition of two built-in beans which can be @Injected in the user code. Those are beans TransctionManager and TransactionSynchronizationRegistry.

The implementation gives priority to the JNDI binding. If there is bound TransactionManager/TransactionSynchronizationRegistry in the JNDI then such instance is returned at the injection point. If the user defines his own CDI bean or a CDI producer which provides an instance of those two classes then such instance is grabbed for the injection.

As the last resort, the default Narayana implementation of both classes is used. You can consider the TransactionManagerImple and the TransactionSynchronizationRegistryImple to be used.

You can check one example of it in the following quickstart which demonstrates how to use JTA 1.2 annotations and CDI beans in standalone application: https://github.com/jbosstm/quickstart/tree/master/jta-1_2-standalone

Found the article helpful? if so please follow us on Socials