diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRestartSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRestartSpec.scala index 3f1398e596..2b52c5cd4f 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRestartSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRestartSpec.scala @@ -36,7 +36,6 @@ object ActorRestartSpec { case "get" ⇒ self reply xx } override def preStart { testActor ! (("preStart", gen)) } - override def postStop { testActor ! (("postStop", gen)) } override def preRestart(cause: Throwable) { testActor ! (("preRestart", gen)) } override def postRestart(cause: Throwable) { testActor ! (("postRestart", gen)) } override def freshInstance() = { @@ -71,13 +70,21 @@ class ActorRestartSpec extends WordSpec with MustMatchers with TestKit with Befo import ActorRestartSpec._ override def beforeEach { generation = 0 } + override def afterEach { toStop foreach (_.stop()) } + + private var toStop = List.empty[ActorRef] + private def newActor(f: ⇒ Actor): ActorRef = { + val ref = actorOf(f) + toStop ::= ref + ref.start() + } "An Actor restart" must { "invoke preRestart, preStart, postRestart" in { - val actor = actorOf(new Restarter(testActor)).start() + val actor = newActor(new Restarter(testActor)) expectMsg(1 second, ("preStart", 1)) - val supervisor = actorOf[Supervisor].start() + val supervisor = newActor(new Supervisor) supervisor link actor actor ! Kill within(1 second) { @@ -89,9 +96,9 @@ class ActorRestartSpec extends WordSpec with MustMatchers with TestKit with Befo } "support creation of nested actors in freshInstance()" in { - val actor = actorOf(new Restarter(testActor)).start() + val actor = newActor(new Restarter(testActor)) expectMsg(1 second, ("preStart", 1)) - val supervisor = actorOf[Supervisor].start() + val supervisor = newActor(new Supervisor) supervisor link actor actor ! Nested actor ! Kill @@ -108,9 +115,9 @@ class ActorRestartSpec extends WordSpec with MustMatchers with TestKit with Befo } "use freshInstance() if available" in { - val actor = actorOf(new Restarter(testActor)).start() + val actor = newActor(new Restarter(testActor)) expectMsg(1 second, ("preStart", 1)) - val supervisor = actorOf[Supervisor].start() + val supervisor = newActor(new Supervisor) supervisor link actor actor ! 42 actor ! Handover @@ -126,9 +133,9 @@ class ActorRestartSpec extends WordSpec with MustMatchers with TestKit with Befo } "fall back to default factory if freshInstance() fails" in { - val actor = actorOf(new Restarter(testActor)).start() + val actor = newActor(new Restarter(testActor)) expectMsg(1 second, ("preStart", 1)) - val supervisor = actorOf[Supervisor].start() + val supervisor = newActor(new Supervisor) supervisor link actor actor ! 42 actor ! Fail @@ -143,6 +150,22 @@ class ActorRestartSpec extends WordSpec with MustMatchers with TestKit with Befo expectMsg(1 second, 0) } + "call preRestart(cause, currentMessage) if defined" in { + val actor = newActor(new Actor { + def receive = { case _ ⇒ } + override def preRestart(cause: Throwable, currentMessage: Option[Any]) { + testActor ! (("preRestart", currentMessage)) + } + }) + val supervisor = newActor(new Supervisor) + supervisor link actor + actor ! Kill + within(1 second) { + expectMsg(("preRestart", Some(Kill))) + expectNoMsg + } + } + } } diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 9925c060a5..bb4bb47327 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -658,10 +658,21 @@ trait Actor { /** * User overridable callback. *
- * Is called on a crashed Actor right BEFORE it is restarted to allow clean up of resources before Actor is terminated. + * Is called on a crashed Actor right BEFORE it is restarted to allow clean + * up of resources before Actor is terminated. Override either the variant + * with or without the currentMessage argument. */ def preRestart(reason: Throwable) {} + /** + * User overridable callback. + * + * Is called on a crashed Actor right BEFORE it is restarted to allow clean + * up of resources before Actor is terminated. Override either the variant + * with or without the currentMessage argument. + */ + def preRestart(reason: Throwable, message: Option[Any]) { preRestart(reason) } + /** * User overridable callback. * diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index 5e3c5fd3cf..1287324288 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -766,7 +766,8 @@ class LocalActorRef private[akka] ( def performRestart() { val failedActor = actorInstance.get if (Actor.debugLifecycle) EventHandler.debug(failedActor, "restarting") - failedActor.preRestart(reason) + val message = if (currentMessage ne null) Some(currentMessage.message) else None + failedActor.preRestart(reason, message) val freshActor = newActor setActorSelfFields(failedActor, null) // Only null out the references if we could instantiate the new actor actorInstance.set(freshActor) // Assign it here so if preStart fails, we can null out the sef-refs next call diff --git a/akka-docs/scala/actors.rst b/akka-docs/scala/actors.rst index ad9fbf2756..b23d53385e 100644 --- a/akka-docs/scala/actors.rst +++ b/akka-docs/scala/actors.rst @@ -99,11 +99,15 @@ The :class:`Actor` trait defines only one abstract method, the abovementioned :meth:`receive`. In addition, it offers two convenience methods :meth:`become`/:meth:`unbecome` for modifying the hotswap behavior stack as described in :ref:`Actor.HotSwap` and the :obj:`self` reference to this actor’s -:class:`ActorRef` object. The remaining visible methods are user-overridable -life-cycle hooks which are described in the following:: +:class:`ActorRef` object. If the current actor behavior does not match a +received message, :meth:`unhandled` is called, which by default throws an +:class:`UnhandledMessageException`. + +The remaining visible methods are user-overridable life-cycle hooks which are +described in the following:: def preStart() {} - def preRestart(cause: Throwable) {} + def preRestart(cause: Throwable, message: Option[Any]) {} def freshInstance(): Option[Actor] = None def postRestart(cause: Throwable) {} def postStop() {} @@ -135,7 +139,10 @@ handling strategy, will be restarted in case an exception is thrown while processing a message. This restart involves four of the hooks mentioned above: 1. The old actor is informed by calling :meth:`preRestart` with the exception - which caused the restart; this method is the best place for cleaning up, + which caused the restart and the message which triggered that exception; the + latter may be ``None`` if the restart was not caused by processing a + message, e.g. when a supervisor does not trap the exception and is restarted + in turn by its supervisor. This method is the best place for cleaning up, preparing hand-over to the fresh actor instance, etc. 2. The old actor’s :meth:`freshInstance` factory method is invoked, which may optionally produce the new actor instance which will replace this actor. If @@ -158,7 +165,7 @@ processing a message. This restart involves four of the hooks mentioned above: An actor restart replaces only the actual actor object; the contents of the mailbox and the hotswap stack are unaffected by the restart, so processing of messages will resume after the :meth:`postRestart` hook returns. Any message -sent to an actor which is being restarted will be queued to its mailbox as +sent to an actor while it is being restarted will be queued to its mailbox as usual. Stop Hook