Merge pull request #1277 from akka/wip-3074-deprecate-actorFor-patriknw

Deprecate actorFor in favor of ActorSelection, see #3074
This commit is contained in:
Patrik Nordwall 2013-04-08 11:48:48 -07:00
commit c77cdeb86b
80 changed files with 1496 additions and 619 deletions

View file

@ -95,14 +95,14 @@ What is the Difference Between Actor Reference and Path?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
An actor reference designates a single actor and the life-cycle of the reference
matches that actors life-cycle; an actor path represents a name which may or
may not be inhabited by an actor and the path itself does not have a life-cycle,
it never becomes invalid. You can create an actor path without creating an actor,
but you cannot create an actor reference without creating corresponding actor.
matches that actors life-cycle; an actor path represents a name which may or
may not be inhabited by an actor and the path itself does not have a life-cycle,
it never becomes invalid. You can create an actor path without creating an actor,
but you cannot create an actor reference without creating corresponding actor.
.. note::
That definition does not hold for ``actorFor``, which is one of the reasons why
That definition does not hold for ``actorFor``, which is one of the reasons why
``actorFor`` is deprecated in favor of ``actorSelection``.
You can create an actor, terminate it, and then create a new actor with the same
@ -194,34 +194,43 @@ Looking up Actors by Concrete Path
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In addition, actor references may be looked up using the
:meth:`ActorSystem.actorFor` method, which returns a local or remote actor
reference. The reference can be reused for communicating with said actor during
the whole lifetime of the actor. In the case of a local actor reference, the
named actor needs to exist before the lookup, or else the acquired reference
will be an :class:`EmptyLocalActorRef`. This will be true even if an actor with
that exact path is created after acquiring the actor reference. For remote actor
references acquired with `actorFor` the behaviour is different and sending messages
to such a reference will under the hood look up the actor by path on the remote
system for every message send.
:meth:`ActorSystem.actorSelection` method. The selection can be used for
communicating with said actor and the actor corresponding to the selection
is looked up when delivering each message.
To acquire an :class:`ActorRef` that is bound to the life-cycle of a specific actor
you need to send a message, such as the built-in :class:`Identify` message, to the actor
and use the ``sender`` reference of a reply from the actor.
.. note::
``actorFor`` is deprecated in favor of ``actorSelection`` because actor references
acquired with ``actorFor`` behave differently for local and remote actors.
In the case of a local actor reference, the named actor needs to exist before the
lookup, or else the acquired reference will be an :class:`EmptyLocalActorRef`.
This will be true even if an actor with that exact path is created after acquiring
the actor reference. For remote actor references acquired with `actorFor` the
behaviour is different and sending messages to such a reference will under the hood
look up the actor by path on the remote system for every message send.
Absolute vs. Relative Paths
```````````````````````````
In addition to :meth:`ActorSystem.actorFor` there is also
:meth:`ActorContext.actorFor`, which is available inside any actor as
``context.actorFor``. This yields an actor reference much like its twin on
In addition to :meth:`ActorSystem.actorSelection` there is also
:meth:`ActorContext.actorSelection`, which is available inside any actor as
``context.actorSelection``. This yields an actor selection much like its twin on
:class:`ActorSystem`, but instead of looking up the path starting from the root
of the actor tree it starts out on the current actor. Path elements consisting
of two dots (``".."``) may be used to access the parent actor. You can for
example send a message to a specific sibling::
context.actorFor("../brother") ! msg
context.actorSelection("../brother") ! msg
Absolute paths may of course also be looked up on `context` in the usual way, i.e.
.. code-block:: scala
context.actorFor("/user/serviceA") ! msg
context.actorSelection("/user/serviceA") ! msg
will work as expected.
@ -249,10 +258,10 @@ extracting the sender references, and then watch all discovered concrete
actors. This scheme of resolving a selection may be improved upon in a future
release.
.. _actorOf-vs-actorFor:
.. _actorOf-vs-actorSelection:
Summary: ``actorOf`` vs. ``actorFor``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Summary: ``actorOf`` vs. ``actorSelection`` vs. ``actorFor``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. note::
@ -263,8 +272,12 @@ Summary: ``actorOf`` vs. ``actorFor``
child of the context on which this method is invoked (which may be any
actor or actor system).
- ``actorFor`` only ever looks up an existing actor, i.e. does not create
one.
- ``actorSelection`` only ever looks up existing actors when messages are
delivered, i.e. does not create actors, or verify existence of actors
when the selection is created.
- ``actorFor`` (deprecated in favor of actorSelection) only ever looks up an
existing actor, i.e. does not create one.
Actor Reference and Path Equality
---------------------------------
@ -273,13 +286,13 @@ Equality of ``ActorRef`` match the intention that an ``ActorRef`` corresponds to
the target actor incarnation. Two actor references are compared equal when they have
the same path and point to the same actor incarnation. A reference pointing to a
terminated actor does not compare equal to a reference pointing to another (re-created)
actor with the same path. Note that a restart of an actor caused by a failure still
means that it is the same actor incarnation, i.e. a restart is not visible for the
actor with the same path. Note that a restart of an actor caused by a failure still
means that it is the same actor incarnation, i.e. a restart is not visible for the
consumer of the ``ActorRef``.
Remote actor references acquired with ``actorFor`` do not include the full
information about the underlying actor identity and therefore such references
do not compare equal to references acquired with ``actorOf``, ``sender``,
information about the underlying actor identity and therefore such references
do not compare equal to references acquired with ``actorOf``, ``sender``,
or ``context.self``. Because of this ``actorFor`` is deprecated in favor of
``actorSelection``.
@ -297,7 +310,7 @@ While it is possible to create an actor at a later time with an identical
path—simply due to it being impossible to enforce the opposite without keeping
the set of all actors ever created available—this is not good practice: remote
actor references acquired with ``actorFor`` which “died” suddenly start to work
again, but without any guarantee of ordering between this transition and any
again, but without any guarantee of ordering between this transition and any
other event, hence the new inhabitant of the path may receive messages which were destined for the
previous tenant.

View file

@ -176,12 +176,13 @@ public class TypedActorDocTestBase {
@Test public void proxyAnyActorRef() {
try {
final ActorRef actorRefToRemoteActor = system.deadLetters();
//#typed-actor-remote
Squarer typedActor =
TypedActor.get(system).
typedActorOf(
new TypedProps<Squarer>(Squarer.class),
system.actorFor("akka://SomeSystem@somehost:2552/user/some/foobar")
actorRefToRemoteActor
);
//Use "typedActor" as a FooBar
//#typed-actor-remote

View file

@ -15,6 +15,7 @@ import akka.dispatch.Futures;
import akka.dispatch.Mapper;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;
import akka.testkit.AkkaSpec;
import akka.util.Timeout;
//#import-future
@ -31,6 +32,12 @@ import akka.japi.Procedure;
import akka.actor.Terminated;
//#import-watch
//#import-identify
import akka.actor.ActorSelection;
import akka.actor.Identify;
import akka.actor.ActorIdentity;
//#import-identify
//#import-gracefulStop
import static akka.pattern.Patterns.gracefulStop;
import scala.concurrent.Future;
@ -58,6 +65,8 @@ import akka.actor.UntypedActor;
import akka.actor.UntypedActorFactory;
import org.junit.Test;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import scala.Option;
import java.lang.Object;
import java.util.Iterator;
@ -65,6 +74,19 @@ import akka.pattern.Patterns;
public class UntypedActorDocTestBase {
private static ActorSystem system;
@BeforeClass
public static void beforeAll() {
system = ActorSystem.create("MySystem", AkkaSpec.testConf());
}
@AfterClass
public static void afterAll() {
system.shutdown();
system = null;
}
@Test
public void createProps() {
//#creating-props-config
@ -96,86 +118,71 @@ public class UntypedActorDocTestBase {
@Test
public void contextActorOf() {
//#context-actorOf
ActorSystem system = ActorSystem.create("MySystem");
ActorRef myActor = system.actorOf(new Props(MyUntypedActor.class), "myactor");
ActorRef myActor = system.actorOf(new Props(MyUntypedActor.class), "myactor2");
//#context-actorOf
myActor.tell("test", null);
system.shutdown();
}
@Test
public void constructorActorOf() {
ActorSystem system = ActorSystem.create("MySystem");
//#creating-constructor
// allows passing in arguments to the MyActor constructor
ActorRef myActor = system.actorOf(new Props(new UntypedActorFactory() {
public UntypedActor create() {
return new MyActor("...");
}
}), "myactor");
}), "myactor3");
//#creating-constructor
myActor.tell("test", null);
system.shutdown();
}
@Test
public void propsActorOf() {
ActorSystem system = ActorSystem.create("MySystem");
//#creating-props
ActorRef myActor = system.actorOf(
new Props(MyUntypedActor.class).withDispatcher("my-dispatcher"), "myactor");
new Props(MyUntypedActor.class).withDispatcher("my-dispatcher"), "myactor4");
//#creating-props
myActor.tell("test", null);
system.shutdown();
}
@Test
public void usingAsk() throws Exception {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef myActor = system.actorOf(new Props(new UntypedActorFactory() {
public UntypedActor create() {
return new MyAskActor();
}
}), "myactor");
}), "myactor5");
//#using-ask
Future<Object> future = Patterns.ask(myActor, "Hello", 1000);
Object result = Await.result(future, Duration.create(1, TimeUnit.SECONDS));
//#using-ask
system.shutdown();
}
@Test
public void receiveTimeout() {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef myActor = system.actorOf(new Props(MyReceivedTimeoutUntypedActor.class));
myActor.tell("Hello", null);
system.shutdown();
}
@Test
public void usePoisonPill() {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef myActor = system.actorOf(new Props(MyUntypedActor.class));
//#poison-pill
myActor.tell(PoisonPill.getInstance(), null);
//#poison-pill
system.shutdown();
}
@Test
public void useKill() {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef victim = system.actorOf(new Props(MyUntypedActor.class));
//#kill
victim.tell(Kill.getInstance(), null);
//#kill
system.shutdown();
}
@Test
public void useBecome() {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef myActor = system.actorOf(new Props(new UntypedActorFactory() {
public UntypedActor create() {
return new HotSwapActor();
@ -184,21 +191,24 @@ public class UntypedActorDocTestBase {
myActor.tell("foo", null);
myActor.tell("bar", null);
myActor.tell("bar", null);
system.shutdown();
}
@Test
public void useWatch() throws Exception {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef myActor = system.actorOf(new Props(WatchActor.class));
Future<Object> future = Patterns.ask(myActor, "kill", 1000);
assert Await.result(future, Duration.create("1 second")).equals("finished");
system.shutdown();
}
@Test
public void useIdentify() throws Exception {
ActorRef a = system.actorOf(new Props(MyUntypedActor.class), "another");
ActorRef b = system.actorOf(new Props(Follower.class));
system.stop(a);
}
@Test
public void usePatternsGracefulStop() throws Exception {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef actorRef = system.actorOf(new Props(MyUntypedActor.class));
//#gracefulStop
try {
@ -210,7 +220,6 @@ public class UntypedActorDocTestBase {
// the actor wasn't stopped within 5 seconds
}
//#gracefulStop
system.shutdown();
}
class Result {
@ -225,7 +234,6 @@ public class UntypedActorDocTestBase {
@Test
public void usePatternsAskPipe() {
ActorSystem system = ActorSystem.create("MySystem");
ActorRef actorA = system.actorOf(new Props(MyUntypedActor.class));
ActorRef actorB = system.actorOf(new Props(MyUntypedActor.class));
ActorRef actorC = system.actorOf(new Props(MyUntypedActor.class));
@ -251,7 +259,6 @@ public class UntypedActorDocTestBase {
pipe(transformed, system.dispatcher()).to(actorC);
//#ask-pipe
system.shutdown();
}
public static class MyActor extends UntypedActor {
@ -399,4 +406,40 @@ public class UntypedActorDocTestBase {
}
//#watch
static
//#identify
public class Follower extends UntypedActor {
String identifyId = "1";
{
ActorSelection selection =
getContext().actorSelection("/user/another");
selection.tell(new Identify(identifyId), getSelf());
}
ActorRef another;
@Override
public void onReceive(Object message) {
if (message instanceof ActorIdentity) {
ActorIdentity identity = (ActorIdentity) message;
if (identity.correlationId().equals(identifyId)) {
ActorRef ref = identity.getRef();
if (ref == null)
getContext().stop(getSelf());
else {
another = ref;
getContext().watch(another);
}
}
} else if (message instanceof Terminated) {
final Terminated t = (Terminated) message;
if (t.getActor().equals(another)) {
getContext().stop(getSelf());
}
} else {
unhandled(message);
}
}
}
//#identify
}

View file

@ -49,10 +49,10 @@ public class SerializationDocTestBase {
//#my-own-serializer
@Test public void serializeActorRefs() {
final ActorSystem theActorSystem =
final ExtendedActorSystem extendedSystem = (ExtendedActorSystem)
ActorSystem.create("whatever");
final ActorRef theActorRef =
theActorSystem.deadLetters(); // Of course this should be you
extendedSystem.deadLetters(); // Of course this should be you
//#actorref-serializer
// Serialize
@ -63,10 +63,10 @@ public class SerializationDocTestBase {
// Deserialize
// (beneath fromBinary)
final ActorRef deserializedActorRef = theActorSystem.actorFor(identifier);
final ActorRef deserializedActorRef = extendedSystem.provider().resolveActorRef(identifier);
// Then just use the ActorRef
//#actorref-serializer
theActorSystem.shutdown();
extendedSystem.shutdown();
}
static
@ -150,7 +150,7 @@ public class SerializationDocTestBase {
return new DefaultAddressExt(system);
}
}
//#external-address-default
public void demonstrateDefaultAddress() {

View file

@ -16,6 +16,7 @@ import akka.actor.ActorKilledException;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Kill;
import akka.actor.PoisonPill;
import akka.actor.Props;
import akka.actor.Terminated;
import akka.actor.UntypedActor;
@ -30,7 +31,7 @@ import akka.testkit.JavaTestKit;
import scala.concurrent.duration.Duration;
public class TestKitDocTest {
//#test-actor-ref
static class MyActor extends UntypedActor {
public void onReceive(Object o) throws Exception {
@ -42,18 +43,18 @@ public class TestKitDocTest {
}
public boolean testMe() { return true; }
}
//#test-actor-ref
private static ActorSystem system;
@BeforeClass
public static void setup() {
final Config config = ConfigFactory.parseString(
"akka.loggers = [akka.testkit.TestEventListener]");
system = ActorSystem.create("demoSystem", config);
}
@AfterClass
public static void cleanup() {
system.shutdown();
@ -68,7 +69,7 @@ public class TestKitDocTest {
assertTrue(actor.testMe());
}
//#test-actor-ref
@Test
public void demonstrateAsk() throws Exception {
//#test-behavior
@ -79,7 +80,7 @@ public class TestKitDocTest {
assertEquals(42, Await.result(future, Duration.Zero()));
//#test-behavior
}
@Test
public void demonstrateExceptions() {
//#test-expecting-exceptions
@ -93,7 +94,7 @@ public class TestKitDocTest {
}
//#test-expecting-exceptions
}
@Test
public void demonstrateWithin() {
//#test-within
@ -108,7 +109,7 @@ public class TestKitDocTest {
}};
//#test-within
}
@Test
public void demonstrateExpectMsg() {
//#test-expectmsg
@ -128,7 +129,7 @@ public class TestKitDocTest {
}};
//#test-expectmsg
}
@Test
public void demonstrateReceiveWhile() {
//#test-receivewhile
@ -136,7 +137,7 @@ public class TestKitDocTest {
getRef().tell(42, null);
getRef().tell(43, null);
getRef().tell("hello", null);
final String[] out =
final String[] out =
new ReceiveWhile<String>(String.class, duration("1 second")) {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
@ -168,7 +169,7 @@ public class TestKitDocTest {
//#test-receivewhile-full
}};
}
@Test
public void demonstrateAwaitCond() {
//#test-awaitCond
@ -205,7 +206,7 @@ public class TestKitDocTest {
}};
//#test-awaitAssert
}
@Test
@SuppressWarnings("unchecked") // due to generic varargs
public void demonstrateExpect() {
@ -236,7 +237,7 @@ public class TestKitDocTest {
assertArrayEquals(new String[] {"hello", "world"}, all);
}};
}
@Test
public void demonstrateIgnoreMsg() {
//#test-ignoreMsg
@ -268,7 +269,7 @@ public class TestKitDocTest {
}};
//#duration-dilation
}
@Test
public void demonstrateProbe() {
//#test-probe
@ -282,11 +283,11 @@ public class TestKitDocTest {
target.forward(msg, getContext());
}
}
new JavaTestKit(system) {{
// create a test probe
final JavaTestKit probe = new JavaTestKit(system);
// create a forwarder, injecting the probes testActor
final Props props = new Props(new UntypedActorFactory() {
private static final long serialVersionUID = 8927158735963950216L;
@ -295,7 +296,7 @@ public class TestKitDocTest {
}
});
final ActorRef forwarder = system.actorOf(props, "forwarder");
// verify correct forwarding
forwarder.tell(42, getRef());
probe.expectMsgEquals(42);
@ -303,7 +304,7 @@ public class TestKitDocTest {
}};
//#test-probe
}
@Test
public void demonstrateSpecialProbe() {
//#test-special-probe
@ -323,20 +324,21 @@ public class TestKitDocTest {
}};
//#test-special-probe
}
@Test
public void demonstrateWatch() {
final ActorRef target = system.actorFor("/buh");
final ActorRef target = system.actorOf(new Props(MyActor.class));
//#test-probe-watch
new JavaTestKit(system) {{
final JavaTestKit probe = new JavaTestKit(system);
probe.watch(target);
target.tell(PoisonPill.getInstance(), null);
final Terminated msg = probe.expectMsgClass(Terminated.class);
assertEquals(msg.getActor(), target);
}};
//#test-probe-watch
}
@Test
public void demonstrateReply() {
//#test-probe-reply
@ -350,7 +352,7 @@ public class TestKitDocTest {
}};
//#test-probe-reply
}
@Test
public void demonstrateForward() {
//#test-probe-forward
@ -364,7 +366,7 @@ public class TestKitDocTest {
}};
//#test-probe-forward
}
@Test
public void demonstrateWithinProbe() {
try {
@ -382,7 +384,7 @@ public class TestKitDocTest {
// expected to fail
}
}
@Test
public void demonstrateAutoPilot() {
//#test-auto-pilot
@ -404,7 +406,7 @@ public class TestKitDocTest {
}};
//#test-auto-pilot
}
// only compilation
public void demonstrateCTD() {
//#calling-thread-dispatcher
@ -413,24 +415,24 @@ public class TestKitDocTest {
.withDispatcher(CallingThreadDispatcher.Id()));
//#calling-thread-dispatcher
}
@Test
public void demonstrateEventFilter() {
//#test-event-filter
new JavaTestKit(system) {{
assertEquals("demoSystem", system.name());
final ActorRef victim = system.actorOf(Props.empty(), "victim");
final int result = new EventFilter<Integer>(ActorKilledException.class) {
protected Integer run() {
victim.tell(Kill.getInstance(), null);
return 42;
}
}.from("akka://demoSystem/user/victim").occurrences(1).exec();
assertEquals(42, result);
}};
//#test-event-filter
}
}

View file

@ -70,17 +70,24 @@ reference file for more information:
Looking up Remote Actors
^^^^^^^^^^^^^^^^^^^^^^^^
``actorFor(path)`` will obtain an ``ActorRef`` to an Actor on a remote node::
``actorSelection(path)`` will obtain an ``ActorSelection`` to an Actor on a remote node::
ActorRef actor = context.actorFor("akka.tcp://app@10.0.0.1:2552/user/serviceA/worker");
ActorSelection selection =
context.actorSelection("akka.tcp://app@10.0.0.1:2552/user/serviceA/worker");
As you can see from the example above the following pattern is used to find an ``ActorRef`` on a remote node::
As you can see from the example above the following pattern is used to find an actor on a remote node::
akka.<protocol>://<actorsystemname>@<hostname>:<port>/<actor path>
Once you obtained a reference to the actor you can interact with it they same way you would with a local actor, e.g.::
Once you obtained a selection to the actor you can interact with it they same way you would with a local actor, e.g.::
actor.tell("Pretty awesome feature", getSelf());
selection.tell("Pretty awesome feature", getSelf());
To acquire an :class:`ActorRef` for an :class:`ActorSelection` you need to
send a message to the selection and use the ``getSender`` reference of the reply from
the actor. There is a built-in ``Identify`` message that all Actors will understand
and automatically reply to with a ``ActorIdentity`` message containing the
:class:`ActorRef`.
.. note::
@ -264,9 +271,9 @@ and it is created from an actor system using the aforementioned clients confi
.. includecode:: ../../../akka-samples/akka-sample-remote/src/main/java/sample/remote/calculator/java/JLookupApplication.java
:include: setup
Requests which come in via ``doSomething`` will be sent to the client actor
along with the reference which was looked up earlier. Observe how the actor
system name using in ``actorFor`` matches the remote systems name, as do IP
Requests which come in via ``doSomething`` will be sent to the client actor,
which will use the actor reference that was identified earlier. Observe how the actor
system name using in ``actorSelection`` matches the remote systems name, as do IP
and port number. Top-level actors are always created below the ``"/user"``
guardian, which supervises them.
@ -481,14 +488,14 @@ SSL can be used as the remote transport by adding ``akka.remote.netty.ssl``
to the ``enabled-transport`` configuration section. See a description of the settings
in the :ref:`remoting-java-configuration` section.
The SSL support is implemented with Java Secure Socket Extension, please consult the offical
The SSL support is implemented with Java Secure Socket Extension, please consult the offical
`Java Secure Socket Extension documentation <http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html>`_
and related resources for troubleshooting.
.. note::
When using SHA1PRNG on Linux it's recommended specify ``-Djava.security.egd=file:/dev/./urandom`` as argument
When using SHA1PRNG on Linux it's recommended specify ``-Djava.security.egd=file:/dev/./urandom`` as argument
to the JVM to prevent blocking. It is NOT as secure because it reuses the seed.
Use '/dev/./urandom', not '/dev/urandom' as that doesn't work according to
Use '/dev/./urandom', not '/dev/urandom' as that doesn't work according to
`Bug ID: 6202721 <http://bugs.sun.com/view_bug.do?bug_id=6202721>`_.

