Updated documentation of Actors (Java). See #1435

* Aligned the Java and Scala documentation for Actors
* Implemented hotswap samples in Java, and documented in same way as Scala docs
* Improved Actors (Scala) docs
* Fixed wrong preRestart and postRestart in UntypedActor
* Changed name of Dispatchers.fromConfig to newFromConfig and made it Java friendly
* Added ActorRef.ask with Timeout parameter in addition to the timeoutMillis
This commit is contained in:
Patrik Nordwall 2011-12-08 14:06:20 +01:00
parent b4f486667f
commit ce128740ab
17 changed files with 848 additions and 327 deletions

View file

@ -58,13 +58,13 @@ class DispatchersSpec extends AkkaSpec {
dispatcher.map(_.throughput) must be(Some(17)) dispatcher.map(_.throughput) must be(Some(17))
} }
"use defined properties when fromConfig" in { "use defined properties when newFromConfig" in {
val dispatcher = fromConfig("myapp.mydispatcher", cfg = dispatcherConf) val dispatcher = newFromConfig("myapp.mydispatcher", defaultGlobalDispatcher, dispatcherConf)
dispatcher.throughput must be(17) dispatcher.throughput must be(17)
} }
"use specific name when fromConfig" in { "use specific name when newFromConfig" in {
val dispatcher = fromConfig("myapp.mydispatcher", cfg = dispatcherConf) val dispatcher = newFromConfig("myapp.mydispatcher", defaultGlobalDispatcher, dispatcherConf)
dispatcher.name must be("mydispatcher") dispatcher.name must be("mydispatcher")
} }

View file

@ -86,13 +86,17 @@ abstract class ActorRef extends java.lang.Comparable[ActorRef] with Serializable
* Sends a message asynchronously returns a future holding the eventual reply message. * Sends a message asynchronously returns a future holding the eventual reply message.
* <p/> * <p/>
* <b>NOTE:</b> * <b>NOTE:</b>
* Use this method with care. In most cases it is better to use 'tell' together with the 'getContext().getSender()' to * Use this method with care. In most cases it is better to use 'tell' together with the sender
* implement request/response message exchanges. * parameter to implement non-blocking request/response message exchanges.
* <p/> * <p/>
* If you are sending messages using <code>ask</code> then you <b>have to</b> use <code>getContext().sender().tell(...)</code> * If you are sending messages using <code>ask</code> and using blocking operations on the Future, such as
* to send a reply message to the original sender. If not then the sender will block until the timeout expires. * 'get', then you <b>have to</b> use <code>getContext().sender().tell(...)</code>
* in the target actor to send a reply message to the original sender, and thereby completing the Future,
* otherwise the sender will block until the timeout expires.
*/ */
def ask(message: AnyRef, timeout: Long): Future[AnyRef] = ?(message, Timeout(timeout)).asInstanceOf[Future[AnyRef]] def ask(message: AnyRef, timeout: Timeout): Future[AnyRef] = ?(message, timeout).asInstanceOf[Future[AnyRef]]
def ask(message: AnyRef, timeoutMillis: Long): Future[AnyRef] = ask(message, new Timeout(timeoutMillis))
/** /**
* Forwards the message and passes the original sender actor as the sender. * Forwards the message and passes the original sender actor as the sender.
@ -147,6 +151,14 @@ trait ScalaActorRef { ref: ActorRef ⇒
/** /**
* Sends a message asynchronously, returning a future which may eventually hold the reply. * Sends a message asynchronously, returning a future which may eventually hold the reply.
* <b>NOTE:</b>
* Use this method with care. In most cases it is better to use '!' together with implicit or explicit
* sender parameter to implement non-blocking request/response message exchanges.
* <p/>
* If you are sending messages using <code>ask</code> and using blocking operations on the Future, such as
* 'get', then you <b>have to</b> use <code>getContext().sender().tell(...)</code>
* in the target actor to send a reply message to the original sender, and thereby completing the Future,
* otherwise the sender will block until the timeout expires.
*/ */
def ?(message: Any)(implicit timeout: Timeout): Future[Any] def ?(message: Any)(implicit timeout: Timeout): Future[Any]

View file

