How to catch the exceptions thrown inside an actor? - scala

In my actor, the methods may throw exception:
class ServerActor extends Actor {
override def receive: Receive = {
case "Start" =>
println("### actor started")
throw new Exception("My exception when starting")
case msg =>
println("### actor get other message: " + msg)
throw new Exception("another exception for other messages: " + msg)
}
}
Is there any way to handle all the exception in a single place? I want to handle them together, e.g. logging them

Well you can try to do this in parent under supervision strategy:
http://doc.akka.io/docs/akka/snapshot/scala/fault-tolerance.html
Or you have this method on actor:
def preRestart(reason: Throwable, message: Option[Any]): Unit
where reason is the exception that caused the actor to crash.

Related

How to stop an Actor reloading on exception

In a scenario where an exception is thrown in an Actor receive I want to prevent this actor from being reloaded. I understood that the correct way to do this is by overriding supervisorStrategy but this does not work as shown in the example below:
class MyActor extends Actor {
println("Created new actor")
def receive = {
case msg =>
println("Received message: " + msg)
throw new Exception()
}
override val supervisorStrategy = OneForOneStrategy() {
case _: Exception => Stop
}
}
val system = ActorSystem("Test")
val actor = system.actorOf(Props(new MyActor()))
actor ! "Hello"
When I run this code "Created new actor" is output twice showing that the Actor is reloaded again after the exception.
What is the correct way to prevent the Actor being reloaded?
When an actor overrides the default supervisor strategy, that strategy applies to that actor's children. Your actor is using the default supervisor strategy, which restarts actors when they throw an exception. Define a parent for your actor and override the supervisor strategy in that parent.
class MyParent extends Actor {
override val supervisorStrategy = OneForOneStrategy() {
case _: Exception => Stop
}
val child = context.actorOf(Props[MyActor])
def receive = {
case msg =>
println(s"Parent received the following message and is sending it to the child: $msg")
child ! msg
}
}
class MyActor extends Actor {
println("Created new actor")
def receive = {
case msg =>
println(s"Received message: $msg")
throw new Exception()
}
}
val system = ActorSystem("Test")
val actor = system.actorOf(Props[MyParent])
actor ! "Hello"
In the above example, a MyActor is created as a child of MyParent. When the latter receives the "Hello" message, it sends the same message to the child. The child is stopped when it throws the exception, and "Created new actor" is therefore printed only once.

How to intercept and change exception in Scala Akka in play framework 2.4.6

Actor (OrgaActor) asks a database actor (DbActor) for a document. The DbActor does not always deliver a document, but also a database error exception. The OrgaActor shall now alter the received exception with details (that it is actually an Organization query, not a generic database query). I struggle to intercept the exception from DbActor and change it in the result sent to the controller (OrgaCtl). Non working sample code of OrgaActor
def receive = {
case GetDocument(id: String) => {
try {
val answer = (dbActor ? DbActor.GetDocument("Orga", id)).mapTo[JsValue]
play.Logger.debug("in answer")
answer.pipeTo(sender())
} catch {
case e: Exception =>
play.Logger.debug("in exception")
val fhe = error.FhException(e, 404, error.Error.ORGA_LOAD, "error on load " + id)
sender() ! akka.actor.Status.Failure(fhe)
}
}
The DbActor's GetDocument :
case GetDocument(coll: String, id: String) =>
try {
val response = new FigureheadDb().get(coll, id)
sender() ! response.jsonElementPayload()
} catch {
case e: Exception =>
val fhe = error.FhException(e, 404, error.Error.FH_ID, "error on load " + id + " in coll " + coll)
sender() ! akka.actor.Status.Failure(fhe)
throw fhe
}
The inException debug message is never shown, so I guess the throw fhe code in DbActor never arrives at the calling OrgaActor. The DbActor is tied into OrgaActor with val dbActor = context.actorOf(Props[DbActor], name = "db-actor").
The question is: How can I intercept the error thrown by the database actor in the organization actor and deliver the enriched error to the global error handler? Currently the global error handler always gets the database actor error.
Code based on accepted Answer
dbActor:
case GetDocument(coll: String, id: String, originalSender: ActorRef) =>
try {
val response = new FigureheadDb().get(coll, id)
sender() ! GetDocumentSuccess(response.jsonElementPayload(), originalSender)
} catch {
case e: Exception =>
val fhe = error.FhException(e, 404, error.Error.FH_ID, "error on load " + id + " in coll " + coll)
sender() ! GetDocumentFailed(fhe, id, originalSender)
}
OrgaActor:
case GetDocument(id: String) => {
val answer : Future[Any] = dbActor ? DbActor.GetDocument(coll, id, sender())
answer.map {
case success: DbActor.GetDocumentSuccess =>
play.Logger.debug("in success")
success.originalSender ! success.result.as[JsValue]
case failure: DbActor.GetDocumentFailed => {
play.Logger.debug("in failure")
val fhe = error.FhException(failure.cause, 404, error.Error.ORGA_LOAD, "error on load " + failure.id)
failure.originalSender ! akka.actor.Status.Failure(fhe)
}
case any => {
play.Logger.error("Dead end")
}
}
}
object DbActor:
case class GetDocument(coll: String, id: String, originalSender: ActorRef)
// http://stackoverflow.com/questions/35549414/how-to-intercept-and-change-exception-in-scala-akka-in-play-framework-2-4-6
sealed trait GetDocumentResult
case class GetDocumentSuccess(result: JsValue, originalSender: ActorRef) extends GetDocumentResult
case class GetDocumentFailed(cause: Exception, id: String, originalSender: ActorRef) extends GetDocumentResult
In your OrgaActor you try to catch an exeption which is never thrown.
The code below is quoted from your question, however I included a type annotation for answer.
try {
val answer: Future[JsValue] = (dbActor ? DbActor.GetDocument("Orga", id)).mapTo[JsValue]
play.Logger.debug("in answer")
answer.pipeTo(sender())
} catch { ... }
You would probably expect the second line (val answer ....) to throw an exception, however it will not throw an exception, but a Future is returned, which might succeed or fail.
Using answer.pipeTo(sender()) the result of the future (which could be a Status.Failure) is sent to the sender().
As a solution you can define a sealed trait GetDocumentResult which two case classes implementing the trait:
// you already have this case class, I added the original sender,
// since you might need this information to know where to send the final result
case class GetDocument(coll: String, id: String, originalSender: ActorRef)
sealed trait GetDocumentResult
case class GetDocumentSuccess(result: JsValue, originalSender: ActorRef) extends GetDocumentResult
case class GetDocumentFailed(cause: Exception, id: String, originalSender: ActorRef) extends GetDocumentResult
Using this your DbActor can send either a GetDocumentSuccess or a GetDocumentFailed as a response to the OrgaActor. And once the OrgaActor receives the response, it can send the final result (either the success, or the postproccessed failure message) to the original sender.

Akka supervisor actor do not handle exception when child actor throws an exception within onFailure of a future

I'm facing a problem with an Akka supervisor actor. When the child actor throws an exception within onFailure method of a future result, the supervisor does not handle the error (I want to restart the child in the case of a ConnectException).
I'm using Akka 2.3.7.
This is the supervisor actor:
class MobileUsersActor extends Actor with ActorLogging {
import Model.Implicits._
import Model.MobileNotifications
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 3, withinTimeRange = 1 minute) {
case _: java.net.ConnectException => {
Logger.error("API connection error. Check your proxy configuration.")
Restart
}
}
def receive = {
case Start => findMobileUsers
}
private def findMobileUsers = {
val notis = MobileNotificationsRepository().find()
notis.map(invokePushSender)
}
private def invokePushSender(notis: List[MobileNotifications]) = {
notis.foreach { n =>
val pushSender = context.actorOf(PushSenderActor.props)
pushSender ! Send(n)
}
}
}
And this is the child actor:
class PushSenderActor extends Actor with ActorLogging {
def receive = {
case Send(noti) => {
val response = sendPushNotification(noti) onFailure {
case e: ConnectException => throw e
}
}
}
private def sendPushNotification(noti: MobileNotifications): Future[WSResponse] = {
val message = "Push notification message example"
Logger.info(s"Push Notification >> $message to users " + noti.users)
PushClient.sendNotification(message, noti.users)
}
}
I tried to notify sender with an akka.actor.Status.Failure(e) as is suggested here, but did not work, the exception keep unhandled by the supervisor.
As a workaround, I found this way to get it work:
class PushSenderActor extends Actor with ActorLogging {
def receive = {
case Send(noti) => {
val response = sendPushNotification(noti) onFailure {
case e: ConnectException => self ! APIConnectionError
}
}
case APIConnectionError => throw new ConnectException
}
private def sendPushNotification(noti: MobileNotifications): Future[WSResponse] = {
val message = "Push notification message example"
Logger.info(s"Push Notification >> $message to users " + noti.users)
PushClient.sendNotification(message, noti.users)
}
}
Is this an Akka bug or am I doing something wrong?
Thanks!
I think that the problem is that the exception thrown inside the Future doesn't belong to the same thread (potentially) as the one the Actor is running (someone more experienced can elaborate on this). So, the problem is that the exception thrown inside the Future body is "swallowed" and not propagated to the Actor. Since this is the case, the Actor doesn't fail and so there's no need to apply the supervision strategy. So, the first solution that comes to my mind is to wrap the exception inside the Future in some message, send it to yourself, and then throw it from inside the Actor context itself. This time, the Exception will be caught and the supervision strategy will be applied. Note, however, that unless you send the Send(noti) message again, you will not see the Exception happening since the Actor was restarted. All in all, the code would be like this:
class PushSenderActor extends Actor with ActorLogging {
case class SmthFailed(e: Exception)
def receive = {
case Send(noti) => {
val response = sendPushNotification(noti) onFailure {
case e: ConnectException => self ! SmthFailed(e) // send the exception to yourself
}
}
case SmthFailed(e) =>
throw e // this one will be caught by the supervisor
}
private def sendPushNotification(noti: MobileNotifications): Future[WSResponse] = {
val message = "Push notification message example"
Logger.info(s"Push Notification >> $message to users " + noti.users)
PushClient.sendNotification(message, noti.users)
}
}
Hope it helped.