View file

@ -206,11 +206,11 @@ Proxying
--------
You can use the ``typedActorOf`` that takes a TypedProps and an ActorRef to proxy the given ActorRef as a TypedActor.
This is usable if you want to communicate remotely with TypedActors on other machines, just look them up with ``actorFor`` and pass the ``ActorRef`` to ``typedActorOf``.
This is usable if you want to communicate remotely with TypedActors on other machines, just pass the ``ActorRef`` to ``typedActorOf``.
Lookup & Remoting
-----------------
Since ``TypedActors`` are backed by ``Akka Actors``, you can use ``actorFor`` together with ``typedActorOf`` to proxy ``ActorRefs`` potentially residing on remote nodes.
Since ``TypedActors`` are backed by ``Akka Actors``, you can use ``typedActorOf`` to proxy ``ActorRefs`` potentially residing on remote nodes.
.. includecode:: code/docs/actor/TypedActorDocTestBase.java#typed-actor-remote

View file

@ -27,8 +27,7 @@ Creating Actors
Since Akka enforces parental supervision every actor is supervised and
(potentially) the supervisor of its children, it is advisable that you
familiarize yourself with :ref:`actor-systems` and :ref:`supervision` and it
may also help to read :ref:`actorOf-vs-actorFor` (the whole of
:ref:`addressing` is recommended reading in any case).
may also help to read :ref:`addressing`.
Defining an Actor class
-----------------------
@ -130,7 +129,7 @@ The :class:`UntypedActor` class defines only one abstract method, the above ment
If the current actor behavior does not match a received message, it's recommended that
you call the :meth:`unhandled` method, which by default publishes a ``new
akka.actor.UnhandledMessage(message, sender, recipient)`` on the actor systems
event stream (set configuration item ``akka.actor.debug.unhandled`` to ``on``
event stream (set configuration item ``akka.actor.debug.unhandled`` to ``on``
to have them converted into actual Debug messages).
In addition, it offers:
@ -230,7 +229,7 @@ mentioned above:
in turn by its supervisor, or if an actor is restarted due to a siblings
failure. If the message is available, then that messages sender is also
accessible in the usual way (i.e. by calling ``getSender()``).
This method is the best place for cleaning up, preparing hand-over to the
fresh actor instance, etc. By default it stops all children and calls
:meth:`postStop`.
@ -267,8 +266,10 @@ sent to a stopped actor will be redirected to the :obj:`deadLetters` of the
:obj:`ActorSystem`.
Identifying Actors
==================
.. _actorSelection-java:
Identifying Actors via Actor Selection
======================================
As described in :ref:`addressing`, each actor has a unique logical path, which
is obtained by following the chain of actors from child to parent until
@ -277,11 +278,13 @@ differ if the supervision chain includes any remote supervisors. These paths
are used by the system to look up actors, e.g. when a remote message is
received and the recipient is searched, but they are also useful more directly:
actors may look up other actors by specifying absolute or relative
paths—logical or physical—and receive back an :class:`ActorRef` with the
paths—logical or physical—and receive back an :class:`ActorSelection` with the
result::
getContext().actorFor("/user/serviceA/actor") // will look up this absolute path
getContext().actorFor("../joe") // will look up sibling beneath same supervisor
// will look up this absolute path
getContext().actorSelection("/user/serviceA/actor");
// will look up sibling beneath same supervisor
getContext().actorSelection("../joe");
The supplied path is parsed as a :class:`java.net.URI`, which basically means
that it is split on ``/`` into path elements. If the path starts with ``/``, it
@ -292,18 +295,43 @@ currently traversed actor, otherwise it will step “down” to the named child.
It should be noted that the ``..`` in actor paths here always means the logical
structure, i.e. the supervisor.
If the path being looked up does not exist, a special actor reference is
returned which behaves like the actor systems dead letter queue but retains
its identity (i.e. the path which was looked up).
The path elements of an actor selection may contain wildcard patterns allowing for
broadcasting of messages to that section::
Remote actor addresses may also be looked up, if remoting is enabled::
// will look all children to serviceB with names starting with worker
getContext().actorSelection("/user/serviceB/worker*");
// will look up all siblings beneath same supervisor
getContext().actorSelection("../*");
getContext().actorFor("akka.tcp://app@otherhost:1234/user/serviceB")
Messages can be sent via the :class:`ActorSelection` and the path of the
:class:`ActorSelection` is looked up when delivering each message. If the selection
does not match any actors the message will be dropped.
These look-ups return a (possibly remote) actor reference immediately, so you
will have to send to it and await a reply in order to verify that ``serviceB``
is actually reachable and running. An example demonstrating actor look-up is
given in :ref:`remote-lookup-sample-java`.
To acquire an :class:`ActorRef` for an :class:`ActorSelection` you need to
send a message to the selection and use the ``getSender`` reference of the reply from
the actor. There is a built-in ``Identify`` message that all Actors will understand
and automatically reply to with a ``ActorIdentity`` message containing the
:class:`ActorRef`.
.. includecode:: code/docs/actor/UntypedActorDocTestBase.java
:include: identify-imports,identify
Remote actor addresses may also be looked up, if :ref:`remoting <remoting-java>` is enabled::
getContext().actorSelection("akka.tcp://app@otherhost:1234/user/serviceB");
An example demonstrating remote actor look-up is given in :ref:`remote-lookup-sample-java`.
.. note::
``actorFor`` is deprecated in favor of ``actorSelection`` because actor references
acquired with ``actorFor`` behave differently for local and remote actors.
In the case of a local actor reference, the named actor needs to exist before the
lookup, or else the acquired reference will be an :class:`EmptyLocalActorRef`.
This will be true even if an actor with that exact path is created after acquiring
the actor reference. For remote actor references acquired with `actorFor` the
behaviour is different and sending messages to such a reference will under the hood
look up the actor by path on the remote system for every message send.
Messages and immutability
=========================
@ -606,7 +634,7 @@ The other way of using :meth:`become` does not replace but add to the top of
the behavior stack. In this case care must be taken to ensure that the number
of “pop” operations (i.e. :meth:`unbecome`) matches the number of “push” ones
in the long run, otherwise this amounts to a memory leak (which is why this
behavior is not the default).
behavior is not the default).
.. includecode:: code/docs/actor/UntypedActorSwapper.java#swapper