@ -75,30 +75,36 @@ abstract class UntypedActor extends Actor {
/** /**
* User overridable callback. * User overridable callback.
* <p/> * <p/>
* Is called when an Actor is started, this only happens at most once in the life of an actor. * Is called when an Actor is started.
* Actor are automatically started asynchronously when created.
* Empty default implementation.
*/ */
override def preStart() {} override def preStart() {}
/** /**
* User overridable callback. * User overridable callback.
* <p/> * <p/>
* Is called when 'actor.stop()' is invoked. * Is called asynchronously after 'actor.stop()' is invoked.
* Empty default implementation.
*/ */
override def postStop() {} override def postStop() {}
/** /**
* User overridable callback. * User overridable callback.
* <p/> * <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.
* By default it calls postStop()
*/ */
override def preRestart(reason: Throwable, lastMessage: Option[Any]) {} override def preRestart(reason: Throwable, message: Option[Any]) { postStop() }
/** /**
* User overridable callback. * User overridable callback.
* <p/> * <p/>
* Is called right AFTER restart on the newly created Actor to allow reinitialization after an Actor crash. * Is called right AFTER restart on the newly created Actor to allow reinitialization after an Actor crash.
* By default it calls preStart()
*/ */
override def postRestart(reason: Throwable) {} override def postRestart(reason: Throwable) { preStart() }
/** /**
* User overridable callback. * User overridable callback.

View file

@ -262,7 +262,7 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
} }
/** /**
* Trait to be used for hooking in new dispatchers into Dispatchers.fromConfig * Trait to be used for hooking in new dispatchers into Dispatchers.from(cfg: Config)
*/ */
abstract class MessageDispatcherConfigurator() { abstract class MessageDispatcherConfigurator() {
/** /**

View file

@ -173,12 +173,11 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
new BalancingDispatcher(prerequisites, name, throughput, throughputDeadline, mailboxType, new BalancingDispatcher(prerequisites, name, throughput, throughputDeadline, mailboxType,
config, settings.DispatcherDefaultShutdown), ThreadPoolConfig()) config, settings.DispatcherDefaultShutdown), ThreadPoolConfig())
/** /**
* Utility function that tries to load the specified dispatcher config from the akka.conf * Creates a new dispatcher as specified in configuration
* or if not defined it uses the supplied dispatcher. * or if not defined it uses the supplied dispatcher.
* Uses default values from default-dispatcher, i.e. all options doesn't need to be defined * Uses default values from default-dispatcher, i.e. all options doesn't need to be defined.
* in config.
*/ */
def fromConfig(key: String, default: MessageDispatcher = defaultGlobalDispatcher, cfg: Config = settings.config): MessageDispatcher = { def newFromConfig(key: String, default: MessageDispatcher, cfg: Config): MessageDispatcher = {
import scala.collection.JavaConverters._ import scala.collection.JavaConverters._
def simpleName = key.substring(key.lastIndexOf('.') + 1) def simpleName = key.substring(key.lastIndexOf('.') + 1)
cfg.hasPath(key) match { cfg.hasPath(key) match {
@ -190,6 +189,14 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
} }
} }
/**
* Creates a new dispatcher as specified in configuration, or if not defined it uses
* the default dispatcher.
* Uses default configuration values from default-dispatcher, i.e. all options doesn't
* need to be defined.
*/
def newFromConfig(key: String): MessageDispatcher = newFromConfig(key, defaultGlobalDispatcher, settings.config)
/* /*
* Creates of obtains a dispatcher from a ConfigMap according to the format below. * Creates of obtains a dispatcher from a ConfigMap according to the format below.
* Uses default values from default-dispatcher. * Uses default values from default-dispatcher.

View file

@ -61,6 +61,8 @@ volatile variable rule. This means that you, the Akka user, do not need to worry
such a "happens before" relation, because it is the responsibility of Akka. So you have your hands free to deal with your such a "happens before" relation, because it is the responsibility of Akka. So you have your hands free to deal with your
business logic, and the Akka framework makes sure that those rules are guaranteed on your behalf. business logic, and the Akka framework makes sure that those rules are guaranteed on your behalf.
.. _jmm-shared-state:
Actors and shared mutable state Actors and shared mutable state
------------------------------- -------------------------------

View file

@ -0,0 +1,17 @@
package akka.docs.actor;
import akka.actor.ActorRef;
import static akka.actor.Actors.*;
import akka.actor.UntypedActor;
//#context-actorOf
public class FirstUntypedActor extends UntypedActor {
ActorRef myActor = getContext().actorOf(MyActor.class);
//#context-actorOf
public void onReceive(Object message) {
myActor.forward(message, getContext());
myActor.tell(poisonPill());
}
}

View file

@ -0,0 +1,25 @@
package akka.docs.actor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
//#immutable-message
public class ImmutableMessage {
private final int sequenceNumber;
private final List<String> values;
public ImmutableMessage(int sequenceNumber, List<String> values) {
this.sequenceNumber = sequenceNumber;
this.values = Collections.unmodifiableList(new ArrayList<String>(values));
}
public int getSequenceNumber() {
return sequenceNumber;
}
public List<String> getValues() {
return values;
}
}
//#immutable-message

View file

@ -0,0 +1,26 @@
package akka.docs.actor;
//#receive-timeout
import akka.actor.Actors;
import akka.actor.ReceiveTimeout;
import akka.actor.UnhandledMessageException;
import akka.actor.UntypedActor;
import akka.util.Duration;
public class MyReceivedTimeoutUntypedActor extends UntypedActor {
public MyReceivedTimeoutUntypedActor() {
getContext().setReceiveTimeout(Duration.parse("30 seconds"));
}
public void onReceive(Object message) throws Exception {
if (message.equals("Hello")) {
getSender().tell("Hello world");
} else if (message == Actors.receiveTimeout()) {
throw new RuntimeException("received timeout");
} else {
throw new UnhandledMessageException(message, getSelf());
}
}
}
//#receive-timeout

View file

@ -0,0 +1,20 @@
package akka.docs.actor;
//#my-untyped-actor
import akka.actor.UntypedActor;
import akka.actor.UnhandledMessageException;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class MyUntypedActor extends UntypedActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
public void onReceive(Object message) throws Exception {
if (message instanceof String)
log.info("Received String message: {}", message);
else
throw new UnhandledMessageException(message, getSelf());
}
}
//#my-untyped-actor

View file

@ -0,0 +1,53 @@
package akka.docs.actor;
import static akka.docs.actor.UntypedActorSwapper.Swap.SWAP;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.UnhandledMessageException;
import akka.actor.UntypedActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.Procedure;
//#swapper
public class UntypedActorSwapper {
public static class Swap {
public static Swap SWAP = new Swap();
private Swap() {
}
}
public static class Swapper extends UntypedActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
public void onReceive(Object message) throws Exception {
if (message == SWAP) {
log.info("Hi");
getContext().become(new Procedure<Object>() {
@Override
public void apply(Object message) {
log.info("Ho");
getContext().unbecome(); // resets the latest 'become' (just for fun)
}
});
} else {
throw new UnhandledMessageException(message, getSelf());
}
}
}
public static void main(String... args) {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef swap = system.actorOf(Swapper.class);
swap.tell(SWAP); // logs Hi
swap.tell(SWAP); // logs Ho
swap.tell(SWAP); // logs Hi
swap.tell(SWAP); // logs Ho
swap.tell(SWAP); // logs Hi
swap.tell(SWAP); // logs Ho
}
}
//#swapper

View file

@ -0,0 +1,5 @@
package akka.docs.actor
import org.scalatest.junit.JUnitSuite
class UntypedActorTest extends UntypedActorTestBase with JUnitSuite

View file

@ -0,0 +1,239 @@
package akka.docs.actor;
//#imports
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
//#imports
//#import-future
import akka.dispatch.Future;
//#import-future
//#import-actors
import static akka.actor.Actors.*;
//#import-actors
//#import-procedure
import akka.japi.Procedure;
//#import-procedure
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.actor.UntypedActorFactory;
import akka.dispatch.MessageDispatcher;
import org.junit.Test;
import scala.Option;
import static org.junit.Assert.*;
public class UntypedActorTestBase {
@Test
public void systemActorOf() {
//#system-actorOf
ActorSystem system = ActorSystem.create("MySystem");
ActorRef myActor = system.actorOf(MyUntypedActor.class);
//#system-actorOf
myActor.tell("test");
system.stop();
}
@Test
public void contextActorOf() {
//#context-actorOf
ActorSystem system = ActorSystem.create("MySystem");
ActorRef myActor = system.actorOf(MyUntypedActor.class);
//#context-actorOf
myActor.tell("test");
system.stop();
}
@Test
public void constructorActorOf() {
ActorSystem system = ActorSystem.create("MySystem");
//#creating-constructor
// allows passing in arguments to the MyActor constructor
ActorRef myActor = system.actorOf(new UntypedActorFactory() {
public UntypedActor create() {
return new MyActor("...");
}
});
//#creating-constructor
myActor.tell("test");
system.stop();
}
@Test
public void propsActorOf() {
ActorSystem system = ActorSystem.create("MySystem");
//#creating-props
MessageDispatcher dispatcher = system.dispatcherFactory().newFromConfig("my-dispatcher");
ActorRef myActor = system.actorOf(new Props().withCreator(MyUntypedActor.class).withDispatcher(dispatcher),
"myactor");
//#creating-props
myActor.tell("test");
system.stop();
}
@Test
public void usingAsk() {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef myActor = system.actorOf(new UntypedActorFactory() {
public UntypedActor create() {
return new MyAskActor();
}
});
//#using-ask
Future future = myActor.ask("Hello", 1000);
future.await();
if (future.isCompleted()) {
Option resultOption = future.result();
if (resultOption.isDefined()) {
Object result = resultOption.get();
// ...
} else {
//... whatever
}
}
//#using-ask
system.stop();
}
@Test
public void receiveTimeout() {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef myActor = system.actorOf(MyReceivedTimeoutUntypedActor.class);
myActor.tell("Hello");
system.stop();
}
@Test
public void usePoisonPill() {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef myActor = system.actorOf(MyUntypedActor.class);
//#poison-pill
myActor.tell(poisonPill());
//#poison-pill
system.stop();
}
@Test
public void useKill() {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef victim = system.actorOf(MyUntypedActor.class);
//#kill
victim.tell(kill());
//#kill
system.stop();
}
@Test
public void useBecome() {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef myActor = system.actorOf(new UntypedActorFactory() {
public UntypedActor create() {
return new HotSwapActor();
}
});
myActor.tell("foo");
myActor.tell("bar");
myActor.tell("bar");
system.stop();
}
public static class MyActor extends UntypedActor {
public MyActor(String s) {
}
public void onReceive(Object message) throws Exception {
try {
operation();
} catch (Exception e) {
getSender().tell(new akka.actor.Status.Failure(e));
throw e;
}
}
private void operation() {
}
//#lifecycle-callbacks
public void preStart() {
}
public void preRestart(Throwable reason, Option<Object> message) {
postStop();
}
public void postRestart(Throwable reason) {
preStart();
}
public void postStop() {
}
//#lifecycle-callbacks
}
public static class MyAskActor extends UntypedActor {
public void onReceive(Object message) throws Exception {
//#reply-exception
try {
String result = operation();
getSender().tell(result);
} catch (Exception e) {
getSender().tell(new akka.actor.Status.Failure(e));
throw e;
}
//#reply-exception
}
private String operation() {
return "Hi";
}
}
//#hot-swap-actor
public static class HotSwapActor extends UntypedActor {
Procedure<Object> angry = new Procedure<Object>() {
@Override
public void apply(Object message) {
if (message.equals("foo")) {
getSender().tell("I am already angry?");
} else if (message.equals("foo")) {
getContext().become(happy);
}
}
};
Procedure<Object> happy = new Procedure<Object>() {
@Override
public void apply(Object message) {
if (message.equals("bar")) {
getSender().tell("I am already happy :-)");
} else if (message.equals("foo")) {
getContext().become(angry);
}
}
};
public void onReceive(Object message) {
if (message.equals("bar")) {
getContext().become(angry);
} else if (message.equals("foo")) {
getContext().become(happy);
}
}
}
//#hot-swap-actor
}

View file

@ -1,370 +1,470 @@
.. _untyped-actors-java: .. _untyped-actors-java:
Actors (Java) ################
============= Actors (Java)
################
.. sidebar:: Contents .. sidebar:: Contents
.. contents:: :local: .. contents:: :local:
Module stability: **SOLID**
The `Actor Model <http://en.wikipedia.org/wiki/Actor_model>`_ provides a higher level of abstraction for writing concurrent and distributed systems. It alleviates the developer from having to deal with explicit locking and thread management, making it easier to write correct concurrent and parallel systems. Actors were defined in the 1973 paper by Carl Hewitt but have been popularized by the Erlang language, and used for example at Ericsson with great success to build highly concurrent and reliable telecom systems. The `Actor Model`_ provides a higher level of abstraction for writing concurrent
and distributed systems. It alleviates the developer from having to deal with
explicit locking and thread management, making it easier to write correct
concurrent and parallel systems. Actors were defined in the 1973 paper by Carl
Hewitt but have been popularized by the Erlang language, and used for example at
Ericsson with great success to build highly concurrent and reliable telecom
systems.
The API of Akkas Actors is similar to Scala Actors which has borrowed some of
its syntax from Erlang.
.. _Actor Model: http://en.wikipedia.org/wiki/Actor_model
Creating Actors
===============
Defining an Actor class Defining an Actor class
----------------------- -----------------------
Actors in Java are created either by extending the 'UntypedActor' class and implementing the 'onReceive' method. This method takes the message as a parameter. Actor in Java are implemented by extending the ``UntypedActor`` class and implementing the
:meth:`onReceive` method. This method takes the message as a parameter.
Here is an example: Here is an example:
.. includecode:: code/akka/docs/actor/MyUntypedActor.java#my-untyped-actor
Creating Actors with default constructor
----------------------------------------
.. includecode:: code/akka/docs/actor/UntypedActorTestBase.java
:include: imports,system-actorOf
The call to :meth:`actorOf` returns an instance of ``ActorRef``. This is a handle to
the ``UntypedActor`` instance which you can use to interact with the ``UntypedActor``. The
``ActorRef`` is immutable and has a one to one relationship with the Actor it
represents. The ``ActorRef`` is also serializable and network-aware. This means
that you can serialize it, send it over the wire and use it on a remote host and
it will still be representing the same Actor on the original node, across the
network.
In the above example the actor was created from the system. It is also possible
to create actors from other actors with the actor ``context``. The difference is
how the supervisor hierarchy is arranged. When using the context the current actor
will be supervisor of the created child actor. When using the system it will be
a top level actor, that is supervised by the system (internal guardian actor).
.. includecode:: code/akka/docs/actor/FirstUntypedActor.java#context-actorOf
Actors are automatically started asynchronously when created.
When you create the ``UntypedActor`` then it will automatically call the ``preStart``
callback method on the ``UntypedActor`` class. This is an excellent place to
add initialization code for the actor.
.. code-block:: java .. code-block:: java
import akka.actor.UntypedActor; @Override
import akka.event.EventHandler; public void preStart() {
... // initialization code
public class SampleUntypedActor extends UntypedActor {
public void onReceive(Object message) throws Exception {
if (message instanceof String)
EventHandler.info(this, String.format("Received String message: %s",
message));
else
throw new IllegalArgumentException("Unknown message: " + message);
}
} }
Creating Actors
^^^^^^^^^^^^^^^
Creating an Actor is done using the 'akka.actor.Actors.actorOf' factory method. This method returns a reference to the UntypedActor's ActorRef. This 'ActorRef' is an immutable serializable reference that you should use to communicate with the actor, send messages, link to it etc. This reference also functions as the context for the actor and holds run-time type information such as sender of the last message,
.. code-block:: java
ActorRef myActor = Actors.actorOf(SampleUntypedActor.class);
Normally you would want to import the 'actorOf' method like this:
.. code-block:: java
import static akka.actor.Actors.*;
ActorRef myActor = actorOf(SampleUntypedActor.class);
To avoid prefix it with 'Actors' every time you use it.
You can also create & start the actor in one statement:
.. code-block:: java
ActorRef myActor = actorOf(SampleUntypedActor.class);
The call to 'actorOf' returns an instance of 'ActorRef'. This is a handle to the 'UntypedActor' instance which you can use to interact with the Actor, like send messages to it etc. more on this shortly. The 'ActorRef' is immutable and has a one to one relationship with the Actor it represents. The 'ActorRef' is also serializable and network-aware. This means that you can serialize it, send it over the wire and use it on a remote host and it will still be representing the same Actor on the original node, across the network.
Creating Actors with non-default constructor Creating Actors with non-default constructor
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --------------------------------------------
If your UntypedActor has a constructor that takes parameters then you can't create it using 'actorOf(clazz)'. Instead you can use a variant of 'actorOf' that takes an instance of an 'UntypedActorFactory' in which you can create the Actor in any way you like. If you use this method then you to make sure that no one can get a reference to the actor instance. If they can get a reference it then they can touch state directly in bypass the whole actor dispatching mechanism and create race conditions which can lead to corrupt data. If your UntypedActor has a constructor that takes parameters then you can't create it using 'actorOf(clazz)'.
Instead you can use a variant of ``actorOf`` that takes an instance of an 'UntypedActorFactory'
in which you can create the Actor in any way you like. If you use this method then you to make sure that
no one can get a reference to the actor instance. If they can get a reference it then they can
touch state directly in bypass the whole actor dispatching mechanism and create race conditions
which can lead to corrupt data.
Here is an example: Here is an example:
.. code-block:: java .. includecode:: code/akka/docs/actor/UntypedActorTestBase.java#creating-constructor
ActorRef actor = actorOf(new UntypedActorFactory() {
public UntypedActor create() {
return new MyUntypedActor("service:name", 5);
}
});
This way of creating the Actor is also great for integrating with Dependency Injection (DI) frameworks like Guice or Spring. This way of creating the Actor is also great for integrating with Dependency Injection (DI) frameworks like Guice or Spring.
UntypedActor context Creating Actors with Props
-------------------- --------------------------
The UntypedActor base class contains almost no member fields or methods to invoke. It only has the 'onReceive(Object message)' method, which is defining the Actor's message handler, and some life-cycle callbacks that you can choose to implement: ``Props`` is a configuration object to specify additional things for the actor to
## preStart be created, such as the ``MessageDispatcher``.
## postStop
## preRestart
## postRestart
Most of the API is in the UnypedActorRef a reference for the actor. This reference is available in the 'getContext()' method in the UntypedActor (or you can use its alias, the 'context()' method, if you prefer. Here, for example, you find methods to reply to messages, send yourself messages, define timeouts, fault tolerance etc., start and stop etc. .. includecode:: code/akka/docs/actor/UntypedActorTestBase.java#creating-props
Identifying Actors
------------------
Each ActorRef has two methods: UntypedActor API
* getContext().getUuid(); ================
* getContext().getId();
The difference is that the 'uuid' is generated by the runtime, guaranteed to be unique and can't be modified. While the 'id' can be set by the user (using 'getContext().setId(...)', and defaults to Actor class name. You can retrieve Actors by both UUID and ID using the 'ActorRegistry', see the section further down for details. The :class:`UntypedActor` class defines only one abstract method, the above mentioned
:meth:`onReceive(Object message)`, which implements the behavior of the actor.
Messages and immutability In addition, it offers:
-------------------------
**IMPORTANT**: Messages can be any kind of object but have to be immutable. Akka cant enforce immutability (yet) so this has to be by convention. * :obj:`getSelf()` reference to the :class:`ActorRef` of the actor
* :obj:`getSender()` reference sender Actor of the last received message, typically used as described in :ref:`UntypedActor.Reply`
* :obj:`getContext()` exposes contextual information for the actor and the current message, such as:
Send messages * factory methods to create child actors (:meth:`actorOf`)
* system that the actor belongs to
* parent supervisor
* supervised children
* hotswap behavior stack as described in :ref:`UntypedActor.HotSwap`
The remaining visible methods are user-overridable life-cycle hooks which are
described in the following:
.. includecode:: code/akka/docs/actor/UntypedActorTestBase.java#lifecycle-callbacks
The implementations shown above are the defaults provided by the :class:`UntypedActor`
class.
Start Hook
----------
Right after starting the actor, its :meth:`preStart` method is invoked.
::
@Override
public void preStart() {
// registering with other actors
someService.tell(Register(getSelf());
}
Restart Hooks
------------- -------------
Messages are sent to an Actor through one of the 'send' methods. All actors are supervised, i.e. linked to another actor with a fault
* 'tell' means “fire-and-forget”, e.g. send a message asynchronously and return immediately. handling strategy. Actors will be restarted in case an exception is thrown while
* 'ask' sends a message asynchronously and returns a 'Future'. processing a message. This restart involves the hooks mentioned above:
In all these methods you have the option of passing along your 'ActorRef' context variable. Make it a practice of doing so because it will allow the receiver actors to be able to respond to your message, since the sender reference is sent along with the message. 1. The old actor is informed by calling :meth:`preRestart` with the exception
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.
By default it calls :meth:`postStop`.
2. The initial factory from the ``actorOf`` call is used
to produce the fresh instance.
3. The new actors :meth:`postRestart` method is invoked with the exception
which caused the restart. By default the :meth:`preStart`
is called, just as in the normal start-up case.
Fire-forget
^^^^^^^^^^^
This is the preferred way of sending messages. No blocking waiting for a message. Give best concurrency and scalability characteristics. 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. The message
that triggered the exception will not be received again. Any message
sent to an actor while it is being restarted will be queued to its mailbox as
usual.
Stop Hook
---------
After stopping an actor, its :meth:`postStop` hook is called, which may be used
e.g. for deregistering this actor from other services. This hook is guaranteed
to run after message queuing has been disabled for this actor, i.e. messages
sent to a stopped actor will be redirected to the :obj:`deadLetters` of the
:obj:`ActorSystem`.
Identifying Actors
==================
FIXME Actor Path documentation
Messages and immutability
=========================
**IMPORTANT**: Messages can be any kind of object but have to be
immutable. Akka cant enforce immutability (yet) so this has to be by
convention.
Here is an example of an immutable message:
.. includecode:: code/akka/docs/actor/ImmutableMessage.java#immutable-message
Send messages
=============
Messages are sent to an Actor through one of the following methods.
* ``tell`` means “fire-and-forget”, e.g. send a message asynchronously and return
immediately.
* ``ask`` sends a message asynchronously and returns a :class:`Future`
representing a possible reply.
Message ordering is guaranteed on a per-sender basis.
In all these methods you have the option of passing along your own ``ActorRef``.
Make it a practice of doing so because it will allow the receiver actors to be able to respond
to your message, since the sender reference is sent along with the message.
Tell: Fire-forget
-----------------
This is the preferred way of sending messages. No blocking waiting for a
message. This gives the best concurrency and scalability characteristics.
.. code-block:: java .. code-block:: java
actor.tell("Hello"); actor.tell("Hello");
Or with the sender reference passed along: Or with the sender reference passed along with the message and available to the receiving Actor
in its ``getSender: ActorRef`` member field. The target actor can use this
to reply to the original sender, by using ``getSender().tell(replyMsg)``.
.. code-block:: java .. code-block:: java
actor.tell("Hello", getContext()); actor.tell("Hello", getSelf());
If invoked from within an Actor, then the sending actor reference will be implicitly passed along with the message and available to the receiving Actor in its 'getContext().getSender();' method. He can use this to reply to the original sender or use the 'getContext().reply(message);' method. If invoked without the sender parameter the sender will be
:obj:`deadLetters` actor reference in the target actor.
If invoked from an instance that is **not** an Actor there will be no implicit sender passed along the message and you will get an 'IllegalStateException' if you call 'getContext().reply(..)'. Ask: Send-And-Receive-Future
----------------------------
Send-And-Receive-Future Using ``ask`` will send a message to the receiving Actor asynchronously and
^^^^^^^^^^^^^^^^^^^^^^^ will immediately return a :class:`Future`:
Using 'ask' will send a message to the receiving Actor asynchronously and will immediately return a 'Future'.
.. code-block:: java .. code-block:: java
Future future = actorRef.ask("Hello", getContext(), 1000); long timeoutMillis = 1000;
Future future = actorRef.ask("Hello", timeoutMillis);
The 'Future' interface looks like this: The receiving actor should reply to this message, which will complete the
future with the reply message as value; ``getSender.tell(result)``.
.. code-block:: java To complete the future with an exception you need send a Failure message to the sender.
This is not done automatically when an actor throws an exception while processing a
message.
interface Future<T> { .. includecode:: code/akka/docs/actor/UntypedActorTestBase.java#reply-exception
void await();
boolean isCompleted();
boolean isExpired();
long timeoutInNanos();
Option<T> result();
Option<Throwable> exception();
Future<T> onComplete(Procedure<Future<T>> procedure);
}
So the normal way of working with futures is something like this: If the actor does not complete the future, it will expire after the timeout period,
specified as parameter to the ``ask`` method.
.. code-block:: java See :ref:`futures-java` for more information on how to await or query a
future.
Future future = actorRef.ask("Hello", getContext(), 1000); The ``onComplete``, ``onResult``, or ``onTimeout`` methods of the ``Future`` can be
future.await(); used to register a callback to get a notification when the Future completes.
if (future.isCompleted()) { Gives you a way to avoid blocking.
Option resultOption = future.result();
if (resultOption.isDefined()) {
Object result = resultOption.get();
...
}
... // whatever
}
The 'onComplete' callback can be used to register a callback to get a notification when the Future completes. Gives you a way to avoid blocking. .. warning::
When using future callbacks, inside actors you need to carefully avoid closing over
the containing actors reference, i.e. do not call methods or access mutable state
on the enclosing actor from within the callback. This would break the actor
encapsulation and may introduce synchronization bugs and race conditions because
the callback will be scheduled concurrently to the enclosing actor. Unfortunately
there is not yet a way to detect these illegal accesses at compile time. See also:
:ref:`jmm-shared-state`
The future returned from the ``ask`` method can conveniently be passed around or
chained with further processing steps, but sometimes you just need the value,
even if that entails waiting for it (but keep in mind that waiting inside an
actor is prone to dead-locks, e.g. if obtaining the result depends on
processing another message on this actor).
.. includecode:: code/akka/docs/actor/UntypedActorTestBase.java
:include: import-future,using-ask
Forward message Forward message
^^^^^^^^^^^^^^^ ---------------
You can forward a message from one actor to another. This means that the original sender address/reference is maintained even though the message is going through a 'mediator'. This can be useful when writing actors that work as routers, load-balancers, replicators etc. You need to pass along your ActorRef context variable as well. You can forward a message from one actor to another. This means that the
original sender address/reference is maintained even though the message is going
through a 'mediator'. This can be useful when writing actors that work as
routers, load-balancers, replicators etc.
You need to pass along your context variable as well.
.. code-block:: java .. code-block:: java
getContext().forward(message, getContext()); myActor.forward(message, getContext());
Receive messages Receive messages
---------------- ================
When an actor receives a message it is passed into the 'onReceive' method, this is an abstract method on the 'UntypedActor' base class that needs to be defined. When an actor receives a message it is passed into the ``onReceive`` method, this is
an abstract method on the ``UntypedActor`` base class that needs to be defined.
Here is an example: Here is an example:
.. code-block:: java .. includecode:: code/akka/docs/actor/MyUntypedActor.java#my-untyped-actor
public class SampleUntypedActor extends UntypedActor { An alternative to using if-instanceof checks is to use `Apache Commons MethodUtils
<http://commons.apache.org/beanutils/api/org/apache/commons/beanutils/MethodUtils.html#invokeMethod(java.lang.Object,%20java.lang.String,%20java.lang.Object)>`_
to invoke a named method whose parameter type matches the message type.
public void onReceive(Object message) throws Exception { .. _UntypedActor.Reply:
if (message instanceof String)
EventHandler.info(this, String.format("Received String message: %s", message));
else
throw new IllegalArgumentException("Unknown message: " + message);
}
}
Reply to messages Reply to messages
----------------- =================
Reply using the channel If you want to have a handle for replying to a message, you can use
^^^^^^^^^^^^^^^^^^^^^^^ ``getSender()``, which gives you an ActorRef. You can reply by sending to
that ActorRef with ``getSender().tell(replyMsg)``. You can also store the ActorRef
If you want to have a handle to an object to whom you can reply to the message, you can use the Channel abstraction. for replying later, or passing on to other actors. If there is no sender (a
Simply call getContext().channel() and then you can forward that to others, store it away or otherwise until you want to reply, message was sent without an actor or future context) then the sender
which you do by Channel.tell(msg) defaults to a 'dead-letter' actor ref.
.. code-block:: java .. code-block:: java
public void onReceive(Object message) throws Exception { public void onReceive(Object request) {
if (message instanceof String) { String result = process(request);
String msg = (String)message; getSender().tell(result); // will have dead-letter actor as default
if (msg.equals("Hello")) {
// Reply to original sender of message using the channel
getContext().channel().tell(msg + " from " + getContext().getUuid());
}
}
} }
We recommend that you as first choice use the channel abstraction instead of the other ways described in the following sections. Initial receive timeout
=======================
Reply using the 'tryReply' and 'reply' methods A timeout mechanism can be used to receive a message when no initial message is
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ received within a certain time. To receive this timeout you have to set the
``receiveTimeout`` property and declare handing for the ReceiveTimeout
message.
If you want to send a message back to the original sender of the message you just received then you can use the 'getContext().reply(..)' method. .. includecode:: code/akka/docs/actor/MyReceivedTimeoutUntypedActor.java#receive-timeout
.. code-block:: java
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
String msg = (String)message;
if (msg.equals("Hello")) {
// Reply to original sender of message using the 'reply' method
getContext().reply(msg + " from " + getContext().getUuid());
}
}
}
In this case we will a reply back to the Actor that sent the message.
The 'reply' method throws an 'IllegalStateException' if unable to determine what to reply to, e.g. the sender has not been passed along with the message when invoking one of 'send*' methods. You can also use the more forgiving 'tryReply' method which returns 'true' if reply was sent, and 'false' if unable to determine what to reply to.
.. code-block:: java
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
String msg = (String)message;
if (msg.equals("Hello")) {
// Reply to original sender of message using the 'reply' method
if (getContext().tryReply(msg + " from " + getContext().getUuid())) ... // success
else ... // handle failure
}
}
}
Summary of reply semantics and options
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* ``getContext().reply(...)`` can be used to reply to an ``Actor`` or a
``Future`` from within an actor; the current actor will be passed as reply
channel if the current channel supports this.
* ``getContext().channel`` is a reference providing an abstraction for the
reply channel; this reference may be passed to other actors or used by
non-actor code.
.. note::
There used to be two methods for determining the sending Actor or Future for the current invocation:
* ``getContext().getSender()`` yielded a :class:`Option[ActorRef]`
* ``getContext().getSenderFuture()`` yielded a :class:`Option[CompletableFuture[Any]]`
These two concepts have been unified into the ``channel``. If you need to
know the nature of the channel, you may do so using instance tests::
if (getContext().channel() instanceof ActorRef) {
...
} else if (getContext().channel() instanceof ActorPromise) {
...
}
Promise represents the write-side of a Future, enabled by the methods
* completeWithResult(..)
* completeWithException(..)
Starting actors
---------------
Actors are started when they are created by invoking the actorOf method.
.. code-block:: java
ActorRef actor = actorOf(SampleUntypedActor.class);
When you create the actor then it will automatically call the 'preStart' callback method on the 'UntypedActor'. This is an excellent place to add initialization code for the actor.
.. code-block:: java
@Override
void preStart() {
... // initialization code
}
Stopping actors Stopping actors
--------------- ===============
Actors are stopped by invoking the stop method. Actors are stopped by invoking the ``stop`` method of the ``ActorRef``.
The actual termination of the actor is performed asynchronously, i.e.
``stop`` may return before the actor is stopped.
.. code-block:: java .. code-block:: java
actor.stop(); actor.stop();
When stop is called then a call to the postStop callback method will take place. The Actor can use this callback to implement shutdown behavior. Processing of the current message, if any, will continue before the actor is stopped,
but additional messages in the mailbox will not be processed. By default these
messages are sent to the :obj:`deadLetters` of the :obj:`ActorSystem`, but that
depends on the mailbox implementation.
When stop is called then a call to the ``def postStop`` callback method will
take place. The ``Actor`` can use this callback to implement shutdown behavior.
.. code-block:: java .. code-block:: java
@Override public void postStop() {
void postStop() {
... // clean up resources ... // clean up resources
} }
You can shut down all Actors in the system by invoking:
.. code-block:: java All Actors are stopped when the ``ActorSystem`` is stopped.
Supervised actors are stopped when the supervisor is stopped, i.e. children are stopped
when parent is stopped.
Actors.registry().shutdownAll();
PoisonPill PoisonPill
---------- ----------
You can also send an actor the akka.actor.PoisonPill message, which will stop the actor when the message is processed. You can also send an actor the ``akka.actor.PoisonPill`` message, which will
If the sender is a Future, the Future will be completed with an akka.actor.ActorKilledException("PoisonPill") stop the actor when the message is processed. ``PoisonPill`` is enqueued as
ordinary messages and will be handled after messages that were already queued
in the mailbox.
If the ``PoisonPill`` was sent with ``ask``, the ``Future`` will be completed with an
``akka.actor.ActorKilledException("PoisonPill")``.
Use it like this: Use it like this:
.. includecode:: code/akka/docs/actor/UntypedActorTestBase.java
:include: import-actors,poison-pill
.. _UntypedActor.HotSwap:
HotSwap
=======
Upgrade
-------
Akka supports hotswapping the Actors message loop (e.g. its implementation) at
runtime. Use the ``getContext().become`` method from within the Actor.
The hotswapped code is kept in a Stack which can be pushed and popped.
.. warning::
Please note that the actor will revert to its original behavior when restarted by its Supervisor.
To hotswap the Actor using ``getContext().become``:
.. includecode:: code/akka/docs/actor/UntypedActorTestBase.java
:include: import-procedure,hot-swap-actor
The ``become`` method is useful for many different things, such as to implement
a Finite State Machine (FSM).
Here is another little cute example of ``become`` and ``unbecome`` in action:
.. includecode:: code/akka/docs/actor/UntypedActorSwapper.java#swapper
Downgrade
---------
Since the hotswapped code is pushed to a Stack you can downgrade the code as
well. Use the ``getContext().unbecome`` method from within the Actor.
.. code-block:: java .. code-block:: java
import static akka.actor.Actors.*; public void onReceive(Object message) {
if (message.equals("revert")) getContext().unbecome();
actor.tell(poisonPill()); }
Killing an Actor Killing an Actor
---------------- ================
You can kill an actor by sending a 'new Kill()' message. This will restart the actor through regular supervisor semantics. You can kill an actor by sending a ``Kill`` message. This will restart the actor
through regular supervisor semantics.
Use it like this: Use it like this:
.. code-block:: java .. includecode:: code/akka/docs/actor/UntypedActorTestBase.java
:include: import-actors,kill
import static akka.actor.Actors.*; Actors and exceptions
=====================
// kill the actor called 'victim' It can happen that while a message is being processed by an actor, that some
victim.tell(kill()); kind of exception is thrown, e.g. a database exception.
Actor life-cycle What happens to the Message
---------------- ---------------------------
The actor has a well-defined non-circular life-cycle. If an exception is thrown while a message is being processed (so taken of his
mailbox and handed over the the receive), then this message will be lost. It is
important to understand that it is not put back on the mailbox. So if you want
to retry processing of a message, you need to deal with it yourself by catching
the exception and retry your flow. Make sure that you put a bound on the number
of retries since you don't want a system to livelock (so consuming a lot of cpu
cycles without making progress).
:: What happens to the mailbox
---------------------------
NEW (newly created actor) - can't receive messages (yet) If an exception is thrown while a message is being processed, nothing happens to
=> STARTED (when 'start' is invoked) - can receive messages the mailbox. If the actor is restarted, the same mailbox will be there. So all
=> SHUT DOWN (when 'exit' or 'stop' is invoked) - can't do anything messages on that mailbox, will be there as well.
What happens to the actor
-------------------------
If an exception is thrown, the actor instance is discarded and a new instance is
created. This new instance will now be used in the actor references to this actor
(so this is done invisible to the developer). Note that this means that current
state of the failing actor instance is lost if you don't store and restore it in
``preRestart`` and ``postRestart`` callbacks.

View file

@ -50,8 +50,8 @@ be able to handle unknown messages then you need to have a default case as in
the example above. Otherwise an ``UnhandledMessageException`` will be the example above. Otherwise an ``UnhandledMessageException`` will be
thrown and the actor is restarted when an unknown message is received. thrown and the actor is restarted when an unknown message is received.
Creating Actors Creating Actors with default constructor
--------------- ----------------------------------------
.. includecode:: code/ActorDocSpec.scala .. includecode:: code/ActorDocSpec.scala
:include: imports2,system-actorOf :include: imports2,system-actorOf
@ -73,6 +73,15 @@ a top level actor, that is supervised by the system (internal guardian actor).
.. includecode:: code/ActorDocSpec.scala#context-actorOf .. includecode:: code/ActorDocSpec.scala#context-actorOf
Actors are automatically started asynchronously when created. Actors are automatically started asynchronously when created.
When you create the ``Actor`` then it will automatically call the ``preStart``
callback method on the ``Actor`` trait. This is an excellent place to
add initialization code for the actor.
.. code-block:: scala
override def preStart() = {
... // initialization code
}
Creating Actors with non-default constructor Creating Actors with non-default constructor
-------------------------------------------- --------------------------------------------
@ -110,6 +119,7 @@ When spawning actors for specific sub-tasks from within an actor, it may be conv
introduce synchronization bugs and race conditions because the other actors introduce synchronization bugs and race conditions because the other actors
code will be scheduled concurrently to the enclosing actor. Unfortunately code will be scheduled concurrently to the enclosing actor. Unfortunately
there is not yet a way to detect these illegal accesses at compile time. there is not yet a way to detect these illegal accesses at compile time.
See also: :ref:`jmm-shared-state`
Actor API Actor API
@ -127,7 +137,7 @@ In addition, it offers:
* :obj:`sender` reference sender Actor of the last received message, typically used as described in :ref:`Actor.Reply` * :obj:`sender` reference sender Actor of the last received message, typically used as described in :ref:`Actor.Reply`
* :obj:`context` exposes contextual information for the actor and the current message, such as: * :obj:`context` exposes contextual information for the actor and the current message, such as:
* factory method to create child actors (:meth:`actorOf`) * factory methods to create child actors (:meth:`actorOf`)
* system that the actor belongs to * system that the actor belongs to
* parent supervisor * parent supervisor
* supervised children * supervised children
@ -242,8 +252,8 @@ Messages are sent to an Actor through one of the following methods.
Message ordering is guaranteed on a per-sender basis. Message ordering is guaranteed on a per-sender basis.
Fire-forget Tell: Fire-forget
----------- -----------------
This is the preferred way of sending messages. No blocking waiting for a This is the preferred way of sending messages. No blocking waiting for a
message. This gives the best concurrency and scalability characteristics. message. This gives the best concurrency and scalability characteristics.
@ -260,11 +270,11 @@ to reply to the original sender, by using ``sender ! replyMsg``.
If invoked from an instance that is **not** an Actor the sender will be If invoked from an instance that is **not** an Actor the sender will be
:obj:`deadLetters` actor reference by default. :obj:`deadLetters` actor reference by default.
Send-And-Receive-Future Ask: Send-And-Receive-Future
----------------------- ----------------------------
Using ``?`` will send a message to the receiving Actor asynchronously and Using ``?`` will send a message to the receiving Actor asynchronously and
will return a :class:`Future`: will immediately return a :class:`Future`:
.. code-block:: scala .. code-block:: scala
@ -277,15 +287,7 @@ To complete the future with an exception you need send a Failure message to the
This is not done automatically when an actor throws an exception while processing a This is not done automatically when an actor throws an exception while processing a
message. message.
.. code-block:: scala .. includecode:: code/ActorDocSpec.scala#reply-exception
try {
operation()
} catch {
case e: Exception =>
sender ! akka.actor.Status.Failure(e)
throw e
}
If the actor does not complete the future, it will expire after the timeout period, If the actor does not complete the future, it will expire after the timeout period,
which is taken from one of the following locations in order of precedence: which is taken from one of the following locations in order of precedence:
@ -304,18 +306,19 @@ which is taken from one of the following locations in order of precedence:
See :ref:`futures-scala` for more information on how to await or query a See :ref:`futures-scala` for more information on how to await or query a
future. future.
The ``onComplete``, ``onResult``, or ``onTimeout`` methods of the ``Future`` can be
used to register a callback to get a notification when the Future completes.
Gives you a way to avoid blocking.
.. warning:: .. warning::
When using future callbacks, such as ``onComplete``, ``onResult``, and ``onTimeout``, When using future callbacks, inside actors you need to carefully avoid closing over
inside actors you need to carefully avoid closing over the containing actors the containing actors reference, i.e. do not call methods or access mutable state
reference, i.e. do not call methods or access mutable state on the enclosing actor on the enclosing actor from within the callback. This would break the actor
from within the callback. This would break the actor encapsulation and may encapsulation and may introduce synchronization bugs and race conditions because
introduce synchronization bugs and race conditions because the callback the callback will be scheduled concurrently to the enclosing actor. Unfortunately
will be scheduled concurrently to the enclosing actor. Unfortunately
there is not yet a way to detect these illegal accesses at compile time. there is not yet a way to detect these illegal accesses at compile time.
See also: :ref:`jmm-shared-state`
Send-And-Receive-Eventually
---------------------------
The future returned from the ``?`` method can conveniently be passed around or The future returned from the ``?`` method can conveniently be passed around or
chained with further processing steps, but sometimes you just need the value, chained with further processing steps, but sometimes you just need the value,
@ -344,7 +347,7 @@ routers, load-balancers, replicators etc.
.. code-block:: scala .. code-block:: scala
actor.forward(message) myActor.forward(message)
Receive messages Receive messages
@ -375,7 +378,7 @@ Reply to messages
If you want to have a handle for replying to a message, you can use If you want to have a handle for replying to a message, you can use
``sender``, which gives you an ActorRef. You can reply by sending to ``sender``, which gives you an ActorRef. You can reply by sending to
that ActorRef with ``sender ! Message``. You can also store the ActorRef that ActorRef with ``sender ! replyMsg``. You can also store the ActorRef
for replying later, or passing on to other actors. If there is no sender (a for replying later, or passing on to other actors. If there is no sender (a
message was sent without an actor or future context) then the sender message was sent without an actor or future context) then the sender
defaults to a 'dead-letter' actor ref. defaults to a 'dead-letter' actor ref.
@ -383,8 +386,8 @@ defaults to a 'dead-letter' actor ref.
.. code-block:: scala .. code-block:: scala
case request => case request =>
val result = process(request) val result = process(request)
sender ! result // will have dead-letter actor as default sender ! result // will have dead-letter actor as default
Initial receive timeout Initial receive timeout
======================= =======================
@ -396,26 +399,6 @@ object.
.. includecode:: code/ActorDocSpec.scala#receive-timeout .. includecode:: code/ActorDocSpec.scala#receive-timeout
Starting actors
===============
Actors are created & started by invoking the ``actorOf`` method.
.. code-block:: scala
val actor = actorOf[MyActor]
actor
When you create the ``Actor`` then it will automatically call the ``def
preStart`` callback method on the ``Actor`` trait. This is an excellent place to
add initialization code for the actor.
.. code-block:: scala
override def preStart() = {
... // initialization code
}
Stopping actors Stopping actors
=============== ===============
@ -442,17 +425,20 @@ take place. The ``Actor`` can use this callback to implement shutdown behavior.
... // clean up resources ... // clean up resources
} }
All Actors are stopped when the ``ActorSystem`` is stopped.
Supervised actors are stopped when the supervisor is stopped, i.e. children are stopped
when parent is stopped.
PoisonPill PoisonPill
========== ----------
You can also send an actor the ``akka.actor.PoisonPill`` message, which will You can also send an actor the ``akka.actor.PoisonPill`` message, which will
stop the actor when the message is processed. ``PoisonPill`` is enqueued as stop the actor when the message is processed. ``PoisonPill`` is enqueued as
ordinary messages and will be handled after messages that were already queued ordinary messages and will be handled after messages that were already queued
in the mailbox. in the mailbox.
If the sender is a ``Future`` (e.g. the message is sent with ``?``), the If the ``PoisonPill`` was sent with ``?``, the ``Future`` will be completed with an
``Future`` will be completed with an
``akka.actor.ActorKilledException("PoisonPill")``. ``akka.actor.ActorKilledException("PoisonPill")``.
@ -465,7 +451,7 @@ Upgrade
------- -------
Akka supports hotswapping the Actors message loop (e.g. its implementation) at Akka supports hotswapping the Actors message loop (e.g. its implementation) at
runtime: Invoke the ``become`` method from within the Actor. runtime: Invoke the ``context.become`` method from within the Actor.
Become takes a ``PartialFunction[Any, Unit]`` that implements Become takes a ``PartialFunction[Any, Unit]`` that implements
the new message handler. The hotswapped code is kept in a Stack which can be the new message handler. The hotswapped code is kept in a Stack which can be
@ -499,7 +485,7 @@ Downgrade
--------- ---------
Since the hotswapped code is pushed to a Stack you can downgrade the code as Since the hotswapped code is pushed to a Stack you can downgrade the code as
well, all you need to do is to: Invoke the ``unbecome`` method from within the Actor. well, all you need to do is to: Invoke the ``context.unbecome`` method from within the Actor.
This will pop the Stack and replace the Actor's implementation with the This will pop the Stack and replace the Actor's implementation with the
``PartialFunction[Any, Unit]`` that is at the top of the Stack. ``PartialFunction[Any, Unit]`` that is at the top of the Stack.
@ -509,7 +495,7 @@ Here's how you use the ``unbecome`` method:
.. code-block:: scala .. code-block:: scala
def receive = { def receive = {
case "revert" => unbecome() case "revert" => context.unbecome()
} }

View file

@ -24,7 +24,7 @@ class MyActor extends Actor {
} }
//#my-actor //#my-actor
case class DoIt(msg: Message) case class DoIt(msg: ImmutableMessage)
case class Message(s: String) case class Message(s: String)
//#context-actorOf //#context-actorOf
@ -41,7 +41,7 @@ class FirstActor extends Actor {
sender ! replyMsg sender ! replyMsg
self.stop() self.stop()
} }
def doSomeDangerousWork(msg: Message): String = { "done" } def doSomeDangerousWork(msg: ImmutableMessage): String = { "done" }
}) ! m }) ! m
case replyMsg: String sender ! replyMsg case replyMsg: String sender ! replyMsg
@ -52,9 +52,29 @@ class FirstActor extends Actor {
//#system-actorOf //#system-actorOf
object Main extends App { object Main extends App {
val system = ActorSystem("MySystem") val system = ActorSystem("MySystem")
val myActor = system.actorOf[FirstActor] val myActor = system.actorOf[MyActor]
//#system-actorOf //#system-actorOf
} }
class ReplyException extends Actor {
def receive = {
case _
//#reply-exception
try {
val result = operation()
sender ! result
} catch {
case e: Exception
sender ! akka.actor.Status.Failure(e)
throw e
}
//#reply-exception
}
def operation(): String = { "Hi" }
}
//#swapper //#swapper
case object Swap case object Swap
class Swapper extends Actor { class Swapper extends Actor {
@ -167,7 +187,7 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
"creating actor with Props" in { "creating actor with Props" in {
//#creating-props //#creating-props
import akka.actor.Props import akka.actor.Props
val dispatcher = system.dispatcherFactory.fromConfig("my-dispatcher") val dispatcher = system.dispatcherFactory.newFromConfig("my-dispatcher")
val myActor = system.actorOf(Props[MyActor].withDispatcher(dispatcher), name = "myactor") val myActor = system.actorOf(Props[MyActor].withDispatcher(dispatcher), name = "myactor")
//#creating-props //#creating-props
@ -230,6 +250,9 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
} }
} }
//#hot-swap-actor //#hot-swap-actor
val actor = system.actorOf(new HotSwapActor)
} }
} }

View file

@ -47,7 +47,7 @@ class Remote(val system: ActorSystemImpl, val nodename: String) {
val remoteDaemonServiceName = "akka-system-remote-daemon".intern val remoteDaemonServiceName = "akka-system-remote-daemon".intern
val computeGridDispatcher = dispatcherFactory.fromConfig("akka.remote.compute-grid-dispatcher") val computeGridDispatcher = dispatcherFactory.newFromConfig("akka.remote.compute-grid-dispatcher")
// FIXME it is probably better to create another supervisor for handling the children created by handle_*, ticket #1408 // FIXME it is probably better to create another supervisor for handling the children created by handle_*, ticket #1408
private[remote] lazy val remoteDaemonSupervisor = system.actorOf(Props( private[remote] lazy val remoteDaemonSupervisor = system.actorOf(Props(