second iteration of STM done, simple tests work now
This commit is contained in:
parent
2639d14e1a
commit
cd1ef83e49
40 changed files with 4102 additions and 3215 deletions
|
|
@ -14,6 +14,14 @@ import java.lang.annotation.Annotation
|
|||
sealed class ActiveObjectException(msg: String) extends RuntimeException(msg)
|
||||
class ActiveObjectInvocationTimeoutException(msg: String) extends ActiveObjectException(msg)
|
||||
|
||||
object Annotation {
|
||||
import se.scalablesolutions.akka.annotation._
|
||||
val transactional = classOf[transactional]
|
||||
val oneway = classOf[oneway]
|
||||
val immutable = classOf[immutable]
|
||||
val state = classOf[state]
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
|
|
@ -70,11 +78,7 @@ object ActiveObject {
|
|||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class ActiveObjectProxy(val intf: Class[_], val target: Class[_], val timeout: Int) extends InvocationHandler {
|
||||
val transactional = classOf[se.scalablesolutions.akka.annotation.transactional]
|
||||
val oneway = classOf[se.scalablesolutions.akka.annotation.oneway]
|
||||
val immutable = classOf[se.scalablesolutions.akka.annotation.immutable]
|
||||
val state= classOf[se.scalablesolutions.akka.annotation.state]
|
||||
|
||||
import ActiveObject.threadBoundTx
|
||||
private[this] var activeTx: Option[Transaction] = None
|
||||
|
||||
private var targetInstance: AnyRef = _
|
||||
|
|
@ -86,58 +90,39 @@ class ActiveObjectProxy(val intf: Class[_], val target: Class[_], val timeout: I
|
|||
}
|
||||
}
|
||||
|
||||
private[this] val dispatcher = new GenericServer {
|
||||
override def body: PartialFunction[Any, Unit] = {
|
||||
case invocation: Invocation =>
|
||||
val tx = invocation.tx
|
||||
ActiveObject.threadBoundTx.set(tx)
|
||||
try {
|
||||
reply(ErrRef(invocation.invoke, tx))
|
||||
} catch {
|
||||
case e: InvocationTargetException =>
|
||||
val te = e.getTargetException
|
||||
te.printStackTrace
|
||||
reply(ErrRef({ throw te }, tx))
|
||||
case e =>
|
||||
e.printStackTrace
|
||||
reply(ErrRef({ throw e }, tx))
|
||||
}
|
||||
case 'exit => exit; reply()
|
||||
case unexpected => throw new ActiveObjectException("Unexpected message to actor proxy: " + unexpected)
|
||||
}
|
||||
}
|
||||
|
||||
private[kernel] val server = new GenericServerContainer(target.getName, () => dispatcher)
|
||||
private[kernel] val server = new GenericServerContainer(target.getName, () => new Dispatcher(target.getName))
|
||||
server.setTimeout(timeout)
|
||||
|
||||
def invoke(proxy: AnyRef, m: Method, args: Array[AnyRef]): AnyRef = {
|
||||
if (m.isAnnotationPresent(transactional)) {
|
||||
if (m.isAnnotationPresent(Annotation.transactional)) {
|
||||
// FIXME: check if we are already in a transaction if so NEST (set parent)
|
||||
val newTx = new Transaction
|
||||
newTx.begin(server)
|
||||
ActiveObject.threadBoundTx.set(Some(newTx))
|
||||
threadBoundTx.set(Some(newTx))
|
||||
}
|
||||
val cflowTx = ActiveObject.threadBoundTx.get
|
||||
|
||||
// println("========== invoking: " + m.getName)
|
||||
// println("========== cflowTx: " + cflowTx)
|
||||
// println("========== activeTx: " + activeTx)
|
||||
val cflowTx = threadBoundTx.get
|
||||
activeTx match {
|
||||
case Some(tx) =>
|
||||
if (cflowTx.isDefined && cflowTx.get != tx) {
|
||||
// new tx in scope; try to commit
|
||||
tx.commit(server)
|
||||
threadBoundTx.set(None)
|
||||
activeTx = None
|
||||
}
|
||||
case None =>
|
||||
if (cflowTx.isDefined) activeTx = Some(cflowTx.get)
|
||||
if (cflowTx.isDefined) {
|
||||
val currentTx = cflowTx.get
|
||||
currentTx.join(server)
|
||||
activeTx = Some(currentTx)
|
||||
}
|
||||
}
|
||||
activeTx = ActiveObject.threadBoundTx.get
|
||||
activeTx = threadBoundTx.get
|
||||
invoke(Invocation(m, args, targetInstance, activeTx))
|
||||
}
|
||||
|
||||
private def invoke(invocation: Invocation): AnyRef = {
|
||||
val result: AnyRef =
|
||||
if (invocation.method.isAnnotationPresent(oneway)) server ! invocation
|
||||
if (invocation.method.isAnnotationPresent(Annotation.oneway)) server ! invocation
|
||||
else {
|
||||
val result: ErrRef[AnyRef] =
|
||||
server !!! (invocation, {
|
||||
|
|
@ -153,6 +138,7 @@ class ActiveObjectProxy(val intf: Class[_], val target: Class[_], val timeout: I
|
|||
throw e
|
||||
}
|
||||
}
|
||||
// FIXME: clear threadBoundTx on successful commit
|
||||
if (activeTx.isDefined) activeTx.get.precommit(server)
|
||||
result
|
||||
}
|
||||
|
|
@ -160,45 +146,76 @@ class ActiveObjectProxy(val intf: Class[_], val target: Class[_], val timeout: I
|
|||
private def rollback(tx: Option[Transaction]) = tx match {
|
||||
case None => {} // no tx; nothing to do
|
||||
case Some(tx) =>
|
||||
println("================ ROLLING BACK")
|
||||
tx.rollback(server)
|
||||
ActiveObject.threadBoundTx.set(Some(tx))
|
||||
threadBoundTx.set(Some(tx))
|
||||
}
|
||||
|
||||
private def getStateList(targetInstance: AnyRef): List[State[_,_]] = {
|
||||
require(targetInstance != null)
|
||||
import se.scalablesolutions.akka.kernel.configuration.ConfigurationException
|
||||
val states = for {
|
||||
field <- target.getDeclaredFields
|
||||
if field.isAnnotationPresent(state)
|
||||
state = field.get(targetInstance)
|
||||
val states: List[State[_,_]] = for {
|
||||
field <- target.getDeclaredFields.toArray.toList
|
||||
if field.isAnnotationPresent(Annotation.state)
|
||||
state = {
|
||||
field.setAccessible(true)
|
||||
field.get(targetInstance)
|
||||
}
|
||||
if state != null
|
||||
} yield {
|
||||
if (!state.isInstanceOf[State[_, _]]) throw new ConfigurationException("Fields annotated with [@state] needs to to be a subtype of [se.scalablesolutions.akka.kernel.State[K, V]]")
|
||||
state
|
||||
state.asInstanceOf[State[_,_]]
|
||||
}
|
||||
states
|
||||
// if (fields.size > 1) throw new ConfigurationException("Stateful active object can only have one single field '@Inject TransientObjectState state' defined")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic GenericServer managing Invocation dispatch, transaction and error management.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
private[kernel] class Dispatcher(val targetName: String) extends GenericServer {
|
||||
override def body: PartialFunction[Any, Unit] = {
|
||||
case invocation: Invocation =>
|
||||
val tx = invocation.tx
|
||||
ActiveObject.threadBoundTx.set(tx)
|
||||
try {
|
||||
reply(ErrRef(invocation.invoke, tx))
|
||||
} catch {
|
||||
case e: InvocationTargetException =>
|
||||
val ref = ErrRef(tx); ref() = throw e.getTargetException; reply(ref)
|
||||
case e =>
|
||||
val ref = ErrRef(tx); ref() = throw e; reply(ref)
|
||||
}
|
||||
case 'exit =>
|
||||
exit; reply()
|
||||
case unexpected =>
|
||||
throw new ActiveObjectException("Unexpected message [" + unexpected + "] to [" + this + "] from [" + sender + "]")
|
||||
}
|
||||
|
||||
override def toString(): String = "GenericServer[" + targetName + "]"
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a snapshot of the current invocation.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
case class Invocation(val method: Method,
|
||||
val args: Array[AnyRef],
|
||||
val target: AnyRef,
|
||||
val tx: Option[Transaction]) {
|
||||
private[kernel] case class Invocation(val method: Method,
|
||||
val args: Array[AnyRef],
|
||||
val target: AnyRef,
|
||||
val tx: Option[Transaction]) {
|
||||
method.setAccessible(true)
|
||||
|
||||
def invoke: AnyRef = method.invoke(target, args:_*)
|
||||
def invoke: AnyRef = synchronized {
|
||||
method.invoke(target, args:_*)
|
||||
}
|
||||
|
||||
override def toString: String =
|
||||
override def toString: String = synchronized {
|
||||
"Invocation [method: " + method.getName + ", args: " + argsToString(args) + ", target: " + target + "]"
|
||||
|
||||
override def hashCode(): Int = {
|
||||
}
|
||||
|
||||
override def hashCode(): Int = synchronized {
|
||||
var result = HashCode.SEED
|
||||
result = HashCode.hash(result, method)
|
||||
result = HashCode.hash(result, args)
|
||||
|
|
@ -206,7 +223,7 @@ case class Invocation(val method: Method,
|
|||
result
|
||||
}
|
||||
|
||||
override def equals(that: Any): Boolean = {
|
||||
override def equals(that: Any): Boolean = synchronized {
|
||||
that != null &&
|
||||
that.isInstanceOf[Invocation] &&
|
||||
that.asInstanceOf[Invocation].method == method &&
|
||||
|
|
@ -214,14 +231,13 @@ case class Invocation(val method: Method,
|
|||
isEqual(that.asInstanceOf[Invocation].args, args)
|
||||
}
|
||||
|
||||
private def isEqual(a1: Array[Object], a2: Array[Object]): Boolean =
|
||||
private[this] def isEqual(a1: Array[Object], a2: Array[Object]): Boolean =
|
||||
(a1 == null && a2 == null) ||
|
||||
(a1 != null &&
|
||||
a2 != null &&
|
||||
a1.size == a2.size &&
|
||||
a1.zip(a2).find(t => t._1 == t._2).isDefined)
|
||||
|
||||
private def argsToString(array: Array[Object]): String = synchronized {
|
||||
private[this] def argsToString(array: Array[Object]): String =
|
||||
array.foldLeft("(")(_ + " " + _) + ")"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -267,8 +267,7 @@ class GenericServerContainer(
|
|||
private[kernel] def terminate(reason: AnyRef, shutdownTime: Int) = lock.withReadLock {
|
||||
if (shutdownTime > 0) {
|
||||
log.debug("Waiting [%s milliseconds for the server to shut down before killing it.", shutdownTime)
|
||||
// server !? (shutdownTime, Shutdown(reason)) match {
|
||||
server !? Shutdown(reason) match {
|
||||
server !? (shutdownTime, Shutdown(reason)) match {
|
||||
case Some('success) => log.debug("Server [%s] has been shut down cleanly.", id)
|
||||
case None => log.warning("Server [%s] was **not able** to complete shutdown cleanly within its configured shutdown time [%s]", id, shutdownTime)
|
||||
}
|
||||
|
|
@ -292,5 +291,7 @@ class GenericServerContainer(
|
|||
private[kernel] def swapServer(newServer: GenericServer) = lock.withWriteLock {
|
||||
server = newServer
|
||||
}
|
||||
|
||||
override def toString(): String = "GenericServerContainer[" + server + "]"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
package se.scalablesolutions.akka.kernel
|
||||
|
||||
import org.apache.zookeeper.jmx.ManagedUtil
|
||||
import org.apache.zookeeper.server.persistence.FileTxnSnapLog
|
||||
import org.apache.zookeeper.server.ServerConfig
|
||||
import org.apache.zookeeper.server.NIOServerCnxn
|
||||
//import org.apache.zookeeper.jmx.ManagedUtil
|
||||
//import org.apache.zookeeper.server.persistence.FileTxnSnapLog
|
||||
//import org.apache.zookeeper.server.ServerConfig
|
||||
//import org.apache.zookeeper.server.NIOServerCnxn
|
||||
|
||||
import voldemort.client.{SocketStoreClientFactory, StoreClient, StoreClientFactory}
|
||||
import voldemort.server.{VoldemortConfig, VoldemortServer}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ trait Transactional {
|
|||
private[kernel] def rollback
|
||||
}
|
||||
|
||||
sealed trait State[K, V] {
|
||||
sealed trait State[K, V] extends Transactional {
|
||||
def put(key: K, value: V)
|
||||
def remove(key: K)
|
||||
def get(key: K): V
|
||||
|
|
@ -23,7 +23,10 @@ sealed trait State[K, V] {
|
|||
def clear
|
||||
}
|
||||
|
||||
sealed class TransientState[K, V] extends State[K, V] with Transactional {
|
||||
/**
|
||||
* Not thread-safe, but should only be using from within an Actor, e.g. one single thread at a time.
|
||||
*/
|
||||
sealed class TransientState[K, V] extends State[K, V] {
|
||||
private[kernel] var state = new HashTrie[K, V]
|
||||
private[kernel] var snapshot = state
|
||||
|
||||
|
|
@ -32,6 +35,7 @@ sealed class TransientState[K, V] extends State[K, V] with Transactional {
|
|||
}
|
||||
|
||||
private[kernel] override def commit = {
|
||||
snapshot = state
|
||||
}
|
||||
|
||||
private[kernel] override def rollback = {
|
||||
|
|
@ -60,6 +64,9 @@ sealed class TransientState[K, V] extends State[K, V] with Transactional {
|
|||
final class TransientStringState extends TransientState[String, String]
|
||||
final class TransientObjectState extends TransientState[String, AnyRef]
|
||||
|
||||
/**
|
||||
* Not thread-safe, but should only be using from within an Actor, e.g. one single thread at a time.
|
||||
*/
|
||||
trait UnitOfWork[K, V] extends State[K, V] with Transactional {
|
||||
this: TransientState[K, V] =>
|
||||
private[kernel] val changeSet = new HashMap[K, V]
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ abstract class SupervisorFactory extends Logging {
|
|||
val supervisor = create(restartStrategy)
|
||||
supervisor.start
|
||||
supervisor !? Configure(config, this) match {
|
||||
case 'success => log.debug("Supervisor successfully configured")
|
||||
case 'configSuccess => log.debug("Supervisor successfully configured")
|
||||
case _ => log.error("Supervisor could not be configured")
|
||||
}
|
||||
supervisor
|
||||
|
|
@ -148,7 +148,7 @@ class Supervisor(faultHandler: FaultHandlingStrategy) extends Actor with Logging
|
|||
case Configure(config, factory) =>
|
||||
log.debug("Configuring supervisor:%s ", this)
|
||||
configure(config, factory)
|
||||
reply('success)
|
||||
reply('configSuccess)
|
||||
|
||||
case Start =>
|
||||
state.serverContainers.foreach { serverContainer =>
|
||||
|
|
@ -229,20 +229,21 @@ abstract class FaultHandlingStrategy(val maxNrOfRetries: Int, val withinTimeRang
|
|||
private[kernel] def restart(serverContainer: GenericServerContainer, reason: AnyRef, state: SupervisorState) = {
|
||||
preRestart(serverContainer)
|
||||
serverContainer.lock.withWriteLock {
|
||||
|
||||
// TODO: this is the place to fail-over all pending messages in the failing actor's mailbox, if possible to get a hold of them
|
||||
// e.g. something like 'serverContainer.getServer.getPendingMessages.map(newServer ! _)'
|
||||
|
||||
self.unlink(serverContainer.getServer)
|
||||
serverContainer.lifeCycle match {
|
||||
case None => throw new IllegalStateException("Server [" + serverContainer.id + "] does not have a life-cycle defined.")
|
||||
case Some(LifeCycle(scope, shutdownTime)) =>
|
||||
case None =>
|
||||
throw new IllegalStateException("Server [" + serverContainer.id + "] does not have a life-cycle defined.")
|
||||
case Some(LifeCycle(scope, shutdownTime)) => {
|
||||
serverContainer.terminate(reason, shutdownTime)
|
||||
|
||||
scope match {
|
||||
case Permanent =>
|
||||
case Permanent => {
|
||||
log.debug("Restarting server [%s] configured as PERMANENT.", serverContainer.id)
|
||||
serverContainer.reconfigure(reason, supervisor.spawnLink(serverContainer), state.supervisor)
|
||||
}
|
||||
|
||||
case Temporary =>
|
||||
if (reason == 'normal) {
|
||||
|
|
@ -253,6 +254,7 @@ abstract class FaultHandlingStrategy(val maxNrOfRetries: Int, val withinTimeRang
|
|||
case Transient =>
|
||||
log.info("Server [%s] configured as TRANSIENT will not be restarted.", serverContainer.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
postRestart(serverContainer)
|
||||
|
|
|
|||
|
|
@ -36,21 +36,25 @@ class Transaction extends Logging {
|
|||
|
||||
log.debug("Creating a new transaction [%s]", id)
|
||||
private[this] var parent: Option[Transaction] = None
|
||||
private[this] var participants = new HashMap[GenericServerContainer, GenericServer]
|
||||
private[this] var participants: List[GenericServerContainer] = Nil
|
||||
private[this] var precommitted: List[GenericServerContainer] = Nil
|
||||
@volatile private[this] var status: TransactionStatus = TransactionStatus.New
|
||||
|
||||
def begin(server: GenericServerContainer) = synchronized {
|
||||
println("===== begin 1 " + server)
|
||||
if (status == TransactionStatus.Aborted) throw new IllegalStateException("Can't begin ABORTED transaction")
|
||||
if (status == TransactionStatus.Completed) throw new IllegalStateException("Can't begin COMPLETED transaction")
|
||||
if (status == TransactionStatus.New) log.debug("Actor [%s] is starting NEW transaction", server)
|
||||
else log.debug("Actor [%s] is participating in transaction", server)
|
||||
if (server.state.isDefined) server.state.get.begin
|
||||
println("===== begin 2 " + server)
|
||||
server.states.foreach(_.begin)
|
||||
participants ::= server
|
||||
status = TransactionStatus.Active
|
||||
}
|
||||
|
||||
def precommit(server: GenericServerContainer) = synchronized {
|
||||
if (status == TransactionStatus.Active) {
|
||||
println("===== precommit " + server)
|
||||
log.debug("Pre-committing transaction for actor [%s]", server)
|
||||
precommitted ::= server
|
||||
}
|
||||
|
|
@ -58,10 +62,11 @@ class Transaction extends Logging {
|
|||
|
||||
def commit(server: GenericServerContainer) = synchronized {
|
||||
if (status == TransactionStatus.Active) {
|
||||
println("===== commit " + server)
|
||||
log.debug("Committing transaction for actor [%s]", server)
|
||||
val haveAllPreCommitted =
|
||||
if (participants.size == precommitted.size) {{
|
||||
for (server <- participants.keys) yield {
|
||||
for (server <- participants) yield {
|
||||
if (precommitted.exists(_.id == server.id)) true
|
||||
else false
|
||||
}}.exists(_ == false)
|
||||
|
|
@ -73,14 +78,18 @@ class Transaction extends Logging {
|
|||
|
||||
def rollback(server: GenericServerContainer) = synchronized {
|
||||
ensureIsActiveOrAborted
|
||||
log.debug("Actor [%s] has initiated transaction rollback, rolling back [%s]" , server, participants.keys)
|
||||
participants.foreach(entry => {
|
||||
val (server, backup) = entry
|
||||
if (server.state.isDefined) server.state.get.rollback
|
||||
})
|
||||
println("===== rollback " + server)
|
||||
log.debug("Actor [%s] has initiated transaction rollback, rolling back [%s]" , server, participants)
|
||||
participants.foreach(_.states.foreach(_.rollback))
|
||||
status = TransactionStatus.Aborted
|
||||
}
|
||||
|
||||
def join(server: GenericServerContainer) = synchronized {
|
||||
println("===== joining " + server)
|
||||
server.states.foreach(_.begin)
|
||||
participants ::= server
|
||||
}
|
||||
|
||||
private def ensureIsActive = if (status != TransactionStatus.Active)
|
||||
throw new IllegalStateException("Expected ACTIVE transaction - current status [" + status + "]")
|
||||
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ class Vector[+T] private (val length: Int, shift: Int, root: Array[AnyRef], tail
|
|||
back
|
||||
}
|
||||
|
||||
override def flatMap[A](f: (T)=>Iterable[A]) = {
|
||||
override def flatMap[A](f: (T)=>Iterable[A]): Vector[A] = {
|
||||
var back = new Vector[A]
|
||||
var i = 0
|
||||
|
||||
|
|
@ -234,7 +234,7 @@ class Vector[+T] private (val length: Int, shift: Int, root: Array[AnyRef], tail
|
|||
back
|
||||
}
|
||||
|
||||
override def map[A](f: (T)=>A) = {
|
||||
override def map[A](f: (T)=>A): Vector[A] = {
|
||||
var back = new Vector[A]
|
||||
var i = 0
|
||||
|
||||
|
|
|
|||
|
|
@ -1,188 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009 Scalable Solutions.
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.kernel
|
||||
|
||||
import org.scalatest.Spec
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import org.scalatest.junit.JUnit3Suite
|
||||
|
||||
import se.scalablesolutions.akka.annotation.{oneway, transactional, stateful}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object ActiveObjectSpec {
|
||||
var messageLog = ""
|
||||
}
|
||||
class ActiveObjectSpec extends Spec with ShouldMatchers {
|
||||
|
||||
describe("An ActiveObject") {
|
||||
|
||||
it("(with default supervisor) should dispatch method calls normally") {
|
||||
val foo = ActiveObject.newInstance[Foo](classOf[Foo], new FooImpl, 1000)
|
||||
|
||||
val result = foo.foo("foo ")
|
||||
ActiveObjectSpec.messageLog += result
|
||||
|
||||
foo.bar("bar ")
|
||||
ActiveObjectSpec.messageLog += "before_bar "
|
||||
|
||||
Thread.sleep(500)
|
||||
ActiveObjectSpec.messageLog should equal ("foo return_foo before_bar bar ")
|
||||
}
|
||||
|
||||
it("should not rollback state for a stateful server in case of success") {
|
||||
val stateful = ActiveObject.newInstance[Stateful](classOf[Stateful], new StatefulImpl, 1000)
|
||||
|
||||
stateful.success("new state")
|
||||
stateful.state should equal ("new state")
|
||||
}
|
||||
|
||||
it("should rollback state for a stateful server in case of failure") {
|
||||
val stateful = ActiveObject.newInstance[Stateful](classOf[Stateful], new StatefulImpl, 1000)
|
||||
val failer = ActiveObject.newInstance[Failer](classOf[Failer], new FailerImpl, 1000)
|
||||
|
||||
stateful.failure("new state", failer)
|
||||
stateful.state should equal ("nil")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait Foo {
|
||||
def foo(msg: String): String
|
||||
@transactional def fooInTx(msg: String): String
|
||||
@oneway def bar(msg: String)
|
||||
def longRunning
|
||||
def throwsException
|
||||
}
|
||||
|
||||
class FooImpl extends Foo {
|
||||
val bar: Bar = new BarImpl
|
||||
def foo(msg: String): String = {
|
||||
ActiveObjectSpec.messageLog += msg
|
||||
"return_foo "
|
||||
}
|
||||
def fooInTx(msg: String): String = {
|
||||
ActiveObjectSpec.messageLog += msg
|
||||
"return_foo "
|
||||
}
|
||||
def bar(msg: String) = bar.bar(msg)
|
||||
def longRunning = Thread.sleep(10000)
|
||||
def throwsException = error("expected")
|
||||
}
|
||||
|
||||
trait Bar {
|
||||
@oneway def bar(msg: String)
|
||||
}
|
||||
|
||||
class BarImpl extends Bar {
|
||||
def bar(msg: String) = {
|
||||
Thread.sleep(100)
|
||||
ActiveObjectSpec.messageLog += msg
|
||||
}
|
||||
}
|
||||
|
||||
trait Stateful {
|
||||
@transactional def success(msg: String)
|
||||
@transactional def failure(msg: String, failer: Failer)
|
||||
def state: String
|
||||
}
|
||||
|
||||
@stateful
|
||||
class StatefulImpl extends Stateful {
|
||||
var state: String = "nil"
|
||||
def success(msg: String) = state = msg
|
||||
def failure(msg: String, failer: Failer) = {
|
||||
state = msg
|
||||
failer.fail
|
||||
}
|
||||
}
|
||||
|
||||
trait Failer {
|
||||
def fail
|
||||
}
|
||||
|
||||
class FailerImpl extends Failer {
|
||||
def fail = throw new RuntimeException("expected")
|
||||
}
|
||||
|
||||
// @Test { val groups=Array("unit") }
|
||||
// def testCreateGenericServerBasedComponentUsingCustomSupervisorConfiguration = {
|
||||
// val proxy = new ActiveObjectProxy(new FooImpl, 1000)
|
||||
|
||||
// val supervisor =
|
||||
// ActiveObject.supervise(
|
||||
// RestartStrategy(AllForOne, 3, 100),
|
||||
// Component(
|
||||
// proxy,
|
||||
// LifeCycle(Permanent, 100))
|
||||
// :: Nil)
|
||||
|
||||
// val foo = ActiveObject.newInstance[Foo](classOf[Foo], proxy)
|
||||
|
||||
// val result = foo.foo("foo ")
|
||||
// messageLog += result
|
||||
|
||||
// foo.bar("bar ")
|
||||
// messageLog += "before_bar "
|
||||
|
||||
// Thread.sleep(500)
|
||||
// assert(messageLog === "foo return_foo before_bar bar ")
|
||||
|
||||
// supervisor ! Stop
|
||||
// }
|
||||
|
||||
// @Test { val groups=Array("unit") }
|
||||
// def testCreateTwoGenericServerBasedComponentUsingCustomSupervisorConfiguration = {
|
||||
// val fooProxy = new ActiveObjectProxy(new FooImpl, 1000)
|
||||
// val barProxy = new ActiveObjectProxy(new BarImpl, 1000)
|
||||
|
||||
// val supervisor =
|
||||
// ActiveObject.supervise(
|
||||
// RestartStrategy(AllForOne, 3, 100),
|
||||
// Component(
|
||||
// fooProxy,
|
||||
// LifeCycle(Permanent, 100)) ::
|
||||
// Component(
|
||||
// barProxy,
|
||||
// LifeCycle(Permanent, 100))
|
||||
// :: Nil)
|
||||
|
||||
// val foo = ActiveObject.newInstance[Foo](classOf[Foo], fooProxy)
|
||||
// val bar = ActiveObject.newInstance[Bar](classOf[Bar], barProxy)
|
||||
|
||||
// val result = foo.foo("foo ")
|
||||
// messageLog += result
|
||||
|
||||
// bar.bar("bar ")
|
||||
// messageLog += "before_bar "
|
||||
|
||||
// Thread.sleep(500)
|
||||
// assert(messageLog === "foo return_foo before_bar bar ")
|
||||
|
||||
// supervisor ! Stop
|
||||
// }
|
||||
|
||||
// @Test { val groups=Array("unit") }
|
||||
// def testCreateGenericServerBasedComponentUsingDefaultSupervisorAndForcedTimeout = {
|
||||
// val foo = ActiveObject.newInstance[Foo](classOf[Foo], new FooImpl, 1000)
|
||||
// intercept(classOf[ActiveObjectInvocationTimeoutException]) {
|
||||
// foo.longRunning
|
||||
// }
|
||||
// assert(true === true)
|
||||
// }
|
||||
|
||||
// @Test { val groups=Array("unit") }
|
||||
// def testCreateGenericServerBasedComponentUsingDefaultSupervisorAndForcedException = {
|
||||
// val foo = ActiveObject.newInstance[Foo](classOf[Foo], new FooImpl, 10000)
|
||||
// intercept(classOf[RuntimeException]) {
|
||||
// foo.throwsException
|
||||
// }
|
||||
// assert(true === true)
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
|
@ -10,9 +10,14 @@ import org.scalatest._
|
|||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class AllSuite extends SuperSuite(
|
||||
|
||||
List(
|
||||
new ActiveObjectSpec,
|
||||
new RestManagerSpec
|
||||
new SupervisorSpec,
|
||||
new SupervisorStateSpec,
|
||||
new GenericServerSpec,
|
||||
new GenericServerContainerSpec
|
||||
// new ActiveObjectSpec,
|
||||
// new RestManagerSpec
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
212
kernel/src/test/scala/GenericServerContainerSuite.scala
Executable file
212
kernel/src/test/scala/GenericServerContainerSuite.scala
Executable file
|
|
@ -0,0 +1,212 @@
|
|||
/**
|
||||
* Copyright (C) 2009 Scalable Solutions.
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.kernel
|
||||
|
||||
import scala.actors._
|
||||
import scala.actors.Actor._
|
||||
|
||||
import org.scalatest._
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class GenericServerContainerSpec extends Suite {
|
||||
|
||||
var inner: GenericServerContainerActor = null
|
||||
var server: GenericServerContainer = null
|
||||
def createProxy(f: () => GenericServer) = { val server = new GenericServerContainer("server", f); server.setTimeout(100); server }
|
||||
|
||||
def setup = {
|
||||
inner = new GenericServerContainerActor
|
||||
server = createProxy(() => inner)
|
||||
server.newServer
|
||||
server.start
|
||||
}
|
||||
|
||||
def testInit = {
|
||||
setup
|
||||
server.init("testInit")
|
||||
Thread.sleep(100)
|
||||
expect("initializing: testInit") {
|
||||
inner.log
|
||||
}
|
||||
}
|
||||
|
||||
def testTerminateWithReason = {
|
||||
setup
|
||||
server.terminate("testTerminateWithReason", 100)
|
||||
Thread.sleep(100)
|
||||
expect("terminating: testTerminateWithReason") {
|
||||
inner.log
|
||||
}
|
||||
}
|
||||
|
||||
def test_bang_1 = {
|
||||
setup
|
||||
server ! OneWay
|
||||
Thread.sleep(100)
|
||||
expect("got a oneway") {
|
||||
inner.log
|
||||
}
|
||||
}
|
||||
|
||||
def test_bang_2 = {
|
||||
setup
|
||||
server ! Ping
|
||||
Thread.sleep(100)
|
||||
expect("got a ping") {
|
||||
inner.log
|
||||
}
|
||||
}
|
||||
|
||||
def test_bangbangbang = {
|
||||
setup
|
||||
expect("pong") {
|
||||
(server !!! Ping).getOrElse("nil")
|
||||
}
|
||||
expect("got a ping") {
|
||||
inner.log
|
||||
}
|
||||
}
|
||||
|
||||
def test_bangquestion = {
|
||||
setup
|
||||
expect("pong") {
|
||||
val res: String = server !? Ping
|
||||
res
|
||||
}
|
||||
expect("got a ping") {
|
||||
inner.log
|
||||
}
|
||||
}
|
||||
|
||||
def test_bangbangbang_Timeout1 = {
|
||||
setup
|
||||
expect("pong") {
|
||||
(server !!! Ping).getOrElse("nil")
|
||||
}
|
||||
expect("got a ping") {
|
||||
inner.log
|
||||
}
|
||||
}
|
||||
|
||||
def test_bangbangbang_Timeout2 = {
|
||||
setup
|
||||
expect("error handler") {
|
||||
server !!! (OneWay, "error handler")
|
||||
}
|
||||
expect("got a oneway") {
|
||||
inner.log
|
||||
}
|
||||
}
|
||||
|
||||
def test_bangbangbang_GetFutureTimeout1 = {
|
||||
setup
|
||||
val future = server !! Ping
|
||||
future.receiveWithin(100) match {
|
||||
case None => fail("timed out") // timed out
|
||||
case Some(reply) =>
|
||||
expect("got a ping") {
|
||||
inner.log
|
||||
}
|
||||
assert("pong" === reply)
|
||||
}
|
||||
}
|
||||
|
||||
def test_bangbangbang_GetFutureTimeout2 = {
|
||||
setup
|
||||
val future = server !! OneWay
|
||||
future.receiveWithin(100) match {
|
||||
case None =>
|
||||
expect("got a oneway") {
|
||||
inner.log
|
||||
}
|
||||
case Some(reply) =>
|
||||
fail("expected a timeout, got Some(reply)")
|
||||
}
|
||||
}
|
||||
|
||||
def testHotSwap = {
|
||||
setup
|
||||
// using base
|
||||
expect("pong") {
|
||||
(server !!! Ping).getOrElse("nil")
|
||||
}
|
||||
|
||||
// hotswapping
|
||||
server.hotswap(Some({
|
||||
case Ping => reply("hotswapped pong")
|
||||
}))
|
||||
expect("hotswapped pong") {
|
||||
(server !!! Ping).getOrElse("nil")
|
||||
}
|
||||
}
|
||||
|
||||
def testDoubleHotSwap = {
|
||||
setup
|
||||
// using base
|
||||
expect("pong") {
|
||||
(server !!! Ping).getOrElse("nil")
|
||||
}
|
||||
|
||||
// hotswapping
|
||||
server.hotswap(Some({
|
||||
case Ping => reply("hotswapped pong")
|
||||
}))
|
||||
expect("hotswapped pong") {
|
||||
(server !!! Ping).getOrElse("nil")
|
||||
}
|
||||
|
||||
// hotswapping again
|
||||
server.hotswap(Some({
|
||||
case Ping => reply("hotswapped pong again")
|
||||
}))
|
||||
expect("hotswapped pong again") {
|
||||
(server !!! Ping).getOrElse("nil")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def testHotSwapReturnToBase = {
|
||||
setup
|
||||
// using base
|
||||
expect("pong") {
|
||||
(server !!! Ping).getOrElse("nil")
|
||||
}
|
||||
|
||||
// hotswapping
|
||||
server.hotswap(Some({
|
||||
case Ping => reply("hotswapped pong")
|
||||
}))
|
||||
expect("hotswapped pong") {
|
||||
(server !!! Ping).getOrElse("nil")
|
||||
}
|
||||
|
||||
// restoring original base
|
||||
server.hotswap(None)
|
||||
expect("pong") {
|
||||
(server !!! Ping).getOrElse("nil")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class GenericServerContainerActor extends GenericServer {
|
||||
var log = ""
|
||||
|
||||
override def body: PartialFunction[Any, Unit] = {
|
||||
case Ping =>
|
||||
log = "got a ping"
|
||||
reply("pong")
|
||||
|
||||
case OneWay =>
|
||||
log = "got a oneway"
|
||||
}
|
||||
|
||||
override def init(config: AnyRef) = log = "initializing: " + config
|
||||
override def shutdown(reason: AnyRef) = log = "terminating: " + reason
|
||||
}
|
||||
|
||||
|
||||
37
kernel/src/test/scala/GenericServerSpec.scala
Executable file
37
kernel/src/test/scala/GenericServerSpec.scala
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* Copyright (C) 2009 Scalable Solutions.
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.kernel
|
||||
|
||||
import org.scalatest._
|
||||
|
||||
import scala.actors.Actor._
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class GenericServerSpec extends Suite {
|
||||
|
||||
def testSendRegularMessage = {
|
||||
val server = new MyGenericServerActor
|
||||
server.start
|
||||
server !? Ping match {
|
||||
case reply: String =>
|
||||
assert("got a ping" === server.log)
|
||||
assert("pong" === reply)
|
||||
case _ => fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MyGenericServerActor extends GenericServer {
|
||||
var log: String = ""
|
||||
|
||||
override def body: PartialFunction[Any, Unit] = {
|
||||
case Ping =>
|
||||
log = "got a ping"
|
||||
reply("pong")
|
||||
}
|
||||
}
|
||||
|
||||
12
kernel/src/test/scala/Messages.scala
Executable file
12
kernel/src/test/scala/Messages.scala
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* Copyright (C) 2009 Scalable Solutions.
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.kernel
|
||||
|
||||
sealed abstract class TestMessage
|
||||
case object Ping extends TestMessage
|
||||
case object Pong extends TestMessage
|
||||
case object OneWay extends TestMessage
|
||||
case object Die extends TestMessage
|
||||
case object NotifySupervisorExit extends TestMessage
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009 Scalable Solutions.
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.kernel
|
||||
|
||||
import org.scalatest.Spec
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
import javax.ws.rs.{Produces, Path, GET}
|
||||
|
||||
//import com.sun.net.httpserver.HttpServer;
|
||||
//import com.sun.ws.rest.api.client.Client;
|
||||
//import com.sun.ws.rest.api.client.ClientResponse;
|
||||
//import com.sun.ws.rest.api.client.ResourceProxy;
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class RestManagerSpec extends Spec with ShouldMatchers {
|
||||
|
||||
describe("A RestManager") {
|
||||
|
||||
it("should be able to start and stop") {
|
||||
val threadSelector = Kernel.startJersey
|
||||
/* val cc = new DefaultClientConfig
|
||||
val c = Client.create(cc)
|
||||
val resource = c.proxy("http://localhost:9998/")
|
||||
val hello = resource.get(classOf[HelloWorldResource])
|
||||
val msg = hello.getMessage
|
||||
println("=============: " + msg)
|
||||
*/ threadSelector.stopEndpoint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Path("/helloworld")
|
||||
class HelloWorldResource {
|
||||
@GET
|
||||
@Produces(Array("text/plain"))
|
||||
def getMessage = "Hello World"
|
||||
}
|
||||
430
kernel/src/test/scala/SupervisorSpec.scala
Executable file
430
kernel/src/test/scala/SupervisorSpec.scala
Executable file
|
|
@ -0,0 +1,430 @@
|
|||
/**
|
||||
* Copyright (C) 2009 Scalable Solutions.
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.kernel
|
||||
|
||||
import scala.actors._
|
||||
import scala.actors.Actor._
|
||||
import scala.collection.Map
|
||||
import scala.collection.mutable.HashMap
|
||||
|
||||
import org.scalatest._
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class SupervisorSpec extends Suite {
|
||||
|
||||
var messageLog: String = ""
|
||||
val pingpong1 = new GenericServerContainer("pingpong1", () => new PingPong1Actor)
|
||||
val pingpong2 = new GenericServerContainer("pingpong2", () => new PingPong2Actor)
|
||||
val pingpong3 = new GenericServerContainer("pingpong3", () => new PingPong3Actor)
|
||||
|
||||
pingpong1.setTimeout(100)
|
||||
pingpong2.setTimeout(100)
|
||||
pingpong3.setTimeout(100)
|
||||
|
||||
def testStartServer = {
|
||||
messageLog = ""
|
||||
val sup = getSingleActorAllForOneSupervisor
|
||||
sup ! Start
|
||||
|
||||
expect("pong") {
|
||||
(pingpong1 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
}
|
||||
|
||||
def testGetServer = {
|
||||
messageLog = ""
|
||||
val sup = getSingleActorAllForOneSupervisor
|
||||
sup ! Start
|
||||
val server = sup.getServerOrElse("pingpong1", throw new RuntimeException("server not found"))
|
||||
assert(server.isInstanceOf[GenericServerContainer])
|
||||
assert(server === pingpong1)
|
||||
}
|
||||
|
||||
def testGetServerOrFail = {
|
||||
messageLog = ""
|
||||
val sup = getSingleActorAllForOneSupervisor
|
||||
sup ! Start
|
||||
intercept(classOf[RuntimeException]) {
|
||||
sup.getServerOrElse("wrong_name", throw new RuntimeException("server not found"))
|
||||
}
|
||||
}
|
||||
|
||||
def testKillSingleActorOneForOne = {
|
||||
messageLog = ""
|
||||
val sup = getSingleActorOneForOneSupervisor
|
||||
sup ! Start
|
||||
|
||||
intercept(classOf[RuntimeException]) {
|
||||
pingpong1 !!! (Die, throw new RuntimeException("TIME OUT"))
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("oneforone") {
|
||||
messageLog
|
||||
}
|
||||
}
|
||||
|
||||
def testCallKillCallSingleActorOneForOne = {
|
||||
messageLog = ""
|
||||
val sup = getSingleActorOneForOneSupervisor
|
||||
sup ! Start
|
||||
|
||||
expect("pong") {
|
||||
(pingpong1 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("ping") {
|
||||
messageLog
|
||||
}
|
||||
intercept(classOf[RuntimeException]) {
|
||||
pingpong1 !!! (Die, throw new RuntimeException("TIME OUT"))
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pingoneforone") {
|
||||
messageLog
|
||||
}
|
||||
expect("pong") {
|
||||
(pingpong1 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pingoneforoneping") {
|
||||
messageLog
|
||||
}
|
||||
}
|
||||
|
||||
def testKillSingleActorAllForOne = {
|
||||
messageLog = ""
|
||||
val sup = getSingleActorAllForOneSupervisor
|
||||
sup ! Start
|
||||
intercept(classOf[RuntimeException]) {
|
||||
pingpong1 !!! (Die, throw new RuntimeException("TIME OUT"))
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("allforone") {
|
||||
messageLog
|
||||
}
|
||||
}
|
||||
|
||||
def testCallKillCallSingleActorAllForOne = {
|
||||
messageLog = ""
|
||||
val sup = getSingleActorAllForOneSupervisor
|
||||
sup ! Start
|
||||
expect("pong") {
|
||||
(pingpong1 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("ping") {
|
||||
messageLog
|
||||
}
|
||||
intercept(classOf[RuntimeException]) {
|
||||
pingpong1 !!! (Die, throw new RuntimeException("TIME OUT"))
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pingallforone") {
|
||||
messageLog
|
||||
}
|
||||
expect("pong") {
|
||||
(pingpong1 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pingallforoneping") {
|
||||
messageLog
|
||||
}
|
||||
}
|
||||
|
||||
def testKillMultipleActorsOneForOne = {
|
||||
messageLog = ""
|
||||
val sup = getMultipleActorsOneForOneConf
|
||||
sup ! Start
|
||||
intercept(classOf[RuntimeException]) {
|
||||
pingpong3 !!! (Die, throw new RuntimeException("TIME OUT"))
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("oneforone") {
|
||||
messageLog
|
||||
}
|
||||
}
|
||||
|
||||
def tesCallKillCallMultipleActorsOneForOne = {
|
||||
messageLog = ""
|
||||
val sup = getMultipleActorsOneForOneConf
|
||||
sup ! Start
|
||||
expect("pong") {
|
||||
(pingpong1 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pong") {
|
||||
(pingpong2 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pong") {
|
||||
(pingpong3 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pingpingping") {
|
||||
messageLog
|
||||
}
|
||||
intercept(classOf[RuntimeException]) {
|
||||
pingpong2 !!! (Die, throw new RuntimeException("TIME OUT"))
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pingpingpingoneforone") {
|
||||
messageLog
|
||||
}
|
||||
expect("pong") {
|
||||
(pingpong1 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pong") {
|
||||
(pingpong2 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pong") {
|
||||
(pingpong3 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pingpingpingoneforonepingpingping") {
|
||||
messageLog
|
||||
}
|
||||
}
|
||||
|
||||
def testKillMultipleActorsAllForOne = {
|
||||
messageLog = ""
|
||||
val sup = getMultipleActorsAllForOneConf
|
||||
sup ! Start
|
||||
intercept(classOf[RuntimeException]) {
|
||||
pingpong2 !!! (Die, throw new RuntimeException("TIME OUT"))
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("allforoneallforoneallforone") {
|
||||
messageLog
|
||||
}
|
||||
}
|
||||
|
||||
def tesCallKillCallMultipleActorsAllForOne = {
|
||||
messageLog = ""
|
||||
val sup = getMultipleActorsAllForOneConf
|
||||
sup ! Start
|
||||
expect("pong") {
|
||||
(pingpong1 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pong") {
|
||||
(pingpong2 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pong") {
|
||||
(pingpong3 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pingpingping") {
|
||||
messageLog
|
||||
}
|
||||
intercept(classOf[RuntimeException]) {
|
||||
pingpong2 !!! (Die, throw new RuntimeException("TIME OUT"))
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pingpingpingallforoneallforoneallforone") {
|
||||
messageLog
|
||||
}
|
||||
expect("pong") {
|
||||
(pingpong1 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pong") {
|
||||
(pingpong2 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pong") {
|
||||
(pingpong3 !!! Ping).getOrElse("nil")
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("pingpingpingallforoneallforoneallforonepingpingping") {
|
||||
messageLog
|
||||
}
|
||||
}
|
||||
|
||||
def testTerminateFirstLevelActorAllForOne = {
|
||||
messageLog = ""
|
||||
val sup = getNestedSupervisorsAllForOneConf
|
||||
sup ! Start
|
||||
intercept(classOf[RuntimeException]) {
|
||||
pingpong1 !!! (Die, throw new RuntimeException("TIME OUT"))
|
||||
}
|
||||
Thread.sleep(100)
|
||||
expect("allforoneallforoneallforone") {
|
||||
messageLog
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =============================================
|
||||
// Creat some supervisors with different configurations
|
||||
|
||||
def getSingleActorAllForOneSupervisor: Supervisor = {
|
||||
|
||||
// Create an abstract SupervisorContainer that works for all implementations
|
||||
// of the different Actors (Services).
|
||||
//
|
||||
// Then create a concrete container in which we mix in support for the specific
|
||||
// implementation of the Actors we want to use.
|
||||
|
||||
object factory extends TestSupervisorFactory {
|
||||
override def getSupervisorConfig: SupervisorConfig = {
|
||||
SupervisorConfig(
|
||||
RestartStrategy(AllForOne, 3, 100),
|
||||
Worker(
|
||||
pingpong1,
|
||||
LifeCycle(Permanent, 100))
|
||||
:: Nil)
|
||||
}
|
||||
}
|
||||
factory.newSupervisor
|
||||
}
|
||||
|
||||
def getSingleActorOneForOneSupervisor: Supervisor = {
|
||||
object factory extends TestSupervisorFactory {
|
||||
override def getSupervisorConfig: SupervisorConfig = {
|
||||
SupervisorConfig(
|
||||
RestartStrategy(OneForOne, 3, 100),
|
||||
Worker(
|
||||
pingpong1,
|
||||
LifeCycle(Permanent, 100))
|
||||
:: Nil)
|
||||
}
|
||||
}
|
||||
factory.newSupervisor
|
||||
}
|
||||
|
||||
def getMultipleActorsAllForOneConf: Supervisor = {
|
||||
object factory extends TestSupervisorFactory {
|
||||
override def getSupervisorConfig: SupervisorConfig = {
|
||||
SupervisorConfig(
|
||||
RestartStrategy(AllForOne, 3, 100),
|
||||
Worker(
|
||||
pingpong1,
|
||||
LifeCycle(Permanent, 100))
|
||||
::
|
||||
Worker(
|
||||
pingpong2,
|
||||
LifeCycle(Permanent, 100))
|
||||
::
|
||||
Worker(
|
||||
pingpong3,
|
||||
LifeCycle(Permanent, 100))
|
||||
:: Nil)
|
||||
}
|
||||
}
|
||||
factory.newSupervisor
|
||||
}
|
||||
|
||||
def getMultipleActorsOneForOneConf: Supervisor = {
|
||||
object factory extends TestSupervisorFactory {
|
||||
override def getSupervisorConfig: SupervisorConfig = {
|
||||
SupervisorConfig(
|
||||
RestartStrategy(OneForOne, 3, 100),
|
||||
Worker(
|
||||
pingpong1,
|
||||
LifeCycle(Permanent, 100))
|
||||
::
|
||||
Worker(
|
||||
pingpong2,
|
||||
LifeCycle(Permanent, 100))
|
||||
::
|
||||
Worker(
|
||||
pingpong3,
|
||||
LifeCycle(Permanent, 100))
|
||||
:: Nil)
|
||||
}
|
||||
}
|
||||
factory.newSupervisor
|
||||
}
|
||||
|
||||
def getNestedSupervisorsAllForOneConf: Supervisor = {
|
||||
object factory extends TestSupervisorFactory {
|
||||
override def getSupervisorConfig: SupervisorConfig = {
|
||||
SupervisorConfig(
|
||||
RestartStrategy(AllForOne, 3, 100),
|
||||
Worker(
|
||||
pingpong1,
|
||||
LifeCycle(Permanent, 100))
|
||||
::
|
||||
SupervisorConfig(
|
||||
RestartStrategy(AllForOne, 3, 100),
|
||||
Worker(
|
||||
pingpong2,
|
||||
LifeCycle(Permanent, 100))
|
||||
::
|
||||
Worker(
|
||||
pingpong3,
|
||||
LifeCycle(Permanent, 100))
|
||||
:: Nil)
|
||||
:: Nil)
|
||||
}
|
||||
}
|
||||
factory.newSupervisor
|
||||
}
|
||||
|
||||
class PingPong1Actor extends GenericServer {
|
||||
override def body: PartialFunction[Any, Unit] = {
|
||||
case Ping =>
|
||||
messageLog += "ping"
|
||||
reply("pong")
|
||||
case Die =>
|
||||
throw new RuntimeException("Recieved Die message")
|
||||
}
|
||||
}
|
||||
|
||||
class PingPong2Actor extends GenericServer {
|
||||
override def body: PartialFunction[Any, Unit] = {
|
||||
case Ping =>
|
||||
messageLog += "ping"
|
||||
reply("pong")
|
||||
case Die =>
|
||||
throw new RuntimeException("Recieved Die message")
|
||||
}
|
||||
}
|
||||
|
||||
class PingPong3Actor extends GenericServer {
|
||||
override def body: PartialFunction[Any, Unit] = {
|
||||
case Ping =>
|
||||
messageLog += "ping"
|
||||
reply("pong")
|
||||
case Die =>
|
||||
throw new RuntimeException("Recieved Die message")
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================
|
||||
|
||||
class TestAllForOneStrategy(maxNrOfRetries: Int, withinTimeRange: Int) extends AllForOneStrategy(maxNrOfRetries, withinTimeRange) {
|
||||
override def postRestart(serverContainer: GenericServerContainer) = {
|
||||
messageLog += "allforone"
|
||||
}
|
||||
}
|
||||
|
||||
class TestOneForOneStrategy(maxNrOfRetries: Int, withinTimeRange: Int) extends OneForOneStrategy(maxNrOfRetries, withinTimeRange) {
|
||||
override def postRestart(serverContainer: GenericServerContainer) = {
|
||||
messageLog += "oneforone"
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TestSupervisorFactory extends SupervisorFactory {
|
||||
override def create(strategy: RestartStrategy): Supervisor = strategy match {
|
||||
case RestartStrategy(scheme, maxNrOfRetries, timeRange) =>
|
||||
scheme match {
|
||||
case AllForOne => new Supervisor(new TestAllForOneStrategy(maxNrOfRetries, timeRange))
|
||||
case OneForOne => new Supervisor(new TestOneForOneStrategy(maxNrOfRetries, timeRange))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
92
kernel/src/test/scala/SupervisorStateSpec.scala
Executable file
92
kernel/src/test/scala/SupervisorStateSpec.scala
Executable file
|
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* Copyright (C) 2009 Scalable Solutions.
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.kernel
|
||||
|
||||
import org.scalatest._
|
||||
|
||||
import scala.actors.Actor._
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class SupervisorStateSpec extends Suite {
|
||||
|
||||
val dummyActor = new GenericServer { override def body: PartialFunction[Any, Unit] = { case _ => }}
|
||||
val newDummyActor = () => dummyActor
|
||||
var state: SupervisorState = _
|
||||
var proxy: GenericServerContainer = _
|
||||
var supervisor: Supervisor = _
|
||||
|
||||
def setup = {
|
||||
proxy = new GenericServerContainer("server1", newDummyActor)
|
||||
object factory extends SupervisorFactory {
|
||||
override def getSupervisorConfig: SupervisorConfig = {
|
||||
SupervisorConfig(
|
||||
RestartStrategy(AllForOne, 3, 100),
|
||||
Worker(
|
||||
proxy,
|
||||
LifeCycle(Permanent, 100))
|
||||
:: Nil)
|
||||
}
|
||||
}
|
||||
supervisor = factory.newSupervisor
|
||||
state = new SupervisorState(supervisor, new AllForOneStrategy(3, 100))
|
||||
}
|
||||
|
||||
def testAddServer = {
|
||||
setup
|
||||
state.addServerContainer(proxy)
|
||||
state.getServerContainer("server1") match {
|
||||
case None => fail("should have returned server")
|
||||
case Some(server) =>
|
||||
assert(server != null)
|
||||
assert(server.isInstanceOf[GenericServerContainer])
|
||||
assert(proxy === server)
|
||||
}
|
||||
}
|
||||
|
||||
def testGetServer = {
|
||||
setup
|
||||
state.addServerContainer(proxy)
|
||||
state.getServerContainer("server1") match {
|
||||
case None => fail("should have returned server")
|
||||
case Some(server) =>
|
||||
assert(server != null)
|
||||
assert(server.isInstanceOf[GenericServerContainer])
|
||||
assert(proxy === server)
|
||||
}
|
||||
}
|
||||
|
||||
def testRemoveServer = {
|
||||
setup
|
||||
state.addServerContainer(proxy)
|
||||
|
||||
state.removeServerContainer("server1")
|
||||
state.getServerContainer("server1") match {
|
||||
case Some(_) => fail("should have returned None")
|
||||
case None =>
|
||||
}
|
||||
state.getServerContainer("dummyActor") match {
|
||||
case Some(_) => fail("should have returned None")
|
||||
case None =>
|
||||
}
|
||||
}
|
||||
|
||||
def testGetNonExistingServerBySymbol = {
|
||||
setup
|
||||
state.getServerContainer("server2") match {
|
||||
case Some(_) => fail("should have returned None")
|
||||
case None =>
|
||||
}
|
||||
}
|
||||
|
||||
def testGetNonExistingServerByActor = {
|
||||
setup
|
||||
state.getServerContainer("dummyActor") match {
|
||||
case Some(_) => fail("should have returned None")
|
||||
case None =>
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue