refactor fsm

This commit is contained in:
momania 2010-07-19 16:28:35 +02:00
parent b5bf62ded9
commit 2af5fda73e
3 changed files with 42 additions and 38 deletions

View file

@ -25,40 +25,6 @@ trait Transactor extends Actor {
self.makeTransactionRequired
}
trait FsmActor[S] extends Actor {
type State = scala.PartialFunction[Event, NextState]
@volatile var currentState: NextState = initialState
@volatile var timeoutActor: Option[ActorRef] = None
def initialState: NextState
def handleEvent: State = {
case event@Event(value,stateData) =>
log.warning("No state for event with value %s - keeping current state %s", value, stateData)
NextState(currentState.state, stateData, currentState.timeout)
}
protected def receive = {
case value => {
timeoutActor.foreach{ref => Scheduler.unschedule(ref); timeoutActor = None }
val event = Event(value, currentState.stateData)
currentState = (currentState.state orElse handleEvent).apply(event)
currentState.timeout.foreach{timeout =>
timeoutActor = Some(Scheduler.scheduleOnce(self, StateTimeout, timeout, TimeUnit.MILLISECONDS))
}
}
}
case class NextState(state: State, stateData: S, timeout: Option[Int] = None)
case class Event(event: Any, stateData: S)
object StateTimeout
}
/**
* Extend this abstract class to create a remote actor.
* <p/>

View file

@ -0,0 +1,38 @@
package actor
import java.util.concurrent.TimeUnit
import se.scalablesolutions.akka.actor.{ActorRef, Scheduler, Actor}
trait Fsm[S] { self: Actor =>
type State = scala.PartialFunction[Event, NextState]
@volatile var currentState: NextState = initialState
@volatile var timeoutActor: Option[ActorRef] = None
def initialState: NextState
def handleEvent: State = {
case event@Event(value,stateData) =>
log.warning("No state for event with value %s - keeping current state %s", value, stateData)
NextState(currentState.state, stateData, currentState.timeout)
}
override protected def receive: Receive = {
case value => {
timeoutActor.foreach{ref => Scheduler.unschedule(ref); timeoutActor = None }
val event = Event(value, currentState.stateData)
currentState = (currentState.state orElse handleEvent).apply(event)
currentState.timeout.foreach{timeout =>
timeoutActor = Some(Scheduler.scheduleOnce(this.self, StateTimeout, timeout, TimeUnit.MILLISECONDS))
}
}
}
case class NextState(state: State, stateData: S, timeout: Option[Int] = None)
case class Event(event: Any, stateData: S)
}
object StateTimeout

View file

@ -7,7 +7,7 @@ package se.scalablesolutions.akka.actor
import org.scalatest.junit.JUnitSuite
import org.junit.Test
import org.multiverse.api.latches.StandardLatch
import Actor._
import actor.{StateTimeout, Fsm}
import java.util.concurrent.TimeUnit
object FsmActorSpec {
@ -15,7 +15,7 @@ object FsmActorSpec {
class Lock(code: String,
timeout: Int,
unlockedLatch: StandardLatch,
lockedLatch: StandardLatch) extends FsmActor[CodeState] {
lockedLatch: StandardLatch) extends Actor with Fsm[CodeState] {
def initialState = NextState(locked, CodeState("", "33221"))
@ -26,7 +26,7 @@ object FsmActorSpec {
NextState(locked, CodeState(incomplete, code))
case codeTry if (codeTry == code) => {
doUnlock
NextState(open, CodeState("", code), Some(timeout))
new NextState(open, CodeState("", code), Some(timeout))
}
case wrong => {
log.error("Wrong code %s", wrong)
@ -66,7 +66,7 @@ class FsmActorSpec extends JUnitSuite {
val lockedLatch = new StandardLatch
// lock that locked after being open for 1 sec
val lock = actorOf(new Lock("33221", 1000, unlockedLatch, lockedLatch)).start
val lock = Actor.actorOf(new Lock("33221", 1000, unlockedLatch, lockedLatch)).start
lock ! '3'
lock ! '3'