2009-04-19 10:58:20 +02:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2009 Scalable Solutions.
|
|
|
|
|
*/
|
|
|
|
|
|
2009-09-03 11:02:21 +02:00
|
|
|
package se.scalablesolutions.akka.state
|
2009-04-19 10:58:20 +02:00
|
|
|
|
2009-09-03 11:02:21 +02:00
|
|
|
import stm.TransactionManagement
|
2009-10-06 00:07:27 +02:00
|
|
|
import stm.TransactionManagement.currentTransaction
|
2009-07-12 23:08:17 +02:00
|
|
|
import akka.collection._
|
2009-06-22 13:13:58 +02:00
|
|
|
|
2009-07-12 23:08:17 +02:00
|
|
|
import org.codehaus.aspectwerkz.proxy.Uuid
|
2009-06-22 13:13:58 +02:00
|
|
|
|
2009-07-12 23:08:17 +02:00
|
|
|
import scala.collection.mutable.{ArrayBuffer, HashMap}
|
2009-04-19 10:58:20 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
class NoTransactionInScopeException extends RuntimeException
|
|
|
|
|
|
2009-09-01 08:56:44 +02:00
|
|
|
sealed abstract class PersistentStateConfig
|
|
|
|
|
abstract class PersistentStorageConfig extends PersistentStateConfig
|
2009-08-03 09:03:51 +02:00
|
|
|
case class CassandraStorageConfig extends PersistentStorageConfig
|
2009-06-24 15:12:47 +02:00
|
|
|
case class TerracottaStorageConfig extends PersistentStorageConfig
|
|
|
|
|
case class TokyoCabinetStorageConfig extends PersistentStorageConfig
|
2009-08-12 17:01:11 +05:30
|
|
|
case class MongoStorageConfig extends PersistentStorageConfig
|
2009-06-22 14:12:09 +02:00
|
|
|
|
2009-06-29 15:01:20 +02:00
|
|
|
/**
|
|
|
|
|
* Example Scala usage:
|
|
|
|
|
* <pre>
|
2009-09-01 08:56:44 +02:00
|
|
|
* val myMap = PersistentState.newMap(CassandraStorageConfig)
|
2009-06-29 15:01:20 +02:00
|
|
|
* </pre>
|
|
|
|
|
* <p/>
|
2009-09-17 09:47:22 +02:00
|
|
|
*
|
2009-06-29 15:01:20 +02:00
|
|
|
* Example Java usage:
|
|
|
|
|
* <pre>
|
2009-09-17 09:47:22 +02:00
|
|
|
* TransactionalMap myMap = PersistentState.newMap(new CassandraStorageConfig());
|
2009-06-29 15:01:20 +02:00
|
|
|
* </pre>
|
|
|
|
|
*/
|
2009-09-17 09:47:22 +02:00
|
|
|
object PersistentState {
|
2009-09-10 18:34:07 +02:00
|
|
|
def newMap(config: PersistentStorageConfig): PersistentMap = config match {
|
2009-09-10 01:33:01 +02:00
|
|
|
case CassandraStorageConfig() => new CassandraPersistentMap
|
|
|
|
|
case MongoStorageConfig() => new MongoPersistentMap
|
2009-06-29 15:01:20 +02:00
|
|
|
case TerracottaStorageConfig() => throw new UnsupportedOperationException
|
|
|
|
|
case TokyoCabinetStorageConfig() => throw new UnsupportedOperationException
|
2009-06-22 14:12:09 +02:00
|
|
|
}
|
|
|
|
|
|
2009-09-10 18:34:07 +02:00
|
|
|
def newVector(config: PersistentStorageConfig): PersistentVector = config match {
|
2009-09-10 01:33:01 +02:00
|
|
|
case CassandraStorageConfig() => new CassandraPersistentVector
|
|
|
|
|
case MongoStorageConfig() => new MongoPersistentVector
|
2009-06-29 15:01:20 +02:00
|
|
|
case TerracottaStorageConfig() => throw new UnsupportedOperationException
|
|
|
|
|
case TokyoCabinetStorageConfig() => throw new UnsupportedOperationException
|
2009-06-22 14:12:09 +02:00
|
|
|
}
|
|
|
|
|
|
2009-09-10 18:34:07 +02:00
|
|
|
def newRef(config: PersistentStorageConfig): PersistentRef = config match {
|
2009-09-10 01:33:01 +02:00
|
|
|
case CassandraStorageConfig() => new CassandraPersistentRef
|
|
|
|
|
case MongoStorageConfig() => new MongoPersistentRef
|
2009-06-29 15:01:20 +02:00
|
|
|
case TerracottaStorageConfig() => throw new UnsupportedOperationException
|
|
|
|
|
case TokyoCabinetStorageConfig() => throw new UnsupportedOperationException
|
2009-06-22 14:12:09 +02:00
|
|
|
}
|
2009-04-27 19:55:57 +02:00
|
|
|
}
|
2009-04-19 10:58:20 +02:00
|
|
|
|
2009-04-27 19:55:57 +02:00
|
|
|
/**
|
2009-09-10 01:33:01 +02:00
|
|
|
* Implementation of <tt>PersistentMap</tt> for every concrete
|
2009-08-12 17:01:11 +05:30
|
|
|
* storage will have the same workflow. This abstracts the workflow.
|
|
|
|
|
*
|
|
|
|
|
* Subclasses just need to provide the actual concrete instance for the
|
|
|
|
|
* abstract val <tt>storage</tt>.
|
2009-06-10 20:04:33 +02:00
|
|
|
*
|
2009-04-27 20:06:48 +02:00
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
2009-04-27 19:55:57 +02:00
|
|
|
*/
|
2009-10-06 00:07:27 +02:00
|
|
|
trait PersistentMap extends scala.collection.mutable.Map[AnyRef, AnyRef] with Transactional with Committable {
|
2009-09-10 01:33:01 +02:00
|
|
|
protected val newAndUpdatedEntries = TransactionalState.newMap[AnyRef, AnyRef]
|
|
|
|
|
protected val removedEntries = TransactionalState.newMap[AnyRef, AnyRef]
|
2009-09-17 09:47:22 +02:00
|
|
|
protected val shouldClearOnCommit = TransactionalRef[Boolean]()
|
2009-08-12 17:01:11 +05:30
|
|
|
|
|
|
|
|
// to be concretized in subclasses
|
|
|
|
|
val storage: MapStorage
|
2009-05-13 19:28:55 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
def commit = {
|
2009-09-10 01:33:01 +02:00
|
|
|
storage.removeMapStorageFor(uuid, removedEntries.toList)
|
|
|
|
|
storage.insertMapStorageEntriesFor(uuid, newAndUpdatedEntries.toList)
|
2009-10-06 00:07:27 +02:00
|
|
|
if (shouldClearOnCommit.isDefined && shouldClearOnCommit.get.get) storage.removeMapStorageFor(uuid)
|
2009-09-10 01:33:01 +02:00
|
|
|
newAndUpdatedEntries.clear
|
|
|
|
|
removedEntries.clear
|
2009-08-11 13:13:16 +02:00
|
|
|
}
|
|
|
|
|
|
2009-09-10 01:33:01 +02:00
|
|
|
def -=(key: AnyRef) = remove(key)
|
2009-08-11 13:13:16 +02:00
|
|
|
|
2009-09-10 01:33:01 +02:00
|
|
|
def +=(key: AnyRef, value: AnyRef) = put(key, value)
|
2009-05-13 19:28:55 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
override def put(key: AnyRef, value: AnyRef): Option[AnyRef] = {
|
|
|
|
|
register
|
|
|
|
|
newAndUpdatedEntries.put(key, value)
|
|
|
|
|
}
|
2009-09-10 01:33:01 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
override def update(key: AnyRef, value: AnyRef) = {
|
|
|
|
|
register
|
|
|
|
|
newAndUpdatedEntries.update(key, value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def remove(key: AnyRef) = {
|
|
|
|
|
register
|
|
|
|
|
removedEntries.remove(key)
|
|
|
|
|
}
|
2009-09-10 01:33:01 +02:00
|
|
|
|
|
|
|
|
def slice(start: Option[AnyRef], count: Int): List[Tuple2[AnyRef, AnyRef]] = slice(start, None, count)
|
2009-06-10 20:04:33 +02:00
|
|
|
|
2009-09-10 01:33:01 +02:00
|
|
|
def slice(start: Option[AnyRef], finish: Option[AnyRef], count: Int): List[Tuple2[AnyRef, AnyRef]] = try {
|
2009-10-06 00:07:27 +02:00
|
|
|
storage.getMapStorageRangeFor(uuid, start, finish, count)
|
|
|
|
|
} catch { case e: Exception => Nil }
|
2009-07-12 23:08:17 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
override def clear = {
|
|
|
|
|
register
|
|
|
|
|
shouldClearOnCommit.swap(true)
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-10 01:33:01 +02:00
|
|
|
override def contains(key: AnyRef): Boolean = try {
|
2009-10-06 00:07:27 +02:00
|
|
|
newAndUpdatedEntries.contains(key) || storage.getMapStorageEntryFor(uuid, key).isDefined
|
|
|
|
|
} catch { case e: Exception => false }
|
2009-07-12 23:08:17 +02:00
|
|
|
|
2009-09-10 01:33:01 +02:00
|
|
|
override def size: Int = try {
|
2009-10-06 00:07:27 +02:00
|
|
|
storage.getMapStorageSizeFor(uuid) + newAndUpdatedEntries.size
|
|
|
|
|
} catch { case e: Exception => 0 }
|
2009-06-10 20:04:33 +02:00
|
|
|
|
2009-08-11 13:13:16 +02:00
|
|
|
override def get(key: AnyRef): Option[AnyRef] = {
|
2009-09-10 01:33:01 +02:00
|
|
|
if (newAndUpdatedEntries.contains(key)) newAndUpdatedEntries.get(key)
|
|
|
|
|
else try {
|
2009-09-17 09:51:32 +02:00
|
|
|
storage.getMapStorageEntryFor(uuid, key)
|
|
|
|
|
} catch { case e: Exception => None }
|
2009-06-29 17:33:38 +02:00
|
|
|
}
|
|
|
|
|
|
2009-08-11 13:13:16 +02:00
|
|
|
override def elements: Iterator[Tuple2[AnyRef, AnyRef]] = {
|
|
|
|
|
new Iterator[Tuple2[AnyRef, AnyRef]] {
|
|
|
|
|
private val originalList: List[Tuple2[AnyRef, AnyRef]] = try {
|
2009-08-12 17:01:11 +05:30
|
|
|
storage.getMapStorageFor(uuid)
|
2009-07-12 23:08:17 +02:00
|
|
|
} catch {
|
|
|
|
|
case e: Throwable => Nil
|
|
|
|
|
}
|
2009-09-10 01:33:01 +02:00
|
|
|
// FIXME how to deal with updated entries, these should be replaced in the originalList not just added
|
|
|
|
|
private var elements = newAndUpdatedEntries.toList ::: originalList.reverse
|
2009-08-11 13:13:16 +02:00
|
|
|
override def next: Tuple2[AnyRef, AnyRef]= synchronized {
|
2009-04-27 19:55:57 +02:00
|
|
|
val element = elements.head
|
|
|
|
|
elements = elements.tail
|
|
|
|
|
element
|
2009-06-10 20:04:33 +02:00
|
|
|
}
|
2009-04-27 19:55:57 +02:00
|
|
|
override def hasNext: Boolean = synchronized { !elements.isEmpty }
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-06 00:07:27 +02:00
|
|
|
|
|
|
|
|
private def register = {
|
|
|
|
|
if (currentTransaction.get.isEmpty) throw new NoTransactionInScopeException
|
|
|
|
|
currentTransaction.get.get.register(uuid, this)
|
|
|
|
|
}
|
2009-04-27 19:55:57 +02:00
|
|
|
}
|
2009-04-19 10:58:20 +02:00
|
|
|
|
2009-08-12 17:01:11 +05:30
|
|
|
/**
|
|
|
|
|
* Implements a persistent transactional map based on the Cassandra distributed P2P key-value storage.
|
|
|
|
|
*
|
|
|
|
|
* @author <a href="http://debasishg.blogspot.com">Debasish Ghosh</a>
|
|
|
|
|
*/
|
2009-09-10 18:34:07 +02:00
|
|
|
class CassandraPersistentMap extends PersistentMap {
|
2009-08-12 17:01:11 +05:30
|
|
|
val storage = CassandraStorage
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Implements a persistent transactional map based on the MongoDB distributed P2P key-value storage.
|
|
|
|
|
*
|
|
|
|
|
* @author <a href="http://debasishg.blogspot.com">Debasish Ghosh</a>
|
|
|
|
|
*/
|
2009-09-10 18:34:07 +02:00
|
|
|
class MongoPersistentMap extends PersistentMap {
|
2009-08-12 17:01:11 +05:30
|
|
|
val storage = MongoStorage
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-10 20:04:33 +02:00
|
|
|
/**
|
2009-08-12 17:01:11 +05:30
|
|
|
* Implements a template for a concrete persistent transactional vector based storage.
|
2009-06-10 20:04:33 +02:00
|
|
|
*
|
2009-09-10 18:34:07 +02:00
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
2009-06-10 20:04:33 +02:00
|
|
|
*/
|
2009-10-06 00:07:27 +02:00
|
|
|
trait PersistentVector extends RandomAccessSeq[AnyRef] with Transactional with Committable {
|
2009-09-10 01:33:01 +02:00
|
|
|
protected val newElems = TransactionalState.newVector[AnyRef]
|
|
|
|
|
protected val updatedElems = TransactionalState.newMap[Int, AnyRef]
|
|
|
|
|
protected val removedElems = TransactionalState.newVector[AnyRef]
|
2009-09-17 09:47:22 +02:00
|
|
|
protected val shouldClearOnCommit = TransactionalRef[Boolean]()
|
2009-08-12 17:01:11 +05:30
|
|
|
|
|
|
|
|
val storage: VectorStorage
|
2009-06-10 20:04:33 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
def commit = {
|
2009-09-10 01:33:01 +02:00
|
|
|
// FIXME: should use batch function once the bug is resolved
|
|
|
|
|
for (element <- newElems) storage.insertVectorStorageEntryFor(uuid, element)
|
|
|
|
|
for (entry <- updatedElems) storage.updateVectorStorageEntryFor(uuid, entry._1, entry._2)
|
|
|
|
|
newElems.clear
|
|
|
|
|
updatedElems.clear
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
def +(elem: AnyRef) = add(elem)
|
|
|
|
|
|
|
|
|
|
def add(elem: AnyRef) = {
|
|
|
|
|
register
|
|
|
|
|
newElems + elem
|
|
|
|
|
}
|
2009-09-10 18:34:07 +02:00
|
|
|
|
2009-09-10 01:33:01 +02:00
|
|
|
def apply(index: Int): AnyRef = get(index)
|
|
|
|
|
|
|
|
|
|
def get(index: Int): AnyRef = {
|
|
|
|
|
if (newElems.size > index) newElems(index)
|
2009-08-12 17:01:11 +05:30
|
|
|
else storage.getVectorStorageEntryFor(uuid, index)
|
2009-06-29 23:38:10 +02:00
|
|
|
}
|
2009-07-12 23:08:17 +02:00
|
|
|
|
2009-09-10 01:33:01 +02:00
|
|
|
override def slice(start: Int, count: Int): RandomAccessSeq[AnyRef] = slice(Some(start), None, count)
|
2009-08-11 13:13:16 +02:00
|
|
|
|
2009-09-18 08:56:03 +02:00
|
|
|
def slice(start: Option[Int], finish: Option[Int], count: Int): RandomAccessSeq[AnyRef] = {
|
|
|
|
|
val buffer = new scala.collection.mutable.ArrayBuffer[AnyRef]
|
|
|
|
|
storage.getVectorStorageRangeFor(uuid, start, finish, count).foreach(buffer.append(_))
|
|
|
|
|
buffer
|
|
|
|
|
}
|
2009-07-12 23:08:17 +02:00
|
|
|
|
2009-09-10 01:33:01 +02:00
|
|
|
/**
|
|
|
|
|
* Removes the <i>tail</i> element of this vector.
|
|
|
|
|
*/
|
2009-09-10 18:34:07 +02:00
|
|
|
// FIXME: implement persistent vector pop
|
2009-10-06 00:07:27 +02:00
|
|
|
def pop: AnyRef = {
|
|
|
|
|
register
|
|
|
|
|
throw new UnsupportedOperationException("need to implement persistent vector pop")
|
|
|
|
|
}
|
2009-07-12 23:08:17 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
def update(index: Int, newElem: AnyRef) = {
|
|
|
|
|
register
|
|
|
|
|
storage.updateVectorStorageEntryFor(uuid, index, newElem)
|
|
|
|
|
}
|
2009-07-12 23:08:17 +02:00
|
|
|
|
2009-06-10 20:04:33 +02:00
|
|
|
override def first: AnyRef = get(0)
|
2009-07-12 23:08:17 +02:00
|
|
|
|
2009-06-29 15:01:20 +02:00
|
|
|
override def last: AnyRef = {
|
2009-09-10 01:33:01 +02:00
|
|
|
if (newElems.length != 0) newElems.last
|
|
|
|
|
else {
|
|
|
|
|
val len = length
|
|
|
|
|
if (len == 0) throw new NoSuchElementException("Vector is empty")
|
|
|
|
|
get(len - 1)
|
|
|
|
|
}
|
2009-06-29 15:01:20 +02:00
|
|
|
}
|
2009-06-10 20:04:33 +02:00
|
|
|
|
2009-09-10 01:33:01 +02:00
|
|
|
def length: Int = storage.getVectorStorageSizeFor(uuid) + newElems.length
|
2009-10-06 00:07:27 +02:00
|
|
|
|
|
|
|
|
private def register = {
|
|
|
|
|
if (currentTransaction.get.isEmpty) throw new NoTransactionInScopeException
|
|
|
|
|
currentTransaction.get.get.register(uuid, this)
|
|
|
|
|
}
|
2009-06-10 20:04:33 +02:00
|
|
|
}
|
|
|
|
|
|
2009-08-12 17:01:11 +05:30
|
|
|
/**
|
|
|
|
|
* Implements a persistent transactional vector based on the Cassandra distributed P2P key-value storage.
|
|
|
|
|
*
|
2009-09-10 18:34:07 +02:00
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
2009-08-12 17:01:11 +05:30
|
|
|
*/
|
2009-09-10 18:34:07 +02:00
|
|
|
class CassandraPersistentVector extends PersistentVector {
|
2009-08-12 17:01:11 +05:30
|
|
|
val storage = CassandraStorage
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Implements a persistent transactional vector based on the MongoDB distributed P2P key-value storage.
|
|
|
|
|
*
|
|
|
|
|
* @author <a href="http://debasishg.blogspot.com">Debaissh Ghosh</a>
|
|
|
|
|
*/
|
2009-09-10 18:34:07 +02:00
|
|
|
class MongoPersistentVector extends PersistentVector {
|
2009-08-12 17:01:11 +05:30
|
|
|
val storage = MongoStorage
|
2009-09-01 08:56:44 +02:00
|
|
|
}
|
2009-06-11 13:47:07 +02:00
|
|
|
|
2009-09-10 18:34:07 +02:00
|
|
|
/**
|
|
|
|
|
* Implements a persistent reference with abstract storage.
|
|
|
|
|
*
|
|
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
|
|
|
|
*/
|
2009-10-06 00:07:27 +02:00
|
|
|
trait PersistentRef extends Transactional with Committable {
|
2009-09-10 01:33:01 +02:00
|
|
|
protected val ref = new TransactionalRef[AnyRef]
|
|
|
|
|
|
2009-08-14 16:05:35 +05:30
|
|
|
val storage: RefStorage
|
|
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
def commit = if (ref.isDefined) {
|
|
|
|
|
storage.insertRefStorageFor(uuid, ref.get.get)
|
2009-09-10 01:33:01 +02:00
|
|
|
ref.swap(null)
|
2009-07-04 06:38:47 +02:00
|
|
|
}
|
2009-07-12 23:08:17 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
def swap(elem: AnyRef) = {
|
|
|
|
|
register
|
|
|
|
|
ref.swap(elem)
|
|
|
|
|
}
|
2009-09-10 18:34:07 +02:00
|
|
|
|
2009-09-10 01:33:01 +02:00
|
|
|
def get: Option[AnyRef] = if (ref.isDefined) ref.get else storage.getRefStorageFor(uuid)
|
2009-07-12 23:08:17 +02:00
|
|
|
|
2009-09-10 01:33:01 +02:00
|
|
|
def isDefined: Boolean = ref.isDefined || storage.getRefStorageFor(uuid).isDefined
|
2009-07-12 23:08:17 +02:00
|
|
|
|
2009-09-10 01:33:01 +02:00
|
|
|
def getOrElse(default: => AnyRef): AnyRef = {
|
|
|
|
|
val current = get
|
2009-10-06 00:07:27 +02:00
|
|
|
if (current.isDefined) current.get
|
2009-06-11 13:47:07 +02:00
|
|
|
else default
|
|
|
|
|
}
|
2009-10-06 00:07:27 +02:00
|
|
|
|
|
|
|
|
private def register = {
|
|
|
|
|
if (currentTransaction.get.isEmpty) throw new NoTransactionInScopeException
|
|
|
|
|
currentTransaction.get.get.register(uuid, this)
|
|
|
|
|
}
|
2009-08-02 16:14:12 +02:00
|
|
|
}
|
2009-08-14 16:05:35 +05:30
|
|
|
|
2009-09-10 18:34:07 +02:00
|
|
|
class CassandraPersistentRef extends PersistentRef {
|
2009-08-14 16:05:35 +05:30
|
|
|
val storage = CassandraStorage
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-10 18:34:07 +02:00
|
|
|
class MongoPersistentRef extends PersistentRef {
|
2009-08-14 16:05:35 +05:30
|
|
|
val storage = MongoStorage
|
|
|
|
|
}
|