I would like to do stashing/unstashing with FSM Akka Acctor. I don't know where to put the stash()
and unstashAll()
.
I have a simplified example below:
import akka.actor.{ActorSystem, FSM, Props, Stash}
trait TestState
case object StateA extends TestState
case object StateB extends TestState
case class TestData()
case class MessageA(msg: String)
case class MessageB(msg: String)
case object ChangeState
class TestFSM extends FSM[TestState, TestData] with Stash {
startWith(StateA, TestData())
when(StateA) {
case Event(MessageA(msgA), _) =>
println(s"In StateA: $msgA")
stay()
case Event(ChangeState, _) =>
println("Changing state from A to B")
goto(StateB)
}
when(StateB) {
case Event(MessageB(msgB), _) =>
println(s"In StateB: $msgB")
stay()
}
whenUnhandled {
case Event(e, _) =>
println(s"Unhandled event: $e")
stay()
}
}
object TestFSM extends App {
val system = ActorSystem("test-system")
val actor = system.actorOf(Props[TestFSM])
actor ! MessageA("Apple 1")
actor ! MessageB("Banana 1")
actor ! MessageA("Apple 2")
actor ! ChangeState
actor ! MessageB("Banana 2")
}
The initial state is StateA
.
When in StateA
the actor should only handle messages of type MessageA
. If it receives any other type of message (except for ChangeState
) it should stash it. Upon receiving a message ChangeState
, the actor should change to StateB
.
Upon changing from StateA
to StateB
, it should unstash all the messages.
When in StateB
the actor should only handle messages of type MessageB
.
I am not sure exactly where to use the stash()
and unstashAll()
to achieve this.
The output that I get on running is:
In StateA: Apple 1
Unhandled event: MessageB(Banana 1)
In StateA: Apple 2
Changing state from A to B
In StateB: Banana 2
The output I would like to see is:
In StateA: Apple 1
In StateA: Apple 2
Changing state from A to B
In StateB: Banana 1
In StateB: Banana 2
Thanks a lot.
You can achieve this by using the onTransition
method on the FSM
. This gets executed when a state change occurs and we can use that moment to unstash all messages. As for stashing, you need to do it in the whenUnhandled
method. I've also made a small update so you can cycle between states:
class TestFSM extends FSM[TestState, TestData] with Stash {
startWith(StateA, TestData())
when(StateA) {
case Event(MessageA(msgA), _) =>
println(s"In StateA: $msgA")
stay()
case Event(ChangeState, _) =>
println("Changing state from A to B")
goto(StateB)
}
when(StateB) {
case Event(MessageB(msgB), _) =>
println(s"In StateB: $msgB")
stay()
case Event(ChangeState, _) =>
println("Changing state from B to A")
goto(StateA)
}
/**
* Here we can stash all messages. For example when we're in state A,
* we handle both `MessageA` and `ChangeState` messages, but we don't
* handle `MessageB` instances which will end up here. The opposite
* happens when we're in state B.
*/
whenUnhandled {
case _: Event =>
stash()
stay()
}
// When transitioning into another state, unstash all messages.
onTransition {
case StateA -> StateB => unstashAll()
case StateB -> StateA => unstashAll()
}
}
Let me know if you have any more questions :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With