How should an akka actor be created that might throw an exception?

I am migrating a project from scala actors to akka actors. I used to have something like this where the constructor of MyActor may throw an exception if a certain system resource is unavailable:
var myActor: MyActor = null
try {
myActor = new MyActor(3)
}
catch {
case e: SomeUserDefinedException => println("failed!")
}
With akka, I migrated the code to this:
val someParam = 3
var myActor: ActorRef = null
try {
myActor = context.actorOf(Props(classOf[MyActor], someParam), "myActor")
}
catch {
case e: SomeUserDefinedException => println("failed!")
}
The problem I'm having is that it seems like in the akka case, the context.actorOf call isn't actually creating the MyActor object itself, but deferring it to another thread. So when the constructor throws an exception, the try/catch block that I put in has no effect.
How can I migrate this scala actor code into akka actors? Ideally I would prefer to avoid adding a lot of additional complexity.
You can catch the exception in the constructor of MyActor and notify other actors (e.g. the parent) about this exception. Try this:
class MyActor(val parent: ActorRef) extends Actor {
try{
throw new RuntimeException("failed construct")
} catch {
case e: Throwable =>
parent ! e
throw e
}
def receive: Actor.Receive = {case _ => }
}
class ParentActor extends Actor {
val child = context.actorOf(Props(classOf[MyActor], self), "child")
override def receive = {
case e: Throwable => println(e)
}
}

How to catch an exception within loop/react of an actor?

Is it possible to catch an exception raised within behaviour? Where to place the try/catch? I know that react uses exceptions to reuse the same thread for different actors and that´s why I don´t know where to put the try/catch. I want to catch certain exceptions by myself for logging.
import scala.actors._
def behaviour: PartialFunction[Any,Unit] = {
case x =>
println(x)
throw new IllegalStateException
}
val a = new Actor {
def act {
loop {
react {
behaviour
}
}
}
}
a.start
a ! "Bam"
eThe Actor has a exception handler function which can be overridden:
override def exceptionHandler = {
case e: Exception =>
println(e.getMessage())
}
Whenever a exception is raised in the actor that would normally cause it to terminate - the exceptionHandler partial function is applied to the exception.
Edit
With Exception filter:
class ExceptionalActor extends Actor{
def act() {
loop {
react {
case "bad" => throw new NoSuchFieldException("Bad Message")
case "impossible" => throw new Exception("Impossible Exception")
case m => println("non-bad message " + m )
}
}
}
override def exceptionHandler = {
case e: NoSuchFieldException => println("handled " + e.getMessage() )
}
}
object Tester extends App {
val eActr = new ExceptionalActor
eActr start
eActr ! "any message1"
eActr ! "bad"
eActr ! "any message2"
eActr ! "impossible"
eActr ! "any message3"
}
produces:
non-bad message any message1
handled Bad Message
non-bad message any message2
org.scratch.act.ExceptionalActor#7f5663a2: caught java.lang.Exception: Impossible Exception
:
And actor death.
ref: Actors in Scala