Sending Response outside akka Actor System - scala

I have a play(2.4.2 which has akka 2.4.18) application in which I am using akka actors for uploading a file. I have a parent supervisor Actor with this kind of hierarchy
UploadSupervisor ---child---> UploadActor ---child--->
DataWriteActor & MetaWriteActor
The leaf actors MetaWriteActor & DataWriteActor does the actual writing. A very simplified version of my code is below:
First I have a actor supervisor:
class UploadSupervisor extends Actor {
val uploadActor = context.actorOf(Props(new UploadActor), "UploadActor")
override def supervisorStrategy = OneForOneStrategy() {
case _: Throwable => Restart
}
override def receive: Receive = {
case data: Data => uploadActor ! data
case meta: MetaInfo => uploadActor ! meta
//How do I send response outside of actor system?
case dataSuccess: DataUploadResponse => ??? //Line 10
case metaSuccess: MetaUploadResponse => ??? //Line 11
}
object UploadSupervisor {
val uploadSupervisor = Akka.system
.actorOf(Props(new UploadSupervisor), "UploadSupervisor")
}
//Request & Response case classes
case class Data(content: String)
case class MetaInfo(id: String, createdDate: Timestamp)
case class DataUploadResponse(location: String)
case class MetaUploadResponse(location: String)
UploadActor:-
class UploadActor extends Actor {
val dataWriteActor = context.actorOf(Props(new DataWriteActor), "dataWriteActor")
val metaWriteActor = context.actorOf(Props(new MetaWriteActor), "UploadActor")
override def receive = {
case data: Data => dataWriteActor ! data
case meta: MetaInfo => metaWriteActor ! meta
case dataResp: DataUploadResponse => context.parent ! dataResp
case metaResp: MetaUploadResponse => context.parent ! metaResp
}
}
DataWriteActor :
class DataWriteActor extends Actor {
case data: Data => //Do the writing
println("data write completed")
sender() ! DataUploadResponse("someLocation")
}
MetaWriteActor
class MetaWriteActor extends Actor {
case meta: MetaInfo=> //Do the writing
println(" meta info writing completed")
sender() ! MetaUploadResponse("someOtherLocation")
}
Somewhere outside Actor system:-
implicit val timeout = Timeout(10 seconds)
val f1 = UploadSupervisor.uploadSupervisor ? Data("Hello Akka").mapTo(implicitly[scala.reflect.ClassTag[DataUploadResponse]])
val f2 = UploadSupervisor.uploadSupervisor ? MetaInfo("1234", new Timestamp(new Date().getTime).mapTo(implicitly[scala.reflect.ClassTag[MetaUploadResponse]])
//Do something with futures
The question is how to send the response outside the actor system? Because in Line 10 & 11, I can't use sender ! msg because the current sender is the UploadActor.

You could keep in UploadSupervisor references to the initial senders:
class UploadSupervisor extends Actor {
val uploadActor = context.actorOf(Props[UploadActor], "UploadActor")
override val supervisorStrategy = OneForOneStrategy() {
case _ => Restart
}
var dataSender: Option[ActorRef] = None
var metaSender: Option[ActorRef] = None
def receive = {
case data: Data =>
val s = sender
dataSender = Option(s)
uploadActor ! data
case meta: MetaInfo =>
val s = sender
metaSender = Option(s)
uploadActor ! meta
case dataSuccess: DataUploadResponse =>
dataSender.foreach(_ ! dataSuccess)
case metaSuccess: MetaUploadResponse =>
metaSender.foreach(_ ! metaSuccess)
}
}
To send messages to UploadSupervisor:
implicit val timeout = Timeout(10 seconds)
val f1 = (UploadSupervisor.uploadSupervisor ? Data("Hello Akka")).mapTo[DataUploadResponse]
val f2 = (UploadSupervisor.uploadSupervisor ? MetaInfo("1234", new Timestamp(new Date().getTime)).mapTo[MetaUploadResponse]
The above assumes that you're sending one Data message and one MetaInfo message to UploadSupervisor at a time. This approach will break down if you send multiple Data and MetaInfo messages and expect concurrent replies. A more general solution is to include the reference to the initial sender in additional case classes that wrap your existing case classes, passing this reference through your actor hierarchy:
case class DataMsg(data: Data, target: ActorRef)
case class MetaInfoMsg(metaInfo: MetaInfo, target: ActorRef)
case class DataUploadMsg(response: DataUploadResponse, target: ActorRef)
case class MetaUploadMsg(response: MetaUploadResponse, target: ActorRef)
class UploadSupervisor extends Actor {
val uploadActor = context.actorOf(Props[UploadActor], "UploadActor")
override val supervisorStrategy = OneForOneStrategy() {
case _ => Restart
}
def receive = {
case data: Data =>
val s = sender
uploadActor ! DataMsg(data, s)
case meta: MetaInfo =>
val s = sender
uploadActor ! MetaInfoMsg(meta, s)
case DataUploadMsg(response, target) =>
target ! response
case MetaUploadMsg(response, target) =>
target ! response
}
}
The UploadActor:
class UploadActor extends Actor {
val dataWriteActor = context.actorOf(Props[DataWriteActor], "dataWriteActor")
val metaWriteActor = context.actorOf(Props[MetaWriteActor], "UploadActor")
def receive = {
case data: DataMsg => dataWriteActor ! data
case meta: MetaInfoMsg => metaWriteActor ! meta
case dataResp: DataUploadMsg => context.parent ! dataResp
case metaResp: MetaUploadMsg => context.parent ! metaResp
}
}
The writers:
class DataWriteActor extends Actor {
def receive = {
case DataMsg(data, target) =>
// do the writing
println("data write completed")
sender ! DataUploadMsg(DataUploadResponse("someLocation"), target)
}
}
class MetaWriteActor extends Actor {
def receive = {
case MetaInfoMsg(meta, target) =>
// do the writing
println("meta info writing completed")
sender ! MetaUploadMsg(MetaUploadResponse("someOtherLocation"), target)
}
}

Related

Scala Play Websocket - use one out actor to send both: Array[Byte] and String messages

I have Play websockets action:
def socket = WebSocket.acceptWithActor[String, Array[Byte]] { request => out =>
Props(new WebSocketInActor(out))
}
Generally I need to send to browser large raw arrays of data. But sometimes I need to send some small string data. In browser I can detect is data in text format or raw ArrayBuffer.
If I create actor that sends String, I can send string messages, If I create actor that sends with Array[Byte], I can send raw arrays. Both situations I don't need to change client code. So, how can I force Play to use both sending methods with one out actor?
Ah, those answers that comes just after you post question on SO. Looking through reference and sourcecode, I found that there is mixedFrame FrameFromatter: https://github.com/playframework/playframework/blob/2.4.x/framework/src/play/src/main/scala/play/api/mvc/WebSocket.scala#L75
So you just need to say that you will respond with Either[String, Array[Byte]] and if you want to send string use Left(somestring) or else use Right[somearray].
class WebSocketInActor(out: ActorRef) extends Actor {
override def preStart() = {
println("User connected")
val s = "Hello"
out ! Left(s)
out ! Right(s.getBytes("utf8"))
}
override def postStop() = {
println("User discconnected")
}
def receive = {
case msg: String => {
}
case _ =>
}
}
def socket = WebSocket.acceptWithActor[String, Either[String, Array[Byte]]] { request => out =>
Props(new WebSocketInActor(out))
}
UPDATE:
Or you can go one step further and create your own frame formatter
sealed trait WSMessage
case class StringMessage(s: String) extends WSMessage
case class BinaryMessage(a: Array[Byte]) extends WSMessage
case class JsonMessage(js: JsValue) extends WSMessage
implicit object myFrameFormatter extends BasicFrameFormatter[WSMessage] {
private val textFrameClass = classOf[TextFrame]
private val binaryFrameClass = classOf[BinaryFrame]
def toFrame(message: WSMessage): BasicFrame = message match {
case StringMessage(s) => TextFrame(s)
case BinaryMessage(a) => BinaryFrame(a)
case JsonMessage(js) => TextFrame(Json.stringify(js))
}
def fromFrame(frame: BasicFrame): WSMessage = frame match {
case TextFrame(s) => StringMessage(s)
case BinaryFrame(a) => BinaryMessage(a)
}
def fromFrameDefined(clazz: Class[_]): Boolean = clazz match {
case `textFrameClass` => true
case `binaryFrameClass` => true
case _ => false // shouldn't be reachable
}
}
class WebSocketInActor(out: ActorRef) extends Actor {
override def preStart() = {
println("User connected")
val s = "Hello"
val a:Array[Byte] = Array(100, 50, 30).map(_.toByte)
out ! StringMessage(s)
out ! JsonMessage(Json.obj("txt" -> s, "array" -> a))
out ! BinaryMessage(a)
}
override def postStop() = {
println("User discconnected")
}
def receive = {
case msg: String => {
}
case _ =>
}
}
def socket = WebSocket.acceptWithActor[String, WSMessage] { request => out =>
Props(new WebSocketInActor(out))
}

Akka - use actorSelection with ask pattern

So, when sending a message using the ask pattern (actor ? msg), it creates a "one-off actor" behind the scenes.
My question is - Is it possible to send this temporary actor a message using actorSelection?
For example, the following code works well:
object Test extends App {
case class WrappedMsg(msg: String, replyTo: ActorRef)
class Source(target: ActorRef) extends Actor {
def receive = { case _ => } // doesn't matter
implicit val execution = context.dispatcher
implicit val timeout = Timeout(5.seconds)
val middleware = context.actorOf(Props(new Middleware(target)))
(middleware ? "Something").mapTo[String].onComplete {
case Success(msg) => println("Success: " + msg)
case Failure(err) => println("Failure: " + err)
}
}
class Middleware(target: ActorRef) extends Actor {
def receive = {
case msg: String =>
val wrappedMsg = WrappedMsg(replyTo = sender(), msg = msg)
target ! wrappedMsg
}
}
class Target extends Actor {
def receive = {
case wrappedMsg: WrappedMsg => wrappedMsg.replyTo ! "Received"
}
}
val system = ActorSystem()
val target = system.actorOf(Props(new Target))
val source = system.actorOf(Props(new Source(target)))
}
But, if I make the following changes, in order to use the actor url instead of ActorRef, it fails:
case class WrappedMsg(msg: String, replyTo: String)
...
val wrappedMsg = WrappedMsg(replyTo = sender().path.toSerializationFormat, msg = msg)
...
case wrappedMsg: WrappedMsg => context.actorSelection(wrappedMsg.replyTo) ! "Received"
Thanks
Thanks to #Zernike (see the comments in the question), we've found out that in Akka 2.3.12 it works well - the temporary actor is resolved using actorSelection. (The original code that caused the failure was tested in Akka 2.3.6)

PersistentActor cannot invoke persist handler in Future onComplete

I am pretty new using PersistentActor ,
when I try to call updateState from a future onComplete, fails , nothing happanes , tried to debug it and I do get to the persist call but not into the updateState
trait Event
case class Cmd(data: String)
case class Evt(data: String) extends Event
class BarActor extends PersistentActor{
implicit val system = context.system
implicit val executionContext = system.dispatcher
def updateState(event: Evt): Unit ={
println("Updating state")
state = state.updated(event)
sender() ! state
}
def timeout(implicit ec: ExecutionContext) =
akka.pattern.after(duration = 2 seconds, using = system.scheduler)(Future.failed(new TimeoutException("Got timed out!")))
val receiveCommand: Receive = {
case Cmd(data) =>
def anotherFuture(i: Int)(implicit system: ActorSystem) = {
val realF = Future {
i % 2 match {
case 0 =>
Thread.sleep(100)
case _ =>
Thread.sleep(500)
}
i
}
Future.firstCompletedOf(Seq(realF, timeout))
.recover {
case _ => -1
}
}
val res = (1 to 10).map(anotherFuture(_))
val list = Future.sequence(res)
list.onComplete{
case _ =>
persist(Evt("testing"))(updateState)
}
}
}
You could try this:
list.onComplete {
case _ => self ! Evt("testing")
}
And add this to receiveCommand
case evt: Evt =>
persist(Evt("testing"))(updateStates)

Akka consolidate concurrent database requests

I want to be able to make concurrent requests to multiple data repositories and consolidate the results. I am trying to understand if my approach is at all valid or if there is a better way to approach this problem. I am definitely new to Akka / Spray / Scala and really want to get a better understanding of how to properly build these components. Any suggestions / Tips would be greatly appreciated. Trying to wrap my head around the use of actors and futures for this type of implementation.
Spray Service:
trait DemoService extends HttpService with Actor with ActorLogging {
implicit val timeout = Timeout(5 seconds) // needed for `?` below
val mongoMasterActor = context.actorOf(Props[MongoMasterActor], "redisactor")
val dbMaster = context.actorOf(Props[DbMasterActor], "dbactor")
val messageApiRouting =
path("summary" / Segment / Segment) { (dataset, timeslice) =>
onComplete(getDbResponses(dbMaster, dataset, timeslice)) {
case Success(dbMessageResponse) => complete(s"The result was $dbMessageResponse")
case Failure(ex) => complete(s"An error occurred: ${ex.getMessage}")
}
}
/** Passes the desired actor reference for a specific dataset and timeslice for summary data retrieval
*
* #param mongoActor an actor reference to the RedisActor that will handle the appropriate request routing
* #param dataset The dataset for which the summary has been requested
* #param timeslice The timeslice (Month, Week, Day, etc.) for which the summary has been requested
*/
def getSummary(mongoActor: ActorRef, dataset: String, timeslice: String): Future[DbMessageResponse] = {
log.debug(s"dataset: $dataset timeslice: $timeslice")
val dbMessage = DbMessage("summary", dataset + timeslice)
(mongoActor ? dbMessage).mapTo[DbMessageResponse]
}
def getDbResponses(dbActor: ActorRef, dataset: String, timeslice: String): Future[SummaryResponse] = {
log.debug(s"dataset: $dataset timeslice: $timeslice")
val dbMessage = DbMessage("summary", dataset + timeslice)
(dbActor ? dbMessage).mapTo[SummaryResponse]
}
def getSummaryPayload(mongoSummary: DbMessageResponse, redisSummary: DbMessageResponse): String = {
mongoSummary.response + redisSummary.response
}
}
Akka Actor / Future mock db requests:
class DbMasterActor extends Actor with ActorLogging {
private var originalSender: ActorRef = _
//TODO: Need to add routing to the config to limit instances
val summaryActor = context.actorOf(Props(new SummaryActor), "summaryactor")
def receive = {
case msg: DbMessage => {
this.originalSender = sender
msg.query match {
case "summary" => {
getDbResults().onComplete{
case Success(result) => originalSender ! result
case Failure(ex) => log.error(ex.getMessage)
}
}
}
}
//If not match log an error
case _ => log.error("Received unknown message: {} ")
}
def getDbResults(): Future[SummaryResponse] = {
log.debug("hitting db results")
val mongoResult = Future{ Thread.sleep(500); "Mongo"}
val redisResult = Future{ Thread.sleep(800); "redis"}
for{
mResult <- mongoResult
rResult <- redisResult
} yield SummaryResponse(mResult, rResult)
}
}
Following the reading of Effective Akka by Jamie Allen, I am going to attempt to apply his "Cameo" pattern suggestion.
Slideshare:
http://www.slideshare.net/shinolajla/effective-akka-scalaio
Github:
https://github.com/jamie-allen/effective_akka
I think what I created will work, but doesn't sound like the best approach based on Jamie's comments in his talks. I will update / edit back to this post what I have implemented (or try to).
Summary Actor (Cameo Actor):
object SummaryResponseHandler {
case object DbRetrievalTimeout
def props(mongoDb: ActorRef, redisDb: ActorRef, originalSender: ActorRef): Props = {
Props(new SummaryResponseHandler(mongoDb, redisDb, originalSender))
}
}
class SummaryResponseHandler(mongoDb: ActorRef, redisDb: ActorRef,
originalSender: ActorRef) extends Actor with ActorLogging {
import SummaryResponseHandler._
var mongoSummary, redisSummary: Option[String] = None
def receive = LoggingReceive {
case MongoSummary(summary) =>
log.debug(s"Received mongo summary: $summary")
mongoSummary = summary
collectSummaries
case RedisSummary(summary) =>
log.debug(s"Received redis summary: $summary")
redisSummary = summary
collectSummaries
case DbRetrievalTimeout =>
log.debug("Timeout occurred")
sendResponseAndShutdown(DbRetrievalTimeout)
}
def collectSummaries = (mongoSummary, redisSummary) match {
case (Some(m), Some(r)) =>
log.debug(s"Values received for both databases")
timeoutMessager.cancel
sendResponseAndShutdown(DataSetSummary(mongoSummary, redisSummary))
case _ =>
}
def sendResponseAndShutdown(response: Any) = {
originalSender ! response
log.debug("Stopping context capturing actor")
context.stop(self)
}
import context.dispatcher
val timeoutMessager = context.system.scheduler.scheduleOnce(
250 milliseconds, self, DbRetrievalTimeout)
}
class SummaryRetriever(mongoDb: ActorRef, redisDb: ActorRef) extends Actor with ActorLogging {
def receive = {
case GetSummary(dataSet) =>
log.debug("received dataSet")
val originalSender = sender
val handler = context.actorOf(SummaryResponseHandler.props(mongoDb,redisDb, originalSender), "cameo-message-handler")
mongoDb.tell(GetSummary(dataSet), handler)
redisDb.tell(GetSummary(dataSet), handler)
case _ => log.debug(s"Unknown result $GetSummary(datset)")
}
}
Common:
case class GetSummary(dataSet: String)
case class DataSetSummary(
mongo: Option[String],
redis: Option[String]
)
case class MongoSummary(
summary: Option[String]
)
case class RedisSummary(
summary: Option[String]
)
trait MongoProxy extends Actor with ActorLogging
trait RedisProxy extends Actor with ActorLogging
Mock Stubs:
class MongoProxyStub extends RedisProxy {
val summaryData = Map[String, String](
"dataset1" -> "MongoData1",
"dataset2" -> "MongoData2")
def receive = LoggingReceive {
case GetSummary(dataSet: String) =>
log.debug(s"Received GetSummary for ID: $dataSet")
summaryData.get(dataSet) match {
case Some(data) => sender ! MongoSummary(Some(data))
case None => sender ! MongoSummary(Some(""))
}
}
}
class RedisProxyStub extends MongoProxy{
val summaryData = Map[String, String](
"dataset1" -> "RedisData1",
"dataset2" -> "RedisData2")
def receive = LoggingReceive {
case GetSummary(dataSet: String) =>
log.debug(s"Received GetSummary for ID: $dataSet")
summaryData.get(dataSet) match {
case Some(data) => sender ! RedisSummary(Some(data))
case None => sender ! RedisSummary(Some(""))
}
}
}
Boot (You should use test, but was just wanting to run from boot):
object Boot extends App{
val system = ActorSystem("DbSystem")
val redisProxy = system.actorOf(Props[RedisProxyStub], "cameo-success-mongo")
val mongoProxy = system.actorOf(Props[MongoProxyStub], "cameo-success-redis")
val summaryRetrieverActor = system.actorOf(Props(new SummaryRetriever(redisProxy, mongoProxy)), "cameo-retriever1")
implicit val timeout = Timeout(5 seconds)
val future = summaryRetrieverActor ? GetSummary("dataset1")
val result = Await.result(future, timeout.duration).asInstanceOf[DataSetSummary]
println(Some(result.mongo).x)
println(result.redis)
system.shutdown()
}
Application Config:
akka.loglevel = "DEBUG"
akka.event-handlers = ["akka.event.slf4j.Slf4jEventHandler"]
akka.actor.debug.autoreceive = on
akka.actor.debug.lifecycle = on
akka.actor.debug.receive = on
akka.actor.debug.event-stream = on

tell don't ask and use Actor tell instead future

this question about design, for example now I have some process that load task list and try process each task, it looks like this:
import scala.concurrent.{ExecutionContext, Future}
case class TaskId(processorName: String, id: String)
case class TaskDetails(id: TaskId, status: String, json: String)
trait DAO {
def loadTaskIds(processorName: String): Future[Seq[TaskId]]
def loadTaskDetails(id: TaskId): Future[Option[TaskDetails]]
def deleteTask(id: TaskId): Future[Boolean]
def markSuccess(id: TaskId): Future[Boolean]
}
class Processor(name: String, dao: DAO, implicit val ec: ExecutionContext) {
def process(): Unit = {
dao
.loadTaskIds(name)
.map(_.map {
case taskId =>
dao.loadTaskDetails(taskId).flatMap {
case None =>
delete(taskId)
case Some(task) if task.status == "success" =>
delete(taskId)
case Some(task) =>
doProcess(task).flatMap {
case true =>
dao.markSuccess(taskId).map {
case true =>
Some(task)
case false =>
// log.error(...)
None
}
case false =>
// log.error(...)
Future.successful(None)
}
}
}).map {
case processingFutures =>
Future.sequence(processingFutures).map(_.flatten).map {
case completedTasks =>
// log.info(s"Processing '$name' tasks, complete [${completedTasks.map(_.id).mkString(", ")}]")
}
}
}
private def doProcess(task: TaskDetails): Future[Boolean] = ???
private def delete(taskId: TaskId): Future[Option[TaskDetails]] =
dao.deleteTask(taskId).map {
case true =>
None
case false =>
// log.error(...)
None
}
}
My question: how I need to change this code for getting 'True' Actor based application without using futures or ask pattern.
PS: I read that I need use only actor tell (!) but I can't understand how I can rewrite my logic and DAO as an actor.
PSS: my own approach
import akka.actor._
import scala.concurrent.duration._
object ExampleActorBoot extends App {
val system = ActorSystem("ExampleActorBoot")
val dao = system.actorOf(Props[DaoActor], "dao")
val processor = system.actorOf(Props(new ProcessorActor("test", dao)), "processor")
processor ! ProcessorActor.Process
}
case class TaskId(processorName: String, id: String)
case class TaskDetails(id: TaskId, status: String, json: String)
object DaoActor {
sealed trait DaoActorMessage {
def receiver: ActorRef
}
case class LoadTaskIds(processorName: String, receiver: ActorRef) extends DaoActorMessage
case class LoadTaskDetails(id: TaskId, receiver: ActorRef) extends DaoActorMessage
case class DeleteTask(id: TaskId, receiver: ActorRef) extends DaoActorMessage
case class MarkSuccess(id: TaskId, receiver: ActorRef) extends DaoActorMessage
}
class DaoActor extends Actor with ActorLogging {
import com.episodetracker.boot.DaoActor._
override def receive = {
case LoadTaskIds(processorName, receiver) =>
val result = 1 to 4 map {
case i =>
TaskId(processorName, i.toString)
}
receiver ! result
case LoadTaskDetails(id, receiver) =>
val result = TaskDetails(id, "pending", "JSON")
receiver ! result
case DeleteTask(id, receiver) =>
receiver ! true
case MarkSuccess(id, receiver) =>
receiver ! true
}
}
object ProcessorActor {
sealed trait ProcessorActorMessage
case object Process extends ProcessorActorMessage
private case class TaskComplete(id: TaskId, isSuccess: Boolean) extends ProcessorActorMessage
private case class ProcessingComplete(result: Seq[(TaskId, Boolean)]) extends ProcessorActorMessage
}
class ProcessorActor(name: String, dao: ActorRef) extends Actor with ActorLogging {
import com.episodetracker.boot.ProcessorActor._
override def receive = {
case Process =>
dao ! DaoActor.LoadTaskIds(name, context.actorOf(Props(new TasksProcessorResultActor())))
case ProcessingComplete(result) =>
sender() ! PoisonPill
log.info(s"Processing complete with results [$result]")
case unknown =>
log.error(s"Unknown message [$unknown]")
}
val emptyResult = Seq[(TaskId, Boolean)]()
class TasksProcessorResultActor extends Actor {
context.setReceiveTimeout(3 seconds)
override def receive = default(0, emptyResult)
def default(awaitResults: Int, completedTasks: Seq[(TaskId, Boolean)]): Receive = {
case tasks: Seq[TaskId] =>
tasks.foreach {
case task =>
context.actorOf(Props(new TaskProcessorActor(task)))
}
context become default(tasks.size, emptyResult)
case TaskComplete(id, isSuccess) =>
val results = completedTasks :+ ((id, isSuccess))
if (results.size == awaitResults) {
context.parent ! ProcessingComplete(results)
} else {
context become default(awaitResults, results)
}
case unknown =>
log.error(s"Unknown message [$unknown]")
}
}
class TaskProcessorActor(taskId: TaskId) extends Actor with ActorLogging {
override def receive = default
dao ! DaoActor.LoadTaskDetails(taskId, self)
def default: Receive = {
case taskDetails: TaskDetails =>
taskDetails.status match {
case "success" =>
dao ! DaoActor.DeleteTask(taskDetails.id, self)
context.become(waitDaoResponse)
case _ =>
//TODO: do some processing
dao ! DaoActor.MarkSuccess(taskDetails.id, self)
context.become(waitDaoResponse)
}
case unknown =>
log.error(s"Unknown message [$unknown]")
}
def waitDaoResponse: Receive = {
case true =>
context.parent ! TaskComplete(taskId, true)
case false =>
context.parent ! TaskComplete(taskId, false)
}
}
}
Looks pretty good except you don't need to pass the receiver: ActorRef in your DaoActorMessges. The DaoActor includes a sender method as part of the actor trait. You can use this to send a message back to the sender. This reduces a lot of the clutter of your code, for example:
case DeleteTask(id) =>
sender ! true