Tuesday, November 5, 2013

Scala Actors. Handling messages using Try, Success, Failure

In this short post I will try to show a general method that could be used to handle actor messages. I want to also mention that this is not specifically related to actor supervision or other Akka actor advanced topics.

Let's say that we have an actor that is supposed to receive some events.

We want that this actor to be able to handle the received events in a generic way, so it should catch the exceptions occurring inside the actual handlers and it should also know to handle them accordingly.

I will provide you with a possible implementation that is using the Try Scala type.
More information about it can be found here:
http://www.scala-lang.org/api/current/index.html#scala.util.Try

First, we should introduce a class hierarchy for the types of events our actor is going to receive, send back to its senders, or forward to other actors:

  1. Msg any event received by our actor should extend this class
  2. ResponseMsg - any response sent by the actor back or forwarded to another actor should also extend this event
  3. SuccessMsg, FailureMsg - these are the messages used to wrap the results in case of a successful or a failed execution that might happen inside our actor event handlers

abstract class Msg
abstract class ResponseMsg
abstract class SuccessMsg extends ResponseMsg
abstract class FailureMsg extends ResponseMsg

Now, I will define the actual GenericActorHandler trait:

import akka.actor.{ Actor, ActorRef, ActorSystem, Props }
import scala.util.{ Try, Success, Failure }
trait GenericActorHandler extends Actor {
  def handleTryWithReply[S <: SuccessMsg, F <: FailureMsg](fun: => Try[S], createFailureMsg: Throwable => F): Unit = {
    fun match {
      case Success(successResp) =>
        sender ! successResp
      case Failure(e) =>
        sender ! createFailureMsg(e)
    }
  }
  def handleTryWithForward[M <: Msg, F <: FailureMsg](fun: => Try[(ActorRef, M)], createFailureMsg: Throwable => F): Unit = {
    fun match {
      case Success(actorMsgTuple) =>
        actorMsgTuple._1 forward actorMsgTuple._2
      case Failure(e) =>
        
actorMsgTuple._1 forward createFailureMsg(e)
    }
  }
}


Here is an example of an actor that mixes in the GenericActorHandler actor:

import akka.actor.{ActorRef, Props }
import scala.util.{ Try, Success, Failure }

class CalculatorActor extends GenericHandlerActor {
  def receive = {
    case evt: AddEvt =>
      handleTryWithReply(handleAddEvt(evt), ex => FailureAddEvt(ex.getMessage))
    case evt: DivideEvt =>
      handleTryWithReply(handleDivideEvt(evt), ex => FailureDivideEvt(ex.getMessage))
  }

  private def handleAddEvt(evt: AddEvt ): Try[SuccessAddEvt] =
    Try {
      SuccessAddEvt( evt.a + evt.b)
    }

  private def handleDivideEvt(evt: DivideEvt ): Try[SuccessDivideEvt] =
    Try {
     SuccessDivideEvt(evt.a / evt.b)
    }

and here are the events or messages our calculator actor is handling:

case class AddEvt (a: Int, b: Int) extends Msg
case class SuccessAddEvt(result: Int) extends SuccessMsg
case class FailureAddEvt(reason: String) extends FailureMsg 

case class DivideEvt (a: Int, b: Int) extends Msg
case class SuccessDivideEvt(result: Int) extends SuccessMsg
case class FailureDivideEvt(reason: String) extends FailureMsg
Assuming, that we send DivideEvt(10, 0) to our actor. This event  will be handled by handleDivideEvt. In this case its computation will fail when trying to divide by 0.
The actor will still be able to send back to its sender the exception wrapped inside the FailureDivideEvt response.