diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/FSMActorSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/FSMActorSpec.scala index 0841a377c7..d0610d16e9 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/FSMActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/FSMActorSpec.scala @@ -155,7 +155,6 @@ class FSMActorSpec extends WordSpec with MustMatchers with TestKit { case Ev("go") ⇒ goto(2) } }).start() - val reff = testActor val logger = Actor.actorOf(new Actor { def receive = { case x ⇒ testActor forward x @@ -169,5 +168,18 @@ class FSMActorSpec extends WordSpec with MustMatchers with TestKit { EventHandler.removeListener(logger) } + "run onTermination upon ActorRef.stop()" in { + lazy val fsm = new Actor with FSM[Int, Null] { + startWith(1, null) + when(1) { NullFunction } + onTermination { + case x ⇒ testActor ! x + } + } + val ref = Actor.actorOf(fsm).start() + ref.stop() + expectMsg(fsm.StopEvent(Shutdown, 1, null)) + } + } } diff --git a/akka-actor/src/main/scala/akka/actor/FSM.scala b/akka-actor/src/main/scala/akka/actor/FSM.scala index ba1b68ad22..1d157c659e 100644 --- a/akka-actor/src/main/scala/akka/actor/FSM.scala +++ b/akka-actor/src/main/scala/akka/actor/FSM.scala @@ -480,6 +480,8 @@ trait FSM[S, D] extends ListenerManagement { } } + override def postStop { terminate(Shutdown) } + private def terminate(reason: Reason) = { reason match { case Failure(ex: Throwable) ⇒ EventHandler.error(ex, self, "terminating due to Failure") diff --git a/akka-docs/scala/fsm.rst b/akka-docs/scala/fsm.rst index 8f4b6187ce..bdd7499c8a 100644 --- a/akka-docs/scala/fsm.rst +++ b/akka-docs/scala/fsm.rst @@ -430,8 +430,8 @@ queued it. The status of any timer may be inquired with These named timers complement state timeouts because they are not affected by intervening reception of other messages. -Termination ------------ +Termination from Inside +----------------------- The FSM is stopped by specifying the result state as @@ -471,6 +471,21 @@ a :class:`StopEvent(reason, stateName, stateData)` as argument: As for the :func:`whenUnhandled` case, this handler is not stacked, so each invocation of :func:`onTermination` replaces the previously installed handler. +Termination from Outside +------------------------ + +When an :class:`ActorRef` associated to a FSM is stopped using the +:meth:`stop()` method, its :meth:`postStop` hook will be executed. The default +implementation by the :class:`FSM` trait is to execute the +:meth:`onTermination` handler if that is prepared to handle a +:obj:`StopEvent(Shutdown, ...)`. + +.. warning:: + + In case you override :meth:`postStop` and want to have your + :meth:`onTermination` handler called, do not forget to call + ``super.postStop``. + Examples ========