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:
- Msg any event received by our actor should extend this class
- ResponseMsg - any response sent by the actor back or forwarded to another actor should also extend this event
- 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
The actor will still be able to send back to its sender the exception wrapped inside the FailureDivideEvt response.