add akka-typed project with generic ActorRef
This is the first step towards more type-safety in Actor interactions, comprising: * generic ActorRef[T] that only accepts T messages * generic ActorSystem[T] extends ActorRef[T] (sending to the guardian, whose Props[T] are provided for ActorSystem construction) * removed the Actor trait: everything in there has been made into messages and signals * new Behavior[T] abstraction that consumes messages (of type T) or Signals (lifecycle hooks, Terminated, ReceiveTimeout, Failed), producing the next Behavior[T] as the result each time * the ask pattern is provided and yields properly typed Futures * variants of ActorContext are provided for synchronous testing of Behaviors All of this is implemented without touching code outside akka-typed (apart from making guardianProps configurable), creating wrapper objects around ActorRef, ActorContext, ActorSystem, Props and providing an Actor implementation that just runs a Behavior.
This commit is contained in:
parent
50d1569f37
commit
d9efd041f7
40 changed files with 4724 additions and 21 deletions
131
akka-docs/rst/scala/code/docs/akka/typed/IntroSpec.scala
Normal file
131
akka-docs/rst/scala/code/docs/akka/typed/IntroSpec.scala
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/**
|
||||
* Copyright (C) 2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package docs.akka.typed
|
||||
|
||||
//#imports
|
||||
import akka.typed._
|
||||
import akka.typed.ScalaDSL._
|
||||
import akka.typed.AskPattern._
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.Await
|
||||
//#imports
|
||||
import akka.testkit.AkkaSpec
|
||||
import akka.typed.TypedSpec
|
||||
|
||||
object IntroSpec {
|
||||
|
||||
//#hello-world-actor
|
||||
object HelloWorld {
|
||||
final case class Greet(whom: String, replyTo: ActorRef[Greeted])
|
||||
final case class Greeted(whom: String)
|
||||
|
||||
val greeter = Static[Greet] { msg ⇒
|
||||
println(s"Hello ${msg.whom}!")
|
||||
msg.replyTo ! Greeted(msg.whom)
|
||||
}
|
||||
}
|
||||
//#hello-world-actor
|
||||
|
||||
//#chatroom-actor
|
||||
object ChatRoom {
|
||||
//#chatroom-protocol
|
||||
sealed trait Command
|
||||
final case class GetSession(screenName: String, replyTo: ActorRef[SessionEvent])
|
||||
extends Command
|
||||
//#chatroom-protocol
|
||||
//#chatroom-behavior
|
||||
private final case class PostSessionMessage(screenName: String, message: String)
|
||||
extends Command
|
||||
//#chatroom-behavior
|
||||
//#chatroom-protocol
|
||||
|
||||
sealed trait SessionEvent
|
||||
final case class SessionGranted(handle: ActorRef[PostMessage]) extends SessionEvent
|
||||
final case class SessionDenied(reason: String) extends SessionEvent
|
||||
final case class MessagePosted(screenName: String, message: String) extends SessionEvent
|
||||
|
||||
final case class PostMessage(message: String)
|
||||
//#chatroom-protocol
|
||||
//#chatroom-behavior
|
||||
|
||||
val behavior: Behavior[GetSession] =
|
||||
ContextAware[Command] { ctx ⇒
|
||||
var sessions = List.empty[ActorRef[SessionEvent]]
|
||||
|
||||
Static {
|
||||
case GetSession(screenName, client) ⇒
|
||||
sessions ::= client
|
||||
val wrapper = ctx.spawnAdapter {
|
||||
p: PostMessage ⇒ PostSessionMessage(screenName, p.message)
|
||||
}
|
||||
client ! SessionGranted(wrapper)
|
||||
case PostSessionMessage(screenName, message) ⇒
|
||||
val mp = MessagePosted(screenName, message)
|
||||
sessions foreach (_ ! mp)
|
||||
}
|
||||
}.narrow // only expose GetSession to the outside
|
||||
//#chatroom-behavior
|
||||
}
|
||||
//#chatroom-actor
|
||||
|
||||
}
|
||||
|
||||
class IntroSpec extends TypedSpec {
|
||||
import IntroSpec._
|
||||
|
||||
def `must say hello`(): Unit = {
|
||||
//#hello-world
|
||||
import HelloWorld._
|
||||
// using global pool since we want to run tasks after system shutdown
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
val system: ActorSystem[Greet] = ActorSystem("hello", Props(greeter))
|
||||
|
||||
val future: Future[Greeted] = system ? (Greet("world", _))
|
||||
|
||||
for {
|
||||
greeting <- future.recover { case ex => ex.getMessage }
|
||||
done <- { println(s"result: $greeting"); system.terminate() }
|
||||
} println("system terminated")
|
||||
//#hello-world
|
||||
}
|
||||
|
||||
def `must chat`(): Unit = {
|
||||
//#chatroom-gabbler
|
||||
import ChatRoom._
|
||||
|
||||
val gabbler: Behavior[SessionEvent] =
|
||||
Total {
|
||||
case SessionDenied(reason) ⇒
|
||||
println(s"cannot start chat room session: $reason")
|
||||
Stopped
|
||||
case SessionGranted(handle) ⇒
|
||||
handle ! PostMessage("Hello World!")
|
||||
Same
|
||||
case MessagePosted(screenName, message) ⇒
|
||||
println(s"message has been posted by '$screenName': $message")
|
||||
Stopped
|
||||
}
|
||||
//#chatroom-gabbler
|
||||
|
||||
//#chatroom-main
|
||||
val main: Behavior[Unit] =
|
||||
Full {
|
||||
case Sig(ctx, PreStart) ⇒
|
||||
val chatRoom = ctx.spawn(Props(ChatRoom.behavior), "chatroom")
|
||||
val gabblerRef = ctx.spawn(Props(gabbler), "gabbler")
|
||||
ctx.watch(gabblerRef)
|
||||
chatRoom ! GetSession("ol’ Gabbler", gabblerRef)
|
||||
Same
|
||||
case Sig(_, Terminated(ref)) ⇒
|
||||
Stopped
|
||||
}
|
||||
|
||||
val system = ActorSystem("ChatRoomDemo", Props(main))
|
||||
Await.result(system.whenTerminated, 1.second)
|
||||
//#chatroom-main
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue