1
votes

This scenario using CMT is working:

  • Stateless session bean with CMT, one method annotated with @TransactionAttribute(TransactionAttributeType.MANDATORY). Within this method, a record is written into a RDBMS using an XA data source and plain JDBC.

  • The stand-alone client (separate JVM, command-line Java application) is getting a UserTransaction from the application server (by JNDI lookup), starts the transaction, and calls the EJB.

  • If the client commits the UserTransaction, the record is written into the database.

  • If the client rollbacks the UserTransaction, the record is not written into the database.
  • In the PostgreSql log files, one can see the prepared transaction with BEGIN, and COMMIT or ROLLBACK

  • If the client does not start a transaction before calling the EJB, a javax.ejb.EJBTransactionRequiredException is thrown (as expected, TransactionAttributeType.MANDATORY).

Now I switch from CMT to BMT

  • Again, if the client does not start a transaction before calling the EJB, a javax.ejb.EJBTransactionRequiredException is thrown (as expected, TransactionAttributeType.MANDATORY).

  • If I call sessionContext.getUserTransaction().getStatus(), it always reports Status.STATUS_NO_TRANSACTION.

  • The record is always written into the database, if the client calls commit or rollback.

  • In the PostgreSql log files, there are no prepared transactions, just plain insert commands.

The source of the EJB:

@Remote(DemoIfc.class)
@Stateless(name = "DemoBmt")
@TransactionManagement(TransactionManagementType.BEAN)
public class DemoBmt implements DemoIfc {
    @Resource
    private SessionContext sessionContext;

    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public String ping(final String s) throws SystemException {
        try {
            System.out.println("TX: status: "
                    + this.sessionContext.getUserTransaction().getStatus());
        } catch (Exception e) {
            System.out.println("TX: status: " + e.getMessage());
        }

        try {
            writeIntoDb();
            if (s.startsWith("crash")) {
                throw new SystemException("Simulated crash");
            }
            return s.toUpperCase();
        } catch (NamingException e) {
            throw new SystemException(e.getMessage());
        } catch (SQLException e) {
            throw new SystemException(e.getMessage());
        }
    }
}

The client's source:

final UserTransaction ut = (UserTransaction) initialContext
        .lookup("UserTransaction");
try {
    ut.begin();
    System.out.println(demo.ping("crash: DemoBmt with UT"));
    ut.commit();
} catch (Exception ex) {
    System.out.println("Expected rollback");
    ut.rollback();
}

I am using JBoss 6.0.0 final.

How can I properly propagate the client-side UserTransaction into the EJB with BMT?

1

1 Answers

3
votes

BMT beans cannot participate in an existing transaction

From EJB 3.1 spec.:

13.6.1 Bean-Managed Transaction Demarcation

The container must manage client invocations to an enterprise bean instance with bean-managed transaction demarcation as follows. When a client invokes a business method via one of the enterprise bean’s client views, the container suspends any transaction that may be associated with the client request....