View file

@ -152,6 +152,33 @@ available via the ``inbound`` boolean field of the event.
New configuration settings are also available, see the remoting documentation for more detail: :ref:`remoting-scala`
.. _migration_2.2_actorSelection:
Use ``actorSelection`` instead of ``actorFor``
==============================================
``actorFor`` is deprecated in favor of ``actorSelection`` because actor references
acquired with ``actorFor`` behave differently for local and remote actors.
In the case of a local actor reference, the named actor needs to exist before the
lookup, or else the acquired reference will be an :class:`EmptyLocalActorRef`.
This will be true even if an actor with that exact path is created after acquiring
the actor reference. For remote actor references acquired with `actorFor` the
behaviour is different and sending messages to such a reference will under the hood
look up the actor by path on the remote system for every message send.
Messages can be sent via the :class:`ActorSelection` and the path of the
:class:`ActorSelection` is looked up when delivering each message. If the selection
does not match any actors the message will be dropped.
To acquire an :class:`ActorRef` for an :class:`ActorSelection` you need to
send a message to the selection and use the ``sender`` reference of the reply from
the actor. There is a built-in ``Identify`` message that all Actors will understand
and automatically reply to with a ``ActorIdentity`` message containing the
:class:`ActorRef`.
Read more about ``actorSelection`` in :ref:`docs for Java <actorSelection-java>` or
:ref:`docs for Scala <actorSelection-scala>`.
ActorRef equality and sending to remote actors
==============================================
@ -159,15 +186,15 @@ Sending messages to an ``ActorRef`` must have the same semantics no matter if th
on a remote host or in the same ``ActorSystem`` in the same JVM. This was not always the case. For example
when the target actor is terminated and created again under the same path. Sending to local references
of the previous incarnation of the actor will not be delivered to the new incarnation, but that was the case
for remote references. The reason was that the target actor was looked up by its path on every message
for remote references. The reason was that the target actor was looked up by its path on every message
delivery and the path didn't distinguish between the two incarnations of the actor. This has been fixed, and
sending messages to remote references that points to a terminated actor will not be delivered to a new
sending messages to remote references that points to a terminated actor will not be delivered to a new
actor with the same path.
Equality of ``ActorRef`` has been changed to match the intention that an ``ActorRef`` corresponds to the target
actor instance. Two actor references are compared equal when they have the same path and point to the same
actor incarnation. A reference pointing to a terminated actor does not compare equal to a reference pointing
to another (re-created) actor with the same path. Note that a restart of an actor caused by a failure still
to another (re-created) actor with the same path. Note that a restart of an actor caused by a failure still
means that it's the same actor incarnation, i.e. a restart is not visible for the consumer of the ``ActorRef``.
Equality in 2.1 was only based on the path of the ``ActorRef``. If you need to keep track of actor references
@ -175,8 +202,8 @@ in a collection and do not care about the exact actor incarnation you can use th
the identifier of the target actor is not taken into account when comparing actor paths.
Remote actor references acquired with ``actorFor`` do not include the full information about the underlying actor
identity and therefore such references do not compare equal to references acquired with ``actorOf``,
``sender``, or ``context.self``. Because of this ``actorFor`` is deprecated, as explained in
identity and therefore such references do not compare equal to references acquired with ``actorOf``,
``sender``, or ``context.self``. Because of this ``actorFor`` is deprecated, as explained in
:ref:`migration_2.2_actorSelection`.
Note that when a parent actor is restarted its children are by default stopped and re-created, i.e. the child
@ -187,9 +214,32 @@ messages to remote deployed children of a restarted parent.
This may also have implications if you compare the ``ActorRef`` received in a ``Terminated`` message
with an expected ``ActorRef``.
.. _migration_2.2_actorSelection:
The following will not match::
Use ``actorSelection`` instead of ``actorFor``
==============================================
val ref = context.actorFor("akka.tcp://actorSystemName@10.0.0.1:2552/user/actorName")
FIXME: ticket #3074
def receive = {
case Terminated(`ref`) => // ...
}
Instead, use actorSelection followed by identify request, and watch the verified actor reference::
val selection = context.actorSelection("akka.tcp://actorSystemName@10.0.0.1:2552/user/actorName")
selection ! Identify(None)
var ref: ActorRef = _
def receive = {
case ActorIdentity(_, Some(actorRef)) =>
ref = actorRef
context watch ref
case ActorIdentity(_, None) => // not alive
case Terminated(`ref`) => // ...
}
Use ``watch`` instead of ``isTerminated``
=========================================
``ActorRef.isTerminated`` is deprecated in favor of ``ActorContext.watch`` because
``isTerminated`` behaves differently for local and remote actors.

View file

@ -27,8 +27,7 @@ Creating Actors
Since Akka enforces parental supervision every actor is supervised and
(potentially) the supervisor of its children, it is advisable that you
familiarize yourself with :ref:`actor-systems` and :ref:`supervision` and it
may also help to read :ref:`actorOf-vs-actorFor` (the whole of
:ref:`addressing` is recommended reading in any case).
may also help to read :ref:`addressing`.
Defining an Actor class
-----------------------
@ -220,7 +219,7 @@ detects if the runtime class of the statically given actor subtype extends the
:class:`Stash` trait (this is a complicated way of saying that ``new Act with
Stash`` would not work because its runtime erased type is just an anonymous
subtype of ``Act``). The purpose is to automatically use a dispatcher with the
appropriate deque-based mailbox, ``akka.actor.default-stash-dispatcher``.
appropriate deque-based mailbox, ``akka.actor.default-stash-dispatcher``.
If you want to use this magic, simply extend :class:`ActWithStash`:
.. includecode:: ../../../akka-actor-tests/src/test/scala/akka/actor/ActorDSLSpec.scala#act-with-stash
@ -343,7 +342,7 @@ mentioned above:
in turn by its supervisor, or if an actor is restarted due to a siblings
failure. If the message is available, then that messages sender is also
accessible in the usual way (i.e. by calling ``sender``).
This method is the best place for cleaning up, preparing hand-over to the
fresh actor instance, etc. By default it stops all children and calls
:meth:`postStop`.
@ -378,8 +377,10 @@ 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
==================
.. _actorSelection-scala:
Identifying Actors via Actor Selection
======================================
As described in :ref:`addressing`, each actor has a unique logical path, which
is obtained by following the chain of actors from child to parent until
@ -388,11 +389,13 @@ differ if the supervision chain includes any remote supervisors. These paths
are used by the system to look up actors, e.g. when a remote message is
received and the recipient is searched, but they are also useful more directly:
actors may look up other actors by specifying absolute or relative
paths—logical or physical—and receive back an :class:`ActorRef` with the
paths—logical or physical—and receive back an :class:`ActorSelection` with the
result::
context.actorFor("/user/serviceA/aggregator") // will look up this absolute path
context.actorFor("../joe") // will look up sibling beneath same supervisor
// will look up this absolute path
context.actorSelection("/user/serviceA/aggregator")
// will look up sibling beneath same supervisor
context.actorSelection("../joe")
The supplied path is parsed as a :class:`java.net.URI`, which basically means
that it is split on ``/`` into path elements. If the path starts with ``/``, it
@ -403,18 +406,42 @@ currently traversed actor, otherwise it will step “down” to the named child.
It should be noted that the ``..`` in actor paths here always means the logical
structure, i.e. the supervisor.
If the path being looked up does not exist, a special actor reference is
returned which behaves like the actor systems dead letter queue but retains
its identity (i.e. the path which was looked up).
The path elements of an actor selection may contain wildcard patterns allowing for
broadcasting of messages to that section::
Remote actor addresses may also be looked up, if remoting is enabled::
// will look all children to serviceB with names starting with worker
context.actorSelection("/user/serviceB/worker*")
// will look up all siblings beneath same supervisor
context.actorSelection("../*")
context.actorFor("akka.tcp://app@otherhost:1234/user/serviceB")
Messages can be sent via the :class:`ActorSelection` and the path of the
:class:`ActorSelection` is looked up when delivering each message. If the selection
does not match any actors the message will be dropped.
These look-ups return a (possibly remote) actor reference immediately, so you
will have to send to it and await a reply in order to verify that ``serviceB``
is actually reachable and running. An example demonstrating actor look-up is
given in :ref:`remote-lookup-sample-scala`.
To acquire an :class:`ActorRef` for an :class:`ActorSelection` you need to
send a message to the selection and use the ``sender`` reference of the reply from
the actor. There is a built-in ``Identify`` message that all Actors will understand
and automatically reply to with a ``ActorIdentity`` message containing the
:class:`ActorRef`.
.. includecode:: code/docs/actor/ActorDocSpec.scala#identify
Remote actor addresses may also be looked up, if :ref:`remoting <remoting-scala>` is enabled::
context.actorSelection("akka.tcp://app@otherhost:1234/user/serviceB")
An example demonstrating actor look-up is given in :ref:`remote-lookup-sample-scala`.
.. note::
``actorFor`` is deprecated in favor of ``actorSelection`` because actor references
acquired with ``actorFor`` behaves different for local and remote actors.
In the case of a local actor reference, the named actor needs to exist before the
lookup, or else the acquired reference will be an :class:`EmptyLocalActorRef`.
This will be true even if an actor with that exact path is created after acquiring
the actor reference. For remote actor references acquired with `actorFor` the
behaviour is different and sending messages to such a reference will under the hood
look up the actor by path on the remote system for every message send.
Messages and immutability
=========================
@ -721,7 +748,7 @@ The other way of using :meth:`become` does not replace but add to the top of
the behavior stack. In this case care must be taken to ensure that the number
of “pop” operations (i.e. :meth:`unbecome`) matches the number of “push” ones
in the long run, otherwise this amounts to a memory leak (which is why this
behavior is not the default).
behavior is not the default).
.. includecode:: code/docs/actor/ActorDocSpec.scala#swapper

View file

@ -327,6 +327,37 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
expectMsg("finished")
}
"using Identify" in {
//#identify
import akka.actor.{ Actor, Props, Identify, ActorIdentity, Terminated }
class Follower extends Actor {
val identifyId = 1
context.actorSelection("/user/another") ! Identify(identifyId)
def receive = {
case ActorIdentity(`identifyId`, Some(ref))
context.watch(ref)
context.become(active(ref))
case ActorIdentity(`identifyId`, None) context.stop(self)
}
def active(another: ActorRef): Actor.Receive = {
case Terminated(`another`) context.stop(self)
}
}
//#identify
val a = system.actorOf(Props(new Actor {
def receive = Actor.emptyBehavior
}))
val b = system.actorOf(Props(new Follower))
watch(b)
system.stop(a)
expectMsgType[akka.actor.Terminated].actor must be === b
}
"using pattern gracefulStop" in {
val actorRef = system.actorOf(Props[MyActor])
//#gracefulStop
@ -386,9 +417,9 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
lastSender must be === actor
actor ! me
expectMsg("reply")
lastSender must be === system.actorFor("/user")
lastSender.path.elements.mkString("/", "/", "") must be === "/user"
expectMsg("reply")
lastSender must be === system.actorFor("/user")
lastSender.path.elements.mkString("/", "/", "") must be === "/user"
}
"using ActorDSL outside of akka.actor package" in {

View file

@ -4,17 +4,15 @@
package docs.actor
import language.postfixOps
//#imports
import scala.concurrent.{ Promise, Future, Await }
import scala.concurrent.duration._
import akka.actor.{ ActorContext, TypedActor, TypedProps }
//#imports
import org.scalatest.{ BeforeAndAfterAll, WordSpec }
import org.scalatest.matchers.MustMatchers
import akka.testkit._
//#typed-actor-impl
import java.lang.String.{ valueOf println } //Mr funny man avoids printing to stdout AND keeping docs alright
import akka.actor.ActorRef
//#typed-actor-iface
trait Squarer {
@ -46,8 +44,6 @@ class SquarerImpl(val name: String) extends Squarer {
def squareNow(i: Int): Int = i * i
//#typed-actor-impl-methods
}
//#typed-actor-impl
import java.lang.String.{ valueOf println } //Mr funny man avoids printing to stdout AND keeping docs alright
//#typed-actor-supercharge
trait Foo {
def doFoo(times: Int): Unit = println("doFoo(" + times + ")")
@ -145,12 +141,13 @@ class TypedActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
}
"proxy any ActorRef" in {
val actorRefToRemoteActor: ActorRef = system.deadLetters
//#typed-actor-remote
val typedActor: Foo with Bar =
TypedActor(system).
typedActorOf(
TypedProps[FooBar],
system.actorFor("akka://SomeSystem@somehost:2552/user/some/foobar"))
actorRefToRemoteActor)
//Use "typedActor" as a FooBar
//#typed-actor-remote
}

View file

@ -10,13 +10,15 @@ import akka.routing.RoundRobinRouter
import akka.testkit._
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._
import akka.actor.ActorPath
object RouterViaProgramDocSpec {
case class Message1(nbr: Int)
case class Reply1(name: String, m: Message1)
class ExampleActor1 extends Actor {
def receive = {
case m @ Message1(nbr) sender ! ((self, m))
case m @ Message1(nbr) sender ! Reply1(self.path.name, m)
}
}
@ -43,9 +45,9 @@ class RouterViaProgramDocSpec extends AkkaSpec with ImplicitSender {
1 to 6 foreach { i router ! Message1(i) }
val received = receiveN(6, 5.seconds.dilated)
1 to 6 foreach { i
val expectedActor = system.actorFor(routees((i - 1) % routees.length))
val expectedName = (routees((i - 1) % routees.length)).split("/").last
val expectedMsg = Message1(i)
received must contain[AnyRef]((expectedActor, expectedMsg))
received must contain[AnyRef](Reply1(expectedName, expectedMsg))
}
}

View file

@ -157,7 +157,7 @@ package docs.serialization {
"demonstrate serialization of ActorRefs" in {
val theActorRef: ActorRef = system.deadLetters
val theActorSystem: ActorSystem = system
val extendedSystem: ExtendedActorSystem = system.asInstanceOf[ExtendedActorSystem]
//#actorref-serializer
// Serialize
@ -168,7 +168,7 @@ package docs.serialization {
// Deserialize
// (beneath fromBinary)
val deserializedActorRef = theActorSystem actorFor identifier
val deserializedActorRef = extendedSystem.provider.resolveActorRef(identifier)
// Then just use the ActorRef
//#actorref-serializer
@ -182,7 +182,7 @@ package docs.serialization {
}
def serializeTo(ref: ActorRef, remote: Address): String =
ref.path.toSerializationFormatWithAddress(ExternalAddress(theActorSystem).addressFor(remote))
ref.path.toSerializationFormatWithAddress(ExternalAddress(extendedSystem).addressFor(remote))
//#external-address
}

View file

@ -208,10 +208,13 @@ class TestkitDocSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
"demonstrate probe watch" in {
import akka.testkit.TestProbe
val target = system.actorFor("/buh")
val target = system.actorOf(Props(new Actor {
def receive = Actor.emptyBehavior
}))
//#test-probe-watch
val probe = TestProbe()
probe watch target
target ! PoisonPill
probe.expectMsgType[Terminated].actor must be(target)
//#test-probe-watch
}

View file

@ -69,7 +69,7 @@ Types of Remote Interaction
Akka has two ways of using remoting:
* Lookup : used to look up an actor on a remote node with ``actorFor(path)``
* Lookup : used to look up an actor on a remote node with ``actorSelection(path)``
* Creation : used to create an actor on a remote node with ``actorOf(Props(...), actorName)``
In the next sections the two alternatives are described in detail.
@ -77,17 +77,24 @@ In the next sections the two alternatives are described in detail.
Looking up Remote Actors
^^^^^^^^^^^^^^^^^^^^^^^^
``actorFor(path)`` will obtain an ``ActorRef`` to an Actor on a remote node, e.g.::
``actorSelection(path)`` will obtain an ``ActorSelection`` to an Actor on a remote node, e.g.::
val actor = context.actorFor("akka.tcp://actorSystemName@10.0.0.1:2552/user/actorName")
val selection =
context.actorSelection("akka.tcp://actorSystemName@10.0.0.1:2552/user/actorName")
As you can see from the example above the following pattern is used to find an ``ActorRef`` on a remote node::
As you can see from the example above the following pattern is used to find an actor on a remote node::
akka.<protocol>://<actor system>@<hostname>:<port>/<actor path>
Once you obtained a reference to the actor you can interact with it they same way you would with a local actor, e.g.::
Once you obtained a selection to the actor you can interact with it they same way you would with a local actor, e.g.::
actor ! "Pretty awesome feature"
selection ! "Pretty awesome feature"
To acquire an :class:`ActorRef` for an :class:`ActorSelection` you need to
send a message to the selection and use the ``sender`` reference of the reply from
the actor. There is a built-in ``Identify`` message that all Actors will understand
and automatically reply to with a ``ActorIdentity`` message containing the
:class:`ActorRef`.
.. note::
@ -271,9 +278,9 @@ and it is created from an actor system using the aforementioned clients confi
.. includecode:: ../../../akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/LookupApplication.scala
:include: setup
Requests which come in via ``doSomething`` will be sent to the client actor
along with the reference which was looked up earlier. Observe how the actor
system name using in ``actorFor`` matches the remote systems name, as do IP
Requests which come in via ``doSomething`` will be sent to the client actor,
which will use the actor reference that was identified earlier. Observe how the actor
system name using in ``actorSelection`` matches the remote systems name, as do IP
and port number. Top-level actors are always created below the ``"/user"``
guardian, which supervises them.
@ -481,15 +488,15 @@ SSL
SSL can be used as the remote transport by adding ``akka.remote.netty.ssl``
to the ``enabled-transport`` configuration section. See a description of the settings
in the :ref:`remoting-scala-configuration` section.
The SSL support is implemented with Java Secure Socket Extension, please consult the offical
The SSL support is implemented with Java Secure Socket Extension, please consult the offical
`Java Secure Socket Extension documentation <http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html>`_
and related resources for troubleshooting.
.. note::
When using SHA1PRNG on Linux it's recommended specify ``-Djava.security.egd=file:/dev/./urandom`` as argument
When using SHA1PRNG on Linux it's recommended specify ``-Djava.security.egd=file:/dev/./urandom`` as argument
to the JVM to prevent blocking. It is NOT as secure because it reuses the seed.
Use '/dev/./urandom', not '/dev/urandom' as that doesn't work according to
Use '/dev/./urandom', not '/dev/urandom' as that doesn't work according to
`Bug ID: 6202721 <http://bugs.sun.com/view_bug.do?bug_id=6202721>`_.

View file

@ -204,7 +204,7 @@ Proxying
--------
You can use the ``typedActorOf`` that takes a TypedProps and an ActorRef to proxy the given ActorRef as a TypedActor.
This is usable if you want to communicate remotely with TypedActors on other machines, just look them up with ``actorFor`` and pass the ``ActorRef`` to ``typedActorOf``.
This is usable if you want to communicate remotely with TypedActors on other machines, just pass the ``ActorRef`` to ``typedActorOf``.
.. note::
@ -213,7 +213,7 @@ This is usable if you want to communicate remotely with TypedActors on other mac
Lookup & Remoting
-----------------
Since ``TypedActors`` are backed by ``Akka Actors``, you can use ``actorFor`` together with ``typedActorOf`` to proxy ``ActorRefs`` potentially residing on remote nodes.
Since ``TypedActors`` are backed by ``Akka Actors``, you can use ``typedActorOf`` to proxy ``ActorRefs`` potentially residing on remote nodes.
.. includecode:: code/docs/actor/TypedActorDocSpec.scala#typed-actor-remote

View file

@ -516,10 +516,10 @@ in case classes with proper type information. But one particular useful feature
of Akka actors is that they have a stable identity by which they can be found,
a unique name. This name is represented as a :class:`String` and naturally does
not bear any type information concerning the actors channels. Thus, when
looking up an actor with ``system.actorFor(...)`` you will only get an untyped
:class:`ActorRef` and not a channel reference. This :class:`ActorRef` can of
course manually be wrapped in a channel reference bearing the desired channels,
but this is not a type-safe operation.
looking up an actor with ``system.actorSelection(...)`` followed by an ``Identify``
request you will only get an untyped :class:`ActorRef` and not a channel reference.
This :class:`ActorRef` can of course manually be wrapped in a channel reference
bearing the desired channels, but this is not a type-safe operation.
The solution in this case must be a runtime check. There is an operation to
“narrow” an :class:`ActorRef` to a channel reference of given type, which