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

@ -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