Added patterns for initialization for Scala #2297
(cherry-picked from e2aa3b5)
This commit is contained in:
parent
469b168ada
commit
7bf9f1f82b
4 changed files with 288 additions and 0 deletions
|
|
@ -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. <http://www.typesafe.com>
|
||||
*/
|
||||
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<Object> 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<Object>() {
|
||||
@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");
|
||||
}};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
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")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue