make currentMessage available in preRestart, test #957

- use Option[Any] since currentMessage may be unavailable (supervisor
  being restarted)
- plus docs
This commit is contained in:
Roland 2011-06-27 22:20:09 +02:00
parent 10256991cb
commit 956d055d87
4 changed files with 58 additions and 16 deletions

View file

@ -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
}
}
}
}

View file

@ -658,10 +658,21 @@ trait Actor {
/**
* User overridable callback.
* <p/>
* 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.
* <p/>
* 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.
* <p/>

View file

@ -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

View file

@ -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 actors
: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 actors :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