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:
parent
b4f486667f
commit
ce128740ab
17 changed files with 848 additions and 327 deletions
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
17
akka-docs/java/code/akka/docs/actor/FirstUntypedActor.java
Normal file
17
akka-docs/java/code/akka/docs/actor/FirstUntypedActor.java
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
25
akka-docs/java/code/akka/docs/actor/ImmutableMessage.java
Normal file
25
akka-docs/java/code/akka/docs/actor/ImmutableMessage.java
Normal 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
|
||||||
|
|
@ -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
|
||||||
20
akka-docs/java/code/akka/docs/actor/MyUntypedActor.java
Normal file
20
akka-docs/java/code/akka/docs/actor/MyUntypedActor.java
Normal 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
|
||||||
|
|
||||||
53
akka-docs/java/code/akka/docs/actor/UntypedActorSwapper.java
Normal file
53
akka-docs/java/code/akka/docs/actor/UntypedActorSwapper.java
Normal 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
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package akka.docs.actor
|
||||||
|
|
||||||
|
import org.scalatest.junit.JUnitSuite
|
||||||
|
|
||||||
|
class UntypedActorTest extends UntypedActorTestBase with JUnitSuite
|
||||||
239
akka-docs/java/code/akka/docs/actor/UntypedActorTestBase.java
Normal file
239
akka-docs/java/code/akka/docs/actor/UntypedActorTestBase.java
Normal 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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 Akka’s 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 can’t 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 actor’s :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 can’t 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 actor’s 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 Actor’s 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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 actor’s
|
introduce synchronization bugs and race conditions because the other actor’s
|
||||||
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 actor’s
|
the containing actor’s 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 Actor’s message loop (e.g. its implementation) at
|
Akka supports hotswapping the Actor’s 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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue