'git mv' rst resources to md

This is mainly intended to keep the git history as neat as possible.
No actual conversion yet, so now both the rst and the paradox docs
are broken, which will be fixed in the next commits.
This commit is contained in:
Arnout Engelen 2017-05-10 15:44:43 +02:00
parent 5507147073
commit a4a0d308ad
553 changed files with 0 additions and 0 deletions

View file

@ -1,663 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor
import jdocs.actor.ImmutableMessage
import language.postfixOps
//#imports1
import akka.actor.Actor
import akka.actor.Props
import akka.event.Logging
//#imports1
import scala.concurrent.Future
import akka.actor.{ ActorRef, ActorSystem, PoisonPill, Terminated, ActorLogging }
import org.scalatest.{ BeforeAndAfterAll, WordSpec }
import akka.testkit._
import akka.util._
import scala.concurrent.duration._
import scala.concurrent.Await
import akka.Done
import akka.actor.CoordinatedShutdown
//#my-actor
class MyActor extends Actor {
val log = Logging(context.system, this)
def receive = {
case "test" => log.info("received test")
case _ => log.info("received unknown message")
}
}
//#my-actor
final case class DoIt(msg: ImmutableMessage)
final case class Message(s: String)
//#context-actorOf
class FirstActor extends Actor {
val child = context.actorOf(Props[MyActor], name = "myChild")
//#plus-some-behavior
def receive = {
case x => sender() ! x
}
//#plus-some-behavior
}
//#context-actorOf
class ActorWithArgs(arg: String) extends Actor {
def receive = { case _ => () }
}
//#actor-with-value-class-argument
class Argument(val value: String) extends AnyVal
class ValueClassActor(arg: Argument) extends Actor {
def receive = { case _ => () }
}
object ValueClassActor {
def props1(arg: Argument) = Props(classOf[ValueClassActor], arg) // fails at runtime
def props2(arg: Argument) = Props(classOf[ValueClassActor], arg.value) // ok
def props3(arg: Argument) = Props(new ValueClassActor(arg)) // ok
}
//#actor-with-value-class-argument
class DemoActorWrapper extends Actor {
//#props-factory
object DemoActor {
/**
* Create Props for an actor of this type.
*
* @param magicNumber The magic number to be passed to this actors constructor.
* @return a Props for creating this actor, which can then be further configured
* (e.g. calling `.withDispatcher()` on it)
*/
def props(magicNumber: Int): Props = Props(new DemoActor(magicNumber))
}
class DemoActor(magicNumber: Int) extends Actor {
def receive = {
case x: Int => sender() ! (x + magicNumber)
}
}
class SomeOtherActor extends Actor {
// Props(new DemoActor(42)) would not be safe
context.actorOf(DemoActor.props(42), "demo")
// ...
//#props-factory
def receive = {
case msg =>
}
//#props-factory
}
//#props-factory
def receive = Actor.emptyBehavior
}
class ActorWithMessagesWrapper {
//#messages-in-companion
object MyActor {
case class Greeting(from: String)
case object Goodbye
}
class MyActor extends Actor with ActorLogging {
import MyActor._
def receive = {
case Greeting(greeter) => log.info(s"I was greeted by $greeter.")
case Goodbye => log.info("Someone said goodbye to me.")
}
}
//#messages-in-companion
def receive = Actor.emptyBehavior
}
class Hook extends Actor {
var child: ActorRef = _
//#preStart
override def preStart() {
child = context.actorOf(Props[MyActor], "child")
}
//#preStart
def receive = Actor.emptyBehavior
//#postStop
override def postStop() {
//#clean-up-some-resources
()
//#clean-up-some-resources
}
//#postStop
}
class ReplyException extends Actor {
def receive = {
case _ =>
//#reply-exception
try {
val result = operation()
sender() ! result
} catch {
case e: Exception =>
sender() ! akka.actor.Status.Failure(e)
throw e
}
//#reply-exception
}
def operation(): String = { "Hi" }
}
class StoppingActorsWrapper {
//#stoppingActors-actor
class MyActor extends Actor {
val child: ActorRef = ???
def receive = {
case "interrupt-child" =>
context stop child
case "done" =>
context stop self
}
}
//#stoppingActors-actor
}
//#gracefulStop-actor
object Manager {
case object Shutdown
}
class Manager extends Actor {
import Manager._
val worker = context.watch(context.actorOf(Props[Cruncher], "worker"))
def receive = {
case "job" => worker ! "crunch"
case Shutdown =>
worker ! PoisonPill
context become shuttingDown
}
def shuttingDown: Receive = {
case "job" => sender() ! "service unavailable, shutting down"
case Terminated(`worker`) =>
context stop self
}
}
//#gracefulStop-actor
class Cruncher extends Actor {
def receive = {
case "crunch" => // crunch...
}
}
//#swapper
case object Swap
class Swapper extends Actor {
import context._
val log = Logging(system, this)
def receive = {
case Swap =>
log.info("Hi")
become({
case Swap =>
log.info("Ho")
unbecome() // resets the latest 'become' (just for fun)
}, discardOld = false) // push on top instead of replace
}
}
object SwapperApp extends App {
val system = ActorSystem("SwapperSystem")
val swap = system.actorOf(Props[Swapper], name = "swapper")
swap ! Swap // logs Hi
swap ! Swap // logs Ho
swap ! Swap // logs Hi
swap ! Swap // logs Ho
swap ! Swap // logs Hi
swap ! Swap // logs Ho
}
//#swapper
//#receive-orElse
trait ProducerBehavior {
this: Actor =>
val producerBehavior: Receive = {
case GiveMeThings =>
sender() ! Give("thing")
}
}
trait ConsumerBehavior {
this: Actor with ActorLogging =>
val consumerBehavior: Receive = {
case ref: ActorRef =>
ref ! GiveMeThings
case Give(thing) =>
log.info("Got a thing! It's {}", thing)
}
}
class Producer extends Actor with ProducerBehavior {
def receive = producerBehavior
}
class Consumer extends Actor with ActorLogging with ConsumerBehavior {
def receive = consumerBehavior
}
class ProducerConsumer extends Actor with ActorLogging
with ProducerBehavior with ConsumerBehavior {
def receive = producerBehavior.orElse[Any, Unit](consumerBehavior)
}
// protocol
case object GiveMeThings
final case class Give(thing: Any)
//#receive-orElse
class ActorDocSpec extends AkkaSpec("""
akka.loglevel = INFO
akka.loggers = []
""") {
"import context" in {
new AnyRef {
//#import-context
class FirstActor extends Actor {
import context._
val myActor = actorOf(Props[MyActor], name = "myactor")
def receive = {
case x => myActor ! x
}
}
//#import-context
val first = system.actorOf(Props(classOf[FirstActor], this), name = "first")
system.stop(first)
}
}
"creating actor with system.actorOf" in {
val myActor = system.actorOf(Props[MyActor])
// testing the actor
// TODO: convert docs to AkkaSpec(Map(...))
val filter = EventFilter.custom {
case e: Logging.Info => true
case _ => false
}
system.eventStream.publish(TestEvent.Mute(filter))
system.eventStream.subscribe(testActor, classOf[Logging.Info])
myActor ! "test"
expectMsgPF(1 second) { case Logging.Info(_, _, "received test") => true }
myActor ! "unknown"
expectMsgPF(1 second) { case Logging.Info(_, _, "received unknown message") => true }
system.eventStream.unsubscribe(testActor)
system.eventStream.publish(TestEvent.UnMute(filter))
system.stop(myActor)
}
"creating a Props config" in {
//#creating-props
import akka.actor.Props
val props1 = Props[MyActor]
val props2 = Props(new ActorWithArgs("arg")) // careful, see below
val props3 = Props(classOf[ActorWithArgs], "arg") // no support for value class arguments
//#creating-props
//#creating-props-deprecated
// NOT RECOMMENDED within another actor:
// encourages to close over enclosing class
val props7 = Props(new MyActor)
//#creating-props-deprecated
}
"creating actor with Props" in {
//#system-actorOf
import akka.actor.ActorSystem
// ActorSystem is a heavy object: create only one per application
val system = ActorSystem("mySystem")
val myActor = system.actorOf(Props[MyActor], "myactor2")
//#system-actorOf
shutdown(system)
}
"creating actor with IndirectActorProducer" in {
class Echo(name: String) extends Actor {
def receive = {
case n: Int => sender() ! name
case message =>
val target = testActor
//#forward
target forward message
//#forward
}
}
val a: { def actorRef: ActorRef } = new AnyRef {
val applicationContext = this
//#creating-indirectly
import akka.actor.IndirectActorProducer
class DependencyInjector(applicationContext: AnyRef, beanName: String)
extends IndirectActorProducer {
override def actorClass = classOf[Actor]
override def produce =
//#obtain-fresh-Actor-instance-from-DI-framework
new Echo(beanName)
def this(beanName: String) = this("", beanName)
//#obtain-fresh-Actor-instance-from-DI-framework
}
val actorRef = system.actorOf(
Props(classOf[DependencyInjector], applicationContext, "hello"),
"helloBean")
//#creating-indirectly
}
val actorRef = {
import scala.language.reflectiveCalls
a.actorRef
}
val message = 42
implicit val self = testActor
//#tell
actorRef ! message
//#tell
expectMsg("hello")
actorRef ! "huhu"
expectMsg("huhu")
}
"using implicit timeout" in {
val myActor = system.actorOf(Props[FirstActor])
//#using-implicit-timeout
import scala.concurrent.duration._
import akka.util.Timeout
import akka.pattern.ask
implicit val timeout = Timeout(5 seconds)
val future = myActor ? "hello"
//#using-implicit-timeout
Await.result(future, timeout.duration) should be("hello")
}
"using explicit timeout" in {
val myActor = system.actorOf(Props[FirstActor])
//#using-explicit-timeout
import scala.concurrent.duration._
import akka.pattern.ask
val future = myActor.ask("hello")(5 seconds)
//#using-explicit-timeout
Await.result(future, 5 seconds) should be("hello")
}
"using receiveTimeout" in {
//#receive-timeout
import akka.actor.ReceiveTimeout
import scala.concurrent.duration._
class MyActor extends Actor {
// To set an initial delay
context.setReceiveTimeout(30 milliseconds)
def receive = {
case "Hello" =>
// To set in a response to a message
context.setReceiveTimeout(100 milliseconds)
case ReceiveTimeout =>
// To turn it off
context.setReceiveTimeout(Duration.Undefined)
throw new RuntimeException("Receive timed out")
}
}
//#receive-timeout
}
//#hot-swap-actor
class HotSwapActor extends Actor {
import context._
def angry: Receive = {
case "foo" => sender() ! "I am already angry?"
case "bar" => become(happy)
}
def happy: Receive = {
case "bar" => sender() ! "I am already happy :-)"
case "foo" => become(angry)
}
def receive = {
case "foo" => become(angry)
case "bar" => become(happy)
}
}
//#hot-swap-actor
"using hot-swap" in {
val actor = system.actorOf(Props(classOf[HotSwapActor], this), name = "hot")
}
"using Stash" in {
//#stash
import akka.actor.Stash
class ActorWithProtocol extends Actor with Stash {
def receive = {
case "open" =>
unstashAll()
context.become({
case "write" => // do writing...
case "close" =>
unstashAll()
context.unbecome()
case msg => stash()
}, discardOld = false) // stack on top instead of replacing
case msg => stash()
}
}
//#stash
}
"using watch" in {
new AnyRef {
//#watch
import akka.actor.{ Actor, Props, Terminated }
class WatchActor extends Actor {
val child = context.actorOf(Props.empty, "child")
context.watch(child) // <-- this is the only call needed for registration
var lastSender = context.system.deadLetters
def receive = {
case "kill" =>
context.stop(child); lastSender = sender()
case Terminated(`child`) => lastSender ! "finished"
}
}
//#watch
val a = system.actorOf(Props(classOf[WatchActor], this))
implicit val sender = testActor
a ! "kill"
expectMsg("finished")
}
}
"demonstrate ActorSelection" in {
val context = system
//#selection-local
// will look up this absolute path
context.actorSelection("/user/serviceA/aggregator")
// will look up sibling beneath same supervisor
context.actorSelection("../joe")
//#selection-local
//#selection-wildcard
// 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("../*")
//#selection-wildcard
//#selection-remote
context.actorSelection("akka.tcp://app@otherhost:1234/user/serviceB")
//#selection-remote
}
"using Identify" in {
new AnyRef {
//#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.empty)
val b = system.actorOf(Props(classOf[Follower], this))
watch(b)
system.stop(a)
expectMsgType[akka.actor.Terminated].actor should be(b)
}
}
"using pattern gracefulStop" in {
val actorRef = system.actorOf(Props[Manager])
//#gracefulStop
import akka.pattern.gracefulStop
import scala.concurrent.Await
try {
val stopped: Future[Boolean] = gracefulStop(actorRef, 5 seconds, Manager.Shutdown)
Await.result(stopped, 6 seconds)
// the actor has been stopped
} catch {
// the actor wasn't stopped within 5 seconds
case e: akka.pattern.AskTimeoutException =>
}
//#gracefulStop
}
"using pattern ask / pipeTo" in {
val actorA, actorB, actorC, actorD = system.actorOf(Props.empty)
//#ask-pipeTo
import akka.pattern.{ ask, pipe }
import system.dispatcher // The ExecutionContext that will be used
final case class Result(x: Int, s: String, d: Double)
case object Request
implicit val timeout = Timeout(5 seconds) // needed for `?` below
val f: Future[Result] =
for {
x <- ask(actorA, Request).mapTo[Int] // call pattern directly
s <- (actorB ask Request).mapTo[String] // call by implicit conversion
d <- (actorC ? Request).mapTo[Double] // call by symbolic name
} yield Result(x, s, d)
f pipeTo actorD // .. or ..
pipe(f) to actorD
//#ask-pipeTo
}
class Replier extends Actor {
def receive = {
case ref: ActorRef =>
//#reply-with-sender
sender().tell("reply", context.parent) // replies will go back to parent
sender().!("reply")(context.parent) // alternative syntax (beware of the parens!)
//#reply-with-sender
case x =>
//#reply-without-sender
sender() ! x // replies will go to this actor
//#reply-without-sender
}
}
"replying with own or other sender" in {
val actor = system.actorOf(Props(classOf[Replier], this))
implicit val me = testActor
actor ! 42
expectMsg(42)
lastSender should be(actor)
actor ! me
expectMsg("reply")
lastSender.path.toStringWithoutAddress should be("/user")
expectMsg("reply")
lastSender.path.toStringWithoutAddress should be("/user")
}
"using ActorDSL outside of akka.actor package" in {
import akka.actor.ActorDSL._
actor(new Act {
superviseWith(OneForOneStrategy() { case _ => Stop; Restart; Resume; Escalate })
superviseWith(AllForOneStrategy() { case _ => Stop; Restart; Resume; Escalate })
})
}
"using CoordinatedShutdown" in {
val someActor = system.actorOf(Props(classOf[Replier], this))
//#coordinated-shutdown-addTask
CoordinatedShutdown(system).addTask(
CoordinatedShutdown.PhaseBeforeServiceUnbind, "someTaskName") { () =>
import akka.pattern.ask
import system.dispatcher
implicit val timeout = Timeout(5.seconds)
(someActor ? "stop").map(_ => Done)
}
//#coordinated-shutdown-addTask
//#coordinated-shutdown-jvm-hook
CoordinatedShutdown(system).addJvmShutdownHook {
println("custom JVM shutdown hook...")
}
//#coordinated-shutdown-jvm-hook
// don't run this
def dummy(): Unit = {
//#coordinated-shutdown-run
val done: Future[Done] = CoordinatedShutdown(system).run()
//#coordinated-shutdown-run
}
}
}

View file

@ -1,42 +0,0 @@
/*
* Copyright (C) 2016-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor
//#bytebufserializer-with-manifest
import java.nio.ByteBuffer
import akka.serialization.ByteBufferSerializer
import akka.serialization.SerializerWithStringManifest
//#bytebufserializer-with-manifest
class ByteBufferSerializerDocSpec {
//#bytebufserializer-with-manifest
class ExampleByteBufSerializer extends SerializerWithStringManifest with ByteBufferSerializer {
override def identifier: Int = 1337
override def manifest(o: AnyRef): String = "naive-toStringImpl"
// Implement this method for compatibility with `SerializerWithStringManifest`.
override def toBinary(o: AnyRef): Array[Byte] = {
// in production code, aquire this from a BufferPool
val buf = ByteBuffer.allocate(256)
toBinary(o, buf)
buf.flip()
val bytes = new Array[Byte](buf.remaining)
buf.get(bytes)
bytes
}
// Implement this method for compatibility with `SerializerWithStringManifest`.
override def fromBinary(bytes: Array[Byte], manifest: String): AnyRef =
fromBinary(ByteBuffer.wrap(bytes), manifest)
// Actual implementation in the ByteBuffer versions of to/fromBinary:
override def toBinary(o: AnyRef, buf: ByteBuffer): Unit = ??? // implement actual logic here
override def fromBinary(buf: ByteBuffer, manifest: String): AnyRef = ??? // implement actual logic here
}
//#bytebufserializer-with-manifest
}

View file

@ -1,231 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor
import language.postfixOps
import akka.testkit.{ AkkaSpec => MyFavoriteTestFrameWorkPlusAkkaTestKit }
import akka.util.ByteString
//#test-code
import akka.actor.Props
import scala.collection.immutable
object FSMDocSpec {
// messages and data types
//#test-code
import akka.actor.ActorRef
//#simple-events
// received events
final case class SetTarget(ref: ActorRef)
final case class Queue(obj: Any)
case object Flush
// sent events
final case class Batch(obj: immutable.Seq[Any])
//#simple-events
//#simple-state
// states
sealed trait State
case object Idle extends State
case object Active extends State
sealed trait Data
case object Uninitialized extends Data
final case class Todo(target: ActorRef, queue: immutable.Seq[Any]) extends Data
//#simple-state
//#test-code
}
class FSMDocSpec extends MyFavoriteTestFrameWorkPlusAkkaTestKit {
import FSMDocSpec._
//#fsm-code-elided
//#simple-imports
import akka.actor.{ ActorRef, FSM }
import scala.concurrent.duration._
//#simple-imports
//#simple-fsm
class Buncher extends FSM[State, Data] {
//#fsm-body
startWith(Idle, Uninitialized)
//#when-syntax
when(Idle) {
case Event(SetTarget(ref), Uninitialized) =>
stay using Todo(ref, Vector.empty)
}
//#when-syntax
//#transition-elided
onTransition {
case Active -> Idle =>
stateData match {
case Todo(ref, queue) => ref ! Batch(queue)
case _ => // nothing to do
}
}
//#transition-elided
//#when-syntax
when(Active, stateTimeout = 1 second) {
case Event(Flush | StateTimeout, t: Todo) =>
goto(Idle) using t.copy(queue = Vector.empty)
}
//#when-syntax
//#unhandled-elided
whenUnhandled {
// common code for both states
case Event(Queue(obj), t @ Todo(_, v)) =>
goto(Active) using t.copy(queue = v :+ obj)
case Event(e, s) =>
log.warning("received unhandled request {} in state {}/{}", e, stateName, s)
stay
}
//#unhandled-elided
//#fsm-body
initialize()
}
//#simple-fsm
object DemoCode {
trait StateType
case object SomeState extends StateType
case object Processing extends StateType
case object Error extends StateType
case object Idle extends StateType
case object Active extends StateType
class Dummy extends FSM[StateType, Int] {
class X
val newData = 42
object WillDo
object Tick
//#modifier-syntax
when(SomeState) {
case Event(msg, _) =>
goto(Processing) using (newData) forMax (5 seconds) replying (WillDo)
}
//#modifier-syntax
//#transition-syntax
onTransition {
case Idle -> Active => setTimer("timeout", Tick, 1 second, repeat = true)
case Active -> _ => cancelTimer("timeout")
case x -> Idle => log.info("entering Idle from " + x)
}
//#transition-syntax
//#alt-transition-syntax
onTransition(handler _)
def handler(from: StateType, to: StateType) {
// handle it here ...
}
//#alt-transition-syntax
//#stop-syntax
when(Error) {
case Event("stop", _) =>
// do cleanup ...
stop()
}
//#stop-syntax
//#transform-syntax
when(SomeState)(transform {
case Event(bytes: ByteString, read) => stay using (read + bytes.length)
} using {
case s @ FSM.State(state, read, timeout, stopReason, replies) if read > 1000 =>
goto(Processing)
})
//#transform-syntax
//#alt-transform-syntax
val processingTrigger: PartialFunction[State, State] = {
case s @ FSM.State(state, read, timeout, stopReason, replies) if read > 1000 =>
goto(Processing)
}
when(SomeState)(transform {
case Event(bytes: ByteString, read) => stay using (read + bytes.length)
} using processingTrigger)
//#alt-transform-syntax
//#termination-syntax
onTermination {
case StopEvent(FSM.Normal, state, data) => // ...
case StopEvent(FSM.Shutdown, state, data) => // ...
case StopEvent(FSM.Failure(cause), state, data) => // ...
}
//#termination-syntax
//#unhandled-syntax
whenUnhandled {
case Event(x: X, data) =>
log.info("Received unhandled event: " + x)
stay
case Event(msg, _) =>
log.warning("Received unknown event: " + msg)
goto(Error)
}
//#unhandled-syntax
}
//#logging-fsm
import akka.actor.LoggingFSM
class MyFSM extends LoggingFSM[StateType, Data] {
//#body-elided
override def logDepth = 12
onTermination {
case StopEvent(FSM.Failure(_), state, data) =>
val lastEvents = getLog.mkString("\n\t")
log.warning("Failure in state " + state + " with data " + data + "\n" +
"Events leading up to this point:\n\t" + lastEvents)
}
// ...
//#body-elided
}
//#logging-fsm
}
//#fsm-code-elided
"simple finite state machine" must {
"demonstrate NullFunction" in {
class A extends FSM[Int, Null] {
val SomeState = 0
//#NullFunction
when(SomeState)(FSM.NullFunction)
//#NullFunction
}
}
"batch correctly" in {
val buncher = system.actorOf(Props(classOf[Buncher], this))
buncher ! SetTarget(testActor)
buncher ! Queue(42)
buncher ! Queue(43)
expectMsg(Batch(immutable.Seq(42, 43)))
buncher ! Queue(44)
buncher ! Flush
buncher ! Queue(45)
expectMsg(Batch(immutable.Seq(44)))
expectMsg(Batch(immutable.Seq(45)))
}
"not batch if uninitialized" in {
val buncher = system.actorOf(Props(classOf[Buncher], this))
buncher ! Queue(42)
expectNoMsg
}
}
}
//#test-code

View file

@ -1,299 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor
import language.postfixOps
//#all
//#imports
import akka.actor._
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._
import akka.util.Timeout
import akka.event.LoggingReceive
import akka.pattern.{ ask, pipe }
import com.typesafe.config.ConfigFactory
//#imports
/**
* Runs the sample
*/
object FaultHandlingDocSample extends App {
import Worker._
val config = ConfigFactory.parseString("""
akka.loglevel = "DEBUG"
akka.actor.debug {
receive = on
lifecycle = on
}
""")
val system = ActorSystem("FaultToleranceSample", config)
val worker = system.actorOf(Props[Worker], name = "worker")
val listener = system.actorOf(Props[Listener], name = "listener")
// start the work and listen on progress
// note that the listener is used as sender of the tell,
// i.e. it will receive replies from the worker
worker.tell(Start, sender = listener)
}
/**
* Listens on progress from the worker and shuts down the system when enough
* work has been done.
*/
class Listener extends Actor with ActorLogging {
import Worker._
// If we don't get any progress within 15 seconds then the service is unavailable
context.setReceiveTimeout(15 seconds)
def receive = {
case Progress(percent) =>
log.info("Current progress: {} %", percent)
if (percent >= 100.0) {
log.info("That's all, shutting down")
context.system.terminate()
}
case ReceiveTimeout =>
// No progress within 15 seconds, ServiceUnavailable
log.error("Shutting down due to unavailable service")
context.system.terminate()
}
}
//#messages
object Worker {
case object Start
case object Do
final case class Progress(percent: Double)
}
//#messages
/**
* Worker performs some work when it receives the `Start` message.
* It will continuously notify the sender of the `Start` message
* of current ``Progress``. The `Worker` supervise the `CounterService`.
*/
class Worker extends Actor with ActorLogging {
import Worker._
import CounterService._
implicit val askTimeout = Timeout(5 seconds)
// Stop the CounterService child if it throws ServiceUnavailable
override val supervisorStrategy = OneForOneStrategy() {
case _: CounterService.ServiceUnavailable => Stop
}
// The sender of the initial Start message will continuously be notified
// about progress
var progressListener: Option[ActorRef] = None
val counterService = context.actorOf(Props[CounterService], name = "counter")
val totalCount = 51
import context.dispatcher // Use this Actors' Dispatcher as ExecutionContext
def receive = LoggingReceive {
case Start if progressListener.isEmpty =>
progressListener = Some(sender())
context.system.scheduler.schedule(Duration.Zero, 1 second, self, Do)
case Do =>
counterService ! Increment(1)
counterService ! Increment(1)
counterService ! Increment(1)
// Send current progress to the initial sender
counterService ? GetCurrentCount map {
case CurrentCount(_, count) => Progress(100.0 * count / totalCount)
} pipeTo progressListener.get
}
}
//#messages
object CounterService {
final case class Increment(n: Int)
sealed abstract class GetCurrentCount
case object GetCurrentCount extends GetCurrentCount
final case class CurrentCount(key: String, count: Long)
class ServiceUnavailable(msg: String) extends RuntimeException(msg)
private case object Reconnect
}
//#messages
/**
* Adds the value received in `Increment` message to a persistent
* counter. Replies with `CurrentCount` when it is asked for `CurrentCount`.
* `CounterService` supervise `Storage` and `Counter`.
*/
class CounterService extends Actor {
import CounterService._
import Counter._
import Storage._
// Restart the storage child when StorageException is thrown.
// After 3 restarts within 5 seconds it will be stopped.
override val supervisorStrategy = OneForOneStrategy(
maxNrOfRetries = 3,
withinTimeRange = 5 seconds) {
case _: Storage.StorageException => Restart
}
val key = self.path.name
var storage: Option[ActorRef] = None
var counter: Option[ActorRef] = None
var backlog = IndexedSeq.empty[(ActorRef, Any)]
val MaxBacklog = 10000
import context.dispatcher // Use this Actors' Dispatcher as ExecutionContext
override def preStart() {
initStorage()
}
/**
* The child storage is restarted in case of failure, but after 3 restarts,
* and still failing it will be stopped. Better to back-off than continuously
* failing. When it has been stopped we will schedule a Reconnect after a delay.
* Watch the child so we receive Terminated message when it has been terminated.
*/
def initStorage() {
storage = Some(context.watch(context.actorOf(Props[Storage], name = "storage")))
// Tell the counter, if any, to use the new storage
counter foreach { _ ! UseStorage(storage) }
// We need the initial value to be able to operate
storage.get ! Get(key)
}
def receive = LoggingReceive {
case Entry(k, v) if k == key && counter == None =>
// Reply from Storage of the initial value, now we can create the Counter
val c = context.actorOf(Props(classOf[Counter], key, v))
counter = Some(c)
// Tell the counter to use current storage
c ! UseStorage(storage)
// and send the buffered backlog to the counter
for ((replyTo, msg) <- backlog) c.tell(msg, sender = replyTo)
backlog = IndexedSeq.empty
case msg: Increment => forwardOrPlaceInBacklog(msg)
case msg: GetCurrentCount => forwardOrPlaceInBacklog(msg)
case Terminated(actorRef) if Some(actorRef) == storage =>
// After 3 restarts the storage child is stopped.
// We receive Terminated because we watch the child, see initStorage.
storage = None
// Tell the counter that there is no storage for the moment
counter foreach { _ ! UseStorage(None) }
// Try to re-establish storage after while
context.system.scheduler.scheduleOnce(10 seconds, self, Reconnect)
case Reconnect =>
// Re-establish storage after the scheduled delay
initStorage()
}
def forwardOrPlaceInBacklog(msg: Any) {
// We need the initial value from storage before we can start delegate to
// the counter. Before that we place the messages in a backlog, to be sent
// to the counter when it is initialized.
counter match {
case Some(c) => c forward msg
case None =>
if (backlog.size >= MaxBacklog)
throw new ServiceUnavailable(
"CounterService not available, lack of initial value")
backlog :+= (sender() -> msg)
}
}
}
//#messages
object Counter {
final case class UseStorage(storage: Option[ActorRef])
}
//#messages
/**
* The in memory count variable that will send current
* value to the `Storage`, if there is any storage
* available at the moment.
*/
class Counter(key: String, initialValue: Long) extends Actor {
import Counter._
import CounterService._
import Storage._
var count = initialValue
var storage: Option[ActorRef] = None
def receive = LoggingReceive {
case UseStorage(s) =>
storage = s
storeCount()
case Increment(n) =>
count += n
storeCount()
case GetCurrentCount =>
sender() ! CurrentCount(key, count)
}
def storeCount() {
// Delegate dangerous work, to protect our valuable state.
// We can continue without storage.
storage foreach { _ ! Store(Entry(key, count)) }
}
}
//#messages
object Storage {
final case class Store(entry: Entry)
final case class Get(key: String)
final case class Entry(key: String, value: Long)
class StorageException(msg: String) extends RuntimeException(msg)
}
//#messages
/**
* Saves key/value pairs to persistent storage when receiving `Store` message.
* Replies with current value when receiving `Get` message.
* Will throw StorageException if the underlying data store is out of order.
*/
class Storage extends Actor {
import Storage._
val db = DummyDB
def receive = LoggingReceive {
case Store(Entry(key, count)) => db.save(key, count)
case Get(key) => sender() ! Entry(key, db.load(key).getOrElse(0L))
}
}
//#dummydb
object DummyDB {
import Storage.StorageException
private var db = Map[String, Long]()
@throws(classOf[StorageException])
def save(key: String, value: Long): Unit = synchronized {
if (11 <= value && value <= 14)
throw new StorageException("Simulated store failure " + value)
db += (key -> value)
}
@throws(classOf[StorageException])
def load(key: String): Option[Long] = synchronized {
db.get(key)
}
}
//#dummydb
//#all

View file

@ -1,184 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor
import language.postfixOps
import akka.actor.{ ActorSystem, ActorRef, Props, Terminated }
import FaultHandlingDocSpec._
//#testkit
import com.typesafe.config.{ Config, ConfigFactory }
import org.scalatest.{ FlatSpecLike, Matchers, BeforeAndAfterAll }
import akka.testkit.{ TestActors, TestKit, ImplicitSender, EventFilter }
//#testkit
object FaultHandlingDocSpec {
//#supervisor
//#child
import akka.actor.Actor
//#child
class Supervisor extends Actor {
//#strategy
import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case _: ArithmeticException => Resume
case _: NullPointerException => Restart
case _: IllegalArgumentException => Stop
case _: Exception => Escalate
}
//#strategy
def receive = {
case p: Props => sender() ! context.actorOf(p)
}
}
//#supervisor
//#supervisor2
class Supervisor2 extends Actor {
//#strategy2
import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case _: ArithmeticException => Resume
case _: NullPointerException => Restart
case _: IllegalArgumentException => Stop
case _: Exception => Escalate
}
//#strategy2
def receive = {
case p: Props => sender() ! context.actorOf(p)
}
// override default to kill all children during restart
override def preRestart(cause: Throwable, msg: Option[Any]) {}
}
//#supervisor2
class Supervisor3 extends Actor {
//#default-strategy-fallback
import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case _: ArithmeticException => Resume
case t =>
super.supervisorStrategy.decider.applyOrElse(t, (_: Any) => Escalate)
}
//#default-strategy-fallback
def receive = Actor.emptyBehavior
}
//#child
class Child extends Actor {
var state = 0
def receive = {
case ex: Exception => throw ex
case x: Int => state = x
case "get" => sender() ! state
}
}
//#child
val testConf: Config = ConfigFactory.parseString("""
akka {
loggers = ["akka.testkit.TestEventListener"]
}
""")
}
//#testkit
class FaultHandlingDocSpec(_system: ActorSystem) extends TestKit(_system)
with ImplicitSender with FlatSpecLike with Matchers with BeforeAndAfterAll {
def this() = this(ActorSystem(
"FaultHandlingDocSpec",
ConfigFactory.parseString("""
akka {
loggers = ["akka.testkit.TestEventListener"]
loglevel = "WARNING"
}
""")))
override def afterAll {
TestKit.shutdownActorSystem(system)
}
"A supervisor" must "apply the chosen strategy for its child" in {
//#testkit
//#create
val supervisor = system.actorOf(Props[Supervisor], "supervisor")
supervisor ! Props[Child]
val child = expectMsgType[ActorRef] // retrieve answer from TestKits testActor
//#create
EventFilter.warning(occurrences = 1) intercept {
//#resume
child ! 42 // set state to 42
child ! "get"
expectMsg(42)
child ! new ArithmeticException // crash it
child ! "get"
expectMsg(42)
//#resume
}
EventFilter[NullPointerException](occurrences = 1) intercept {
//#restart
child ! new NullPointerException // crash it harder
child ! "get"
expectMsg(0)
//#restart
}
EventFilter[IllegalArgumentException](occurrences = 1) intercept {
//#stop
watch(child) // have testActor watch child
child ! new IllegalArgumentException // break it
expectMsgPF() { case Terminated(`child`) => () }
//#stop
}
EventFilter[Exception]("CRASH", occurrences = 2) intercept {
//#escalate-kill
supervisor ! Props[Child] // create new child
val child2 = expectMsgType[ActorRef]
watch(child2)
child2 ! "get" // verify it is alive
expectMsg(0)
child2 ! new Exception("CRASH") // escalate failure
expectMsgPF() {
case t @ Terminated(`child2`) if t.existenceConfirmed => ()
}
//#escalate-kill
//#escalate-restart
val supervisor2 = system.actorOf(Props[Supervisor2], "supervisor2")
supervisor2 ! Props[Child]
val child3 = expectMsgType[ActorRef]
child3 ! 23
child3 ! "get"
expectMsg(23)
child3 ! new Exception("CRASH")
child3 ! "get"
expectMsg(0)
//#escalate-restart
}
//#testkit
// code here
}
}
//#testkit

View file

@ -1,74 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor
import akka.actor.{ Props, Actor }
import akka.testkit.{ ImplicitSender, AkkaSpec }
object InitializationDocSpec {
class PreStartInitExample extends Actor {
override def receive = {
case _ => // Ignore
}
//#preStartInit
override def preStart(): Unit = {
// Initialize children here
}
// Overriding postRestart to disable the call to preStart()
// after restarts
override def postRestart(reason: Throwable): Unit = ()
// The default implementation of preRestart() stops all the children
// of the actor. To opt-out from stopping the children, we
// have to override preRestart()
override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
// Keep the call to postStop(), but no stopping of children
postStop()
}
//#preStartInit
}
class MessageInitExample extends Actor {
//#messageInit
var initializeMe: Option[String] = None
override def receive = {
case "init" =>
initializeMe = Some("Up and running")
context.become(initialized, discardOld = true)
}
def initialized: Receive = {
case "U OK?" => initializeMe foreach { sender() ! _ }
}
//#messageInit
}
}
class InitializationDocSpec extends AkkaSpec with ImplicitSender {
import InitializationDocSpec._
"Message based initialization example" must {
"work correctly" in {
val example = system.actorOf(Props[MessageInitExample], "messageInitExample")
val probe = "U OK?"
example ! probe
expectNoMsg()
example ! "init"
example ! probe
expectMsg("Up and running")
}
}
}

View file

@ -1,44 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor
import akka.actor.{ Actor, Props }
import docs.CompileOnlySpec
import org.scalatest.WordSpec
//#props-edge-cases-value-class
case class MyValueClass(v: Int) extends AnyVal
//#props-edge-cases-value-class
class PropsEdgeCaseSpec extends WordSpec with CompileOnlySpec {
"value-class-edge-case-example" in compileOnlySpec {
//#props-edge-cases-value-class-example
class ValueActor(value: MyValueClass) extends Actor {
def receive = {
case multiplier: Long => sender() ! (value.v * multiplier)
}
}
val valueClassProp = Props(classOf[ValueActor], MyValueClass(5)) // Unsupported
//#props-edge-cases-value-class-example
//#props-edge-cases-default-values
class DefaultValueActor(a: Int, b: Int = 5) extends Actor {
def receive = {
case x: Int => sender() ! ((a + x) * b)
}
}
val defaultValueProp1 = Props(classOf[DefaultValueActor], 2.0) // Unsupported
class DefaultValueActor2(b: Int = 5) extends Actor {
def receive = {
case x: Int => sender() ! (x * b)
}
}
val defaultValueProp2 = Props[DefaultValueActor2] // Unsupported
val defaultValueProp3 = Props(classOf[DefaultValueActor2]) // Unsupported
//#props-edge-cases-default-values
}
}

View file

@ -1,68 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor
import language.postfixOps
//#imports1
import akka.actor.Actor
import akka.actor.Props
import scala.concurrent.duration._
//#imports1
import org.scalatest.{ BeforeAndAfterAll, WordSpec }
import org.scalatest.Matchers
import akka.testkit._
class SchedulerDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
"schedule a one-off task" in {
//#schedule-one-off-message
//Use the system's dispatcher as ExecutionContext
import system.dispatcher
//Schedules to send the "foo"-message to the testActor after 50ms
system.scheduler.scheduleOnce(50 milliseconds, testActor, "foo")
//#schedule-one-off-message
expectMsg(1 second, "foo")
//#schedule-one-off-thunk
//Schedules a function to be executed (send a message to the testActor) after 50ms
system.scheduler.scheduleOnce(50 milliseconds) {
testActor ! System.currentTimeMillis
}
//#schedule-one-off-thunk
}
"schedule a recurring task" in {
new AnyRef {
//#schedule-recurring
val Tick = "tick"
class TickActor extends Actor {
def receive = {
case Tick => //Do something
}
}
val tickActor = system.actorOf(Props(classOf[TickActor], this))
//Use system's dispatcher as ExecutionContext
import system.dispatcher
//This will schedule to send the Tick-message
//to the tickActor after 0ms repeating every 50ms
val cancellable =
system.scheduler.schedule(
0 milliseconds,
50 milliseconds,
tickActor,
Tick)
//This cancels further Ticks to be sent
cancellable.cancel()
//#schedule-recurring
system.stop(tickActor)
}
}
}

View file

@ -1,80 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor
class SharedMutableStateDocSpec {
//#mutable-state
import akka.actor.{ Actor, ActorRef }
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.language.postfixOps
import scala.collection.mutable
case class Message(msg: String)
class EchoActor extends Actor {
def receive = {
case msg => sender() ! msg
}
}
class CleanUpActor extends Actor {
def receive = {
case set: mutable.Set[_] => set.clear()
}
}
class MyActor(echoActor: ActorRef, cleanUpActor: ActorRef) extends Actor {
var state = ""
val mySet = mutable.Set[String]()
def expensiveCalculation(actorRef: ActorRef): String = {
// this is a very costly operation
"Meaning of live is 42"
}
def expensiveCalculation(): String = {
// this is a very costly operation
"Meaning of live is 42"
}
def receive = {
case _ =>
//Wrong ways
implicit val ec = context.dispatcher
implicit val timeout = Timeout(5 seconds) // needed for `?` below
// Very bad, shared mutable state,
// will break your application in weird ways
Future { state = "This will race" }
((echoActor ? Message("With this other one")).mapTo[Message])
.foreach { received => state = received.msg }
// Very bad, shared mutable object,
// the other actor cand mutate your own state,
// or worse, you might get weird race conditions
cleanUpActor ! mySet
// Very bad, "sender" changes for every message,
// shared mutable state bug
Future { expensiveCalculation(sender()) }
//Right ways
// Completely safe, "self" is OK to close over
// and it's an ActorRef, which is thread-safe
Future { expensiveCalculation() } foreach { self ! _ }
// Completely safe, we close over a fixed value
// and it's an ActorRef, which is thread-safe
val currentSender = sender()
Future { expensiveCalculation(currentSender) }
}
}
//#mutable-state
}

View file

@ -1,227 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://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.{ Future, Await }
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)
}
}

View file

@ -1,50 +0,0 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.actor
import akka.actor._
import scala.collection.mutable.ListBuffer
/**
* Requirements are as follows:
* The first thing the actor needs to do, is to subscribe to a channel of events,
* Then it must replay (process) all "old" events
* Then it has to wait for a GoAhead signal to begin processing the new events
* It mustn't "miss" events that happen between catching up with the old events and getting the GoAhead signal
*/
class UnnestedReceives extends Actor {
import context.become
//If you need to store sender/senderFuture you can change it to ListBuffer[(Any, Channel)]
val queue = new ListBuffer[Any]()
//This message processes a message/event
def process(msg: Any): Unit = println("processing: " + msg)
//This method subscribes the actor to the event bus
def subscribe() {} //Your external stuff
//This method retrieves all prior messages/events
def allOldMessages() = List()
override def preStart {
//We override preStart to be sure that the first message the actor gets is
//'Replay, that message will start to be processed _after_ the actor is started
self ! 'Replay
//Then we subscribe to the stream of messages/events
subscribe()
}
def receive = {
case 'Replay => //Our first message should be a 'Replay message, all others are invalid
allOldMessages() foreach process //Process all old messages/events
become { //Switch behavior to look for the GoAhead signal
case 'GoAhead => //When we get the GoAhead signal we process all our buffered messages/events
queue foreach process
queue.clear
become { //Then we change behaviour to process incoming messages/events as they arrive
case msg => process(msg)
}
case msg => //While we haven't gotten the GoAhead signal, buffer all incoming messages
queue += msg //Here you have full control, you can handle overflow etc
}
}
}