diff --git a/akka-docs/rst/java/code/docs/actor/InitializationDocSpecJava.java b/akka-docs/rst/java/code/docs/actor/InitializationDocSpecJava.java new file mode 100644 index 0000000000..2e3f52108b --- /dev/null +++ b/akka-docs/rst/java/code/docs/actor/InitializationDocSpecJava.java @@ -0,0 +1,95 @@ +package docs.actor; + +import akka.actor.*; +import akka.japi.Procedure; +import akka.testkit.JavaTestKit; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import scala.Option; + +/** + * Copyright (C) 2009-2013 Typesafe Inc. + */ +public class InitializationDocSpecJava { + + static public class PreStartInitExample extends UntypedActor { + + public void onReceive(Object message) throws Exception {} + + //#preStartInit + @Override + public void preStart() { + // Initialize children here + } + + // Overriding postRestart to disable the call to preStart() + // after restarts + @Override + public void postRestart(Throwable reason) { + } + + // The default implementation of preRestart() stops all the children + // of the actor. To opt-out from stopping the children, we + // have to override preRestart() + @Override + public void preRestart(Throwable reason, Option message) { + // Keep the call to postStop(), but no stopping of children + postStop(); + } + //#preStartInit + + } + + public static class MessageInitExample extends UntypedActor { + //#messageInit + private String initializeMe = null; + + @Override + public void onReceive(Object message) throws Exception { + if (message.equals("init")) { + initializeMe = "Up and running"; + getContext().become(new Procedure() { + @Override + public void apply(Object message) throws Exception { + if (message.equals("U OK?")) + getSender().tell(initializeMe, getSelf()); + } + }); + } + } + //#messageInit + } + + static ActorSystem system; + + @BeforeClass + public static void setup() { + system = ActorSystem.create("TestSystem"); + } + + @AfterClass + public static void teardown() { + system.shutdown(); + } + + @Test + public void testIt() { + + new JavaTestKit(system) {{ + ActorRef testactor = system.actorOf(new Props(MessageInitExample.class), "testactor"); + String probe = "U OK?"; + + testactor.tell(probe, getRef()); + expectNoMsg(); + + testactor.tell("init", getRef()); + testactor.tell(probe, getRef()); + expectMsgEquals("Up and running"); + }}; + + + + } + +} diff --git a/akka-docs/rst/java/untyped-actors.rst b/akka-docs/rst/java/untyped-actors.rst index b044c26315..62ea154daa 100644 --- a/akka-docs/rst/java/untyped-actors.rst +++ b/akka-docs/rst/java/untyped-actors.rst @@ -684,3 +684,62 @@ created. This new instance will now be used in the actor references to this acto state of the failing actor instance is lost if you don't store and restore it in ``preRestart`` and ``postRestart`` callbacks. +Initialization patterns +======================= + +The rich lifecycle hooks of Actors provide a useful toolkit to implement various initialization patterns. During the +lifetime of an ``ActorRef``, an actor can potentially go through several restarts, where the old instance is replaced by +a fresh one, invisibly to the outside observer who only sees the ``ActorRef``. + +One may think about the new instances as "incarnations". Initialization might be necessary for every incarnation +of an actor, but sometimes one needs initialization to happen only at the birth of the first instance when the +``ActorRef`` is created. The following sections provide patterns for different initialization needs. + +Initialization via constructor +------------------------------ + +Using the constructor for initialization has various benefits. First of all, it makes it possible to use ``val`` fields to store +any state that does not change during the life of the actor instance, making the implementation of the actor more robust. +The constructor is invoked for every incarnation of the actor, therefore the internals of the actor can always assume +that proper initialization happened. This is also the drawback of this approach, as there are cases when one would +like to avoid reinitializing internals on restart. For example, it is often useful to preserve child actors across +restarts. The following section provides a pattern for this case. + +Initialization via preStart +--------------------------- + +The method ``preStart()`` of an actor is only called once directly during the initialization of the first instance, that +is, at creation of its ``ActorRef``. In the case of restarts, ``preStart()`` is called from ``postRestart()``, therefore +if not overridden, ``preStart()`` is called on every incarnation. However, overriding ``postRestart()`` one can disable +this behavior, and ensure that there is only one call to ``preStart()``. + +One useful usage of this pattern is to disable creation of new ``ActorRefs`` for children during restarts. This can be +achieved by overriding ``preRestart()``: + +.. includecode:: code/docs/actor/InitializationDocSpecJava.java#preStartInit + +Please note, that the child actors are *still restarted*, but no new ``ActorRef`` is created. One can recursively apply +the same principles for the children, ensuring that their ``preStart()`` method is called only at the creation of their +refs. + +For more information see :ref:`what-restarting-means-scala`. + +Initialization via message passing +---------------------------------- + +There are cases when it is impossible to pass all the information needed for actor initialization in the constructor, +for example in the presence of circular dependencies. In this case the actor should listen for an initialization message, +and use ``become()`` or a finite state-machine state transition to encode the initialized and uninitialized states +of the actor. + +.. includecode:: code/docs/actor/InitializationDocSpecJava.java#messageInit + +If the actor may receive messages before it has been initialized, a useful tool can be the ``Stash`` to save messages +until the initialization finishes, and replaying them after the actor became initialized. + +.. warning:: + + This pattern should be used with care, and applied only when none of the patterns above are applicable. One of + the potential issues is that messages might be lost when sent to remote actors. Also, publishing an ``ActorRef`` in + an uninitialized state might lead to the condition that it receives a user message before the initialization has been + done. diff --git a/akka-docs/rst/scala/actors.rst b/akka-docs/rst/scala/actors.rst index 31165a8ed2..a6807789c8 100644 --- a/akka-docs/rst/scala/actors.rst +++ b/akka-docs/rst/scala/actors.rst @@ -825,3 +825,63 @@ extend that, either through inheritance or delegation, is to use Or: .. includecode:: code/docs/actor/ActorDocSpec.scala#receive-orElse2 + +Initialization patterns +======================= + +The rich lifecycle hooks of Actors provide a useful toolkit to implement various initialization patterns. During the +lifetime of an ``ActorRef``, an actor can potentially go through several restarts, where the old instance is replaced by +a fresh one, invisibly to the outside observer who only sees the ``ActorRef``. + +One may think about the new instances as "incarnations". Initialization might be necessary for every incarnation +of an actor, but sometimes one needs initialization to happen only at the birth of the first instance when the +``ActorRef`` is created. The following sections provide patterns for different initialization needs. + +Initialization via constructor +------------------------------ + +Using the constructor for initialization has various benefits. First of all, it makes it possible to use ``val`` fields to store +any state that does not change during the life of the actor instance, making the implementation of the actor more robust. +The constructor is invoked for every incarnation of the actor, therefore the internals of the actor can always assume +that proper initialization happened. This is also the drawback of this approach, as there are cases when one would +like to avoid reinitializing internals on restart. For example, it is often useful to preserve child actors across +restarts. The following section provides a pattern for this case. + +Initialization via preStart +--------------------------- + +The method ``preStart()`` of an actor is only called once directly during the initialization of the first instance, that +is, at creation of its ``ActorRef``. In the case of restarts, ``preStart()`` is called from ``postRestart()``, therefore +if not overridden, ``preStart()`` is called on every incarnation. However, overriding ``postRestart()`` one can disable +this behavior, and ensure that there is only one call to ``preStart()``. + +One useful usage of this pattern is to disable creation of new ``ActorRefs`` for children during restarts. This can be +achieved by overriding ``preRestart()``: + +.. includecode:: code/docs/actor/InitializationDocSpec.scala#preStartInit + +Please note, that the child actors are *still restarted*, but no new ``ActorRef`` is created. One can recursively apply +the same principles for the children, ensuring that their ``preStart()`` method is called only at the creation of their +refs. + +For more information see :ref:`what-restarting-means-scala`. + +Initialization via message passing +---------------------------------- + +There are cases when it is impossible to pass all the information needed for actor initialization in the constructor, +for example in the presence of circular dependencies. In this case the actor should listen for an initialization message, +and use ``become()`` or a finite state-machine state transition to encode the initialized and uninitialized states +of the actor. + +.. includecode:: code/docs/actor/InitializationDocSpec.scala#messageInit + +If the actor may receive messages before it has been initialized, a useful tool can be the ``Stash`` to save messages +until the initialization finishes, and replaying them after the actor became initialized. + +.. warning:: + + This pattern should be used with care, and applied only when none of the patterns above are applicable. One of + the potential issues is that messages might be lost when sent to remote actors. Also, publishing an ``ActorRef`` in + an uninitialized state might lead to the condition that it receives a user message before the initialization has been + done. diff --git a/akka-docs/rst/scala/code/docs/actor/InitializationDocSpec.scala b/akka-docs/rst/scala/code/docs/actor/InitializationDocSpec.scala new file mode 100644 index 0000000000..01930e6c23 --- /dev/null +++ b/akka-docs/rst/scala/code/docs/actor/InitializationDocSpec.scala @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2009-2013 Typesafe Inc. + */ +package docs.actor + +import akka.actor.{ Props, Actor } +import akka.testkit.{ ImplicitSender, AkkaSpec } + +object InitializationDocSpec { + + class PreStartInitExample extends Actor { + override def receive = { + case _ ⇒ // Ignore + } + + //#preStartInit + override def preStart(): Unit = { + // Initialize children here + } + + // Overriding postRestart to disable the call to preStart() + // after restarts + override def postRestart(reason: Throwable): Unit = () + + // The default implementation of preRestart() stops all the children + // of the actor. To opt-out from stopping the children, we + // have to override preRestart() + override def preRestart(reason: Throwable, message: Option[Any]): Unit = { + // Keep the call to postStop(), but no stopping of children + postStop() + } + //#preStartInit + } + + class MessageInitExample extends Actor { + //#messageInit + var initializeMe: Option[String] = None + + override def receive = { + case "init" ⇒ + initializeMe = Some("Up and running") + context.become(initialized, discardOld = true) + + } + + def initialized: Receive = { + case "U OK?" ⇒ initializeMe foreach { sender ! _ } + } + //#messageInit + + } +} + +class InitializationDocSpec extends AkkaSpec with ImplicitSender { + import InitializationDocSpec._ + + "Message based initialization example" must { + + "work correctly" in { + val example = system.actorOf(Props[MessageInitExample], "messageInitExample") + val probe = "U OK?" + + example ! probe + expectNoMsg() + + example ! "init" + example ! probe + expectMsg("Up and running") + + } + + } + +}