* Add reason for deprecating TypedActor, #25516 Co-Authored-By: Helena Edelson <helena@users.noreply.github.com>
This commit is contained in:
parent
2051b7ce6b
commit
24a3bba135
5 changed files with 26 additions and 813 deletions
|
|
@ -14,3 +14,4 @@ RedirectMatch 301 ^(.*)/project/migration-guide-stream-2\.0-2\.4.html.* $1/proje
|
|||
|
||||
RedirectMatch 301 ^(.*)/stream/stages-overview\.html$ $1/stream/operators/index.html
|
||||
RedirectMatch 301 ^(.*)/agents\.html$ $1/project/migration-guide-2.5.x-2.6.x.html
|
||||
RedirectMatch 301 ^(.*)/typed-actors\.html$ $1/project/migration-guide-2.5.x-2.6.x.html#typedactor
|
||||
|
|
|
|||
|
|
@ -50,8 +50,31 @@ Use plain `system.actorOf` instead of the DSL to create Actors if you have been
|
|||
### TypedActor
|
||||
|
||||
`akka.actor.TypedActor` has been deprecated as of 2.6 in favor of the
|
||||
`akka.actor.typed` API which should be used instead. For more context on
|
||||
this deprecation [please refer to the discussion here](https://github.com/akka/akka/issues/26144).
|
||||
`akka.actor.typed` API which should be used instead.
|
||||
|
||||
There are several reasons for phasing out the old `TypedActor`. The primary reason is they use transparent
|
||||
remoting which is not our recommended way of implementing and interacting with actors. Transparent remoting
|
||||
is when you try to make remote method invocations look like local calls. In contrast we believe in location
|
||||
transparency with explicit messaging between actors (same type of messaging for both local and remote actors).
|
||||
They also have limited functionality compared to ordinary actors, and worse performance.
|
||||
|
||||
To summarize the fallacy of transparent remoting:
|
||||
* Was used in CORBA, RMI, and DCOM, and all of them failed. Those problems were noted by [Waldo et al already in 1994](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.7628)
|
||||
* Partial failure is a major problem. Remote calls introduce uncertainty whether the function was invoked or not.
|
||||
Typically handled by using timeouts but the client can't always know the result of the call.
|
||||
* Latency of calls over a network are several order of magnitudes longer than latency of local calls,
|
||||
which can be more than surprising if encoded as an innocent looking local method call.
|
||||
* Remote invocations have much lower throughput due to the need of serializing the
|
||||
data and you can't just pass huge datasets in the same way.
|
||||
|
||||
Therefore explicit message passing is preferred. It looks different from local method calls
|
||||
(@scala[`actorRef ! message`]@java[`actorRef.tell(message)`]) and there is no misconception
|
||||
that sending a message will result in it being processed instantaneously. The goal of location
|
||||
transparency is to unify message passing for both local and remote interactions, versus attempting
|
||||
to make remote interactions look like local method calls.
|
||||
|
||||
Warnings about `TypedActor` have been [mentioned in documentation](https://doc.akka.io/docs/akka/2.5/typed-actors.html#when-to-use-typed-actors)
|
||||
for many years.
|
||||
|
||||
## Internal dispatcher introduced
|
||||
|
||||
|
|
|
|||
|
|
@ -1,331 +0,0 @@
|
|||
# Typed Actors
|
||||
|
||||
@@@ note
|
||||
|
||||
This module will be deprecated as it will be superseded by the @ref:[Akka Typed](typed/actors.md)
|
||||
project which is currently being developed in open preview mode.
|
||||
|
||||
@@@
|
||||
|
||||
Akka Typed Actors is an implementation of the [Active Objects](http://en.wikipedia.org/wiki/Active_object) pattern.
|
||||
Essentially turning method invocations into asynchronous dispatch instead of synchronous that has been the default way since Smalltalk came out.
|
||||
|
||||
Typed Actors consist of 2 "parts", a public interface and an implementation, and if you've done any work in "enterprise" Java, this will be very familiar to you. As with normal Actors you have an external API (the public interface instance) that will delegate method calls asynchronously to
|
||||
a private instance of the implementation.
|
||||
|
||||
The advantage of Typed Actors vs. Actors is that with TypedActors you have a
|
||||
static contract, and don't need to define your own messages, the downside is
|
||||
that it places some limitations on what you can do and what you can't, i.e. you
|
||||
cannot use `become`/`unbecome`.
|
||||
|
||||
Typed Actors are implemented using [JDK Proxies](http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html) which provide a pretty easy-worked API to intercept method calls.
|
||||
|
||||
@@@ note
|
||||
|
||||
Just as with regular Akka Actors, Typed Actors process one call at a time.
|
||||
|
||||
@@@
|
||||
|
||||
## When to use Typed Actors
|
||||
|
||||
Typed actors are nice for bridging between actor systems (the “inside”) and
|
||||
non-actor code (the “outside”), because they allow you to write normal
|
||||
OO-looking code on the outside. Think of them like doors: their practicality
|
||||
lies in interfacing between private sphere and the public, but you don’t want
|
||||
that many doors inside your house, do you? For a longer discussion see [this
|
||||
blog post](http://letitcrash.com/post/19074284309/when-to-use-typedactors).
|
||||
|
||||
A bit more background: TypedActors can easily be abused as RPC, and that
|
||||
is an abstraction which is [well-known](http://doc.akka.io/docs/misc/smli_tr-94-29.pdf)
|
||||
to be leaky. Hence TypedActors are not what we think of first when we talk
|
||||
about making highly scalable concurrent software easier to write correctly.
|
||||
They have their niche, use them sparingly.
|
||||
|
||||
## The tools of the trade
|
||||
|
||||
Before we create our first Typed Actor we should first go through the tools that we have at our disposal,
|
||||
it's located in `akka.actor.TypedActor`.
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-extension-tools }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-extension-tools }
|
||||
|
||||
@@@ warning
|
||||
|
||||
Same as not exposing `this` of an Akka Actor, it's important not to expose `this` of a Typed Actor,
|
||||
instead you should pass the external proxy reference, which is obtained from within your Typed Actor as
|
||||
@scala[`TypedActor.self`]@java[`TypedActor.self()`], this is your external identity, as the `ActorRef` is the external identity of
|
||||
an Akka Actor.
|
||||
|
||||
@@@
|
||||
|
||||
## Creating Typed Actors
|
||||
|
||||
To create a Typed Actor you need to have one or more interfaces, and one implementation.
|
||||
|
||||
The following imports are assumed:
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #imports }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #imports }
|
||||
|
||||
Our example interface:
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-iface }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-iface }
|
||||
|
||||
Our example implementation of that interface:
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-impl }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-impl }
|
||||
|
||||
The most trivial way of creating a Typed Actor instance
|
||||
of our `Squarer`:
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-create1 }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-create1 }
|
||||
|
||||
First type is the type of the proxy, the second type is the type of the implementation.
|
||||
If you need to call a specific constructor you do it like this:
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-create2 }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-create2 }
|
||||
|
||||
Since you supply a `Props`, you can specify which dispatcher to use, what the default timeout should be used and more.
|
||||
Now, our `Squarer` doesn't have any methods, so we'd better add those.
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-iface }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-iface }
|
||||
|
||||
Alright, now we've got some methods we can call, but we need to implement those in `SquarerImpl`.
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-impl }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-impl }
|
||||
|
||||
Excellent, now we have an interface and an implementation of that interface,
|
||||
and we know how to create a Typed Actor from that, so let's look at calling these methods.
|
||||
|
||||
## Method dispatch semantics
|
||||
|
||||
Methods returning:
|
||||
|
||||
@@@ div {.group-scala}
|
||||
|
||||
* `Unit` will be dispatched with `fire-and-forget` semantics, exactly like `ActorRef.tell`
|
||||
* `scala.concurrent.Future[_]` will use `send-request-reply` semantics, exactly like `ActorRef.ask`
|
||||
* `scala.Option[_]` will use `send-request-reply` semantics, but *will* block to wait for an answer,
|
||||
and return `scala.None` if no answer was produced within the timeout, or `scala.Some[_]` containing the result otherwise.
|
||||
Any exception that was thrown during this call will be rethrown.
|
||||
* Any other type of value will use `send-request-reply` semantics, but *will* block to wait for an answer,
|
||||
throwing `java.util.concurrent.TimeoutException` if there was a timeout or rethrow any exception that was thrown during this call.
|
||||
|
||||
@@@
|
||||
|
||||
@@@ div {.group-java}
|
||||
|
||||
* `void` will be dispatched with `fire-and-forget` semantics, exactly like `ActorRef.tell`
|
||||
* `scala.concurrent.Future<?>` will use `send-request-reply` semantics, exactly like `ActorRef.ask`
|
||||
* `akka.japi.Option<?>` will use `send-request-reply` semantics, but *will* block to wait for an answer,
|
||||
and return `akka.japi.Option.None` if no answer was produced within the timeout, or `akka.japi.Option.Some<?>` containing the result otherwise.
|
||||
Any exception that was thrown during this call will be rethrown.
|
||||
* Any other type of value will use `send-request-reply` semantics, but *will* block to wait for an answer,
|
||||
throwing `java.util.concurrent.TimeoutException` if there was a timeout or rethrow any exception that was thrown during this call.
|
||||
Note that due to the Java exception and reflection mechanisms, such a `TimeoutException` will be wrapped in a `java.lang.reflect.UndeclaredThrowableException`
|
||||
unless the interface method explicitly declares the `TimeoutException` as a thrown checked exception.
|
||||
|
||||
@@@
|
||||
|
||||
|
||||
## Messages and immutability
|
||||
|
||||
While Akka cannot enforce that the parameters to the methods of your Typed Actors are immutable,
|
||||
we *strongly* recommend that parameters passed are immutable.
|
||||
|
||||
### One-way message send
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-call-oneway }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-call-oneway }
|
||||
|
||||
As simple as that! The method will be executed on another thread; asynchronously.
|
||||
|
||||
### Request-reply message send
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-call-option }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-call-option }
|
||||
|
||||
This will block for as long as the timeout that was set in the `Props` of the Typed Actor,
|
||||
if needed. It will return `None` if a timeout occurs.
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-call-strict }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-call-strict }
|
||||
|
||||
This will block for as long as the timeout that was set in the `Props` of the Typed Actor,
|
||||
if needed. It will throw a `java.util.concurrent.TimeoutException` if a timeout occurs.
|
||||
|
||||
@@@ div {.group-java}
|
||||
|
||||
Note that here, such a `TimeoutException` will be wrapped in a
|
||||
`java.lang.reflect.UndeclaredThrowableException` by the Java reflection mechanism,
|
||||
because the interface method does not explicitly declare the `TimeoutException` as a thrown checked exception.
|
||||
To get the `TimeoutException` directly, declare `throws java.util.concurrent.TimeoutException` at the
|
||||
interface method.
|
||||
|
||||
@@@
|
||||
|
||||
### Request-reply-with-future message send
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-call-future }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-call-future }
|
||||
|
||||
This call is asynchronous, and the Future returned can be used for asynchronous composition.
|
||||
|
||||
## Stopping Typed Actors
|
||||
|
||||
Since Akka's Typed Actors are backed by Akka Actors they must be stopped when they aren't needed anymore.
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-stop }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-stop }
|
||||
|
||||
This asynchronously stops the Typed Actor associated with the specified proxy ASAP.
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-poisonpill }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-poisonpill }
|
||||
|
||||
This asynchronously stops the Typed Actor associated with the specified proxy
|
||||
after it's done with all calls that were made prior to this call.
|
||||
|
||||
## Typed Actor Hierarchies
|
||||
|
||||
Since you can obtain a contextual Typed Actor Extension by passing in an `ActorContext`
|
||||
you can create child Typed Actors by invoking `typedActorOf(..)` on that.
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-hierarchy }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-hierarchy }
|
||||
|
||||
You can also create a child Typed Actor in regular Akka Actors by giving the @scala[`ActorContext`]@java[`AbstractActor.ActorContext`]
|
||||
as an input parameter to `TypedActor.get(…)`.
|
||||
|
||||
## Supervisor Strategy
|
||||
|
||||
By having your Typed Actor implementation class implement `TypedActor.Supervisor`
|
||||
you can define the strategy to use for supervising child actors, as described in
|
||||
@ref:[supervision](general/supervision.md) and @ref:[Fault Tolerance](fault-tolerance.md).
|
||||
|
||||
## Lifecycle callbacks
|
||||
|
||||
By having your Typed Actor implementation class implement any and all of the following:
|
||||
|
||||
* `TypedActor.PreStart`
|
||||
* `TypedActor.PostStop`
|
||||
* `TypedActor.PreRestart`
|
||||
* `TypedActor.PostRestart`
|
||||
|
||||
You can hook into the lifecycle of your Typed Actor.
|
||||
|
||||
## Receive arbitrary messages
|
||||
|
||||
If your implementation class of your TypedActor extends `akka.actor.TypedActor.Receiver`,
|
||||
all messages that are not `MethodCall` instances will be passed into the `onReceive`-method.
|
||||
|
||||
This allows you to react to DeathWatch `Terminated`-messages and other types of messages,
|
||||
e.g. when interfacing with untyped actors.
|
||||
|
||||
## 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, pass the `ActorRef` to `typedActorOf`.
|
||||
|
||||
@@@ note
|
||||
|
||||
The ActorRef needs to accept `MethodCall` messages.
|
||||
|
||||
@@@
|
||||
|
||||
## Lookup & Remoting
|
||||
|
||||
Since `TypedActors` are backed by `Akka Actors`, you can use `typedActorOf` to proxy `ActorRefs` potentially residing on remote nodes.
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-remote }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-actor-remote }
|
||||
|
||||
@@@ div {.group-scala}
|
||||
|
||||
## Supercharging
|
||||
|
||||
Here's an example on how you can use traits to mix in behavior in your Typed Actors.
|
||||
|
||||
@@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-supercharge }
|
||||
|
||||
@@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-actor-supercharge-usage }
|
||||
|
||||
@@@
|
||||
|
||||
## Typed Router pattern
|
||||
|
||||
Sometimes you want to spread messages between multiple actors. The easiest way to achieve this in Akka is to use a @ref:[Router](routing.md),
|
||||
which can implement a specific routing logic, such as `smallest-mailbox` or `consistent-hashing` etc.
|
||||
|
||||
Routers are not provided directly for typed actors, but it is really easy to leverage an untyped router and use a typed proxy in front of it.
|
||||
To showcase this let's create typed actors that assign themselves some random `id`, so we know that in fact, the router has sent the message to different actors:
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-router-types }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-router-types }
|
||||
|
||||
In order to round robin among a few instances of such actors, you can create a plain untyped router,
|
||||
and then facade it with a `TypedActor` like shown in the example below. This works because typed actors
|
||||
communicate using the same mechanisms as normal actors, and methods calls on them get transformed into message sends of `MethodCall` messages.
|
||||
|
||||
Scala
|
||||
: @@snip [TypedActorDocSpec.scala](/akka-docs/src/test/scala/docs/actor/TypedActorDocSpec.scala) { #typed-router }
|
||||
|
||||
Java
|
||||
: @@snip [TypedActorDocTest.java](/akka-docs/src/test/java/jdocs/actor/TypedActorDocTest.java) { #typed-router }
|
||||
|
|
@ -1,257 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.actor;
|
||||
|
||||
// #imports
|
||||
import akka.actor.TypedActor;
|
||||
import akka.actor.*;
|
||||
import akka.japi.*;
|
||||
import akka.dispatch.Futures;
|
||||
|
||||
import jdocs.AbstractJavaTest;
|
||||
import scala.concurrent.Await;
|
||||
import scala.concurrent.Future;
|
||||
import scala.concurrent.duration.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
import akka.routing.RoundRobinGroup;
|
||||
// #imports
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class TypedActorDocTest extends AbstractJavaTest {
|
||||
Object someReference = null;
|
||||
ActorSystem system = null;
|
||||
|
||||
public
|
||||
// #typed-actor-iface
|
||||
static interface Squarer {
|
||||
// #typed-actor-iface-methods
|
||||
void squareDontCare(int i); // fire-forget
|
||||
|
||||
Future<Integer> square(int i); // non-blocking send-request-reply
|
||||
|
||||
Option<Integer> squareNowPlease(int i); // blocking send-request-reply
|
||||
|
||||
int squareNow(int i); // blocking send-request-reply
|
||||
// #typed-actor-iface-methods
|
||||
}
|
||||
// #typed-actor-iface
|
||||
|
||||
static
|
||||
// #typed-actor-impl
|
||||
class SquarerImpl implements Squarer {
|
||||
private String name;
|
||||
|
||||
public SquarerImpl() {
|
||||
this.name = "default";
|
||||
}
|
||||
|
||||
public SquarerImpl(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// #typed-actor-impl-methods
|
||||
|
||||
public void squareDontCare(int i) {
|
||||
int sq = i * i; // Nobody cares :(
|
||||
}
|
||||
|
||||
public Future<Integer> square(int i) {
|
||||
return Futures.successful(i * i);
|
||||
}
|
||||
|
||||
public Option<Integer> squareNowPlease(int i) {
|
||||
return Option.some(i * i);
|
||||
}
|
||||
|
||||
public int squareNow(int i) {
|
||||
return i * i;
|
||||
}
|
||||
// #typed-actor-impl-methods
|
||||
}
|
||||
// #typed-actor-impl
|
||||
|
||||
@Test
|
||||
public void mustGetTheTypedActorExtension() {
|
||||
|
||||
try {
|
||||
// #typed-actor-extension-tools
|
||||
|
||||
// Returns the Typed Actor Extension
|
||||
TypedActorExtension extension =
|
||||
TypedActor.get(system); // system is an instance of ActorSystem
|
||||
|
||||
// Returns whether the reference is a Typed Actor Proxy or not
|
||||
TypedActor.get(system).isTypedActor(someReference);
|
||||
|
||||
// Returns the backing Akka Actor behind an external Typed Actor Proxy
|
||||
TypedActor.get(system).getActorRefFor(someReference);
|
||||
|
||||
// Returns the current ActorContext,
|
||||
// method only valid within methods of a TypedActor implementation
|
||||
ActorContext context = TypedActor.context();
|
||||
|
||||
// Returns the external proxy of the current Typed Actor,
|
||||
// method only valid within methods of a TypedActor implementation
|
||||
Squarer sq = TypedActor.<Squarer>self();
|
||||
|
||||
// Returns a contextual instance of the Typed Actor Extension
|
||||
// this means that if you create other Typed Actors with this,
|
||||
// they will become children to the current Typed Actor.
|
||||
TypedActor.get(TypedActor.context());
|
||||
|
||||
// #typed-actor-extension-tools
|
||||
} catch (Exception e) {
|
||||
// dun care
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createATypedActor() {
|
||||
try {
|
||||
// #typed-actor-create1
|
||||
Squarer mySquarer =
|
||||
TypedActor.get(system)
|
||||
.typedActorOf(new TypedProps<SquarerImpl>(Squarer.class, SquarerImpl.class));
|
||||
// #typed-actor-create1
|
||||
// #typed-actor-create2
|
||||
Squarer otherSquarer =
|
||||
TypedActor.get(system)
|
||||
.typedActorOf(
|
||||
new TypedProps<SquarerImpl>(
|
||||
Squarer.class,
|
||||
new Creator<SquarerImpl>() {
|
||||
public SquarerImpl create() {
|
||||
return new SquarerImpl("foo");
|
||||
}
|
||||
}),
|
||||
"name");
|
||||
// #typed-actor-create2
|
||||
|
||||
// #typed-actor-calls
|
||||
// #typed-actor-call-oneway
|
||||
mySquarer.squareDontCare(10);
|
||||
// #typed-actor-call-oneway
|
||||
|
||||
// #typed-actor-call-future
|
||||
Future<Integer> fSquare = mySquarer.square(10); // A Future[Int]
|
||||
// #typed-actor-call-future
|
||||
|
||||
// #typed-actor-call-option
|
||||
Option<Integer> oSquare = mySquarer.squareNowPlease(10); // Option[Int]
|
||||
// #typed-actor-call-option
|
||||
|
||||
// #typed-actor-call-strict
|
||||
int iSquare = mySquarer.squareNow(10); // Int
|
||||
// #typed-actor-call-strict
|
||||
// #typed-actor-calls
|
||||
|
||||
assertEquals(100, Await.result(fSquare, Duration.create(3, TimeUnit.SECONDS)).intValue());
|
||||
|
||||
assertEquals(100, oSquare.get().intValue());
|
||||
|
||||
assertEquals(100, iSquare);
|
||||
|
||||
// #typed-actor-stop
|
||||
TypedActor.get(system).stop(mySquarer);
|
||||
// #typed-actor-stop
|
||||
|
||||
// #typed-actor-poisonpill
|
||||
TypedActor.get(system).poisonPill(otherSquarer);
|
||||
// #typed-actor-poisonpill
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createHierarchies() {
|
||||
try {
|
||||
// #typed-actor-hierarchy
|
||||
Squarer childSquarer =
|
||||
TypedActor.get(TypedActor.context())
|
||||
.typedActorOf(new TypedProps<SquarerImpl>(Squarer.class, SquarerImpl.class));
|
||||
// Use "childSquarer" as a Squarer
|
||||
// #typed-actor-hierarchy
|
||||
} catch (Exception e) {
|
||||
// dun care
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxyAnyActorRef() {
|
||||
try {
|
||||
final ActorRef actorRefToRemoteActor = system.deadLetters();
|
||||
// #typed-actor-remote
|
||||
Squarer typedActor =
|
||||
TypedActor.get(system)
|
||||
.typedActorOf(new TypedProps<Squarer>(Squarer.class), actorRefToRemoteActor);
|
||||
// Use "typedActor" as a FooBar
|
||||
// #typed-actor-remote
|
||||
} catch (Exception e) {
|
||||
// dun care
|
||||
}
|
||||
}
|
||||
|
||||
// #typed-router-types
|
||||
interface HasName {
|
||||
String name();
|
||||
}
|
||||
|
||||
class Named implements HasName {
|
||||
private int id = new Random().nextInt(1024);
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "name-" + id;
|
||||
}
|
||||
}
|
||||
// #typed-router-types
|
||||
|
||||
@Test
|
||||
public void typedRouterPattern() {
|
||||
try {
|
||||
// #typed-router
|
||||
// prepare routees
|
||||
TypedActorExtension typed = TypedActor.get(system);
|
||||
|
||||
Named named1 = typed.typedActorOf(new TypedProps<Named>(Named.class));
|
||||
|
||||
Named named2 = typed.typedActorOf(new TypedProps<Named>(Named.class));
|
||||
|
||||
List<Named> routees = new ArrayList<Named>();
|
||||
routees.add(named1);
|
||||
routees.add(named2);
|
||||
|
||||
List<String> routeePaths = new ArrayList<String>();
|
||||
routeePaths.add(typed.getActorRefFor(named1).path().toStringWithoutAddress());
|
||||
routeePaths.add(typed.getActorRefFor(named2).path().toStringWithoutAddress());
|
||||
|
||||
// prepare untyped router
|
||||
ActorRef router = system.actorOf(new RoundRobinGroup(routeePaths).props(), "router");
|
||||
|
||||
// prepare typed proxy, forwarding MethodCall messages to `router`
|
||||
Named typedRouter = typed.typedActorOf(new TypedProps<Named>(Named.class), router);
|
||||
|
||||
System.out.println("actor was: " + typedRouter.name()); // name-243
|
||||
System.out.println("actor was: " + typedRouter.name()); // name-614
|
||||
System.out.println("actor was: " + typedRouter.name()); // name-243
|
||||
System.out.println("actor was: " + typedRouter.name()); // name-614
|
||||
|
||||
// #typed-router
|
||||
typed.poisonPill(named1);
|
||||
typed.poisonPill(named2);
|
||||
typed.poisonPill(typedRouter);
|
||||
|
||||
} catch (Exception e) {
|
||||
// dun care
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,223 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package docs.actor
|
||||
|
||||
//#imports
|
||||
import java.lang.String.{ valueOf => println }
|
||||
|
||||
import akka.actor.{ ActorContext, ActorRef, TypedActor, TypedProps }
|
||||
import akka.routing.RoundRobinGroup
|
||||
import akka.testkit._
|
||||
|
||||
import scala.concurrent.{ Await, Future }
|
||||
import scala.concurrent.duration._
|
||||
//#imports
|
||||
|
||||
//Mr funny man avoids printing to stdout AND keeping docs alright
|
||||
import java.lang.String.{ valueOf => println }
|
||||
|
||||
//#typed-actor-iface
|
||||
trait Squarer {
|
||||
//#typed-actor-iface-methods
|
||||
def squareDontCare(i: Int): Unit //fire-forget
|
||||
|
||||
def square(i: Int): Future[Int] //non-blocking send-request-reply
|
||||
|
||||
def squareNowPlease(i: Int): Option[Int] //blocking send-request-reply
|
||||
|
||||
def squareNow(i: Int): Int //blocking send-request-reply
|
||||
|
||||
@throws(classOf[Exception]) //declare it or you will get an UndeclaredThrowableException
|
||||
def squareTry(i: Int): Int //blocking send-request-reply with possible exception
|
||||
//#typed-actor-iface-methods
|
||||
}
|
||||
//#typed-actor-iface
|
||||
|
||||
//#typed-actor-impl
|
||||
class SquarerImpl(val name: String) extends Squarer {
|
||||
|
||||
def this() = this("default")
|
||||
//#typed-actor-impl-methods
|
||||
def squareDontCare(i: Int): Unit = i * i //Nobody cares :(
|
||||
|
||||
def square(i: Int): Future[Int] = Future.successful(i * i)
|
||||
|
||||
def squareNowPlease(i: Int): Option[Int] = Some(i * i)
|
||||
|
||||
def squareNow(i: Int): Int = i * i
|
||||
|
||||
def squareTry(i: Int): Int = throw new Exception("Catch me!")
|
||||
//#typed-actor-impl-methods
|
||||
}
|
||||
//#typed-actor-impl
|
||||
//#typed-actor-supercharge
|
||||
trait Foo {
|
||||
def doFoo(times: Int): Unit = println("doFoo(" + times + ")")
|
||||
}
|
||||
|
||||
trait Bar {
|
||||
def doBar(str: String): Future[String] =
|
||||
Future.successful(str.toUpperCase)
|
||||
}
|
||||
|
||||
class FooBar extends Foo with Bar
|
||||
//#typed-actor-supercharge
|
||||
|
||||
//#typed-router-types
|
||||
trait HasName {
|
||||
def name(): String
|
||||
}
|
||||
|
||||
class Named extends HasName {
|
||||
import scala.util.Random
|
||||
private val id = Random.nextInt(1024)
|
||||
|
||||
def name(): String = "name-" + id
|
||||
}
|
||||
//#typed-router-types
|
||||
|
||||
class TypedActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
|
||||
|
||||
"get the TypedActor extension" in {
|
||||
val someReference: AnyRef = null
|
||||
|
||||
try {
|
||||
//#typed-actor-extension-tools
|
||||
|
||||
import akka.actor.TypedActor
|
||||
|
||||
//Returns the Typed Actor Extension
|
||||
val extension = TypedActor(system) //system is an instance of ActorSystem
|
||||
|
||||
//Returns whether the reference is a Typed Actor Proxy or not
|
||||
TypedActor(system).isTypedActor(someReference)
|
||||
|
||||
//Returns the backing Akka Actor behind an external Typed Actor Proxy
|
||||
TypedActor(system).getActorRefFor(someReference)
|
||||
|
||||
//Returns the current ActorContext,
|
||||
// method only valid within methods of a TypedActor implementation
|
||||
val c: ActorContext = TypedActor.context
|
||||
|
||||
//Returns the external proxy of the current Typed Actor,
|
||||
// method only valid within methods of a TypedActor implementation
|
||||
val s: Squarer = TypedActor.self[Squarer]
|
||||
|
||||
//Returns a contextual instance of the Typed Actor Extension
|
||||
//this means that if you create other Typed Actors with this,
|
||||
//they will become children to the current Typed Actor.
|
||||
TypedActor(TypedActor.context)
|
||||
|
||||
//#typed-actor-extension-tools
|
||||
} catch {
|
||||
case e: Exception => //dun care
|
||||
}
|
||||
}
|
||||
|
||||
"create a typed actor" in {
|
||||
//#typed-actor-create1
|
||||
val mySquarer: Squarer =
|
||||
TypedActor(system).typedActorOf(TypedProps[SquarerImpl]())
|
||||
//#typed-actor-create1
|
||||
//#typed-actor-create2
|
||||
val otherSquarer: Squarer =
|
||||
TypedActor(system).typedActorOf(TypedProps(classOf[Squarer], new SquarerImpl("foo")), "name")
|
||||
//#typed-actor-create2
|
||||
|
||||
//#typed-actor-calls
|
||||
//#typed-actor-call-oneway
|
||||
mySquarer.squareDontCare(10)
|
||||
//#typed-actor-call-oneway
|
||||
|
||||
//#typed-actor-call-future
|
||||
val fSquare = mySquarer.square(10) //A Future[Int]
|
||||
//#typed-actor-call-future
|
||||
|
||||
//#typed-actor-call-option
|
||||
val oSquare = mySquarer.squareNowPlease(10) //Option[Int]
|
||||
//#typed-actor-call-option
|
||||
|
||||
//#typed-actor-call-strict
|
||||
val iSquare = mySquarer.squareNow(10) //Int
|
||||
//#typed-actor-call-strict
|
||||
//#typed-actor-calls
|
||||
|
||||
Await.result(fSquare, 3.seconds) should be(100)
|
||||
|
||||
oSquare should be(Some(100))
|
||||
|
||||
iSquare should be(100)
|
||||
|
||||
//#typed-actor-stop
|
||||
TypedActor(system).stop(mySquarer)
|
||||
//#typed-actor-stop
|
||||
|
||||
//#typed-actor-poisonpill
|
||||
TypedActor(system).poisonPill(otherSquarer)
|
||||
//#typed-actor-poisonpill
|
||||
}
|
||||
|
||||
"proxy any ActorRef" in {
|
||||
val actorRefToRemoteActor: ActorRef = system.deadLetters
|
||||
//#typed-actor-remote
|
||||
val typedActor: Foo with Bar =
|
||||
TypedActor(system).typedActorOf(TypedProps[FooBar], actorRefToRemoteActor)
|
||||
//Use "typedActor" as a FooBar
|
||||
//#typed-actor-remote
|
||||
}
|
||||
|
||||
"create hierarchies" in {
|
||||
try {
|
||||
//#typed-actor-hierarchy
|
||||
//Inside your Typed Actor
|
||||
val childSquarer: Squarer =
|
||||
TypedActor(TypedActor.context).typedActorOf(TypedProps[SquarerImpl]())
|
||||
//Use "childSquarer" as a Squarer
|
||||
//#typed-actor-hierarchy
|
||||
} catch {
|
||||
case e: Exception => //ignore
|
||||
}
|
||||
}
|
||||
|
||||
"supercharge" in {
|
||||
//#typed-actor-supercharge-usage
|
||||
val awesomeFooBar: Foo with Bar =
|
||||
TypedActor(system).typedActorOf(TypedProps[FooBar]())
|
||||
|
||||
awesomeFooBar.doFoo(10)
|
||||
val f = awesomeFooBar.doBar("yes")
|
||||
|
||||
TypedActor(system).poisonPill(awesomeFooBar)
|
||||
//#typed-actor-supercharge-usage
|
||||
Await.result(f, 3.seconds) should be("YES")
|
||||
}
|
||||
|
||||
"typed router pattern" in {
|
||||
//#typed-router
|
||||
def namedActor(): HasName = TypedActor(system).typedActorOf(TypedProps[Named]())
|
||||
|
||||
// prepare routees
|
||||
val routees: List[HasName] = List.fill(5) { namedActor() }
|
||||
val routeePaths = routees.map { r =>
|
||||
TypedActor(system).getActorRefFor(r).path.toStringWithoutAddress
|
||||
}
|
||||
|
||||
// prepare untyped router
|
||||
val router: ActorRef = system.actorOf(RoundRobinGroup(routeePaths).props())
|
||||
|
||||
// prepare typed proxy, forwarding MethodCall messages to `router`
|
||||
val typedRouter: HasName =
|
||||
TypedActor(system).typedActorOf(TypedProps[Named](), actorRef = router)
|
||||
|
||||
println("actor was: " + typedRouter.name()) // name-184
|
||||
println("actor was: " + typedRouter.name()) // name-753
|
||||
println("actor was: " + typedRouter.name()) // name-320
|
||||
println("actor was: " + typedRouter.name()) // name-164
|
||||
//#typed-router
|
||||
|
||||
routees.foreach { TypedActor(system).poisonPill(_) }
|
||||
TypedActor(system).poisonPill(router)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue