|
Akka/Scala example source code file (UidClashTest.scala)
The UidClashTest.scala Akka example source code
/**
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
import akka.testkit.{ TestProbe, AkkaSpec }
import akka.actor.SupervisorStrategy.{ Restart, Stop }
import akka.dispatch.sysmsg.SystemMessage
import akka.event.EventStream
import scala.util.control.NoStackTrace
object UidClashTest {
class TerminatedForNonWatchedActor extends Exception("Received Terminated for actor that was not actually watched")
with NoStackTrace
@volatile var oldActor: ActorRef = _
class EvilCollidingActorRef(override val provider: ActorRefProvider,
override val path: ActorPath,
val eventStream: EventStream) extends MinimalActorRef {
//Ignore everything
override def isTerminated(): Boolean = true
override def sendSystemMessage(message: SystemMessage): Unit = ()
override def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit = ()
}
def createCollidingRef(system: ActorSystem): ActorRef =
new EvilCollidingActorRef(system.asInstanceOf[ActorSystemImpl].provider, oldActor.path, system.eventStream)
case object PleaseRestart
case object PingMyself
case object RestartedSafely
class RestartedActor extends Actor {
def receive = {
case PleaseRestart ⇒ throw new Exception("restart")
case Terminated(ref) ⇒ throw new TerminatedForNonWatchedActor
// This is the tricky part to make this test a positive one (avoid expectNoMsg).
// Since anything enqueued in postRestart will arrive before the Terminated
// the bug triggers, there needs to be a bounce:
// 1. Ping is sent from postRestart to self
// 2. As a response to pint, RestartedSafely is sent to self
// 3a. if Terminated was enqueued during the restart procedure it will arrive before the RestartedSafely message
// 3b. otherwise only the RestartedSafely message arrives
case PingMyself ⇒ self ! RestartedSafely
case RestartedSafely ⇒ context.parent ! RestartedSafely
}
override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
context.children foreach { child ⇒
oldActor = child
context.unwatch(child)
context.stop(child)
}
}
override def preStart(): Unit = context.watch(context.actorOf(Props.empty, "child"))
override def postRestart(reason: Throwable): Unit = {
context.watch(createCollidingRef(context.system))
self ! PingMyself
} // Simulate UID clash
}
class RestartingActor(probe: ActorRef) extends Actor {
override val supervisorStrategy = OneForOneStrategy(loggingEnabled = false) {
case _: TerminatedForNonWatchedActor ⇒
context.stop(self)
Stop
case _ ⇒ Restart
}
val theRestartedOne = context.actorOf(Props[RestartedActor], "theRestartedOne")
def receive = {
case PleaseRestart ⇒ theRestartedOne ! PleaseRestart
case RestartedSafely ⇒ probe ! RestartedSafely
}
}
}
class UidClashTest extends AkkaSpec {
import UidClashTest._
"The Terminated message for an old child stopped in preRestart" should {
"not arrive after restart" in {
val watcher = TestProbe()
val topActor = system.actorOf(Props(classOf[RestartingActor], watcher.ref), "top")
watcher.watch(topActor)
topActor ! PleaseRestart
watcher.expectMsg(RestartedSafely)
}
}
}
Other Akka source code examplesHere is a short list of links related to this Akka UidClashTest.scala source code file: |
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.