Class JmsMatsTransactionManager_JmsAndJdbc

java.lang.Object
io.mats3.impl.jms.JmsMatsTransactionManager_Jms
io.mats3.impl.jms.JmsMatsTransactionManager_JmsAndJdbc
All Implemented Interfaces:
JmsMatsStatics, JmsMatsTransactionManager

public class JmsMatsTransactionManager_JmsAndJdbc extends JmsMatsTransactionManager_Jms
Implementation of JmsMatsTransactionManager that in addition to the JMS transaction also handles a JDBC SQL Connection (using only pure java, i.e. no Spring) for which it keeps transaction demarcation along with the JMS transaction, by means of "Best Effort 1 Phase Commit":
  1. JMS transaction is entered (a transactional JMS Connection is always within a transaction)
  2. JMS Message is retrieved.
  3. SQL transaction is entered
  4. Code is executed, including SQL statements.
  5. SQL transaction is committed - Any errors also rollbacks the JMS Transaction, so that none of them have happened.
  6. JMS transaction is committed.
Out of that order, one can see that if SQL transaction becomes committed, and then the JMS transaction fails, this will be a pretty bad situation. However, of those two transactions, the SQL transaction is absolutely most likely to fail, as this is where you can have business logic failures, concurrency problems (e.g. MS SQL's "Deadlock Victim"), integrity constraints failing etc - that is, failures in both logic and timing. On the other hand, the JMS transaction (which effectively boils down to "yes, I received this message") is much harder to fail, where the only situation where it can fail is due to infrastructure/hardware failures (exploding server / full disk on Message Broker). This is called "Best Effort 1PC", and is nicely explained in this article. If this failure occurs, it will be caught and logged on ERROR level (by JmsMatsTransactionManager_Jms) - and then the Message Broker will probably try to redeliver the message. Also read the Should I use XA Transactions from Apache Active MQ.

Wise tip when working with Message Oriented Middleware: Code idempotent! Handle double-deliveries!

The transactionally demarcated SQL Connection can be retrieved from user code using ProcessContext.getAttribute(Connection.class).

It requires a DataSource upon construction. The DataSource will be asked for a SQL Connection in any MatsStage's StageProcessor that requires it: The fetching of the SQL Connection is lazy in that it won't be retrieved (nor entered into transaction with), until it is actually requested by the user code by means of ProcessContext.getAttribute(Connection.class).

The SQL Connection will be closed after each stage processing (after each transaction, either committed or rollbacked) - if it was requested during the user code.

This implementation will not perform any Connection reuse (caching/pooling). It is up to the supplier to implement any pooling, or make use of a pooled DataSource, if so desired. (Which definitely should be desired, due to the heavy use of "get new - use - commit/rollback - close".)