better Typed HelloWorld, #24491
* single actor hello world is too unrealistic and shows the wrong things * although this is longer I think it's better java and feedback
This commit is contained in:
parent
4b54941947
commit
054573e7e1
7 changed files with 182 additions and 117 deletions
|
|
@ -5,21 +5,19 @@
|
||||||
package jdocs.akka.typed;
|
package jdocs.akka.typed;
|
||||||
|
|
||||||
//#imports
|
//#imports
|
||||||
|
|
||||||
import akka.actor.typed.ActorRef;
|
import akka.actor.typed.ActorRef;
|
||||||
import akka.actor.typed.ActorSystem;
|
import akka.actor.typed.ActorSystem;
|
||||||
import akka.actor.typed.Behavior;
|
import akka.actor.typed.Behavior;
|
||||||
import akka.actor.typed.Terminated;
|
import akka.actor.typed.Terminated;
|
||||||
import akka.actor.typed.javadsl.Behaviors;
|
import akka.actor.typed.javadsl.Behaviors;
|
||||||
import akka.actor.typed.javadsl.AskPattern;
|
|
||||||
import akka.util.Timeout;
|
|
||||||
|
|
||||||
//#imports
|
//#imports
|
||||||
|
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletionStage;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class IntroTest {
|
public class IntroTest {
|
||||||
|
|
||||||
|
|
@ -42,35 +40,81 @@ public class IntroTest {
|
||||||
|
|
||||||
public static final class Greeted {
|
public static final class Greeted {
|
||||||
public final String whom;
|
public final String whom;
|
||||||
|
public final ActorRef<Greet> from;
|
||||||
|
|
||||||
public Greeted(String whom) {
|
public Greeted(String whom, ActorRef<Greet> from) {
|
||||||
this.whom = whom;
|
this.whom = whom;
|
||||||
|
this.from = from;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Behavior<Greet> greeter = Behaviors.receive((ctx, msg) -> {
|
public static final Behavior<Greet> greeter = Behaviors.receive((ctx, msg) -> {
|
||||||
System.out.println("Hello " + msg.whom + "!");
|
ctx.getLog().info("Hello {}!", msg.whom);
|
||||||
msg.replyTo.tell(new Greeted(msg.whom));
|
msg.replyTo.tell(new Greeted(msg.whom, ctx.getSelf()));
|
||||||
return Behaviors.same();
|
return Behaviors.same();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//#hello-world-actor
|
//#hello-world-actor
|
||||||
|
|
||||||
public static void main(String[] args) {
|
//#hello-world-bot
|
||||||
//#hello-world
|
public abstract static class HelloWorldBot {
|
||||||
final ActorSystem<HelloWorld.Greet> system =
|
private HelloWorldBot() {
|
||||||
ActorSystem.create(HelloWorld.greeter, "hello");
|
}
|
||||||
|
|
||||||
final CompletionStage<HelloWorld.Greeted> reply =
|
public static final Behavior<HelloWorld.Greeted> bot(int greetingCounter, int max) {
|
||||||
AskPattern.ask(system,
|
return Behaviors.receive((ctx, msg) -> {
|
||||||
(ActorRef<HelloWorld.Greeted> replyTo) -> new HelloWorld.Greet("world", replyTo),
|
int n = greetingCounter + 1;
|
||||||
new Timeout(3, TimeUnit.SECONDS), system.scheduler());
|
ctx.getLog().info("Greeting {} for {}", n, msg.whom);
|
||||||
|
if (n == max) {
|
||||||
|
return Behaviors.stopped();
|
||||||
|
} else {
|
||||||
|
msg.from.tell(new HelloWorld.Greet(msg.whom, ctx.getSelf()));
|
||||||
|
return bot(n, max);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#hello-world-bot
|
||||||
|
|
||||||
reply.thenAccept(greeting -> {
|
//#hello-world-main
|
||||||
System.out.println("result: " + greeting.whom);
|
public abstract static class HelloWorldMain {
|
||||||
system.terminate();
|
private HelloWorldMain() {
|
||||||
});
|
}
|
||||||
|
|
||||||
|
public static class Start {
|
||||||
|
public final String name;
|
||||||
|
|
||||||
|
public Start(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Behavior<Start> main =
|
||||||
|
Behaviors.setup( context -> {
|
||||||
|
final ActorRef<HelloWorld.Greet> greeter =
|
||||||
|
context.spawn(HelloWorld.greeter, "greeter");
|
||||||
|
|
||||||
|
return Behaviors.receiveMessage(msg -> {
|
||||||
|
ActorRef<HelloWorld.Greeted> replyTo =
|
||||||
|
context.spawn(HelloWorldBot.bot(0, 3), msg.name);
|
||||||
|
greeter.tell(new HelloWorld.Greet(msg.name, replyTo));
|
||||||
|
return Behaviors.same();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//#hello-world-main
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
//#hello-world
|
//#hello-world
|
||||||
|
final ActorSystem<HelloWorldMain.Start> system =
|
||||||
|
ActorSystem.create(HelloWorldMain.main, "hello");
|
||||||
|
|
||||||
|
system.tell(new HelloWorldMain.Start("World"));
|
||||||
|
system.tell(new HelloWorldMain.Start("Akka"));
|
||||||
|
//#hello-world
|
||||||
|
|
||||||
|
Thread.sleep(3000);
|
||||||
|
system.terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
//#chatroom-actor
|
//#chatroom-actor
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,17 @@
|
||||||
package docs.akka.typed
|
package docs.akka.typed
|
||||||
|
|
||||||
//#imports
|
//#imports
|
||||||
import java.net.URLEncoder
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
|
|
||||||
import akka.NotUsed
|
import akka.NotUsed
|
||||||
import akka.actor.typed.scaladsl.AskPattern._
|
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import akka.actor.typed.{ ActorRef, ActorSystem, Behavior, Terminated }
|
import akka.actor.typed.{ ActorRef, ActorSystem, Behavior, Terminated }
|
||||||
import akka.testkit.typed.scaladsl.ActorTestKit
|
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
import scala.concurrent.{ Await, Future }
|
|
||||||
//#imports
|
//#imports
|
||||||
|
|
||||||
|
import akka.testkit.typed.scaladsl.ActorTestKit
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
import scala.concurrent.Await
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
import akka.actor.typed.TypedAkkaSpecWithShutdown
|
import akka.actor.typed.TypedAkkaSpecWithShutdown
|
||||||
|
|
||||||
object IntroSpec {
|
object IntroSpec {
|
||||||
|
|
@ -25,16 +23,51 @@ object IntroSpec {
|
||||||
//#hello-world-actor
|
//#hello-world-actor
|
||||||
object HelloWorld {
|
object HelloWorld {
|
||||||
final case class Greet(whom: String, replyTo: ActorRef[Greeted])
|
final case class Greet(whom: String, replyTo: ActorRef[Greeted])
|
||||||
final case class Greeted(whom: String)
|
final case class Greeted(whom: String, from: ActorRef[Greet])
|
||||||
|
|
||||||
val greeter = Behaviors.receive[Greet] { (_, msg) ⇒
|
val greeter: Behavior[Greet] = Behaviors.receive { (ctx, msg) ⇒
|
||||||
println(s"Hello ${msg.whom}!")
|
ctx.log.info("Hello {}!", msg.whom)
|
||||||
msg.replyTo ! Greeted(msg.whom)
|
msg.replyTo ! Greeted(msg.whom, ctx.self)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//#hello-world-actor
|
//#hello-world-actor
|
||||||
|
|
||||||
|
//#hello-world-bot
|
||||||
|
object HelloWorldBot {
|
||||||
|
|
||||||
|
def bot(greetingCounter: Int, max: Int): Behavior[HelloWorld.Greeted] =
|
||||||
|
Behaviors.receive { (ctx, msg) ⇒
|
||||||
|
val n = greetingCounter + 1
|
||||||
|
ctx.log.info("Greeting {} for {}", n, msg.whom)
|
||||||
|
if (n == max) {
|
||||||
|
Behaviors.stopped
|
||||||
|
} else {
|
||||||
|
msg.from ! HelloWorld.Greet(msg.whom, ctx.self)
|
||||||
|
bot(n, max)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#hello-world-bot
|
||||||
|
|
||||||
|
//#hello-world-main
|
||||||
|
object HelloWorldMain {
|
||||||
|
|
||||||
|
final case class Start(name: String)
|
||||||
|
|
||||||
|
val main: Behavior[Start] =
|
||||||
|
Behaviors.setup { context ⇒
|
||||||
|
val greeter = context.spawn(HelloWorld.greeter, "greeter")
|
||||||
|
|
||||||
|
Behaviors.receiveMessage { msg ⇒
|
||||||
|
val replyTo = context.spawn(HelloWorldBot.bot(greetingCounter = 0, max = 3), msg.name)
|
||||||
|
greeter ! HelloWorld.Greet(msg.name, replyTo)
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#hello-world-main
|
||||||
|
|
||||||
//#chatroom-actor
|
//#chatroom-actor
|
||||||
object ChatRoom {
|
object ChatRoom {
|
||||||
//#chatroom-protocol
|
//#chatroom-protocol
|
||||||
|
|
@ -63,7 +96,7 @@ object IntroSpec {
|
||||||
chatRoom(List.empty)
|
chatRoom(List.empty)
|
||||||
|
|
||||||
private def chatRoom(sessions: List[ActorRef[SessionCommand]]): Behavior[RoomCommand] =
|
private def chatRoom(sessions: List[ActorRef[SessionCommand]]): Behavior[RoomCommand] =
|
||||||
Behaviors.receive[RoomCommand] { (ctx, msg) ⇒
|
Behaviors.receive { (ctx, msg) ⇒
|
||||||
msg match {
|
msg match {
|
||||||
case GetSession(screenName, client) ⇒
|
case GetSession(screenName, client) ⇒
|
||||||
// create a child actor for further interaction with the client
|
// create a child actor for further interaction with the client
|
||||||
|
|
@ -107,46 +140,38 @@ class IntroSpec extends ActorTestKit with TypedAkkaSpecWithShutdown {
|
||||||
|
|
||||||
"Hello world" must {
|
"Hello world" must {
|
||||||
"say hello" in {
|
"say hello" in {
|
||||||
// TODO Implicits.global is not something we would like to encourage in docs
|
|
||||||
//#hello-world
|
//#hello-world
|
||||||
import HelloWorld._
|
|
||||||
// using global pool since we want to run tasks after system.terminate
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
|
|
||||||
val system: ActorSystem[Greet] = ActorSystem(greeter, "hello")
|
val system: ActorSystem[HelloWorldMain.Start] =
|
||||||
|
ActorSystem(HelloWorldMain.main, "hello")
|
||||||
|
|
||||||
val future: Future[Greeted] = system ? (Greet("world", _))
|
system ! HelloWorldMain.Start("World")
|
||||||
|
system ! HelloWorldMain.Start("Akka")
|
||||||
|
|
||||||
for {
|
|
||||||
greeting ← future.recover { case ex ⇒ ex.getMessage }
|
|
||||||
done ← {
|
|
||||||
println(s"result: $greeting")
|
|
||||||
system.terminate()
|
|
||||||
}
|
|
||||||
} println("system terminated")
|
|
||||||
//#hello-world
|
//#hello-world
|
||||||
|
|
||||||
|
Thread.sleep(500) // it will not fail if too short
|
||||||
|
ActorTestKit.shutdown(system)
|
||||||
}
|
}
|
||||||
|
|
||||||
"chat" in {
|
"chat" in {
|
||||||
//#chatroom-gabbler
|
//#chatroom-gabbler
|
||||||
import ChatRoom._
|
import ChatRoom._
|
||||||
|
|
||||||
val gabbler =
|
val gabbler: Behavior[SessionEvent] =
|
||||||
Behaviors.receive[SessionEvent] { (_, msg) ⇒
|
Behaviors.receiveMessage {
|
||||||
msg match {
|
//#chatroom-gabbler
|
||||||
//#chatroom-gabbler
|
// We document that the compiler warns about the missing handler for `SessionDenied`
|
||||||
// We document that the compiler warns about the missing handler for `SessionDenied`
|
case SessionDenied(reason) ⇒
|
||||||
case SessionDenied(reason) ⇒
|
println(s"cannot start chat room session: $reason")
|
||||||
println(s"cannot start chat room session: $reason")
|
Behaviors.stopped
|
||||||
Behaviors.stopped
|
//#chatroom-gabbler
|
||||||
//#chatroom-gabbler
|
case SessionGranted(handle) ⇒
|
||||||
case SessionGranted(handle) ⇒
|
handle ! PostMessage("Hello World!")
|
||||||
handle ! PostMessage("Hello World!")
|
Behaviors.same
|
||||||
Behaviors.same
|
case MessagePosted(screenName, message) ⇒
|
||||||
case MessagePosted(screenName, message) ⇒
|
println(s"message has been posted by '$screenName': $message")
|
||||||
println(s"message has been posted by '$screenName': $message")
|
Behaviors.stopped
|
||||||
Behaviors.stopped
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//#chatroom-gabbler
|
//#chatroom-gabbler
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -211,11 +211,11 @@ object Behaviors {
|
||||||
* final Behavior[DbCommand] dbConnector = ...
|
* final Behavior[DbCommand] dbConnector = ...
|
||||||
*
|
*
|
||||||
* final Behavior[DbCommand] dbRestarts =
|
* final Behavior[DbCommand] dbRestarts =
|
||||||
* Actor.supervise(dbConnector)
|
* Behaviors.supervise(dbConnector)
|
||||||
* .onFailure(SupervisorStrategy.restart) // handle all NonFatal exceptions
|
* .onFailure(SupervisorStrategy.restart) // handle all NonFatal exceptions
|
||||||
*
|
*
|
||||||
* final Behavior[DbCommand] dbSpecificResumes =
|
* final Behavior[DbCommand] dbSpecificResumes =
|
||||||
* Actor.supervise(dbConnector)
|
* Behaviors.supervise(dbConnector)
|
||||||
* .onFailure[IndexOutOfBoundsException](SupervisorStrategy.resume) // resume for IndexOutOfBoundsException exceptions
|
* .onFailure[IndexOutOfBoundsException](SupervisorStrategy.resume) // resume for IndexOutOfBoundsException exceptions
|
||||||
* }}}
|
* }}}
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Support for scheduled `self` messages in an actor.
|
* Support for scheduled `self` messages in an actor.
|
||||||
* It is used with `Actor.withTimers`, which also takes care of the
|
* It is used with `Behaviors.withTimers`, which also takes care of the
|
||||||
* lifecycle of the timers such as cancelling them when the actor
|
* lifecycle of the timers such as cancelling them when the actor
|
||||||
* is restarted or stopped.
|
* is restarted or stopped.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -171,11 +171,11 @@ object Behaviors {
|
||||||
* val dbConnector: Behavior[DbCommand] = ...
|
* val dbConnector: Behavior[DbCommand] = ...
|
||||||
*
|
*
|
||||||
* val dbRestarts =
|
* val dbRestarts =
|
||||||
* Actor.supervise(dbConnector)
|
* Behaviors.supervise(dbConnector)
|
||||||
* .onFailure(SupervisorStrategy.restart) // handle all NonFatal exceptions
|
* .onFailure(SupervisorStrategy.restart) // handle all NonFatal exceptions
|
||||||
*
|
*
|
||||||
* val dbSpecificResumes =
|
* val dbSpecificResumes =
|
||||||
* Actor.supervise(dbConnector)
|
* Behaviors.supervise(dbConnector)
|
||||||
* .onFailure[IndexOutOfBoundsException](SupervisorStrategy.resume) // resume for IndexOutOfBoundsException exceptions
|
* .onFailure[IndexOutOfBoundsException](SupervisorStrategy.resume) // resume for IndexOutOfBoundsException exceptions
|
||||||
* }}}
|
* }}}
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Support for scheduled `self` messages in an actor.
|
* Support for scheduled `self` messages in an actor.
|
||||||
* It is used with `Actor.withTimers`.
|
* It is used with `Behaviors.withTimers`.
|
||||||
* Timers are bound to the lifecycle of the actor that owns it,
|
* Timers are bound to the lifecycle of the actor that owns it,
|
||||||
* and thus are cancelled automatically when it is restarted or stopped.
|
* and thus are cancelled automatically when it is restarted or stopped.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -51,9 +51,9 @@ message.
|
||||||
|
|
||||||
The behavior of the Actor is defined as the `greeter` value with the help
|
The behavior of the Actor is defined as the `greeter` value with the help
|
||||||
of the `receive` behavior factory. Processing the next message then results
|
of the `receive` behavior factory. Processing the next message then results
|
||||||
in a new behavior that can potentially be different from this one. State is
|
in a new behavior that can potentially be different from this one. State is
|
||||||
updated by returning a new behavior that holds the new immutable state. In this
|
updated by returning a new behavior that holds the new immutable state. In this
|
||||||
case we don't need to update any state, so we return `Same`, which means
|
case we don't need to update any state, so we return `same`, which means
|
||||||
the next behavior is "the same as the current one".
|
the next behavior is "the same as the current one".
|
||||||
|
|
||||||
The type of the messages handled by this behavior is declared to be of class
|
The type of the messages handled by this behavior is declared to be of class
|
||||||
|
|
@ -73,6 +73,31 @@ protocol but Actors can model arbitrarily complex protocols when needed. The
|
||||||
protocol is bundled together with the behavior that implements it in a nicely
|
protocol is bundled together with the behavior that implements it in a nicely
|
||||||
wrapped scope—the `HelloWorld` @scala[object]@java[class].
|
wrapped scope—the `HelloWorld` @scala[object]@java[class].
|
||||||
|
|
||||||
|
As Carl Hewitt said, one Actor is no Actor—it would be quite lonely with
|
||||||
|
nobody to talk to. We need another Actor that that interacts with the `greeter`.
|
||||||
|
Let's make a `bot` that receives the reply from the `greeter` and sends a number
|
||||||
|
of additional greeting messages and collect the replies until a given max number
|
||||||
|
of messages have been reached.
|
||||||
|
|
||||||
|
Scala
|
||||||
|
: @@snip [IntroSpec.scala]($akka$/akka-actor-typed-tests/src/test/scala/docs/akka/typed/IntroSpec.scala) { #hello-world-bot }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [IntroSpec.scala]($akka$/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/IntroTest.java) { #hello-world-bot }
|
||||||
|
|
||||||
|
Note how this Actor manages the counter by changing the behavior for each `Greeted` reply
|
||||||
|
rather than using any variables.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
A third actor spawns the `greeter` and the `bot` and starts the interaction between those.
|
||||||
|
|
||||||
|
Scala
|
||||||
|
: @@snip [IntroSpec.scala]($akka$/akka-actor-typed-tests/src/test/scala/docs/akka/typed/IntroSpec.scala) { #hello-world-main }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [IntroSpec.scala]($akka$/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/IntroTest.java) { #hello-world-main }
|
||||||
|
|
||||||
Now we want to try out this Actor, so we must start an ActorSystem to host it:
|
Now we want to try out this Actor, so we must start an ActorSystem to host it:
|
||||||
|
|
||||||
Scala
|
Scala
|
||||||
|
|
@ -81,58 +106,29 @@ Scala
|
||||||
Java
|
Java
|
||||||
: @@snip [IntroSpec.scala]($akka$/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/IntroTest.java) { #hello-world }
|
: @@snip [IntroSpec.scala]($akka$/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/IntroTest.java) { #hello-world }
|
||||||
|
|
||||||
After importing the Actor’s protocol definition we start an Actor system from
|
We start an Actor system from the defined `main` behavior and send two `Start` messages that
|
||||||
the defined `greeter` behavior.
|
will kick-off the interaction between two separate `bot` actors and the single `greeter` actor.
|
||||||
|
|
||||||
As Carl Hewitt said, one Actor is no Actor—it would be quite lonely with
|
The console output may look like this:
|
||||||
nobody to talk to. In this sense the example is a little cruel because we only
|
|
||||||
give the `HelloWorld` Actor a fake person to talk to—the “ask” pattern
|
|
||||||
(represented by the `?` operator) can be used to send a message such that the
|
|
||||||
reply fulfills a @scala[`Promise` to which we get back the corresponding `Future`]@java[`CompletionStage`].
|
|
||||||
|
|
||||||
@@@ div {.group-scala}
|
```
|
||||||
|
[INFO] [03/13/2018 15:50:05.814] [hello-akka.actor.default-dispatcher-4] [akka://hello/user/greeter] Hello World!
|
||||||
Note that the `Future` that is returned by the “ask” operation is
|
[INFO] [03/13/2018 15:50:05.815] [hello-akka.actor.default-dispatcher-4] [akka://hello/user/greeter] Hello Akka!
|
||||||
properly typed already, no type checks or casts needed. This is possible due to
|
[INFO] [03/13/2018 15:50:05.815] [hello-akka.actor.default-dispatcher-2] [akka://hello/user/World] Greeting 1 for World
|
||||||
the type information that is part of the message protocol: the `?` operator
|
[INFO] [03/13/2018 15:50:05.815] [hello-akka.actor.default-dispatcher-4] [akka://hello/user/Akka] Greeting 1 for Akka
|
||||||
takes as argument a function that accepts an `ActorRef[U]` (which
|
[INFO] [03/13/2018 15:50:05.815] [hello-akka.actor.default-dispatcher-5] [akka://hello/user/greeter] Hello World!
|
||||||
explains the `_` hole in the expression on line 7 above) and the `replyTo`
|
[INFO] [03/13/2018 15:50:05.815] [hello-akka.actor.default-dispatcher-5] [akka://hello/user/greeter] Hello Akka!
|
||||||
parameter which we fill in is of type `ActorRef[Greeted]`, which
|
[INFO] [03/13/2018 15:50:05.815] [hello-akka.actor.default-dispatcher-4] [akka://hello/user/World] Greeting 2 for World
|
||||||
means that the value that fulfills the `Promise` can only be of type
|
[INFO] [03/13/2018 15:50:05.815] [hello-akka.actor.default-dispatcher-5] [akka://hello/user/greeter] Hello World!
|
||||||
`Greeted`.
|
[INFO] [03/13/2018 15:50:05.815] [hello-akka.actor.default-dispatcher-4] [akka://hello/user/Akka] Greeting 2 for Akka
|
||||||
|
[INFO] [03/13/2018 15:50:05.816] [hello-akka.actor.default-dispatcher-5] [akka://hello/user/greeter] Hello Akka!
|
||||||
@@@
|
[INFO] [03/13/2018 15:50:05.816] [hello-akka.actor.default-dispatcher-4] [akka://hello/user/World] Greeting 3 for World
|
||||||
|
[INFO] [03/13/2018 15:50:05.816] [hello-akka.actor.default-dispatcher-6] [akka://hello/user/Akka] Greeting 3 for Akka
|
||||||
@@@ div {.group-java}
|
```
|
||||||
|
|
||||||
Note that the `CompletionStage` that is returned by the “ask” operation is
|
|
||||||
properly typed already, no type checks or casts needed. This is possible due to
|
|
||||||
the type information that is part of the message protocol: the `ask` operator
|
|
||||||
takes as argument a function that pass an `ActorRef<U>`, which is the
|
|
||||||
`replyTo` parameter of the `Greet` message, which means that when sending
|
|
||||||
the reply message to that `ActorRef` the message that fulfills the
|
|
||||||
`CompletionStage` can only be of type `Greeted`.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
We use this here to send the `Greet` command to the Actor and when the
|
|
||||||
reply comes back we will print it out and tell the actor system to shut down.
|
|
||||||
|
|
||||||
@@@ div {.group-scala}
|
|
||||||
|
|
||||||
The `recovery` combinator on the original `Future` is
|
|
||||||
needed in order to ensure proper system shutdown even in case something went
|
|
||||||
wrong; the `flatMap` and `map` combinators that the `for` expression gets
|
|
||||||
turned into care only about the “happy path” and if the `future` failed with
|
|
||||||
a timeout then no `greeting` would be extracted and nothing would happen.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
In the next section we demonstrate this on a more realistic example.
|
|
||||||
|
|
||||||
## A More Complex Example
|
## A More Complex Example
|
||||||
|
|
||||||
The next example demonstrates some important patterns:
|
The next example is more realistic and demonstrates some important patterns:
|
||||||
|
|
||||||
* Using a sealed trait and case class/objects to represent multiple messages an actor can receive
|
* Using a sealed trait and case class/objects to represent multiple messages an actor can receive
|
||||||
* Handle sessions by using child actors
|
* Handle sessions by using child actors
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue