Skip to content

Instantly share code, notes, and snippets.

@derekwyatt
Created April 21, 2011 13:33
Show Gist options
  • Save derekwyatt/934475 to your computer and use it in GitHub Desktop.
Save derekwyatt/934475 to your computer and use it in GitHub Desktop.
Pattern for async exception handling?
class NewRouter extends Actor {
self.faultHandler = OneForOneStrategy(List(classOf[Throwable]), 5, 5000)
var sendCount = 0
def receive = {
case Send(tag) =>
val routeBackTo = self.sender
val riskyWorker = actorOf(new Actor {
self.lifeCycle = Permanent
def receive = {
case "go" =>
RemoteServer.send(tag, routeBackTo)
self ! PoisonPill
}
override def postRestart(reason: Throwable) {
// if reason can't be retried
routeBackTo.foreach(_ ! SendFailed(tag))
self ! PoisonPill
}
})
self.startLink(riskyWorker)
sendCount += 1
riskyWorker ! "go"
case GetSendCount =>
self.reply_?(sendCount)
}
}
"run fine when death occurs2" in { // {{{2
RemoteServer.failOnTag = "Die"
val a = actorOf[NewRouter].start
a ! Send("Die")
expectMsg (100 millis) {
case msg =>
msg must be (SendFailed("Die"))
}
a !! GetSendCount must be (Some(1))
a.stop
} // }}}2
import akka.actor._
import akka.actor.Actor._
import akka.config.Supervision._
import scala.collection.mutable.Map
case class Send(tag: String)
case class SendFailed(tag: String)
case class Receive(tag: String)
case object GetSendCount
object RemoteServer {
var failOnTag = ""
def send(tag: String, echoTo: Option[ActorRef]) {
if (tag == failOnTag)
throw new RuntimeException("I'm supposed to fail")
echoTo.foreach(_ ! Receive(tag + " received"))
}
}
class Router extends Actor {
var sendCount = 0
def receive = {
case Send(tag) =>
val routeBackTo = self.sender
val riskyWorker = actorOf(new Actor {
def receive = {
case "go" =>
try {
RemoteServer.send(tag, routeBackTo)
} catch {
case e =>
routeBackTo.foreach(_ ! SendFailed(tag))
}
case msg @ Receive(tag) =>
routeBackTo.foreach(_ ! msg)
self ! PoisonPill
}
})
self.startLink(riskyWorker)
sendCount += 1
riskyWorker ! "go"
case GetSendCount =>
self.reply_?(sendCount)
}
}
import org.scalatest.WordSpec
import org.scalatest.BeforeAndAfterEach
import org.scalatest.matchers.MustMatchers
import akka.actor._
import akka.actor.Actor._
import akka.testkit.TestKit
import akka.util.duration._
class PatternTests extends WordSpec
with BeforeAndAfterEach
with MustMatchers
with TestKit {
override def beforeEach {
RemoteServer.failOnTag = ""
}
"Pattern" should { // {{{1
"run fine" in { // {{{2
val a = actorOf[Router].start
a ! Send("Hithere")
expectMsg (50 millis) {
case msg =>
msg must be (Receive("Hithere received"))
}
a !! GetSendCount must be (Some(1))
a.stop
} // }}}2
"run fine when death occurs" in { // {{{2
RemoteServer.failOnTag = "Die"
val a = actorOf[Router].start
a ! Send("Die")
expectMsg (50 millis) {
case msg =>
msg must be (SendFailed("Die"))
}
a !! GetSendCount must be (Some(1))
a.stop
} // }}}2
} // }}}1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment