Package io.mats3.spring.jms.tx
Class JmsMatsTransactionManager_JmsAndSpringManagedSqlTx
java.lang.Object
io.mats3.impl.jms.JmsMatsTransactionManager_Jms
io.mats3.spring.jms.tx.JmsMatsTransactionManager_JmsAndSpringManagedSqlTx
- All Implemented Interfaces:
JmsMatsStatics
,JmsMatsTransactionManager
public class JmsMatsTransactionManager_JmsAndSpringManagedSqlTx
extends JmsMatsTransactionManager_Jms
Implementation of
JmsMatsTransactionManager
that in addition to the JMS transaction keeps a Spring
PlatformTransactionManager
employing a JDBC DataSource for which it keeps transaction demarcation along with
the JMS transaction, by means of "Best Effort 1 Phase Commit". Note that you can choose between providing a
DataSource, in which case this JmsMatsTransactionManager internally creates a DataSourceTransactionManager
,
or you can provide a PlatformTransactionManager
employing a DataSource
that you've created
and employ externally (i.e. DataSourceTransactionManager
, JpaTransactionManager
or
HibernateTransactionManager
). In the case where you provide a PlatformTransactionManager
,
you should definitely wrap the DataSource employed when creating it by the method
wrapLazyConnectionDatasource(DataSource)
, so that you get lazy Connection fetching and so that Mats can know
whether the SQL Connection was actually employed within a Mats Stage - this also elides the committing of an empty DB
transaction if a Mats Stage does not actually employ a SQL Connection. Note that whether you use the wrapped
DataSource or the non-wrapped DataSource when creating e.g. JdbcTemplate
s does not matter, as Spring's
DataSourceUtils
and TransactionSynchronizationManager
has an unwrapping strategy when retrieving the
transactionally demarcated Connection.
Explanation of Best Effort 1 Phase Commit:
- JMS transaction is entered (a transactional JMS Connection is always within a transaction)
- JMS Message is retrieved.
- SQL transaction is entered
- Code is executed, including SQL statements and production of new "outgoing" JMS Messages.
- SQL transaction is committed - Any errors also rollbacks the JMS Transaction, so that none of them have happened.
- JMS transaction is committed.
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 within Mats Stage lambda code user code using
ProcessContext.getAttribute(Connection.class)
- which also is
available using ContextLocal.getAttribute(Connection.class)
.
Notice: In a Spring context, you can also get the transactionally demarcated thread-bound Connection via
DataSourceUtils.getConnection(dataSource)
- this is indeed what
Spring's JDBC Template and friends are doing. If you go directly to the DataSource, you will get a new Connection
not participating in the transaction! This "feature" might sometimes be of interest if you want something to be
performed regardless of whether the stage processing fails or not. (However, if you do such a thing, you must
remember the built-in retry mechanism JMS Message Brokers has: If something fails, whatever database changes you
performed successfully with such a non-tx-managed Connection will not participate in the rollback, and will already
have been performed when the message is retried. This might, or might not, be what you want.).-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprotected static class
Extension ofDeferredConnectionProxyDataSourceWrapper
which implementsInfrastructureProxy
.Nested classes/interfaces inherited from class io.mats3.impl.jms.JmsMatsTransactionManager_Jms
JmsMatsTransactionManager_Jms.TransactionalContext_Jms
Nested classes/interfaces inherited from interface io.mats3.impl.jms.JmsMatsTransactionManager
JmsMatsTransactionManager.JmsMatsTxContextKey, JmsMatsTransactionManager.ProcessingLambda, JmsMatsTransactionManager.TransactionContext
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final String
ASupplier<Boolean>
bound toMatsFactory.ContextLocal
when inside a Mats-transactional demarcation.Fields inherited from interface io.mats3.impl.jms.JmsMatsStatics
EXTRA_GRACE_MILLIS, ILLEGAL_CALL_FLOWS, JMS_MSG_PROP_AUDIT, JMS_MSG_PROP_DISPATCH_TYPE, JMS_MSG_PROP_FROM, JMS_MSG_PROP_INITIALIZING_APP, JMS_MSG_PROP_INITIATOR_ID, JMS_MSG_PROP_MATS_MESSAGE_ID, JMS_MSG_PROP_MESSAGE_TYPE, JMS_MSG_PROP_TO, JMS_MSG_PROP_TRACE_ID, MAX_STACK_HEIGHT, MAX_TOTAL_CALL_NUMBER, MDC_MATS_APP_NAME, MDC_MATS_APP_VERSION, MDC_MATS_CALL_NUMBER, MDC_MATS_IN_MESSAGE_SYSTEM_ID, MDC_MATS_INIT, MDC_MATS_OUT_MATS_MESSAGE_ID, MDC_MATS_STAGE, MDC_MATS_STAGE_ID, MDC_MATS_STAGE_INDEX, MDC_TRACE_ID, NO_INVOCATION_POINT, RANDOM_ALPHABET, THREAD_PREFIX, TOTAL_JMS_MSG_PROPS_SIZE
-
Method Summary
Modifier and TypeMethodDescriptioncreate
(DataSource dataSource) Simplest, recommended if you do not need the PlatformTransactionManager in your Spring context! - However, if you need the PlatformTransaction manager in the Spring context, then make it externally (typically using a @Bean annotated method, and make sure to wrap the contained DataSource first withwrapLazyConnectionDatasource(DataSource)
), and use the factory methodcreate(PlatformTransactionManager)
(it will find the DataSource from the PlatformTransactionManager by introspection).create
(DataSource dataSource, Function<JmsMatsTransactionManager.JmsMatsTxContextKey, org.springframework.transaction.support.DefaultTransactionDefinition> transactionDefinitionFunction) Creates an internalDataSourceTransactionManager
for this created JmsMatsTransactionManager, and ensures that the suppliedDataSource
is wrapped using thewrapLazyConnectionDatasource(DataSource)
method.create
(org.springframework.transaction.PlatformTransactionManager platformTransactionManager) Next simplest, recommended if you also need the PlatformTransactionManager in your Spring context! (otherwise, use thecreate(DataSource)
factory method).create
(org.springframework.transaction.PlatformTransactionManager platformTransactionManager, Function<JmsMatsTransactionManager.JmsMatsTxContextKey, org.springframework.transaction.support.DefaultTransactionDefinition> transactionDefinitionFunction) Creates an instance of this class from a providedPlatformTransactionManager
(of a type which manages a DataSource), where the supplied instance is introspected to find a methodgetDataSource()
from where to get the underlying DataSource.create
(org.springframework.transaction.PlatformTransactionManager platformTransactionManager, DataSource dataSource) Creates aJmsMatsTransactionManager_JmsAndSpringManagedSqlTx
from a providedPlatformTransactionManager
(of a type which manages a DataSource) - Do note that you should preferably have theDataSource
within the
wrapped using thePlatformTransactionManager
wrapLazyConnectionDatasource(DataSource)
method.create
(org.springframework.transaction.PlatformTransactionManager platformTransactionManager, DataSource dataSource, Function<JmsMatsTransactionManager.JmsMatsTxContextKey, org.springframework.transaction.support.DefaultTransactionDefinition> transactionDefinitionFunction) Creates aJmsMatsTransactionManager_JmsAndSpringManagedSqlTx
from a providedPlatformTransactionManager
(of a type which manages a DataSource) - Do note that you should preferably have theDataSource
within the
wrapped using thePlatformTransactionManager
wrapLazyConnectionDatasource(DataSource)
method.org.springframework.transaction.PlatformTransactionManager
static Function<JmsMatsTransactionManager.JmsMatsTxContextKey,
org.springframework.transaction.support.DefaultTransactionDefinition> getStandardTransactionDefinitionFunctionFor
(Class<? extends org.springframework.transaction.PlatformTransactionManager> platformTransactionManager) Returns the standard TransactionDefinition Function for the supplied PlatformTransactionManager.Provides an implementation ofJmsMatsTransactionManager.TransactionContext
.static DataSource
wrapLazyConnectionDatasource
(DataSource targetDataSource) Creates a proxy/wrapper that has lazy connection getting, and monitoring of whether the connection was actually retrieved.Methods inherited from class io.mats3.impl.jms.JmsMatsTransactionManager_Jms
create
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
Methods inherited from interface io.mats3.impl.jms.JmsMatsStatics
createFlowId, getInvocationPoint, id, id, idThis, ms3, produceAndSendMsgSysMessages, randomString, setConcurrencyWithLog, stageOrInit
-
Field Details
-
CONTEXT_LOCAL_KEY_CONNECTION_EMPLOYED_STATE_SUPPLIER
ASupplier<Boolean>
bound toMatsFactory.ContextLocal
when inside a Mats-transactional demarcation.- See Also:
-
-
Method Details
-
create
Simplest, recommended if you do not need the PlatformTransactionManager in your Spring context! - However, if you need the PlatformTransaction manager in the Spring context, then make it externally (typically using a @Bean annotated method, and make sure to wrap the contained DataSource first withwrapLazyConnectionDatasource(DataSource)
), and use the factory methodcreate(PlatformTransactionManager)
(it will find the DataSource from the PlatformTransactionManager by introspection). Creates an internalDataSourceTransactionManager
for this created JmsMatsTransactionManager, and ensures that the suppliedDataSource
is wrapped using thewrapLazyConnectionDatasource(DataSource)
method. Also with this way to construct the instance, Mats will know whether the stage or initiation actually performed any SQL data access. Uses a defaultTransactionDefinition
Function, which sets the transaction name, sets Isolation Level toTransactionDefinition.ISOLATION_READ_COMMITTED
, and sets Propagation Behavior toTransactionDefinition.PROPAGATION_REQUIRES_NEW
.- Parameters:
dataSource
- the DataSource to make aDataSourceTransactionManager
from - which will be wrapped usingwrapLazyConnectionDatasource(DataSource)
if it not already is.- Returns:
- a new
JmsMatsTransactionManager_JmsAndSpringManagedSqlTx
.
-
create
public static JmsMatsTransactionManager_JmsAndSpringManagedSqlTx create(DataSource dataSource, Function<JmsMatsTransactionManager.JmsMatsTxContextKey, org.springframework.transaction.support.DefaultTransactionDefinition> transactionDefinitionFunction) Creates an internalDataSourceTransactionManager
for this created JmsMatsTransactionManager, and ensures that the suppliedDataSource
is wrapped using thewrapLazyConnectionDatasource(DataSource)
method. Also with this way to construct the instance, Mats will know whether the stage or initiation actually performed any SQL data access. Uses the suppliedTransactionDefinition
Function to define the transactions - considercreate(DataSource)
if you are OK with the standard.- Parameters:
dataSource
- the DataSource to make aDataSourceTransactionManager
from - which will be wrapped usingwrapLazyConnectionDatasource(DataSource)
if it not already is.transactionDefinitionFunction
- aFunction
which returns aDefaultTransactionDefinition
, possibly based on the providedJmsMatsTransactionManager.JmsMatsTxContextKey
(e.g. different isolation level for a special endpoint).- Returns:
- a new
JmsMatsTransactionManager_JmsAndSpringManagedSqlTx
.
-
create
public static JmsMatsTransactionManager_JmsAndSpringManagedSqlTx create(org.springframework.transaction.PlatformTransactionManager platformTransactionManager) Next simplest, recommended if you also need the PlatformTransactionManager in your Spring context! (otherwise, use thecreate(DataSource)
factory method). Creates an instance of this class from a providedPlatformTransactionManager
(of a type which manages a DataSource), where the supplied instance is introspected to find a methodgetDataSource()
from where to get the underlying DataSource. Do note that you should preferably have theDataSource
within the
wrapped using thePlatformTransactionManager
wrapLazyConnectionDatasource(DataSource)
method. If not wrapped as such, Mats will not be able to know whether the stage or initiation actually performed data access. Uses the standardTransactionDefinition
Function, which sets the transaction name, sets Isolation Level toTransactionDefinition.ISOLATION_READ_COMMITTED
(unless HibernateTxMgr), and sets Propagation Behavior toTransactionDefinition.PROPAGATION_REQUIRES_NEW
- seegetStandardTransactionDefinitionFunctionFor(Class)
.- Parameters:
platformTransactionManager
- theDataSourceTransactionManager
to use for transaction management.- Returns:
- a new
JmsMatsTransactionManager_JmsAndSpringManagedSqlTx
.
-
create
public static JmsMatsTransactionManager_JmsAndSpringManagedSqlTx create(org.springframework.transaction.PlatformTransactionManager platformTransactionManager, Function<JmsMatsTransactionManager.JmsMatsTxContextKey, org.springframework.transaction.support.DefaultTransactionDefinition> transactionDefinitionFunction) Creates an instance of this class from a providedPlatformTransactionManager
(of a type which manages a DataSource), where the supplied instance is introspected to find a methodgetDataSource()
from where to get the underlying DataSource. Do note that you should preferably have theDataSource
within the
wrapped using thePlatformTransactionManager
wrapLazyConnectionDatasource(DataSource)
method. If not wrapped as such, Mats will not be able to know whether the stage or initiation actually performed data access. Uses the suppliedTransactionDefinition
Function to define the transactions - considercreate(PlatformTransactionManager)
if you are OK with the standard.- Parameters:
platformTransactionManager
- theDataSourceTransactionManager
to use for transaction management.- Returns:
- a new
JmsMatsTransactionManager_JmsAndSpringManagedSqlTx
.
-
create
public static JmsMatsTransactionManager_JmsAndSpringManagedSqlTx create(org.springframework.transaction.PlatformTransactionManager platformTransactionManager, DataSource dataSource) Creates aJmsMatsTransactionManager_JmsAndSpringManagedSqlTx
from a providedPlatformTransactionManager
(of a type which manages a DataSource) - Do note that you should preferably have theDataSource
within the
wrapped using thePlatformTransactionManager
wrapLazyConnectionDatasource(DataSource)
method. If not wrapped as such, Mats will not be able to know whether the stage or initiation actually performed data access. Note: Only use this method if the variants NOT taking a DataSource fails to work. It is imperative that the DataSource and the PlatformTransactionManager provided "match up", meaning that the DataSource provided is actually the instance which the PlatformTransactionManager handles. Uses the standardTransactionDefinition
Function, which sets the transaction name, sets Isolation Level toTransactionDefinition.ISOLATION_READ_COMMITTED
(unless HibernateTxMgr), and sets Propagation Behavior toTransactionDefinition.PROPAGATION_REQUIRES_NEW
- seegetStandardTransactionDefinitionFunctionFor(Class)
.- Parameters:
platformTransactionManager
- theDataSourceTransactionManager
to use for transaction management.dataSource
- theDataSource
which the suppliedPlatformTransactionManager
handles.- Returns:
- a new
JmsMatsTransactionManager_JmsAndSpringManagedSqlTx
.
-
create
public static JmsMatsTransactionManager_JmsAndSpringManagedSqlTx create(org.springframework.transaction.PlatformTransactionManager platformTransactionManager, DataSource dataSource, Function<JmsMatsTransactionManager.JmsMatsTxContextKey, org.springframework.transaction.support.DefaultTransactionDefinition> transactionDefinitionFunction) Creates aJmsMatsTransactionManager_JmsAndSpringManagedSqlTx
from a providedPlatformTransactionManager
(of a type which manages a DataSource) - Do note that you should preferably have theDataSource
within the
wrapped using thePlatformTransactionManager
wrapLazyConnectionDatasource(DataSource)
method. If not wrapped as such, Mats will not be able to know whether the stage or initiation actually performed data access. Note: Only use this method if the variants NOT taking a DataSource fails to work. It is imperative that the DataSource and the PlatformTransactionManager provided "match up", meaning that the DataSource provided is actually the instance which the PlatformTransactionManager handles. Uses the suppliedTransactionDefinition
Function to define the transactions - considercreate(PlatformTransactionManager, DataSource)
if you are OK with the standard.- Parameters:
platformTransactionManager
- thePlatformTransactionManager
to use for transaction management (must be one employing a DataSource).dataSource
- theDataSource
which the suppliedPlatformTransactionManager
handles.transactionDefinitionFunction
- aFunction
which returns aDefaultTransactionDefinition
, possibly based on the providedJmsMatsTransactionManager.JmsMatsTxContextKey
(e.g. different isolation level for a special endpoint).- Returns:
- a new
JmsMatsTransactionManager_JmsAndSpringManagedSqlTx
.
-
getStandardTransactionDefinitionFunctionFor
public static Function<JmsMatsTransactionManager.JmsMatsTxContextKey,org.springframework.transaction.support.DefaultTransactionDefinition> getStandardTransactionDefinitionFunctionFor(Class<? extends org.springframework.transaction.PlatformTransactionManager> platformTransactionManager) Returns the standard TransactionDefinition Function for the supplied PlatformTransactionManager. Sets Isolation Level toISOLATION_READ_COMMITTED
(unless HibernateTransactionManager, which does not support setting Isolation Level) and Propagation Behavior toPROPAGATION_REQUIRES_NEW
- and also sets the name of the transaction toJmsMatsTransactionManager.JmsMatsTxContextKey
.toString(). -
wrapLazyConnectionDatasource
Creates a proxy/wrapper that has lazy connection getting, and monitoring of whether the connection was actually retrieved. This again enables SpringJmsMats implementation to see if the SQL Connection was actually employed (i.e. a Statement was created) - not only whether we went into transactional demarcation, which a Mats stage always does. This method is internally employed with theDataSource-taking
factories of this class which makes an internalDataSourceTransactionManager
, but should also be employed if you externally create another type ofPlatformTransactionManager
, e.g. the HibernateTransactionManager, and provide that to the factories of this class taking a PlatformTransactionManager. It returns an instance ofDeferredConnectionProxyDataSourceWrapper
, but as an extension that also implements Spring'sInfrastructureProxy
. We want this Lazy-and-Monitored DataSource which is returned here to "compare equals" with that of the DataSource which is supplied to us - and which might be employed by other components "on the outside" of Mats - wrt. how Spring'sDataSourceUtils
compare them in its ThreadLocal cache-hackery. Therefore, the proxy implementInfrastructureProxy
(read its JavaDoc!), which means that Spring can trawl its way down to the actual DataSource when it needs to compare. Note: You will find this proxy-handling in theTransactionSynchronizationManager.getResource(Object)
, which invokesTransactionSynchronizationUtils.unwrapResourceIfNecessary(..)
, where the instanceof-check for InfraStructureProxy resides. "The magnitude of this hack compares favorably with that of the US-of-A's national debt." Note: It is not a problem if the DataSource supplied is already wrapped in aLazyConnectionDataSourceProxy
, but it is completely unnecessary. Tip: If you would want to check/understand how the LazyConnection stuff work, you may within a Mats stage do a DataSourceUtil.getConnection(dataSource) - if the returned Connection's toString() looks like"DeferredConnectionProxy@3ec11999 WITHOUT actual Connection from DataSource@3406472c..."
then the actual Connection is still not gotten. When it is gotten (e.g. after having done a SQL CRUD operation), the toString() will look like"DeferredConnectionProxy@3ec11999 WITH actual Connection@b5cc23a"
-
getPlatformTransactionManager
public org.springframework.transaction.PlatformTransactionManager getPlatformTransactionManager()- Returns:
- the employed Spring
PlatformTransactionManager
, which either was created by this instance if this instance was created using one of the DataSource-taking factory methods, or it was provided if this instance was created using one of the PlatformTransactionManager-taking methods.
-
getDataSource
- Returns:
- the employed
DataSource
, which either was provided when creating this instance (and thus wrapped), or was reflected out of the providedPlatformTransactionManager
. It is hopefully wrapped using thewrapLazyConnectionDatasource(DataSource)
, which is done automatically if this instance was created using one of the DataSource-taking factory methods. However, if this instance was created using one of the PlatformTransactionManager-taking factory methods, it is up to the user to have wrapped it. Note that it works unwrapped too, but SpringJmsMats cannot then know whether the stages actually employ the SQL Connection, and must do full transaction demarcation around every stage.
-
getDataSourceUnwrapped
- Returns:
- the unwrapped variant of
getDataSource()
- note that it is only anyInfrastructureProxy
s that are unwrapped; Any wrapping done by a database pool is left intact.
-
getTransactionContext
public JmsMatsTransactionManager.TransactionContext getTransactionContext(JmsMatsTransactionManager.JmsMatsTxContextKey txContextKey) Description copied from interface:JmsMatsTransactionManager
Provides an implementation ofJmsMatsTransactionManager.TransactionContext
. (JMS Connection and Session handling is done byJmsMatsJmsSessionHandler
).- Specified by:
getTransactionContext
in interfaceJmsMatsTransactionManager
- Overrides:
getTransactionContext
in classJmsMatsTransactionManager_Jms
- Parameters:
txContextKey
- for whichJmsMatsStage
orJmsMatsInitiator
this request forJmsMatsTransactionManager.TransactionContext
is for.- Returns:
- a
JmsMatsTransactionManager.TransactionContext
for the supplied txContextKey.
-