Finalized the JTA support
This commit is contained in:
parent
085d472364
commit
dbc9125177
7 changed files with 208 additions and 129 deletions
|
|
@ -1,50 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
|
||||||
*/
|
|
||||||
|
|
||||||
package se.scalablesolutions.akka.stm
|
|
||||||
|
|
||||||
import javax.transaction.{TransactionManager, UserTransaction, SystemException}
|
|
||||||
import javax.naming.{InitialContext, Context, NamingException}
|
|
||||||
|
|
||||||
import se.scalablesolutions.akka.config.Config._
|
|
||||||
import se.scalablesolutions.akka.util.Logging
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
|
||||||
*/
|
|
||||||
object JtaTransactionManagerDetector extends Logging {
|
|
||||||
val DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction"
|
|
||||||
val FALLBACK_TRANSACTION_MANAGER_NAMES = List("java:comp/TransactionManager",
|
|
||||||
"java:appserver/TransactionManager",
|
|
||||||
"java:pm/TransactionManager",
|
|
||||||
"java:/TransactionManager")
|
|
||||||
val DEFAULT_TRANSACTION_SYNCHRONIZATION_REGISTRY_NAME = "java:comp/TransactionSynchronizationRegistry"
|
|
||||||
val TRANSACTION_SYNCHRONIZATION_REGISTRY_CLASS_NAME = "javax.transaction.TransactionSynchronizationRegistry"
|
|
||||||
|
|
||||||
def findUserTransaction: Option[UserTransaction] = {
|
|
||||||
val located = createInitialContext.lookup(DEFAULT_USER_TRANSACTION_NAME)
|
|
||||||
if (located eq null) None
|
|
||||||
else {
|
|
||||||
log.info("JTA UserTransaction detected [%s]", located)
|
|
||||||
Some(located.asInstanceOf[UserTransaction])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def findTransactionManager: Option[TransactionManager] = {
|
|
||||||
val context = createInitialContext
|
|
||||||
val tms = for {
|
|
||||||
name <- FALLBACK_TRANSACTION_MANAGER_NAMES
|
|
||||||
tm = context.lookup(name)
|
|
||||||
if tm ne null
|
|
||||||
} yield tm
|
|
||||||
tms match {
|
|
||||||
case Nil => None
|
|
||||||
case tm :: _ =>
|
|
||||||
log.info("JTA TransactionManager detected [%s]", tm)
|
|
||||||
Some(tm.asInstanceOf[TransactionManager])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def createInitialContext = new InitialContext(new java.util.Hashtable)
|
|
||||||
}
|
|
||||||
|
|
@ -297,31 +297,24 @@ object Transaction {
|
||||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||||
*/
|
*/
|
||||||
@serializable class Transaction extends Logging {
|
@serializable class Transaction extends Logging {
|
||||||
import JtaTransactionManagerDetector._
|
|
||||||
val JTA_AWARE = config.getBool("akka.stm.jta-aware", false)
|
val JTA_AWARE = config.getBool("akka.stm.jta-aware", false)
|
||||||
val jta: Either[Option[UserTransaction], Option[TransactionManager]] = if (JTA_AWARE) {
|
|
||||||
findUserTransaction match {
|
|
||||||
case None => Right(findTransactionManager)
|
|
||||||
case tm => Left(tm)
|
|
||||||
}
|
|
||||||
} else Left(None)
|
|
||||||
|
|
||||||
val id = Transaction.idFactory.incrementAndGet
|
val id = Transaction.idFactory.incrementAndGet
|
||||||
@volatile private[this] var status: TransactionStatus = TransactionStatus.New
|
@volatile private[this] var status: TransactionStatus = TransactionStatus.New
|
||||||
private[akka] var transaction: Option[MultiverseTransaction] = None
|
private[akka] var transaction: Option[MultiverseTransaction] = None
|
||||||
private[this] val persistentStateMap = new HashMap[String, Committable]
|
private[this] val persistentStateMap = new HashMap[String, Committable]
|
||||||
private[akka] val depth = new AtomicInteger(0)
|
private[akka] val depth = new AtomicInteger(0)
|
||||||
|
|
||||||
|
val tc: Option[TransactionContainer] =
|
||||||
|
if (JTA_AWARE) Some(TransactionContainer())
|
||||||
|
else None
|
||||||
|
|
||||||
log.trace("Creating %s", toString)
|
log.trace("Creating %s", toString)
|
||||||
|
|
||||||
// --- public methods ---------
|
// --- public methods ---------
|
||||||
|
|
||||||
def begin = synchronized {
|
def begin = synchronized {
|
||||||
jta match {
|
tc.foreach(_.begin)
|
||||||
case Left(Some(userTx)) => if (!isJtaTxActive(userTx.getStatus)) userTx.begin
|
|
||||||
case Right(Some(txMan)) => if (!isJtaTxActive(txMan.getStatus)) txMan.begin
|
|
||||||
case _ => {} // do nothing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def commit = synchronized {
|
def commit = synchronized {
|
||||||
|
|
@ -330,20 +323,12 @@ object Transaction {
|
||||||
persistentStateMap.valuesIterator.foreach(_.commit)
|
persistentStateMap.valuesIterator.foreach(_.commit)
|
||||||
}
|
}
|
||||||
status = TransactionStatus.Completed
|
status = TransactionStatus.Completed
|
||||||
jta match {
|
tc.foreach(_.commit)
|
||||||
case Left(Some(userTx)) => if (isJtaTxActive(userTx.getStatus)) userTx.commit
|
|
||||||
case Right(Some(txMan)) => if (isJtaTxActive(txMan.getStatus)) txMan.commit
|
|
||||||
case _ => {} // do nothing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def abort = synchronized {
|
def abort = synchronized {
|
||||||
log.trace("Aborting transaction %s", toString)
|
log.trace("Aborting transaction %s", toString)
|
||||||
jta match {
|
tc.foreach(_.rollback)
|
||||||
case Left(Some(userTx)) => if (isJtaTxActive(userTx.getStatus)) userTx.rollback
|
|
||||||
case Right(Some(txMan)) => if (isJtaTxActive(txMan.getStatus)) txMan.rollback
|
|
||||||
case _ => {} // do nothing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def isNew = synchronized { status == TransactionStatus.New }
|
def isNew = synchronized { status == TransactionStatus.New }
|
||||||
|
|
|
||||||
160
akka-core/src/main/scala/stm/TransactionContainer.scala
Normal file
160
akka-core/src/main/scala/stm/TransactionContainer.scala
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package se.scalablesolutions.akka.stm
|
||||||
|
|
||||||
|
import javax.transaction.{TransactionManager, UserTransaction, Transaction => JtaTransaction, SystemException, Status}
|
||||||
|
import javax.naming.{InitialContext, Context, NamingException}
|
||||||
|
|
||||||
|
import se.scalablesolutions.akka.config.Config._
|
||||||
|
import se.scalablesolutions.akka.util.Logging
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JTA transaction container holding either a UserTransaction or a TransactionManager.
|
||||||
|
* <p/>
|
||||||
|
* The TransactionContainer is created using the factory <tt>val container = TransactionContainer()</tt>
|
||||||
|
*
|
||||||
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||||
|
*/
|
||||||
|
class TransactionContainer private (val tm: Either[Option[UserTransaction], Option[TransactionManager]]) {
|
||||||
|
|
||||||
|
def begin = tm match {
|
||||||
|
case Left(Some(userTx)) => userTx.begin
|
||||||
|
case Right(Some(txMan)) => txMan.begin
|
||||||
|
case _ => throw new IllegalStateException("Does not have a UserTransaction or TransactionManager in scope")
|
||||||
|
}
|
||||||
|
|
||||||
|
def commit = tm match {
|
||||||
|
case Left(Some(userTx)) => userTx.commit
|
||||||
|
case Right(Some(txMan)) => txMan.commit
|
||||||
|
case _ => throw new IllegalStateException("Does not have a UserTransaction or TransactionManager in scope")
|
||||||
|
}
|
||||||
|
|
||||||
|
def rollback = tm match {
|
||||||
|
case Left(Some(userTx)) => userTx.rollback
|
||||||
|
case Right(Some(txMan)) => txMan.rollback
|
||||||
|
case _ => throw new IllegalStateException("Does not have a UserTransaction or TransactionManager in scope")
|
||||||
|
}
|
||||||
|
|
||||||
|
def getStatus = tm match {
|
||||||
|
case Left(Some(userTx)) => userTx.getStatus
|
||||||
|
case Right(Some(txMan)) => txMan.getStatus
|
||||||
|
case _ => throw new IllegalStateException("Does not have a UserTransaction or TransactionManager in scope")
|
||||||
|
}
|
||||||
|
|
||||||
|
def isInExistingTransaction = tm match {
|
||||||
|
case Left(Some(userTx)) => userTx.getStatus == Status.STATUS_ACTIVE
|
||||||
|
case Right(Some(txMan)) => txMan.getStatus == Status.STATUS_ACTIVE
|
||||||
|
case _ => throw new IllegalStateException("Does not have a UserTransaction or TransactionManager in scope")
|
||||||
|
}
|
||||||
|
|
||||||
|
def isRollbackOnly = tm match {
|
||||||
|
case Left(Some(userTx)) => userTx.getStatus == Status.STATUS_MARKED_ROLLBACK
|
||||||
|
case Right(Some(txMan)) => txMan.getStatus == Status.STATUS_MARKED_ROLLBACK
|
||||||
|
case _ => throw new IllegalStateException("Does not have a UserTransaction or TransactionManager in scope")
|
||||||
|
}
|
||||||
|
|
||||||
|
def setRollbackOnly = tm match {
|
||||||
|
case Left(Some(userTx)) => userTx.setRollbackOnly
|
||||||
|
case Right(Some(txMan)) => txMan.setRollbackOnly
|
||||||
|
case _ => throw new IllegalStateException("Does not have a UserTransaction or TransactionManager in scope")
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
def getTransaction: JtaTransaction = tm match {
|
||||||
|
case Left(Some(userTx)) => userTx
|
||||||
|
case Right(Some(txMan)) => txMan.getTransaction
|
||||||
|
case _ => throw new IllegalStateException("Does not have a UserTransaction or TransactionManager in scope")
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
def suspend = tm match {
|
||||||
|
case Right(Some(txMan)) => txMan.suspend
|
||||||
|
case _ => throw new IllegalStateException("Does not have a TransactionManager in scope")
|
||||||
|
}
|
||||||
|
|
||||||
|
def resume(tx: JtaTransaction) = tm match {
|
||||||
|
case Right(Some(txMan)) => txMan.resume(tx)
|
||||||
|
case _ => throw new IllegalStateException("Does not have a TransactionManager in scope")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects if there is a UserTransaction or TransactionManager available in the JNDI.
|
||||||
|
*
|
||||||
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||||
|
*/
|
||||||
|
object TransactionContainer extends Logging {
|
||||||
|
val AKKA_JTA_TRANSACTION_SERVICE_CLASS = "se.scalablesolutions.akka.jta.AtomikosTransactionService"
|
||||||
|
val DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction"
|
||||||
|
val FALLBACK_TRANSACTION_MANAGER_NAMES = "java:comp/TransactionManager" ::
|
||||||
|
"java:appserver/TransactionManager" ::
|
||||||
|
"java:pm/TransactionManager" ::
|
||||||
|
"java:/TransactionManager" :: Nil
|
||||||
|
val DEFAULT_TRANSACTION_SYNCHRONIZATION_REGISTRY_NAME = "java:comp/TransactionSynchronizationRegistry"
|
||||||
|
val TRANSACTION_SYNCHRONIZATION_REGISTRY_CLASS_NAME = "javax.transaction.TransactionSynchronizationRegistry"
|
||||||
|
val JTA_PROVIDER = config.getString("akka.stm.jta.provider", "from-jndi")
|
||||||
|
|
||||||
|
def apply(tm: Either[Option[UserTransaction], Option[TransactionManager]]) = new TransactionContainer(tm)
|
||||||
|
|
||||||
|
def apply(): TransactionContainer =
|
||||||
|
JTA_PROVIDER match {
|
||||||
|
case "from-jndi" =>
|
||||||
|
new TransactionContainer(findUserTransaction match {
|
||||||
|
case None => Right(findTransactionManager)
|
||||||
|
case tm => Left(tm)
|
||||||
|
})
|
||||||
|
case "atomikos" =>
|
||||||
|
try {
|
||||||
|
Class.forName(AKKA_JTA_TRANSACTION_SERVICE_CLASS)
|
||||||
|
.newInstance.asInstanceOf[TransactionService]
|
||||||
|
.transactionContainer
|
||||||
|
} catch {
|
||||||
|
case e: ClassNotFoundException =>
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"JTA provider defined as 'atomikos', but the AtomikosTransactionService classes can not be found." +
|
||||||
|
"\n\tPlease make sure you have 'akka-jta' JAR and its dependencies on your classpath.")
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"No UserTransaction on TransactionManager could be found in scope." +
|
||||||
|
"\n\tEither add 'akka-jta' to the classpath or make sure there is a" +
|
||||||
|
"\n\tTransactionManager or UserTransaction defined in the JNDI.")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def findUserTransaction: Option[UserTransaction] = {
|
||||||
|
val located = createInitialContext.lookup(DEFAULT_USER_TRANSACTION_NAME)
|
||||||
|
if (located eq null) None
|
||||||
|
else {
|
||||||
|
log.info("JTA UserTransaction detected [%s]", located)
|
||||||
|
Some(located.asInstanceOf[UserTransaction])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def findTransactionManager: Option[TransactionManager] = {
|
||||||
|
val context = createInitialContext
|
||||||
|
val tms = for {
|
||||||
|
name <- FALLBACK_TRANSACTION_MANAGER_NAMES
|
||||||
|
tm = context.lookup(name)
|
||||||
|
if tm ne null
|
||||||
|
} yield tm
|
||||||
|
tms match {
|
||||||
|
case Nil => None
|
||||||
|
case tm :: _ =>
|
||||||
|
log.info("JTA TransactionManager detected [%s]", tm)
|
||||||
|
Some(tm.asInstanceOf[TransactionManager])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def createInitialContext = new InitialContext(new java.util.Hashtable)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JTA Transaction service.
|
||||||
|
*
|
||||||
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||||
|
*/
|
||||||
|
trait TransactionService {
|
||||||
|
def transactionContainer: TransactionContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -10,19 +10,22 @@ import com.atomikos.icatch.jta.{J2eeTransactionManager, J2eeUserTransaction}
|
||||||
import com.atomikos.icatch.config.{TSInitInfo, UserTransactionService, UserTransactionServiceImp}
|
import com.atomikos.icatch.config.{TSInitInfo, UserTransactionService, UserTransactionServiceImp}
|
||||||
|
|
||||||
import se.scalablesolutions.akka.config.Config._
|
import se.scalablesolutions.akka.config.Config._
|
||||||
|
import se.scalablesolutions.akka.stm.{TransactionService, TransactionContainer}
|
||||||
|
|
||||||
|
object AtomikosTransactionService extends AtomikosTransactionService
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Atomikos implementation of the transaction service trait.
|
* Atomikos implementation of the transaction service trait.
|
||||||
*
|
*
|
||||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||||
*/
|
*/
|
||||||
object AtomikosTransactionService extends TransactionService with TransactionProtocol {
|
class AtomikosTransactionService extends TransactionService with TransactionProtocol {
|
||||||
|
|
||||||
val JTA_TRANSACTION_TIMEOUT = config.getInt("akka.jta.timeout", 60)
|
val JTA_TRANSACTION_TIMEOUT: Int = config.getInt("akka.stm.jta.timeout", 60000) / 1000
|
||||||
private val txService: UserTransactionService = new UserTransactionServiceImp
|
private val txService: UserTransactionService = new UserTransactionServiceImp
|
||||||
private val info: TSInitInfo = txService.createTSInitInfo
|
private val info: TSInitInfo = txService.createTSInitInfo
|
||||||
|
|
||||||
val transactionManager =
|
val transactionContainer: TransactionContainer = TransactionContainer(Right(Some(
|
||||||
try {
|
try {
|
||||||
txService.init(info)
|
txService.init(info)
|
||||||
val tm: TransactionManager = new J2eeTransactionManager
|
val tm: TransactionManager = new J2eeTransactionManager
|
||||||
|
|
@ -31,7 +34,7 @@ object AtomikosTransactionService extends TransactionService with TransactionPro
|
||||||
} catch {
|
} catch {
|
||||||
case e => throw new SystemException("Could not create a new Atomikos J2EE Transaction Manager, due to: " + e.toString)
|
case e => throw new SystemException("Could not create a new Atomikos J2EE Transaction Manager, due to: " + e.toString)
|
||||||
}
|
}
|
||||||
|
)))
|
||||||
// TODO: gracefully shutdown of the TM
|
// TODO: gracefully shutdown of the TM
|
||||||
//txService.shutdown(false)
|
//txService.shutdown(false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,10 @@ package se.scalablesolutions.akka.jta
|
||||||
|
|
||||||
import javax.transaction.{Transaction, Status, TransactionManager}
|
import javax.transaction.{Transaction, Status, TransactionManager}
|
||||||
|
|
||||||
|
import se.scalablesolutions.akka.stm.{TransactionService, TransactionContainer}
|
||||||
import se.scalablesolutions.akka.util.Logging
|
import se.scalablesolutions.akka.util.Logging
|
||||||
import se.scalablesolutions.akka.config.Config._
|
import se.scalablesolutions.akka.config.Config._
|
||||||
|
|
||||||
/**
|
|
||||||
* JTA Transaction service.
|
|
||||||
*
|
|
||||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
|
||||||
*/
|
|
||||||
trait TransactionService {
|
|
||||||
def transactionManager: TransactionManager
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base monad for the transaction monad implementations.
|
* Base monad for the transaction monad implementations.
|
||||||
*
|
*
|
||||||
|
|
@ -39,11 +31,6 @@ trait TransactionMonad {
|
||||||
// JTA Transaction definitions
|
// JTA Transaction definitions
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current Transaction.
|
|
||||||
*/
|
|
||||||
def getTransaction: Transaction = TransactionContext.getTransactionManager.getTransaction
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the current transaction as doomed.
|
* Marks the current transaction as doomed.
|
||||||
*/
|
*/
|
||||||
|
|
@ -108,13 +95,8 @@ trait TransactionMonad {
|
||||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||||
*/
|
*/
|
||||||
object TransactionContext extends TransactionProtocol with Logging {
|
object TransactionContext extends TransactionProtocol with Logging {
|
||||||
val TRANSACTION_PROVIDER = config.getString("akka.jta.transaction-provider", "atomikos")
|
implicit val tc = TransactionContainer()
|
||||||
|
private[TransactionContext] val stack = new scala.util.DynamicVariable(new TransactionContext(tc))
|
||||||
private implicit val defaultTransactionService = TRANSACTION_PROVIDER match {
|
|
||||||
case "atomikos" => AtomikosTransactionService
|
|
||||||
case _ => throw new IllegalArgumentException("Transaction provider [" + TRANSACTION_PROVIDER + "] is not supported")
|
|
||||||
}
|
|
||||||
private[TransactionContext] val stack = new scala.util.DynamicVariable(new TransactionContext)
|
|
||||||
|
|
||||||
object Required extends TransactionMonad {
|
object Required extends TransactionMonad {
|
||||||
def map[T](f: TransactionMonad => T): T = withTxRequired { f(this) }
|
def map[T](f: TransactionMonad => T): T = withTxRequired { f(this) }
|
||||||
|
|
@ -157,9 +139,7 @@ object TransactionContext extends TransactionProtocol with Logging {
|
||||||
|
|
||||||
private[jta] def isRollbackOnly = current.isRollbackOnly
|
private[jta] def isRollbackOnly = current.isRollbackOnly
|
||||||
|
|
||||||
private[jta] def getTransactionManager: TransactionManager = current.getTransactionManager
|
private[jta] def getTransactionContainer: TransactionContainer = current.getTransactionContainer
|
||||||
|
|
||||||
private[jta] def getTransaction: Transaction = current.getTransactionManager.getTransaction
|
|
||||||
|
|
||||||
private[this] def current = stack.value
|
private[this] def current = stack.value
|
||||||
|
|
||||||
|
|
@ -171,14 +151,14 @@ object TransactionContext extends TransactionProtocol with Logging {
|
||||||
*/
|
*/
|
||||||
private[jta] def withNewContext[T](body: => T): T = {
|
private[jta] def withNewContext[T](body: => T): T = {
|
||||||
val suspendedTx: Option[Transaction] =
|
val suspendedTx: Option[Transaction] =
|
||||||
if (isInExistingTransaction(getTransactionManager)) {
|
if (getTransactionContainer.isInExistingTransaction) {
|
||||||
log.debug("Suspending TX")
|
log.debug("Suspending TX")
|
||||||
Some(getTransactionManager.suspend)
|
Some(getTransactionContainer.suspend)
|
||||||
} else None
|
} else None
|
||||||
val result = stack.withValue(new TransactionContext) { body }
|
val result = stack.withValue(new TransactionContext(tc)) { body }
|
||||||
if (suspendedTx.isDefined) {
|
if (suspendedTx.isDefined) {
|
||||||
log.debug("Resuming TX")
|
log.debug("Resuming TX")
|
||||||
getTransactionManager.resume(suspendedTx.get)
|
getTransactionContainer.resume(suspendedTx.get)
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
@ -189,9 +169,8 @@ object TransactionContext extends TransactionProtocol with Logging {
|
||||||
*
|
*
|
||||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||||
*/
|
*/
|
||||||
class TransactionContext(private implicit val transactionService: TransactionService) {
|
class TransactionContext(val tc: TransactionContainer) {
|
||||||
val tm: TransactionManager = transactionService.transactionManager
|
private def setRollbackOnly = tc.setRollbackOnly
|
||||||
private def setRollbackOnly = tm.setRollbackOnly
|
private def isRollbackOnly: Boolean = tc.getStatus == Status.STATUS_MARKED_ROLLBACK
|
||||||
private def isRollbackOnly: Boolean = tm.getStatus == Status.STATUS_MARKED_ROLLBACK
|
private def getTransactionContainer: TransactionContainer = tc
|
||||||
private def getTransactionManager: TransactionManager = tm
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
package se.scalablesolutions.akka.jta
|
package se.scalablesolutions.akka.jta
|
||||||
|
|
||||||
import se.scalablesolutions.akka.util.Logging
|
import se.scalablesolutions.akka.util.Logging
|
||||||
|
import se.scalablesolutions.akka.stm.TransactionContainer
|
||||||
|
|
||||||
import javax.naming.{NamingException, Context, InitialContext}
|
import javax.naming.{NamingException, Context, InitialContext}
|
||||||
import javax.transaction.{
|
import javax.transaction.{
|
||||||
|
|
@ -71,7 +72,7 @@ trait TransactionProtocol extends Logging {
|
||||||
* <pre>
|
* <pre>
|
||||||
* override def joinTransaction = {
|
* override def joinTransaction = {
|
||||||
* val em = TransactionContext.getEntityManager
|
* val em = TransactionContext.getEntityManager
|
||||||
* val tm = TransactionContext.getTransactionManager
|
* val tm = TransactionContext.getTransactionContainer
|
||||||
* val closeAtTxCompletion: Boolean)
|
* val closeAtTxCompletion: Boolean)
|
||||||
* tm.getTransaction.registerSynchronization(new javax.transaction.Synchronization() {
|
* tm.getTransaction.registerSynchronization(new javax.transaction.Synchronization() {
|
||||||
* def beforeCompletion = {
|
* def beforeCompletion = {
|
||||||
|
|
@ -109,7 +110,7 @@ trait TransactionProtocol extends Logging {
|
||||||
* Here is an example on how to handle JPA exceptions.
|
* Here is an example on how to handle JPA exceptions.
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* def handleException(tm: TransactionManager, e: Exception) = {
|
* def handleException(tm: TransactionContainer, e: Exception) = {
|
||||||
* if (isInExistingTransaction(tm)) {
|
* if (isInExistingTransaction(tm)) {
|
||||||
* // Do not roll back in case of NoResultException or NonUniqueResultException
|
* // Do not roll back in case of NoResultException or NonUniqueResultException
|
||||||
* if (!e.isInstanceOf[NoResultException] &&
|
* if (!e.isInstanceOf[NoResultException] &&
|
||||||
|
|
@ -122,7 +123,7 @@ trait TransactionProtocol extends Logging {
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
def handleException(tm: TransactionManager, e: Exception) = {
|
def handleException(tm: TransactionContainer, e: Exception) = {
|
||||||
tm.setRollbackOnly
|
tm.setRollbackOnly
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|
@ -133,7 +134,7 @@ trait TransactionProtocol extends Logging {
|
||||||
* Creates a new transaction if no transaction is active in scope, else joins the outer transaction.
|
* Creates a new transaction if no transaction is active in scope, else joins the outer transaction.
|
||||||
*/
|
*/
|
||||||
def withTxRequired[T](body: => T): T = {
|
def withTxRequired[T](body: => T): T = {
|
||||||
val tm = TransactionContext.getTransactionManager
|
val tm = TransactionContext.getTransactionContainer
|
||||||
if (!isInExistingTransaction(tm)) {
|
if (!isInExistingTransaction(tm)) {
|
||||||
tm.begin
|
tm.begin
|
||||||
try {
|
try {
|
||||||
|
|
@ -154,7 +155,7 @@ trait TransactionProtocol extends Logging {
|
||||||
* commits or rollbacks new transaction, finally resumes previous transaction.
|
* commits or rollbacks new transaction, finally resumes previous transaction.
|
||||||
*/
|
*/
|
||||||
def withTxRequiresNew[T](body: => T): T = TransactionContext.withNewContext {
|
def withTxRequiresNew[T](body: => T): T = TransactionContext.withNewContext {
|
||||||
val tm = TransactionContext.getTransactionManager
|
val tm = TransactionContext.getTransactionContainer
|
||||||
tm.begin
|
tm.begin
|
||||||
try {
|
try {
|
||||||
joinTransaction
|
joinTransaction
|
||||||
|
|
@ -191,7 +192,7 @@ trait TransactionProtocol extends Logging {
|
||||||
* Throws a TransactionRequiredException if there is no transaction active in scope.
|
* Throws a TransactionRequiredException if there is no transaction active in scope.
|
||||||
*/
|
*/
|
||||||
def withTxMandatory[T](body: => T): T = {
|
def withTxMandatory[T](body: => T): T = {
|
||||||
if (!isInExistingTransaction(TransactionContext.getTransactionManager))
|
if (!isInExistingTransaction(TransactionContext.getTransactionContainer))
|
||||||
throw new TransactionRequiredException("No active TX at method with TX type set to MANDATORY")
|
throw new TransactionRequiredException("No active TX at method with TX type set to MANDATORY")
|
||||||
body
|
body
|
||||||
}
|
}
|
||||||
|
|
@ -202,12 +203,12 @@ trait TransactionProtocol extends Logging {
|
||||||
* Throws a SystemException in case of an existing transaction in scope.
|
* Throws a SystemException in case of an existing transaction in scope.
|
||||||
*/
|
*/
|
||||||
def withTxNever[T](body: => T): T = {
|
def withTxNever[T](body: => T): T = {
|
||||||
if (isInExistingTransaction(TransactionContext.getTransactionManager))
|
if (isInExistingTransaction(TransactionContext.getTransactionContainer))
|
||||||
throw new SystemException("Detected active TX at method with TX type set to NEVER")
|
throw new SystemException("Detected active TX at method with TX type set to NEVER")
|
||||||
body
|
body
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def commitOrRollBack(tm: TransactionManager) = {
|
protected def commitOrRollBack(tm: TransactionContainer) = {
|
||||||
if (isInExistingTransaction(tm)) {
|
if (isInExistingTransaction(tm)) {
|
||||||
if (isRollbackOnly(tm)) {
|
if (isRollbackOnly(tm)) {
|
||||||
log.debug("Rolling back TX marked as ROLLBACK_ONLY")
|
log.debug("Rolling back TX marked as ROLLBACK_ONLY")
|
||||||
|
|
@ -229,7 +230,7 @@ trait TransactionProtocol extends Logging {
|
||||||
* @param tm the transaction manager
|
* @param tm the transaction manager
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
protected def isInExistingTransaction(tm: TransactionManager): Boolean =
|
protected def isInExistingTransaction(tm: TransactionContainer): Boolean =
|
||||||
tm.getStatus != Status.STATUS_NO_TRANSACTION
|
tm.getStatus != Status.STATUS_NO_TRANSACTION
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -238,7 +239,7 @@ trait TransactionProtocol extends Logging {
|
||||||
* @param tm the transaction manager
|
* @param tm the transaction manager
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
protected def isRollbackOnly(tm: TransactionManager): Boolean =
|
protected def isRollbackOnly(tm: TransactionContainer): Boolean =
|
||||||
tm.getStatus == Status.STATUS_MARKED_ROLLBACK
|
tm.getStatus == Status.STATUS_MARKED_ROLLBACK
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
<akka>
|
<akka>
|
||||||
version = "0.9"
|
version = "0.9"
|
||||||
|
|
||||||
# FQN to the class doing initial active object/actor
|
# FQN (Fully Qualified Name) to the class doing initial active object/actor
|
||||||
# supervisor bootstrap, should be defined in default constructor
|
# supervisor bootstrap, should be defined in default constructor
|
||||||
boot = ["sample.camel.Boot",
|
boot = ["sample.camel.Boot",
|
||||||
"sample.rest.java.Boot",
|
"sample.rest.java.Boot",
|
||||||
|
|
@ -31,17 +31,18 @@
|
||||||
|
|
||||||
<stm>
|
<stm>
|
||||||
service = on
|
service = on
|
||||||
fair = on # should transactions be fair or non-fair (non fair yield better performance)
|
fair = on # should transactions be fair or non-fair (non fair yield better performance)
|
||||||
max-nr-of-retries = 1000 # max nr of retries of a failing transaction before giving up
|
max-nr-of-retries = 1000 # max nr of retries of a failing transaction before giving up
|
||||||
timeout = 10000 # transaction timeout; if transaction has not committed within the timeout then it is aborted
|
timeout = 10000 # transaction timeout; if transaction has not committed within the timeout then it is aborted
|
||||||
jta-aware = off # 'on' means that if there JTA Transaction Manager available then the STM will begin (or join), commit or rollback the JTA transaction. Default is 'off'
|
jta-aware = off # 'on' means that if there JTA Transaction Manager available then the STM will
|
||||||
|
# begin (or join), commit or rollback the JTA transaction. Default is 'off'.
|
||||||
</stm>
|
</stm>
|
||||||
|
|
||||||
<jta>
|
<jta>
|
||||||
service = off # 'on' means that if there is a running JTA transaction then the STM will participate in it. Default is 'off'
|
provider = "from-jndi" # Options: "from-jndi", "atomikos"
|
||||||
timeout = 60 # timeout in seconds
|
timeout = 60000
|
||||||
</jta>
|
</jta>
|
||||||
|
|
||||||
<rest>
|
<rest>
|
||||||
service = on
|
service = on
|
||||||
hostname = "localhost"
|
hostname = "localhost"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue