2011-12-19 11:07:59 +01:00
|
|
|
|
/**
|
2015-03-07 22:58:48 -08:00
|
|
|
|
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
|
2011-12-19 11:07:59 +01:00
|
|
|
|
*/
|
2012-05-22 11:37:09 +02:00
|
|
|
|
package docs.actor
|
2011-12-06 16:49:39 +01:00
|
|
|
|
|
2012-06-28 15:33:49 +02:00
|
|
|
|
import language.postfixOps
|
|
|
|
|
|
|
2011-12-06 16:49:39 +01:00
|
|
|
|
//#imports1
|
|
|
|
|
|
import akka.actor.Actor
|
2011-12-13 14:09:40 +01:00
|
|
|
|
import akka.actor.Props
|
2011-12-06 16:49:39 +01:00
|
|
|
|
import akka.event.Logging
|
2011-12-11 20:12:55 +01:00
|
|
|
|
|
2011-12-06 16:49:39 +01:00
|
|
|
|
//#imports1
|
|
|
|
|
|
|
2012-07-04 15:25:30 +02:00
|
|
|
|
import scala.concurrent.Future
|
2014-02-19 15:58:56 +00:00
|
|
|
|
import akka.actor.{ ActorRef, ActorSystem, PoisonPill, Terminated, ActorLogging }
|
2011-10-05 17:41:00 +02:00
|
|
|
|
import org.scalatest.{ BeforeAndAfterAll, WordSpec }
|
2013-12-17 14:25:56 +01:00
|
|
|
|
import org.scalatest.Matchers
|
2011-10-05 17:41:00 +02:00
|
|
|
|
import akka.testkit._
|
2011-12-14 14:05:44 +01:00
|
|
|
|
import akka.util._
|
2012-09-21 14:50:06 +02:00
|
|
|
|
import scala.concurrent.duration._
|
2012-06-29 16:06:26 +02:00
|
|
|
|
import scala.concurrent.Await
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
|
|
|
|
|
//#my-actor
|
|
|
|
|
|
class MyActor extends Actor {
|
2011-12-05 20:01:42 +01:00
|
|
|
|
val log = Logging(context.system, this)
|
2011-10-05 17:41:00 +02:00
|
|
|
|
def receive = {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case "test" => log.info("received test")
|
|
|
|
|
|
case _ => log.info("received unknown message")
|
2011-10-05 17:41:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//#my-actor
|
|
|
|
|
|
|
2014-03-07 13:20:01 +01:00
|
|
|
|
final case class DoIt(msg: ImmutableMessage)
|
|
|
|
|
|
final case class Message(s: String)
|
2011-12-06 16:49:39 +01:00
|
|
|
|
|
|
|
|
|
|
//#context-actorOf
|
|
|
|
|
|
class FirstActor extends Actor {
|
2013-04-14 22:56:41 +02:00
|
|
|
|
val child = context.actorOf(Props[MyActor], name = "myChild")
|
|
|
|
|
|
//#plus-some-behavior
|
2012-02-08 13:37:00 +01:00
|
|
|
|
def receive = {
|
2014-01-16 15:16:35 +01:00
|
|
|
|
case x => sender() ! x
|
2012-02-08 13:37:00 +01:00
|
|
|
|
}
|
2013-04-14 22:56:41 +02:00
|
|
|
|
//#plus-some-behavior
|
|
|
|
|
|
}
|
|
|
|
|
|
//#context-actorOf
|
|
|
|
|
|
|
|
|
|
|
|
class ActorWithArgs(arg: String) extends Actor {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
def receive = { case _ => () }
|
2013-04-14 22:56:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class DemoActorWrapper extends Actor {
|
|
|
|
|
|
//#props-factory
|
|
|
|
|
|
object DemoActor {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Create Props for an actor of this type.
|
2014-10-15 18:35:38 -05:00
|
|
|
|
* @param magicNumber The magic number to be passed to this actor’s constructor.
|
2013-04-14 22:56:41 +02:00
|
|
|
|
* @return a Props for creating this actor, which can then be further configured
|
|
|
|
|
|
* (e.g. calling `.withDispatcher()` on it)
|
|
|
|
|
|
*/
|
2014-01-16 22:44:03 +01:00
|
|
|
|
def props(magicNumber: Int): Props = Props(new DemoActor(magicNumber))
|
2013-04-14 22:56:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-11-15 14:14:57 +01:00
|
|
|
|
class DemoActor(magicNumber: Int) extends Actor {
|
2013-04-14 22:56:41 +02:00
|
|
|
|
def receive = {
|
2014-01-16 15:16:35 +01:00
|
|
|
|
case x: Int => sender() ! (x + magicNumber)
|
2013-04-14 22:56:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-01-16 22:44:03 +01:00
|
|
|
|
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
|
|
|
|
|
|
}
|
2013-04-14 22:56:41 +02:00
|
|
|
|
//#props-factory
|
|
|
|
|
|
|
|
|
|
|
|
def receive = Actor.emptyBehavior
|
2012-02-08 13:37:00 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-16 23:43:27 +05:00
|
|
|
|
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.")
|
2015-01-30 18:34:03 +01:00
|
|
|
|
case Goodbye => log.info("Someone said goodbye to me.")
|
2015-01-16 23:43:27 +05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//#messages-in-companion
|
|
|
|
|
|
|
|
|
|
|
|
def receive = Actor.emptyBehavior
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
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
|
2011-12-06 16:49:39 +01:00
|
|
|
|
}
|
2011-12-08 14:06:20 +01:00
|
|
|
|
|
|
|
|
|
|
class ReplyException extends Actor {
|
|
|
|
|
|
def receive = {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case _ =>
|
2011-12-08 14:06:20 +01:00
|
|
|
|
//#reply-exception
|
|
|
|
|
|
try {
|
|
|
|
|
|
val result = operation()
|
2014-01-16 15:16:35 +01:00
|
|
|
|
sender() ! result
|
2011-12-08 14:06:20 +01:00
|
|
|
|
} catch {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case e: Exception =>
|
2014-01-16 15:16:35 +01:00
|
|
|
|
sender() ! akka.actor.Status.Failure(e)
|
2011-12-08 14:06:20 +01:00
|
|
|
|
throw e
|
|
|
|
|
|
}
|
|
|
|
|
|
//#reply-exception
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def operation(): String = { "Hi" }
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-01-21 17:15:09 +01:00
|
|
|
|
//#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...
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2011-12-06 16:49:39 +01:00
|
|
|
|
//#swapper
|
|
|
|
|
|
case object Swap
|
|
|
|
|
|
class Swapper extends Actor {
|
|
|
|
|
|
import context._
|
|
|
|
|
|
val log = Logging(system, this)
|
|
|
|
|
|
|
|
|
|
|
|
def receive = {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case Swap =>
|
2011-12-06 16:49:39 +01:00
|
|
|
|
log.info("Hi")
|
2012-11-06 11:00:27 +01:00
|
|
|
|
become({
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case Swap =>
|
2011-12-06 16:49:39 +01:00
|
|
|
|
log.info("Ho")
|
|
|
|
|
|
unbecome() // resets the latest 'become' (just for fun)
|
2012-11-06 11:00:27 +01:00
|
|
|
|
}, discardOld = false) // push on top instead of replace
|
2011-12-06 16:49:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
object SwapperApp extends App {
|
|
|
|
|
|
val system = ActorSystem("SwapperSystem")
|
2011-12-16 00:39:29 +01:00
|
|
|
|
val swap = system.actorOf(Props[Swapper], name = "swapper")
|
2011-12-06 16:49:39 +01:00
|
|
|
|
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
|
|
|
|
|
|
|
2014-02-19 15:58:56 +00:00
|
|
|
|
trait ProducerBehavior {
|
|
|
|
|
|
this: Actor =>
|
2011-12-06 16:49:39 +01:00
|
|
|
|
|
2014-02-19 15:58:56 +00:00
|
|
|
|
val producerBehavior: Receive = {
|
|
|
|
|
|
case GiveMeThings =>
|
|
|
|
|
|
sender() ! Give("thing")
|
2011-12-06 16:49:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-02-19 15:58:56 +00:00
|
|
|
|
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)
|
2011-12-06 16:49:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-02-19 15:58:56 +00:00
|
|
|
|
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 {
|
2014-03-03 12:00:25 +01:00
|
|
|
|
|
2015-01-30 18:34:03 +01:00
|
|
|
|
def receive = producerBehavior.orElse[Any, Unit](consumerBehavior)
|
2014-02-19 15:58:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// protocol
|
|
|
|
|
|
case object GiveMeThings
|
2014-03-07 13:20:01 +01:00
|
|
|
|
final case class Give(thing: Any)
|
2014-02-19 15:58:56 +00:00
|
|
|
|
|
2011-12-06 16:49:39 +01:00
|
|
|
|
//#receive-orElse
|
|
|
|
|
|
|
2011-11-15 11:34:39 +01:00
|
|
|
|
class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
2011-12-06 16:49:39 +01:00
|
|
|
|
"import context" in {
|
2013-04-14 22:56:41 +02:00
|
|
|
|
new AnyRef {
|
|
|
|
|
|
//#import-context
|
|
|
|
|
|
class FirstActor extends Actor {
|
|
|
|
|
|
import context._
|
|
|
|
|
|
val myActor = actorOf(Props[MyActor], name = "myactor")
|
|
|
|
|
|
def receive = {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case x => myActor ! x
|
2013-04-14 22:56:41 +02:00
|
|
|
|
}
|
2011-12-06 16:49:39 +01:00
|
|
|
|
}
|
2013-04-14 22:56:41 +02:00
|
|
|
|
//#import-context
|
2011-12-06 16:49:39 +01:00
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
val first = system.actorOf(Props(classOf[FirstActor], this), name = "first")
|
|
|
|
|
|
system.stop(first)
|
|
|
|
|
|
}
|
2011-12-06 16:49:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
"creating actor with system.actorOf" in {
|
2011-12-13 14:09:40 +01:00
|
|
|
|
val myActor = system.actorOf(Props[MyActor])
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
|
|
|
|
|
// testing the actor
|
|
|
|
|
|
|
2011-11-18 11:34:52 +01:00
|
|
|
|
// TODO: convert docs to AkkaSpec(Map(...))
|
2011-10-27 12:23:01 +02:00
|
|
|
|
val filter = EventFilter.custom {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case e: Logging.Info => true
|
|
|
|
|
|
case _ => false
|
2011-10-27 12:23:01 +02:00
|
|
|
|
}
|
2011-11-16 17:18:36 +01:00
|
|
|
|
system.eventStream.publish(TestEvent.Mute(filter))
|
|
|
|
|
|
system.eventStream.subscribe(testActor, classOf[Logging.Info])
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
|
|
|
|
|
myActor ! "test"
|
2013-12-03 16:34:26 +01:00
|
|
|
|
expectMsgPF(1 second) { case Logging.Info(_, _, "received test") => true }
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
|
|
|
|
|
myActor ! "unknown"
|
2013-12-03 16:34:26 +01:00
|
|
|
|
expectMsgPF(1 second) { case Logging.Info(_, _, "received unknown message") => true }
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
2011-11-16 17:18:36 +01:00
|
|
|
|
system.eventStream.unsubscribe(testActor)
|
|
|
|
|
|
system.eventStream.publish(TestEvent.UnMute(filter))
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
2011-12-14 00:06:36 +01:00
|
|
|
|
system.stop(myActor)
|
2011-10-05 17:41:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-12-14 14:05:44 +01:00
|
|
|
|
"creating a Props config" in {
|
2013-04-14 22:56:41 +02:00
|
|
|
|
//#creating-props
|
2011-12-14 14:05:44 +01:00
|
|
|
|
import akka.actor.Props
|
2013-04-14 22:56:41 +02:00
|
|
|
|
|
|
|
|
|
|
val props1 = Props[MyActor]
|
2014-01-16 22:44:03 +01:00
|
|
|
|
val props2 = Props(new ActorWithArgs("arg")) // careful, see below
|
2013-04-14 22:56:41 +02:00
|
|
|
|
val props3 = Props(classOf[ActorWithArgs], "arg")
|
|
|
|
|
|
//#creating-props
|
|
|
|
|
|
|
|
|
|
|
|
//#creating-props-deprecated
|
2014-01-16 22:44:03 +01:00
|
|
|
|
// NOT RECOMMENDED within another actor:
|
|
|
|
|
|
// encourages to close over enclosing class
|
2013-05-28 10:39:38 +02:00
|
|
|
|
val props7 = Props(new MyActor)
|
2013-04-14 22:56:41 +02:00
|
|
|
|
//#creating-props-deprecated
|
2011-12-14 14:05:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-12-06 16:49:39 +01:00
|
|
|
|
"creating actor with Props" in {
|
2013-04-14 22:56:41 +02:00
|
|
|
|
//#system-actorOf
|
|
|
|
|
|
import akka.actor.ActorSystem
|
|
|
|
|
|
|
|
|
|
|
|
// ActorSystem is a heavy object: create only one per application
|
|
|
|
|
|
val system = ActorSystem("mySystem")
|
2013-05-27 20:15:24 +02:00
|
|
|
|
val myActor = system.actorOf(Props[MyActor], "myactor2")
|
2013-04-14 22:56:41 +02:00
|
|
|
|
//#system-actorOf
|
2013-05-02 17:12:36 +02:00
|
|
|
|
shutdown(system)
|
2013-04-14 22:56:41 +02:00
|
|
|
|
}
|
2011-12-06 16:49:39 +01:00
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
"creating actor with IndirectActorProducer" in {
|
|
|
|
|
|
class Echo(name: String) extends Actor {
|
|
|
|
|
|
def receive = {
|
2014-01-16 15:16:35 +01:00
|
|
|
|
case n: Int => sender() ! name
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case message =>
|
2013-04-14 22:56:41 +02:00
|
|
|
|
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
|
|
|
|
|
|
}
|
2014-01-16 22:44:03 +01:00
|
|
|
|
val actorRef = {
|
|
|
|
|
|
import scala.language.reflectiveCalls
|
|
|
|
|
|
a.actorRef
|
|
|
|
|
|
}
|
2013-04-14 22:56:41 +02:00
|
|
|
|
|
|
|
|
|
|
val message = 42
|
|
|
|
|
|
implicit val self = testActor
|
|
|
|
|
|
//#tell
|
|
|
|
|
|
actorRef ! message
|
|
|
|
|
|
//#tell
|
|
|
|
|
|
expectMsg("hello")
|
|
|
|
|
|
actorRef ! "huhu"
|
|
|
|
|
|
expectMsg("huhu")
|
2011-12-06 16:49:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-02-08 13:37:00 +01:00
|
|
|
|
"using implicit timeout" in {
|
2013-04-14 22:56:41 +02:00
|
|
|
|
val myActor = system.actorOf(Props[FirstActor])
|
2011-12-20 08:12:20 +01:00
|
|
|
|
//#using-implicit-timeout
|
2012-09-21 14:50:06 +02:00
|
|
|
|
import scala.concurrent.duration._
|
2011-12-20 08:12:20 +01:00
|
|
|
|
import akka.util.Timeout
|
2012-01-18 10:18:51 +01:00
|
|
|
|
import akka.pattern.ask
|
2012-02-08 13:37:00 +01:00
|
|
|
|
implicit val timeout = Timeout(5 seconds)
|
2011-12-20 08:12:20 +01:00
|
|
|
|
val future = myActor ? "hello"
|
|
|
|
|
|
//#using-implicit-timeout
|
2013-12-17 14:25:56 +01:00
|
|
|
|
Await.result(future, timeout.duration) should be("hello")
|
2011-12-20 08:12:20 +01:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-02-08 13:37:00 +01:00
|
|
|
|
"using explicit timeout" in {
|
2013-04-14 22:56:41 +02:00
|
|
|
|
val myActor = system.actorOf(Props[FirstActor])
|
2011-12-20 08:12:20 +01:00
|
|
|
|
//#using-explicit-timeout
|
2012-09-21 14:50:06 +02:00
|
|
|
|
import scala.concurrent.duration._
|
2012-01-18 10:18:51 +01:00
|
|
|
|
import akka.pattern.ask
|
2012-02-08 13:37:00 +01:00
|
|
|
|
val future = myActor.ask("hello")(5 seconds)
|
2011-12-20 08:12:20 +01:00
|
|
|
|
//#using-explicit-timeout
|
2013-12-17 14:25:56 +01:00
|
|
|
|
Await.result(future, 5 seconds) should be("hello")
|
2011-12-20 08:12:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-12-06 16:49:39 +01:00
|
|
|
|
"using receiveTimeout" in {
|
|
|
|
|
|
//#receive-timeout
|
|
|
|
|
|
import akka.actor.ReceiveTimeout
|
2012-09-21 14:50:06 +02:00
|
|
|
|
import scala.concurrent.duration._
|
2011-12-06 16:49:39 +01:00
|
|
|
|
class MyActor extends Actor {
|
2012-10-14 03:35:09 +02:00
|
|
|
|
// To set an initial delay
|
2011-12-13 11:11:21 +01:00
|
|
|
|
context.setReceiveTimeout(30 milliseconds)
|
2011-12-06 16:49:39 +01:00
|
|
|
|
def receive = {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case "Hello" =>
|
2012-10-14 03:35:09 +02:00
|
|
|
|
// To set in a response to a message
|
2012-10-15 13:49:12 +02:00
|
|
|
|
context.setReceiveTimeout(100 milliseconds)
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case ReceiveTimeout =>
|
2012-10-14 03:35:09 +02:00
|
|
|
|
// To turn it off
|
|
|
|
|
|
context.setReceiveTimeout(Duration.Undefined)
|
|
|
|
|
|
throw new RuntimeException("Receive timed out")
|
2011-12-06 16:49:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//#receive-timeout
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
//#hot-swap-actor
|
|
|
|
|
|
class HotSwapActor extends Actor {
|
|
|
|
|
|
import context._
|
|
|
|
|
|
def angry: Receive = {
|
2014-01-16 15:16:35 +01:00
|
|
|
|
case "foo" => sender() ! "I am already angry?"
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case "bar" => become(happy)
|
2013-04-14 22:56:41 +02:00
|
|
|
|
}
|
2011-12-06 16:49:39 +01:00
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
def happy: Receive = {
|
2014-01-16 15:16:35 +01:00
|
|
|
|
case "bar" => sender() ! "I am already happy :-)"
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case "foo" => become(angry)
|
2013-04-14 22:56:41 +02:00
|
|
|
|
}
|
2011-12-06 16:49:39 +01:00
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
def receive = {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case "foo" => become(angry)
|
|
|
|
|
|
case "bar" => become(happy)
|
2011-12-06 16:49:39 +01:00
|
|
|
|
}
|
2013-04-14 22:56:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
//#hot-swap-actor
|
2011-12-08 14:06:20 +01:00
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
"using hot-swap" in {
|
|
|
|
|
|
val actor = system.actorOf(Props(classOf[HotSwapActor], this), name = "hot")
|
2011-12-06 16:49:39 +01:00
|
|
|
|
}
|
2011-12-28 13:09:56 +01:00
|
|
|
|
|
2012-06-12 15:51:54 +02:00
|
|
|
|
"using Stash" in {
|
|
|
|
|
|
//#stash
|
|
|
|
|
|
import akka.actor.Stash
|
|
|
|
|
|
class ActorWithProtocol extends Actor with Stash {
|
|
|
|
|
|
def receive = {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case "open" =>
|
2012-06-12 15:51:54 +02:00
|
|
|
|
unstashAll()
|
2012-11-06 14:15:57 +01:00
|
|
|
|
context.become({
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case "write" => // do writing...
|
|
|
|
|
|
case "close" =>
|
2012-06-12 15:51:54 +02:00
|
|
|
|
unstashAll()
|
|
|
|
|
|
context.unbecome()
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case msg => stash()
|
2012-11-06 14:15:57 +01:00
|
|
|
|
}, discardOld = false) // stack on top instead of replacing
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case msg => stash()
|
2012-06-12 15:51:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//#stash
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2011-12-28 13:09:56 +01:00
|
|
|
|
"using watch" in {
|
2013-04-14 22:56:41 +02:00
|
|
|
|
new AnyRef {
|
|
|
|
|
|
//#watch
|
|
|
|
|
|
import akka.actor.{ Actor, Props, Terminated }
|
2011-12-28 13:09:56 +01:00
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
class WatchActor extends Actor {
|
|
|
|
|
|
val child = context.actorOf(Props.empty, "child")
|
|
|
|
|
|
context.watch(child) // <-- this is the only call needed for registration
|
|
|
|
|
|
var lastSender = system.deadLetters
|
2011-12-28 13:09:56 +01:00
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
def receive = {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case "kill" =>
|
2014-01-16 15:16:35 +01:00
|
|
|
|
context.stop(child); lastSender = sender()
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case Terminated(`child`) => lastSender ! "finished"
|
2013-04-14 22:56:41 +02:00
|
|
|
|
}
|
2011-12-28 13:09:56 +01:00
|
|
|
|
}
|
2013-04-14 22:56:41 +02:00
|
|
|
|
//#watch
|
|
|
|
|
|
val a = system.actorOf(Props(classOf[WatchActor], this))
|
|
|
|
|
|
implicit val sender = testActor
|
|
|
|
|
|
a ! "kill"
|
|
|
|
|
|
expectMsg("finished")
|
2011-12-28 13:09:56 +01:00
|
|
|
|
}
|
2013-04-14 22:56:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"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
|
2011-12-28 13:09:56 +01:00
|
|
|
|
}
|
2012-01-03 11:41:49 +01:00
|
|
|
|
|
2013-03-26 18:17:50 +01:00
|
|
|
|
"using Identify" in {
|
2013-04-14 22:56:41 +02:00
|
|
|
|
new AnyRef {
|
|
|
|
|
|
//#identify
|
|
|
|
|
|
import akka.actor.{ Actor, Props, Identify, ActorIdentity, Terminated }
|
2013-03-26 18:17:50 +01:00
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
class Follower extends Actor {
|
|
|
|
|
|
val identifyId = 1
|
|
|
|
|
|
context.actorSelection("/user/another") ! Identify(identifyId)
|
2013-03-26 18:17:50 +01:00
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
def receive = {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case ActorIdentity(`identifyId`, Some(ref)) =>
|
2013-04-14 22:56:41 +02:00
|
|
|
|
context.watch(ref)
|
|
|
|
|
|
context.become(active(ref))
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case ActorIdentity(`identifyId`, None) => context.stop(self)
|
2013-03-26 18:17:50 +01:00
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
}
|
2013-03-26 18:17:50 +01:00
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
def active(another: ActorRef): Actor.Receive = {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case Terminated(`another`) => context.stop(self)
|
2013-04-14 22:56:41 +02:00
|
|
|
|
}
|
2013-03-26 18:17:50 +01:00
|
|
|
|
}
|
2013-04-14 22:56:41 +02:00
|
|
|
|
//#identify
|
2013-03-26 18:17:50 +01:00
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
val a = system.actorOf(Props.empty)
|
|
|
|
|
|
val b = system.actorOf(Props(classOf[Follower], this))
|
|
|
|
|
|
watch(b)
|
|
|
|
|
|
system.stop(a)
|
2014-01-31 11:14:13 +01:00
|
|
|
|
expectMsgType[akka.actor.Terminated].actor should be(b)
|
2013-04-14 22:56:41 +02:00
|
|
|
|
}
|
2013-03-26 18:17:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-01-03 11:41:49 +01:00
|
|
|
|
"using pattern gracefulStop" in {
|
2014-01-21 17:15:09 +01:00
|
|
|
|
val actorRef = system.actorOf(Props[Manager])
|
2012-01-03 11:41:49 +01:00
|
|
|
|
//#gracefulStop
|
|
|
|
|
|
import akka.pattern.gracefulStop
|
2012-06-29 16:06:26 +02:00
|
|
|
|
import scala.concurrent.Await
|
2012-01-03 11:41:49 +01:00
|
|
|
|
|
|
|
|
|
|
try {
|
2014-01-21 17:15:09 +01:00
|
|
|
|
val stopped: Future[Boolean] = gracefulStop(actorRef, 5 seconds, Manager.Shutdown)
|
2012-01-03 11:41:49 +01:00
|
|
|
|
Await.result(stopped, 6 seconds)
|
|
|
|
|
|
// the actor has been stopped
|
|
|
|
|
|
} catch {
|
2012-10-01 20:35:19 +02:00
|
|
|
|
// the actor wasn't stopped within 5 seconds
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case e: akka.pattern.AskTimeoutException =>
|
2012-01-03 11:41:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
//#gracefulStop
|
2012-01-20 18:09:26 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"using pattern ask / pipeTo" in {
|
|
|
|
|
|
val actorA, actorB, actorC, actorD = system.actorOf(Props.empty)
|
|
|
|
|
|
//#ask-pipeTo
|
2012-02-01 14:04:01 +01:00
|
|
|
|
import akka.pattern.{ ask, pipe }
|
2012-07-22 21:40:09 +02:00
|
|
|
|
import system.dispatcher // The ExecutionContext that will be used
|
2014-03-07 13:20:01 +01:00
|
|
|
|
final case class Result(x: Int, s: String, d: Double)
|
2012-01-20 18:09:26 +01:00
|
|
|
|
case object Request
|
|
|
|
|
|
|
|
|
|
|
|
implicit val timeout = Timeout(5 seconds) // needed for `?` below
|
|
|
|
|
|
|
|
|
|
|
|
val f: Future[Result] =
|
|
|
|
|
|
for {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
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
|
2012-01-20 18:09:26 +01:00
|
|
|
|
} yield Result(x, s, d)
|
|
|
|
|
|
|
|
|
|
|
|
f pipeTo actorD // .. or ..
|
2012-02-01 14:04:01 +01:00
|
|
|
|
pipe(f) to actorD
|
2012-01-20 18:09:26 +01:00
|
|
|
|
//#ask-pipeTo
|
2012-01-03 11:41:49 +01:00
|
|
|
|
}
|
2012-01-20 18:09:26 +01:00
|
|
|
|
|
2013-04-14 22:56:41 +02:00
|
|
|
|
class Replier extends Actor {
|
|
|
|
|
|
def receive = {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case ref: ActorRef =>
|
2012-02-06 12:18:08 +01:00
|
|
|
|
//#reply-with-sender
|
2014-01-16 15:16:35 +01:00
|
|
|
|
sender().tell("reply", context.parent) // replies will go back to parent
|
|
|
|
|
|
sender().!("reply")(context.parent) // alternative syntax (beware of the parens!)
|
2013-04-14 22:56:41 +02:00
|
|
|
|
//#reply-with-sender
|
2013-12-03 16:34:26 +01:00
|
|
|
|
case x =>
|
2012-02-06 12:18:08 +01:00
|
|
|
|
//#reply-without-sender
|
2014-01-16 15:16:35 +01:00
|
|
|
|
sender() ! x // replies will go to this actor
|
2013-04-14 22:56:41 +02:00
|
|
|
|
//#reply-without-sender
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"replying with own or other sender" in {
|
|
|
|
|
|
val actor = system.actorOf(Props(classOf[Replier], this))
|
2012-02-06 12:18:08 +01:00
|
|
|
|
implicit val me = testActor
|
|
|
|
|
|
actor ! 42
|
|
|
|
|
|
expectMsg(42)
|
2014-01-31 11:14:13 +01:00
|
|
|
|
lastSender should be(actor)
|
2012-02-06 12:18:08 +01:00
|
|
|
|
actor ! me
|
|
|
|
|
|
expectMsg("reply")
|
2014-01-31 11:14:13 +01:00
|
|
|
|
lastSender.path.toStringWithoutAddress should be("/user")
|
2012-02-06 12:18:08 +01:00
|
|
|
|
expectMsg("reply")
|
2014-01-31 11:14:13 +01:00
|
|
|
|
lastSender.path.toStringWithoutAddress should be("/user")
|
2012-02-06 12:18:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-09-25 12:18:44 +02:00
|
|
|
|
"using ActorDSL outside of akka.actor package" in {
|
|
|
|
|
|
import akka.actor.ActorDSL._
|
|
|
|
|
|
actor(new Act {
|
2013-12-03 16:34:26 +01:00
|
|
|
|
superviseWith(OneForOneStrategy() { case _ => Stop; Restart; Resume; Escalate })
|
|
|
|
|
|
superviseWith(AllForOneStrategy() { case _ => Stop; Restart; Resume; Escalate })
|
2012-09-25 12:18:44 +02:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
}
|