154 lines
5.4 KiB
Scala
154 lines
5.4 KiB
Scala
|
|
/**
|
||
|
|
* Copyright (C) 2009 Scalable Solutions.
|
||
|
|
*/
|
||
|
|
|
||
|
|
package se.scalablesolutions.akka.kernel.stm
|
||
|
|
|
||
|
|
import java.lang.reflect.Field
|
||
|
|
import java.util.concurrent.atomic.AtomicBoolean
|
||
|
|
|
||
|
|
import kernel.state.{TransactionalMap, TransactionalRef, TransactionalVector}
|
||
|
|
import kernel.util.Logging
|
||
|
|
|
||
|
|
class TransactionAwareWrapperException(val cause: Throwable, val tx: Option[Transaction]) extends RuntimeException(cause) {
|
||
|
|
override def toString(): String = "TransactionAwareWrapperException[" + cause + ", " + tx + "]"
|
||
|
|
}
|
||
|
|
|
||
|
|
object TransactionManagement {
|
||
|
|
private val txEnabled = new AtomicBoolean(true)
|
||
|
|
|
||
|
|
def isTransactionsEnabled = txEnabled.get
|
||
|
|
def enableTransactions = txEnabled.set(true)
|
||
|
|
|
||
|
|
private[kernel] lazy val threadBoundTx: ThreadLocal[Option[Transaction]] = {
|
||
|
|
val tl = new ThreadLocal[Option[Transaction]]
|
||
|
|
tl.set(None)
|
||
|
|
tl
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// FIXME: STM that allows concurrent updates, detects collision, rolls back and restarts
|
||
|
|
trait TransactionManagement extends Logging {
|
||
|
|
val transactionalInstance: AnyRef
|
||
|
|
|
||
|
|
private lazy val changeSet = new ChangeSet(transactionalInstance.getClass.getName)
|
||
|
|
|
||
|
|
import TransactionManagement.threadBoundTx
|
||
|
|
private[kernel] var activeTx: Option[Transaction] = None
|
||
|
|
|
||
|
|
protected def startNewTransaction = {
|
||
|
|
val (maps, vectors, refs) = getTransactionalItemsFor(transactionalInstance)
|
||
|
|
changeSet.maps = maps
|
||
|
|
changeSet.refs = refs
|
||
|
|
changeSet.vectors = vectors
|
||
|
|
|
||
|
|
val newTx = new Transaction
|
||
|
|
newTx.begin(changeSet)
|
||
|
|
val tx = Some(newTx)
|
||
|
|
activeTx = tx
|
||
|
|
threadBoundTx.set(tx)
|
||
|
|
}
|
||
|
|
|
||
|
|
protected def joinExistingTransaction = {
|
||
|
|
val cflowTx = threadBoundTx.get
|
||
|
|
if (activeTx.isDefined && cflowTx.isDefined && activeTx.get.id == cflowTx.get.id) {
|
||
|
|
val currentTx = cflowTx.get
|
||
|
|
currentTx.join(changeSet)
|
||
|
|
activeTx = Some(currentTx)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
protected def tryToPrecommitTransaction = if (activeTx.isDefined) activeTx.get.precommit(changeSet)
|
||
|
|
|
||
|
|
protected def tryToCommitTransaction: Boolean = if (activeTx.isDefined) {
|
||
|
|
val tx = activeTx.get
|
||
|
|
tx.commit(changeSet)
|
||
|
|
removeTransactionIfTopLevel
|
||
|
|
true
|
||
|
|
} else false
|
||
|
|
|
||
|
|
protected def rollback(tx: Option[Transaction]) = tx match {
|
||
|
|
case None => {} // no tx; nothing to do
|
||
|
|
case Some(tx) =>
|
||
|
|
tx.rollback(changeSet)
|
||
|
|
}
|
||
|
|
|
||
|
|
protected def isInExistingTransaction = {
|
||
|
|
println(TransactionManagement)
|
||
|
|
println(TransactionManagement.threadBoundTx)
|
||
|
|
println(TransactionManagement.threadBoundTx.get)
|
||
|
|
println(TransactionManagement.threadBoundTx.get.isDefined)
|
||
|
|
TransactionManagement.threadBoundTx.get.isDefined
|
||
|
|
}
|
||
|
|
|
||
|
|
protected def isTransactionAborted = activeTx.isDefined && activeTx.get.isAborted
|
||
|
|
|
||
|
|
protected def incrementTransaction = if (activeTx.isDefined) activeTx.get.increment
|
||
|
|
|
||
|
|
protected def decrementTransaction = if (activeTx.isDefined) activeTx.get.decrement
|
||
|
|
|
||
|
|
protected def removeTransactionIfTopLevel =
|
||
|
|
if (activeTx.isDefined && activeTx.get.topLevel_?) {
|
||
|
|
activeTx = None
|
||
|
|
threadBoundTx.set(None)
|
||
|
|
}
|
||
|
|
|
||
|
|
protected def reenteringExistingTransaction= if (activeTx.isDefined) {
|
||
|
|
val cflowTx = threadBoundTx.get
|
||
|
|
if (cflowTx.isDefined && cflowTx.get.id == activeTx.get.id) false
|
||
|
|
else true
|
||
|
|
} else true
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Search for transactional items for a specific target instance, crawl the class hierarchy recursively up to the top.
|
||
|
|
*/
|
||
|
|
protected def getTransactionalItemsFor(targetInstance: AnyRef):
|
||
|
|
Tuple3[List[TransactionalMap[_, _]], List[TransactionalVector[_]], List[TransactionalRef[_]]] = {
|
||
|
|
require(targetInstance != null)
|
||
|
|
var maps: List[TransactionalMap[_, _]] = Nil
|
||
|
|
var refs: List[TransactionalRef[_]] = Nil
|
||
|
|
var vectors: List[TransactionalVector[_]] = Nil
|
||
|
|
|
||
|
|
def getTransactionalItemsFor(target: Class[_]):
|
||
|
|
Tuple3[List[TransactionalMap[_, _]], List[TransactionalVector[_]], List[TransactionalRef[_]]] = {
|
||
|
|
for {
|
||
|
|
field <- target.getDeclaredFields.toArray.toList.asInstanceOf[List[Field]]
|
||
|
|
fieldType = field.getType
|
||
|
|
if (fieldType == classOf[TransactionalMap[_, _]]) ||
|
||
|
|
(fieldType == classOf[TransactionalVector[_]]) ||
|
||
|
|
(fieldType == classOf[TransactionalRef[_]])
|
||
|
|
txItem = {
|
||
|
|
field.setAccessible(true)
|
||
|
|
field.get(targetInstance)
|
||
|
|
}
|
||
|
|
if txItem != null
|
||
|
|
} {
|
||
|
|
log.debug("Managing transactional state [%s]", field)
|
||
|
|
if (txItem.isInstanceOf[TransactionalMap[_, _]]) maps ::= txItem.asInstanceOf[TransactionalMap[_, _]]
|
||
|
|
else if (txItem.isInstanceOf[TransactionalRef[_]]) refs ::= txItem.asInstanceOf[TransactionalRef[_]]
|
||
|
|
else if (txItem.isInstanceOf[TransactionalVector[_]]) vectors ::= txItem.asInstanceOf[TransactionalVector[_]]
|
||
|
|
}
|
||
|
|
val parent = target.getSuperclass
|
||
|
|
if (parent == classOf[Object]) (maps, vectors, refs)
|
||
|
|
else getTransactionalItemsFor(parent)
|
||
|
|
}
|
||
|
|
|
||
|
|
// start the search for transactional items, crawl the class hierarchy up until we reach Object
|
||
|
|
getTransactionalItemsFor(targetInstance.getClass)
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
protected def getResultOrThrowException[T](future: FutureResult): Option[T] =
|
||
|
|
if (future.exception.isDefined) {
|
||
|
|
val (_, cause) = future.exception.get
|
||
|
|
throw new TransactionAwareWrapperException(cause, activeTx)
|
||
|
|
} else {
|
||
|
|
if (future.result.isDefined) {
|
||
|
|
val (res, tx) = future.result.get.asInstanceOf[Tuple2[AnyRef, Option[Transaction]]]
|
||
|
|
Some(res).asInstanceOf[Option[T]]
|
||
|
|
} else None
|
||
|
|
}
|
||
|
|
*/
|
||
|
|
}
|
||
|
|
|