Merge branch 'logging'
known failures in BalancingDispatcherModelSpec and ActorPoolSpec have tickets
This commit is contained in:
commit
70ae4e1984
78 changed files with 1608 additions and 927 deletions
|
|
@ -6,7 +6,6 @@ package akka.actor
|
|||
|
||||
import akka.testkit._
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import akka.testkit.Testing.sleepFor
|
||||
import akka.util.duration._
|
||||
import akka.dispatch.Dispatchers
|
||||
|
||||
|
|
@ -84,7 +83,7 @@ class ActorFireForgetRequestReplySpec extends AkkaSpec with BeforeAndAfterEach {
|
|||
actor.isShutdown must be(false)
|
||||
actor ! "Die"
|
||||
state.finished.await
|
||||
sleepFor(1 second)
|
||||
1.second.dilated.sleep()
|
||||
actor.isShutdown must be(true)
|
||||
supervisor.stop()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import org.scalatest.matchers.MustMatchers
|
|||
|
||||
import akka.testkit._
|
||||
import akka.util.duration._
|
||||
import akka.testkit.Testing.sleepFor
|
||||
import java.lang.IllegalStateException
|
||||
import akka.util.ReflectiveAccess
|
||||
import akka.dispatch.{ DefaultPromise, Promise, Future }
|
||||
|
|
@ -20,8 +19,6 @@ object ActorRefSpec {
|
|||
|
||||
case class ReplyTo(sender: ActorRef)
|
||||
|
||||
val latch = TestLatch(4)
|
||||
|
||||
class ReplyActor extends Actor {
|
||||
var replyTo: ActorRef = null
|
||||
|
||||
|
|
@ -53,11 +50,11 @@ object ActorRefSpec {
|
|||
}
|
||||
|
||||
private def work {
|
||||
sleepFor(1 second)
|
||||
1.second.dilated.sleep
|
||||
}
|
||||
}
|
||||
|
||||
class SenderActor(replyActor: ActorRef) extends Actor {
|
||||
class SenderActor(replyActor: ActorRef, latch: TestLatch) extends Actor {
|
||||
|
||||
def receive = {
|
||||
case "complex" ⇒ replyActor ! "complexRequest"
|
||||
|
|
@ -339,8 +336,9 @@ class ActorRefSpec extends AkkaSpec {
|
|||
}
|
||||
|
||||
"support reply via sender" in {
|
||||
val latch = new TestLatch(4)
|
||||
val serverRef = actorOf(Props[ReplyActor])
|
||||
val clientRef = actorOf(Props(new SenderActor(serverRef)))
|
||||
val clientRef = actorOf(Props(new SenderActor(serverRef, latch)))
|
||||
|
||||
clientRef ! "complex"
|
||||
clientRef ! "simple"
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ class DeathWatchSpec extends AkkaSpec with BeforeAndAfterEach with ImplicitSende
|
|||
}
|
||||
|
||||
"fail a monitor which does not handle Terminated()" in {
|
||||
filterEvents(EventFilter[ActorKilledException], EventFilter[DeathPactException]) {
|
||||
filterEvents(EventFilter[ActorKilledException](), EventFilter[DeathPactException]()) {
|
||||
case class FF(fail: Failed)
|
||||
val supervisor = actorOf(Props[Supervisor]
|
||||
.withFaultHandler(new OneForOneStrategy(FaultHandlingStrategy.makeDecider(List(classOf[Exception])), Some(0)) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ package akka.actor
|
|||
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
|
||||
|
||||
import akka.testkit._
|
||||
import TestEvent.{ Mute, UnMuteAll }
|
||||
import TestEvent.Mute
|
||||
import FSM._
|
||||
import akka.util.Duration
|
||||
import akka.util.duration._
|
||||
|
|
@ -18,19 +18,23 @@ import akka.config.Configuration
|
|||
|
||||
object FSMActorSpec {
|
||||
|
||||
val unlockedLatch = TestLatch()
|
||||
val lockedLatch = TestLatch()
|
||||
val unhandledLatch = TestLatch()
|
||||
val terminatedLatch = TestLatch()
|
||||
val transitionLatch = TestLatch()
|
||||
val initialStateLatch = TestLatch()
|
||||
val transitionCallBackLatch = TestLatch()
|
||||
class Latches(implicit app: AkkaApplication) {
|
||||
val unlockedLatch = TestLatch()
|
||||
val lockedLatch = TestLatch()
|
||||
val unhandledLatch = TestLatch()
|
||||
val terminatedLatch = TestLatch()
|
||||
val transitionLatch = TestLatch()
|
||||
val initialStateLatch = TestLatch()
|
||||
val transitionCallBackLatch = TestLatch()
|
||||
}
|
||||
|
||||
sealed trait LockState
|
||||
case object Locked extends LockState
|
||||
case object Open extends LockState
|
||||
|
||||
class Lock(code: String, timeout: Duration) extends Actor with FSM[LockState, CodeState] {
|
||||
class Lock(code: String, timeout: Duration, latches: Latches) extends Actor with FSM[LockState, CodeState] {
|
||||
|
||||
import latches._
|
||||
|
||||
startWith(Locked, CodeState("", code))
|
||||
|
||||
|
|
@ -61,7 +65,7 @@ object FSMActorSpec {
|
|||
|
||||
whenUnhandled {
|
||||
case Ev(msg) ⇒ {
|
||||
app.eventHandler.info(this, "unhandled event " + msg + " in state " + stateName + " with data " + stateData)
|
||||
log.warning("unhandled event " + msg + " in state " + stateName + " with data " + stateData)
|
||||
unhandledLatch.open
|
||||
stay
|
||||
}
|
||||
|
|
@ -107,8 +111,11 @@ class FSMActorSpec extends AkkaSpec(Configuration("akka.actor.debug.fsm" -> true
|
|||
|
||||
"unlock the lock" in {
|
||||
|
||||
val latches = new Latches
|
||||
import latches._
|
||||
|
||||
// lock that locked after being open for 1 sec
|
||||
val lock = actorOf(new Lock("33221", 1 second))
|
||||
val lock = actorOf(new Lock("33221", 1 second, latches))
|
||||
|
||||
val transitionTester = actorOf(new Actor {
|
||||
def receive = {
|
||||
|
|
@ -131,10 +138,7 @@ class FSMActorSpec extends AkkaSpec(Configuration("akka.actor.debug.fsm" -> true
|
|||
transitionCallBackLatch.await
|
||||
lockedLatch.await
|
||||
|
||||
filterEvents(EventFilter.custom {
|
||||
case EventHandler.Info(_: Lock, _) ⇒ true
|
||||
case _ ⇒ false
|
||||
}) {
|
||||
EventFilter.warning(start = "unhandled event", occurrences = 1) intercept {
|
||||
lock ! "not_handled"
|
||||
unhandledLatch.await
|
||||
}
|
||||
|
|
@ -163,18 +167,13 @@ class FSMActorSpec extends AkkaSpec(Configuration("akka.actor.debug.fsm" -> true
|
|||
case Ev("go") ⇒ goto(2)
|
||||
}
|
||||
})
|
||||
val logger = actorOf(new Actor {
|
||||
def receive = {
|
||||
case x ⇒ testActor forward x
|
||||
}
|
||||
})
|
||||
filterException[EventHandler.EventHandlerException] {
|
||||
app.eventHandler.addListener(logger)
|
||||
filterException[Logging.EventHandlerException] {
|
||||
app.mainbus.subscribe(testActor, classOf[Logging.Error])
|
||||
fsm ! "go"
|
||||
expectMsgPF(1 second) {
|
||||
case EventHandler.Error(_: EventHandler.EventHandlerException, `fsm`, "Next state 2 does not exist") ⇒ true
|
||||
expectMsgPF(1 second, hint = "Next state 2 does not exist") {
|
||||
case Logging.Error(_, `fsm`, "Next state 2 does not exist") ⇒ true
|
||||
}
|
||||
app.eventHandler.removeListener(logger)
|
||||
app.mainbus.unsubscribe(testActor)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,47 +195,40 @@ class FSMActorSpec extends AkkaSpec(Configuration("akka.actor.debug.fsm" -> true
|
|||
|
||||
"log events and transitions if asked to do so" in {
|
||||
new TestKit(AkkaApplication("fsm event", AkkaApplication.defaultConfig ++
|
||||
Configuration("akka.event-handler-level" -> "DEBUG",
|
||||
Configuration("akka.loglevel" -> "DEBUG",
|
||||
"akka.actor.debug.fsm" -> true))) {
|
||||
app.eventHandler.notify(TestEvent.Mute(EventFilter.custom {
|
||||
case _: EventHandler.Debug ⇒ true
|
||||
case _ ⇒ false
|
||||
}))
|
||||
val fsm = TestActorRef(new Actor with LoggingFSM[Int, Null] {
|
||||
startWith(1, null)
|
||||
when(1) {
|
||||
case Ev("go") ⇒
|
||||
setTimer("t", Shutdown, 1.5 seconds, false)
|
||||
goto(2)
|
||||
EventFilter.debug() intercept {
|
||||
val fsm = TestActorRef(new Actor with LoggingFSM[Int, Null] {
|
||||
startWith(1, null)
|
||||
when(1) {
|
||||
case Ev("go") ⇒
|
||||
setTimer("t", Shutdown, 1.5 seconds, false)
|
||||
goto(2)
|
||||
}
|
||||
when(2) {
|
||||
case Ev("stop") ⇒
|
||||
cancelTimer("t")
|
||||
stop
|
||||
}
|
||||
onTermination {
|
||||
case StopEvent(r, _, _) ⇒ testActor ! r
|
||||
}
|
||||
})
|
||||
app.mainbus.subscribe(testActor, classOf[Logging.Debug])
|
||||
fsm ! "go"
|
||||
expectMsgPF(1 second, hint = "processing Event(go,null)") {
|
||||
case Logging.Debug(`fsm`, s: String) if s.startsWith("processing Event(go,null) from Actor[testActor") ⇒ true
|
||||
}
|
||||
when(2) {
|
||||
case Ev("stop") ⇒
|
||||
cancelTimer("t")
|
||||
stop
|
||||
expectMsg(1 second, Logging.Debug(fsm, "setting timer 't'/1500 milliseconds: Shutdown"))
|
||||
expectMsg(1 second, Logging.Debug(fsm, "transition 1 -> 2"))
|
||||
fsm ! "stop"
|
||||
expectMsgPF(1 second, hint = "processing Event(stop,null)") {
|
||||
case Logging.Debug(`fsm`, s: String) if s.startsWith("processing Event(stop,null) from Actor[testActor") ⇒ true
|
||||
}
|
||||
onTermination {
|
||||
case StopEvent(r, _, _) ⇒ testActor ! r
|
||||
}
|
||||
})
|
||||
val logger = actorOf(new Actor {
|
||||
def receive = {
|
||||
case x ⇒ testActor forward x
|
||||
}
|
||||
})
|
||||
app.eventHandler.addListener(logger)
|
||||
fsm ! "go"
|
||||
expectMsgPF(1 second) {
|
||||
case EventHandler.Debug(`fsm`, s: String) if s.startsWith("processing Event(go,null) from Actor[testActor") ⇒ true
|
||||
expectMsgAllOf(1 second, Logging.Debug(fsm, "canceling timer 't'"), Normal)
|
||||
expectNoMsg(1 second)
|
||||
app.mainbus.unsubscribe(testActor)
|
||||
}
|
||||
expectMsg(1 second, EventHandler.Debug(fsm, "setting timer 't'/1500 milliseconds: Shutdown"))
|
||||
expectMsg(1 second, EventHandler.Debug(fsm, "transition 1 -> 2"))
|
||||
fsm ! "stop"
|
||||
expectMsgPF(1 second) {
|
||||
case EventHandler.Debug(`fsm`, s: String) if s.startsWith("processing Event(stop,null) from Actor[testActor") ⇒ true
|
||||
}
|
||||
expectMsgAllOf(1 second, EventHandler.Debug(fsm, "canceling timer 't'"), Normal)
|
||||
expectNoMsg(1 second)
|
||||
app.eventHandler.removeListener(logger)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@
|
|||
|
||||
package akka.actor
|
||||
|
||||
import akka.testkit.{ AkkaSpec, ImplicitSender }
|
||||
import akka.testkit._
|
||||
import akka.util.Duration
|
||||
import akka.util.duration._
|
||||
import akka.event.Logging
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
class FSMTimingSpec extends AkkaSpec with ImplicitSender {
|
||||
|
|
@ -15,7 +16,7 @@ class FSMTimingSpec extends AkkaSpec with ImplicitSender {
|
|||
|
||||
val fsm = actorOf(new StateMachine(testActor))
|
||||
fsm ! SubscribeTransitionCallBack(testActor)
|
||||
expectMsg(200 millis, CurrentState(fsm, Initial))
|
||||
expectMsg(1 second, CurrentState(fsm, Initial))
|
||||
|
||||
ignoreMsg {
|
||||
case Transition(_, Initial, _) ⇒ true
|
||||
|
|
@ -24,8 +25,20 @@ class FSMTimingSpec extends AkkaSpec with ImplicitSender {
|
|||
"A Finite State Machine" must {
|
||||
|
||||
"receive StateTimeout" in {
|
||||
within(50 millis, 250 millis) {
|
||||
within(1 second) {
|
||||
within(500 millis, 1 second) {
|
||||
fsm ! TestStateTimeout
|
||||
expectMsg(Transition(fsm, TestStateTimeout, Initial))
|
||||
}
|
||||
expectNoMsg
|
||||
}
|
||||
}
|
||||
|
||||
"cancel a StateTimeout" in {
|
||||
within(1 second) {
|
||||
fsm ! TestStateTimeout
|
||||
fsm ! Cancel
|
||||
expectMsg(Cancel)
|
||||
expectMsg(Transition(fsm, TestStateTimeout, Initial))
|
||||
expectNoMsg
|
||||
}
|
||||
|
|
@ -36,7 +49,7 @@ class FSMTimingSpec extends AkkaSpec with ImplicitSender {
|
|||
fsm ! TestStateTimeoutOverride
|
||||
expectNoMsg
|
||||
}
|
||||
within(50 millis) {
|
||||
within(500 millis) {
|
||||
fsm ! Cancel
|
||||
expectMsg(Cancel)
|
||||
expectMsg(Transition(fsm, TestStateTimeout, Initial))
|
||||
|
|
@ -44,71 +57,68 @@ class FSMTimingSpec extends AkkaSpec with ImplicitSender {
|
|||
}
|
||||
|
||||
"receive single-shot timer" in {
|
||||
within(50 millis, 250 millis) {
|
||||
fsm ! TestSingleTimer
|
||||
expectMsg(Tick)
|
||||
expectMsg(Transition(fsm, TestSingleTimer, Initial))
|
||||
within(1.5 seconds) {
|
||||
within(500 millis, 1 second) {
|
||||
fsm ! TestSingleTimer
|
||||
expectMsg(Tick)
|
||||
expectMsg(Transition(fsm, TestSingleTimer, Initial))
|
||||
}
|
||||
expectNoMsg
|
||||
}
|
||||
}
|
||||
|
||||
"correctly cancel a named timer" in {
|
||||
fsm ! TestCancelTimer
|
||||
within(100 millis, 200 millis) {
|
||||
within(500 millis) {
|
||||
fsm ! Tick
|
||||
expectMsg(Tick)
|
||||
}
|
||||
within(300 millis, 1 second) {
|
||||
expectMsg(Tock)
|
||||
}
|
||||
fsm ! Cancel
|
||||
expectMsg(Transition(fsm, TestCancelTimer, Initial))
|
||||
expectMsg(1 second, Transition(fsm, TestCancelTimer, Initial))
|
||||
}
|
||||
|
||||
"not get confused between named and state timers" in {
|
||||
fsm ! TestCancelStateTimerInNamedTimerMessage
|
||||
fsm ! Tick
|
||||
expectMsg(100 millis, Tick)
|
||||
Thread.sleep(200)
|
||||
expectMsg(500 millis, Tick)
|
||||
Thread.sleep(200) // this is ugly: need to wait for StateTimeout to be queued
|
||||
resume(fsm)
|
||||
expectMsg(100 millis, Transition(fsm, TestCancelStateTimerInNamedTimerMessage, TestCancelStateTimerInNamedTimerMessage2))
|
||||
expectMsg(500 millis, Transition(fsm, TestCancelStateTimerInNamedTimerMessage, TestCancelStateTimerInNamedTimerMessage2))
|
||||
fsm ! Cancel
|
||||
within(100 millis) {
|
||||
expectMsg(Cancel)
|
||||
within(500 millis) {
|
||||
expectMsg(Cancel) // if this is not received, that means StateTimeout was not properly discarded
|
||||
expectMsg(Transition(fsm, TestCancelStateTimerInNamedTimerMessage2, Initial))
|
||||
}
|
||||
}
|
||||
|
||||
"receive and cancel a repeated timer" in {
|
||||
fsm ! TestRepeatedTimer
|
||||
val seq = receiveWhile(600 millis) {
|
||||
val seq = receiveWhile(1 second) {
|
||||
case Tick ⇒ Tick
|
||||
}
|
||||
seq must have length (5)
|
||||
within(250 millis) {
|
||||
seq must have length 5
|
||||
within(500 millis) {
|
||||
expectMsg(Transition(fsm, TestRepeatedTimer, Initial))
|
||||
expectNoMsg
|
||||
}
|
||||
}
|
||||
|
||||
"notify unhandled messages" in {
|
||||
fsm ! TestUnhandled
|
||||
within(200 millis) {
|
||||
fsm ! Tick
|
||||
expectNoMsg
|
||||
}
|
||||
within(200 millis) {
|
||||
fsm ! SetHandler
|
||||
fsm ! Tick
|
||||
expectMsg(Unhandled(Tick))
|
||||
expectNoMsg
|
||||
}
|
||||
within(200 millis) {
|
||||
fsm ! Unhandled("test")
|
||||
expectNoMsg
|
||||
}
|
||||
within(200 millis) {
|
||||
fsm ! Cancel
|
||||
expectMsg(Transition(fsm, TestUnhandled, Initial))
|
||||
}
|
||||
filterEvents(EventFilter.warning("unhandled event Tick in state TestUnhandled", source = fsm, occurrences = 1),
|
||||
EventFilter.warning("unhandled event Unhandled(test) in state TestUnhandled", source = fsm, occurrences = 1)) {
|
||||
fsm ! TestUnhandled
|
||||
within(1 second) {
|
||||
fsm ! Tick
|
||||
fsm ! SetHandler
|
||||
fsm ! Tick
|
||||
expectMsg(Unhandled(Tick))
|
||||
fsm ! Unhandled("test")
|
||||
fsm ! Cancel
|
||||
expectMsg(Transition(fsm, TestUnhandled, Initial))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -151,7 +161,7 @@ object FSMTimingSpec {
|
|||
startWith(Initial, 0)
|
||||
when(Initial) {
|
||||
case Ev(TestSingleTimer) ⇒
|
||||
setTimer("tester", Tick, 100 millis, false)
|
||||
setTimer("tester", Tick, 500 millis, false)
|
||||
goto(TestSingleTimer)
|
||||
case Ev(TestRepeatedTimer) ⇒
|
||||
setTimer("tester", Tick, 100 millis, true)
|
||||
|
|
@ -160,7 +170,7 @@ object FSMTimingSpec {
|
|||
goto(TestStateTimeout) forMax (Duration.Inf)
|
||||
case Ev(x: FSMTimingSpec.State) ⇒ goto(x)
|
||||
}
|
||||
when(TestStateTimeout, stateTimeout = 100 millis) {
|
||||
when(TestStateTimeout, stateTimeout = 500 millis) {
|
||||
case Ev(StateTimeout) ⇒ goto(Initial)
|
||||
case Ev(Cancel) ⇒ goto(Initial) replying (Cancel)
|
||||
}
|
||||
|
|
@ -173,9 +183,9 @@ object FSMTimingSpec {
|
|||
case Ev(Tick) ⇒
|
||||
tester ! Tick
|
||||
setTimer("hallo", Tock, 1 milli, false)
|
||||
Thread.sleep(10);
|
||||
TestKit.awaitCond(context.hasMessages, 1 second)
|
||||
cancelTimer("hallo")
|
||||
setTimer("hallo", Tock, 100 millis, false)
|
||||
setTimer("hallo", Tock, 500 millis, false)
|
||||
stay
|
||||
case Ev(Tock) ⇒
|
||||
tester ! Tock
|
||||
|
|
@ -195,11 +205,12 @@ object FSMTimingSpec {
|
|||
}
|
||||
}
|
||||
when(TestCancelStateTimerInNamedTimerMessage) {
|
||||
// FSM is suspended after processing this message and resumed 200ms later
|
||||
// FSM is suspended after processing this message and resumed 500ms later
|
||||
case Ev(Tick) ⇒
|
||||
suspend(self)
|
||||
setTimer("named", Tock, 10 millis, false)
|
||||
stay forMax (100 millis) replying Tick
|
||||
setTimer("named", Tock, 1 millis, false)
|
||||
TestKit.awaitCond(context.hasMessages, 1 second)
|
||||
stay forMax (1 millis) replying Tick
|
||||
case Ev(Tock) ⇒
|
||||
goto(TestCancelStateTimerInNamedTimerMessage2)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ import org.scalatest.WordSpec
|
|||
import akka.AkkaApplication
|
||||
import akka.AkkaApplication.defaultConfig
|
||||
import akka.config.Configuration
|
||||
import akka.event.EventHandler
|
||||
import akka.event.Logging
|
||||
import akka.util.Duration
|
||||
|
||||
object LoggingReceiveSpec {
|
||||
class TestLogActor extends Actor {
|
||||
|
|
@ -18,6 +19,7 @@ object LoggingReceiveSpec {
|
|||
}
|
||||
}
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAndAfterAll {
|
||||
|
||||
import LoggingReceiveSpec._
|
||||
|
|
@ -28,13 +30,13 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
|
|||
val appLifecycle = AkkaApplication("lifecycle", config ++ Configuration("akka.actor.debug.lifecycle" -> true))
|
||||
|
||||
val filter = TestEvent.Mute(EventFilter.custom {
|
||||
case _: EventHandler.Debug ⇒ true
|
||||
case _: EventHandler.Info ⇒ true
|
||||
case _ ⇒ false
|
||||
case _: Logging.Debug ⇒ true
|
||||
case _: Logging.Info ⇒ true
|
||||
case _ ⇒ false
|
||||
})
|
||||
appLogging.eventHandler.notify(filter)
|
||||
appAuto.eventHandler.notify(filter)
|
||||
appLifecycle.eventHandler.notify(filter)
|
||||
appLogging.mainbus.publish(filter)
|
||||
appAuto.mainbus.publish(filter)
|
||||
appLifecycle.mainbus.publish(filter)
|
||||
|
||||
def ignoreMute(t: TestKit) {
|
||||
t.ignoreMsg {
|
||||
|
|
@ -42,24 +44,31 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
|
|||
}
|
||||
}
|
||||
|
||||
override def afterAll {
|
||||
appLogging.stop()
|
||||
appAuto.stop()
|
||||
appLifecycle.stop()
|
||||
}
|
||||
|
||||
"A LoggingReceive" must {
|
||||
|
||||
"decorate a Receive" in {
|
||||
new TestKit(appLogging) {
|
||||
app.eventHandler.addListener(testActor)
|
||||
app.mainbus.subscribe(testActor, classOf[Logging.Debug])
|
||||
val r: Actor.Receive = {
|
||||
case null ⇒
|
||||
}
|
||||
val log = Actor.LoggingReceive(this, r)
|
||||
log.isDefinedAt("hallo")
|
||||
expectMsg(1 second, EventHandler.Debug(this, "received unhandled message hallo"))
|
||||
expectMsg(1 second, Logging.Debug(this, "received unhandled message hallo"))
|
||||
}
|
||||
}
|
||||
|
||||
"be added on Actor if requested" in {
|
||||
new TestKit(appLogging) with ImplicitSender {
|
||||
ignoreMute(this)
|
||||
app.eventHandler.addListener(testActor)
|
||||
app.mainbus.subscribe(testActor, classOf[Logging.Debug])
|
||||
app.mainbus.subscribe(testActor, classOf[Logging.Error])
|
||||
val actor = TestActorRef(new Actor {
|
||||
def receive = loggable(this) {
|
||||
case _ ⇒ sender ! "x"
|
||||
|
|
@ -67,7 +76,7 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
|
|||
})
|
||||
actor ! "buh"
|
||||
within(1 second) {
|
||||
expectMsg(EventHandler.Debug(actor.underlyingActor, "received handled message buh"))
|
||||
expectMsg(Logging.Debug(actor.underlyingActor, "received handled message buh"))
|
||||
expectMsg("x")
|
||||
}
|
||||
val r: Actor.Receive = {
|
||||
|
|
@ -75,20 +84,19 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
|
|||
}
|
||||
actor ! HotSwap(_ ⇒ r, false)
|
||||
filterException[UnhandledMessageException] {
|
||||
within(300 millis) {
|
||||
within(500 millis) {
|
||||
actor ! "bah"
|
||||
expectMsgPF() {
|
||||
case EventHandler.Error(_: UnhandledMessageException, `actor`, _) ⇒ true
|
||||
case Logging.Error(_: UnhandledMessageException, `actor`, _) ⇒ true
|
||||
}
|
||||
}
|
||||
}
|
||||
actor.stop()
|
||||
}
|
||||
}
|
||||
|
||||
"not duplicate logging" in {
|
||||
new TestKit(appLogging) with ImplicitSender {
|
||||
app.eventHandler.addListener(testActor)
|
||||
app.mainbus.subscribe(testActor, classOf[Logging.Debug])
|
||||
val actor = TestActorRef(new Actor {
|
||||
def receive = loggable(this)(loggable(this) {
|
||||
case _ ⇒ sender ! "x"
|
||||
|
|
@ -96,7 +104,7 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
|
|||
})
|
||||
actor ! "buh"
|
||||
within(1 second) {
|
||||
expectMsg(EventHandler.Debug(actor.underlyingActor, "received handled message buh"))
|
||||
expectMsg(Logging.Debug(actor.underlyingActor, "received handled message buh"))
|
||||
expectMsg("x")
|
||||
}
|
||||
}
|
||||
|
|
@ -108,62 +116,69 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
|
|||
|
||||
"log AutoReceiveMessages if requested" in {
|
||||
new TestKit(appAuto) {
|
||||
app.eventHandler.addListener(testActor)
|
||||
app.mainbus.subscribe(testActor, classOf[Logging.Debug])
|
||||
val actor = TestActorRef(new Actor {
|
||||
def receive = {
|
||||
case _ ⇒
|
||||
}
|
||||
})
|
||||
actor ! PoisonPill
|
||||
expectMsg(300 millis, EventHandler.Debug(actor.underlyingActor, "received AutoReceiveMessage PoisonPill"))
|
||||
expectMsg(300 millis, Logging.Debug(actor.underlyingActor, "received AutoReceiveMessage PoisonPill"))
|
||||
awaitCond(actor.isShutdown, 100 millis)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO remove ignore as soon as logging is working properly during start-up again
|
||||
"log LifeCycle changes if requested" ignore {
|
||||
"log LifeCycle changes if requested" in {
|
||||
new TestKit(appLifecycle) {
|
||||
ignoreMute(this)
|
||||
app.eventHandler.addListener(testActor)
|
||||
within(2 seconds) {
|
||||
ignoreMsg {
|
||||
case Logging.Debug(ref, _) ⇒
|
||||
val s = ref.toString
|
||||
s.contains("MainBusReaper") || s.contains("Supervisor")
|
||||
}
|
||||
app.mainbus.subscribe(testActor, classOf[Logging.Debug])
|
||||
app.mainbus.subscribe(testActor, classOf[Logging.Error])
|
||||
within(3 seconds) {
|
||||
val supervisor = TestActorRef[TestLogActor](Props[TestLogActor].withFaultHandler(OneForOneStrategy(List(classOf[Throwable]), 5, 5000)))
|
||||
|
||||
expectMsg(EventHandler.Debug(supervisor, "started"))
|
||||
expectMsgPF() {
|
||||
case Logging.Debug(`supervisor`, msg: String) if msg startsWith "started" ⇒
|
||||
}
|
||||
|
||||
val actor = new TestActorRef[TestLogActor](app, Props[TestLogActor], supervisor, "none")
|
||||
|
||||
expectMsgPF() {
|
||||
case EventHandler.Debug(ref, msg: String) ⇒ ref == supervisor && msg.startsWith("now supervising")
|
||||
}
|
||||
|
||||
expectMsg(EventHandler.Debug(actor, "started"))
|
||||
val set = receiveWhile(messages = 2) {
|
||||
case Logging.Debug(`supervisor`, msg: String) if msg startsWith "now supervising" ⇒ 1
|
||||
case Logging.Debug(`actor`, msg: String) if msg startsWith "started" ⇒ 2
|
||||
}.toSet
|
||||
expectNoMsg(Duration.Zero)
|
||||
assert(set == Set(1, 2), set + " was not Set(1, 2)")
|
||||
|
||||
supervisor startsMonitoring actor
|
||||
expectMsgPF(hint = "now monitoring") {
|
||||
case EventHandler.Debug(ref, msg: String) ⇒
|
||||
case Logging.Debug(ref, msg: String) ⇒
|
||||
ref == supervisor.underlyingActor && msg.startsWith("now monitoring")
|
||||
}
|
||||
|
||||
supervisor stopsMonitoring actor
|
||||
expectMsgPF(hint = "stopped monitoring") {
|
||||
case EventHandler.Debug(ref, msg: String) ⇒
|
||||
case Logging.Debug(ref, msg: String) ⇒
|
||||
ref == supervisor.underlyingActor && msg.startsWith("stopped monitoring")
|
||||
}
|
||||
|
||||
filterException[ActorKilledException] {
|
||||
actor ! Kill
|
||||
expectMsgPF() {
|
||||
case EventHandler.Error(_: ActorKilledException, `actor`, "Kill") ⇒ true
|
||||
}
|
||||
expectMsg(EventHandler.Debug(actor, "restarting"))
|
||||
}
|
||||
awaitCond(msgAvailable)
|
||||
expectMsgPF(hint = "restarted") {
|
||||
case EventHandler.Debug(`actor`, "restarted") ⇒ true
|
||||
val set = receiveWhile(messages = 3) {
|
||||
case Logging.Error(_: ActorKilledException, `actor`, "Kill") ⇒ 1
|
||||
case Logging.Debug(`actor`, "restarting") ⇒ 2
|
||||
case Logging.Debug(`actor`, "restarted") ⇒ 3
|
||||
}.toSet
|
||||
expectNoMsg(Duration.Zero)
|
||||
assert(set == Set(1, 2, 3), set + " was not Set(1, 2, 3)")
|
||||
}
|
||||
|
||||
actor.stop()
|
||||
expectMsg(EventHandler.Debug(actor, "stopping"))
|
||||
expectMsg(Logging.Debug(actor, "stopping"))
|
||||
supervisor.stop()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,8 @@ import akka.testkit.AkkaSpec
|
|||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
class RestartStrategySpec extends AkkaSpec {
|
||||
|
||||
override def atStartup() {
|
||||
app.eventHandler.notify(Mute(EventFilter[Exception]("Crashing...")))
|
||||
}
|
||||
|
||||
override def atTermination() {
|
||||
app.eventHandler.notify(UnMuteAll)
|
||||
override def atStartup {
|
||||
app.mainbus.publish(Mute(EventFilter[Exception]("Crashing...")))
|
||||
}
|
||||
|
||||
object Ping
|
||||
|
|
|
|||
|
|
@ -17,10 +17,6 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach {
|
|||
future
|
||||
}
|
||||
|
||||
override def beforeEach {
|
||||
app.eventHandler.notify(Mute(EventFilter[Exception]("CRASH")))
|
||||
}
|
||||
|
||||
override def afterEach {
|
||||
while (futures.peek() ne null) { Option(futures.poll()).foreach(_.cancel(true)) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
package akka.actor
|
||||
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import akka.testkit.Testing.sleepFor
|
||||
import akka.util.duration._
|
||||
import akka.{ Die, Ping }
|
||||
import akka.actor.Actor._
|
||||
|
|
@ -17,7 +16,6 @@ import akka.testkit.AkkaSpec
|
|||
|
||||
object SupervisorSpec {
|
||||
val Timeout = 5 seconds
|
||||
val TimeoutMillis = Timeout.dilated.toMillis.toInt
|
||||
|
||||
case object DieReply
|
||||
|
||||
|
|
@ -70,6 +68,8 @@ class SupervisorSpec extends AkkaSpec with BeforeAndAfterEach with ImplicitSende
|
|||
|
||||
import SupervisorSpec._
|
||||
|
||||
val TimeoutMillis = Timeout.dilated.toMillis.toInt
|
||||
|
||||
// =====================================================
|
||||
// Creating actors and supervisors
|
||||
// =====================================================
|
||||
|
|
@ -121,14 +121,8 @@ class SupervisorSpec extends AkkaSpec with BeforeAndAfterEach with ImplicitSende
|
|||
(pingpong1, pingpong2, pingpong3, topSupervisor)
|
||||
}
|
||||
|
||||
override def atStartup() = {
|
||||
app.eventHandler notify Mute(EventFilter[Exception]("Die"),
|
||||
EventFilter[IllegalStateException]("Don't wanna!"),
|
||||
EventFilter[RuntimeException]("Expected"))
|
||||
}
|
||||
|
||||
override def atTermination() = {
|
||||
app.eventHandler notify UnMuteAll
|
||||
override def atStartup() {
|
||||
app.mainbus.publish(Mute(EventFilter[RuntimeException](ExceptionMessage)))
|
||||
}
|
||||
|
||||
override def beforeEach() = {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ package akka.actor
|
|||
import org.scalatest.WordSpec
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
import akka.util.duration._
|
||||
import akka.testkit.Testing.sleepFor
|
||||
import akka.dispatch.Dispatchers
|
||||
import akka.actor.Actor._
|
||||
import akka.testkit.{ TestKit, EventFilter, filterEvents, filterException }
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
package akka.actor.dispatch
|
||||
|
||||
import org.scalatest.Assertions._
|
||||
import akka.testkit.{ Testing, filterEvents, EventFilter, AkkaSpec }
|
||||
import akka.testkit.{ filterEvents, EventFilter, AkkaSpec }
|
||||
import akka.dispatch._
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import java.util.concurrent.{ ConcurrentHashMap, CountDownLatch, TimeUnit }
|
||||
|
|
@ -15,6 +15,7 @@ import akka.actor._
|
|||
import util.control.NoStackTrace
|
||||
import akka.AkkaApplication
|
||||
import akka.util.duration._
|
||||
import akka.event.Logging.Error
|
||||
|
||||
object ActorModelSpec {
|
||||
|
||||
|
|
@ -154,8 +155,8 @@ object ActorModelSpec {
|
|||
await(deadline)(stops == dispatcher.stops.get)
|
||||
} catch {
|
||||
case e ⇒
|
||||
app.eventHandler.error(e, dispatcher, "actual: starts=" + dispatcher.starts.get + ",stops=" + dispatcher.stops.get +
|
||||
" required: starts=" + starts + ",stops=" + stops)
|
||||
app.mainbus.publish(Error(e, dispatcher, "actual: starts=" + dispatcher.starts.get + ",stops=" + dispatcher.stops.get +
|
||||
" required: starts=" + starts + ",stops=" + stops))
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
|
@ -215,9 +216,9 @@ object ActorModelSpec {
|
|||
await(deadline)(stats.restarts.get() == restarts)
|
||||
} catch {
|
||||
case e ⇒
|
||||
app.eventHandler.error(e, dispatcher, "actual: " + stats + ", required: InterceptorStats(susp=" + suspensions +
|
||||
app.mainbus.publish(Error(e, dispatcher, "actual: " + stats + ", required: InterceptorStats(susp=" + suspensions +
|
||||
",res=" + resumes + ",reg=" + registers + ",unreg=" + unregisters +
|
||||
",recv=" + msgsReceived + ",proc=" + msgsProcessed + ",restart=" + restarts)
|
||||
",recv=" + msgsReceived + ",proc=" + msgsProcessed + ",restart=" + restarts))
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
|
@ -284,13 +285,13 @@ abstract class ActorModelSpec extends AkkaSpec {
|
|||
val a = newTestActor(dispatcher)
|
||||
|
||||
a ! CountDown(start)
|
||||
assertCountDown(start, Testing.testTime(3000), "Should process first message within 3 seconds")
|
||||
assertCountDown(start, 3.seconds.dilated.toMillis, "Should process first message within 3 seconds")
|
||||
assertRefDefaultZero(a)(registers = 1, msgsReceived = 1, msgsProcessed = 1)
|
||||
|
||||
a ! Wait(1000)
|
||||
a ! CountDown(oneAtATime)
|
||||
// in case of serialization violation, restart would happen instead of count down
|
||||
assertCountDown(oneAtATime, Testing.testTime(1500), "Processed message when allowed")
|
||||
assertCountDown(oneAtATime, (1.5 seconds).dilated.toMillis, "Processed message when allowed")
|
||||
assertRefDefaultZero(a)(registers = 1, msgsReceived = 3, msgsProcessed = 3)
|
||||
|
||||
a.stop()
|
||||
|
|
@ -309,7 +310,7 @@ abstract class ActorModelSpec extends AkkaSpec {
|
|||
}
|
||||
}
|
||||
}
|
||||
assertCountDown(counter, Testing.testTime(3000), "Should process 200 messages")
|
||||
assertCountDown(counter, 3.seconds.dilated.toMillis, "Should process 200 messages")
|
||||
assertRefDefaultZero(a)(registers = 1, msgsReceived = 200, msgsProcessed = 200)
|
||||
|
||||
a.stop()
|
||||
|
|
@ -321,7 +322,7 @@ abstract class ActorModelSpec extends AkkaSpec {
|
|||
try {
|
||||
f
|
||||
} catch {
|
||||
case e ⇒ app.eventHandler.error(e, this, "error in spawned thread")
|
||||
case e ⇒ app.mainbus.publish(Error(e, this, "error in spawned thread"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -338,7 +339,7 @@ abstract class ActorModelSpec extends AkkaSpec {
|
|||
assertRefDefaultZero(a)(registers = 1, msgsReceived = 1, suspensions = 1)
|
||||
|
||||
a.resume
|
||||
assertCountDown(done, Testing.testTime(3000), "Should resume processing of messages when resumed")
|
||||
assertCountDown(done, 3.seconds.dilated.toMillis, "Should resume processing of messages when resumed")
|
||||
assertRefDefaultZero(a)(registers = 1, msgsReceived = 1, msgsProcessed = 1,
|
||||
suspensions = 1, resumes = 1)
|
||||
|
||||
|
|
@ -359,7 +360,7 @@ abstract class ActorModelSpec extends AkkaSpec {
|
|||
}).withDispatcher(wavesSupervisorDispatcher(dispatcher)))
|
||||
boss ! "run"
|
||||
try {
|
||||
assertCountDown(cachedMessage.latch, Testing.testTime(20000), "Should process " + num + " countdowns")
|
||||
assertCountDown(cachedMessage.latch, (20 seconds).dilated.toMillis, "Should process " + num + " countdowns")
|
||||
} catch {
|
||||
case e ⇒
|
||||
System.err.println(this.getClass.getName + " error: " + e.getMessage + " missing count downs == " + cachedMessage.latch.getCount() + " out of " + num)
|
||||
|
|
@ -374,7 +375,7 @@ abstract class ActorModelSpec extends AkkaSpec {
|
|||
}
|
||||
|
||||
"continue to process messages when a thread gets interrupted" in {
|
||||
filterEvents(EventFilter[InterruptedException], EventFilter[akka.event.EventHandler.EventHandlerException]) {
|
||||
filterEvents(EventFilter[InterruptedException](), EventFilter[akka.event.Logging.EventHandlerException]()) {
|
||||
implicit val dispatcher = newInterceptedDispatcher
|
||||
implicit val timeout = Timeout(5 seconds)
|
||||
val a = newTestActor(dispatcher)
|
||||
|
|
@ -395,7 +396,7 @@ abstract class ActorModelSpec extends AkkaSpec {
|
|||
}
|
||||
|
||||
"continue to process messages when exception is thrown" in {
|
||||
filterEvents(EventFilter[IndexOutOfBoundsException], EventFilter[RemoteException]) {
|
||||
filterEvents(EventFilter[IndexOutOfBoundsException](), EventFilter[RemoteException]()) {
|
||||
implicit val dispatcher = newInterceptedDispatcher
|
||||
val a = newTestActor(dispatcher)
|
||||
val f1 = a ? Reply("foo")
|
||||
|
|
@ -435,10 +436,10 @@ class DispatcherModelSpec extends ActorModelSpec {
|
|||
val a, b = newTestActor(dispatcher)
|
||||
|
||||
a ! Meet(aStart, aStop)
|
||||
assertCountDown(aStart, Testing.testTime(3000), "Should process first message within 3 seconds")
|
||||
assertCountDown(aStart, 3.seconds.dilated.toMillis, "Should process first message within 3 seconds")
|
||||
|
||||
b ! CountDown(bParallel)
|
||||
assertCountDown(bParallel, Testing.testTime(3000), "Should process other actors in parallel")
|
||||
assertCountDown(bParallel, 3.seconds.dilated.toMillis, "Should process other actors in parallel")
|
||||
|
||||
aStop.countDown()
|
||||
|
||||
|
|
@ -474,10 +475,10 @@ class BalancingDispatcherModelSpec extends ActorModelSpec {
|
|||
val a, b = newTestActor(dispatcher)
|
||||
|
||||
a ! Meet(aStart, aStop)
|
||||
assertCountDown(aStart, Testing.testTime(3000), "Should process first message within 3 seconds")
|
||||
assertCountDown(aStart, 3.seconds.dilated.toMillis, "Should process first message within 3 seconds")
|
||||
|
||||
b ! CountDown(bParallel)
|
||||
assertCountDown(bParallel, Testing.testTime(3000), "Should process other actors in parallel")
|
||||
assertCountDown(bParallel, 3.seconds.dilated.toMillis, "Should process other actors in parallel")
|
||||
|
||||
aStop.countDown()
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ package akka.actor.dispatch
|
|||
|
||||
import java.util.concurrent.{ CountDownLatch, TimeUnit }
|
||||
|
||||
import akka.testkit.TestEvent._
|
||||
import akka.testkit.EventFilter
|
||||
import akka.testkit._
|
||||
import akka.dispatch.{ PinnedDispatcher, Dispatchers }
|
||||
import akka.actor.{ Props, Actor }
|
||||
import akka.testkit.AkkaSpec
|
||||
|
|
@ -24,14 +23,6 @@ class PinnedActorSpec extends AkkaSpec with BeforeAndAfterEach {
|
|||
|
||||
private val unit = TimeUnit.MILLISECONDS
|
||||
|
||||
override def beforeEach {
|
||||
app.eventHandler.notify(Mute(EventFilter[RuntimeException]("Failure")))
|
||||
}
|
||||
|
||||
override def afterEach {
|
||||
app.eventHandler.notify(UnMuteAll)
|
||||
}
|
||||
|
||||
"A PinnedActor" must {
|
||||
|
||||
"support tell" in {
|
||||
|
|
|
|||
|
|
@ -147,7 +147,6 @@ object ActorEventBusSpec {
|
|||
type Classifier = String
|
||||
|
||||
def classify(event: Event) = event.toString
|
||||
protected def compareSubscribers(a: Subscriber, b: Subscriber): Int = a compareTo b
|
||||
protected def mapSize = 32
|
||||
def publish(event: Event, subscriber: Subscriber) = subscriber ! event
|
||||
}
|
||||
|
|
|
|||
75
akka-actor-tests/src/test/scala/akka/event/MainBusSpec.scala
Normal file
75
akka-actor-tests/src/test/scala/akka/event/MainBusSpec.scala
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.event
|
||||
|
||||
import akka.testkit.AkkaSpec
|
||||
import akka.config.Configuration
|
||||
import akka.util.duration._
|
||||
import akka.actor.{ Actor, ActorRef }
|
||||
|
||||
object MainBusSpec {
|
||||
case class M(i: Int)
|
||||
|
||||
case class SetTarget(ref: ActorRef)
|
||||
|
||||
class MyLog extends Actor {
|
||||
var dst: ActorRef = app.deadLetters
|
||||
def receive = {
|
||||
case Logging.InitializeLogger(bus) ⇒ bus.subscribe(context.self, classOf[SetTarget])
|
||||
case SetTarget(ref) ⇒ dst = ref
|
||||
case e: Logging.LogEvent ⇒ dst ! e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MainBusSpec extends AkkaSpec(Configuration(
|
||||
"akka.stdout-loglevel" -> "WARNING",
|
||||
"akka.loglevel" -> "INFO",
|
||||
"akka.event-handlers" -> Seq("akka.event.MainBusSpec$MyLog", Logging.StandardOutLoggerName))) {
|
||||
|
||||
import MainBusSpec._
|
||||
|
||||
"A MainBus" must {
|
||||
|
||||
"manage subscriptions" in {
|
||||
val bus = new MainBus(true)
|
||||
bus.start(app)
|
||||
bus.subscribe(testActor, classOf[M])
|
||||
bus.publish(M(42))
|
||||
within(1 second) {
|
||||
expectMsg(M(42))
|
||||
bus.unsubscribe(testActor)
|
||||
bus.publish(M(13))
|
||||
expectNoMsg
|
||||
}
|
||||
}
|
||||
|
||||
"manage log levels" in {
|
||||
val bus = new MainBus(false)
|
||||
bus.start(app)
|
||||
bus.startDefaultLoggers(app, app.AkkaConfig)
|
||||
bus.publish(SetTarget(testActor))
|
||||
within(1 second) {
|
||||
import Logging._
|
||||
verifyLevel(bus, InfoLevel)
|
||||
bus.logLevel = WarningLevel
|
||||
verifyLevel(bus, WarningLevel)
|
||||
bus.logLevel = DebugLevel
|
||||
verifyLevel(bus, DebugLevel)
|
||||
bus.logLevel = ErrorLevel
|
||||
verifyLevel(bus, ErrorLevel)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private def verifyLevel(bus: LoggingBus, level: Logging.LogLevel) {
|
||||
import Logging._
|
||||
val allmsg = Seq(Debug(this, "debug"), Info(this, "info"), Warning(this, "warning"), Error(this, "error"))
|
||||
val msg = allmsg filter (_.level <= level)
|
||||
allmsg foreach bus.publish
|
||||
msg foreach (x ⇒ expectMsg(x))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ trait MatchingEngine {
|
|||
}
|
||||
|
||||
class AkkaMatchingEngine(val meId: String, val orderbooks: List[Orderbook])
|
||||
extends Actor with MatchingEngine {
|
||||
extends Actor with MatchingEngine with ActorLogging {
|
||||
|
||||
var standby: Option[ActorRef] = None
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ class AkkaMatchingEngine(val meId: String, val orderbooks: List[Orderbook])
|
|||
case standbyRef: ActorRef ⇒
|
||||
standby = Some(standbyRef)
|
||||
case unknown ⇒
|
||||
app.eventHandler.warning(this, "Received unknown message: " + unknown)
|
||||
log.warning("Received unknown message: " + unknown)
|
||||
}
|
||||
|
||||
def handleOrder(order: Order) {
|
||||
|
|
@ -40,7 +40,7 @@ class AkkaMatchingEngine(val meId: String, val orderbooks: List[Orderbook])
|
|||
done(true, order)
|
||||
|
||||
case None ⇒
|
||||
app.eventHandler.warning(this, "Orderbook not handled by this MatchingEngine: " + order.orderbookSymbol)
|
||||
log.warning("Orderbook not handled by this MatchingEngine: " + order.orderbookSymbol)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ trait OrderReceiver {
|
|||
|
||||
}
|
||||
|
||||
class AkkaOrderReceiver extends Actor with OrderReceiver {
|
||||
class AkkaOrderReceiver extends Actor with OrderReceiver with ActorLogging {
|
||||
type ME = ActorRef
|
||||
|
||||
def receive = {
|
||||
case order: Order ⇒ placeOrder(order)
|
||||
case routing @ MatchingEngineRouting(mapping) ⇒
|
||||
refreshMatchingEnginePartitions(routing.asInstanceOf[MatchingEngineRouting[ActorRef]])
|
||||
case unknown ⇒ app.eventHandler.warning(this, "Received unknown message: " + unknown)
|
||||
case unknown ⇒ log.warning("Received unknown message: " + unknown)
|
||||
}
|
||||
|
||||
def placeOrder(order: Order) = {
|
||||
|
|
@ -40,7 +40,7 @@ class AkkaOrderReceiver extends Actor with OrderReceiver {
|
|||
case Some(m) ⇒
|
||||
m forward order
|
||||
case None ⇒
|
||||
app.eventHandler.warning(this, "Unknown orderbook: " + order.orderbookSymbol)
|
||||
log.warning("Unknown orderbook: " + order.orderbookSymbol)
|
||||
sender ! Rsp(order, false)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ class TradingLatencyPerformanceSpec extends PerformanceSpec {
|
|||
def receive = {
|
||||
case Rsp(order, status) ⇒
|
||||
if (!status) {
|
||||
app.eventHandler.error(this, "Invalid rsp")
|
||||
log.error("Invalid rsp")
|
||||
}
|
||||
val duration = System.nanoTime - order.nanoTime
|
||||
stat.addValue(duration)
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ class TradingThroughputPerformanceSpec extends PerformanceSpec {
|
|||
def receive = {
|
||||
case Rsp(order, status) ⇒
|
||||
if (!status) {
|
||||
app.eventHandler.error(this, "Invalid rsp")
|
||||
log.error("Invalid rsp")
|
||||
}
|
||||
received += 1
|
||||
if (sent < repeat) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import java.io.PrintWriter
|
|||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import scala.collection.mutable.{ Map ⇒ MutableMap }
|
||||
import akka.AkkaApplication
|
||||
import akka.event.Logging
|
||||
|
||||
trait BenchResultRepository {
|
||||
def add(stats: Stats)
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ trait PerformanceSpec extends AkkaSpec with BeforeAndAfterEach {
|
|||
report.html(resultRepository.get(stats.name))
|
||||
} catch {
|
||||
// don't fail test due to problems saving bench report
|
||||
case e: Exception ⇒ app.eventHandler.error(e, this, e.getMessage)
|
||||
case e: Exception ⇒ log.error(e, e.getMessage)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import java.util.Date
|
|||
import scala.collection.JavaConversions.asScalaBuffer
|
||||
import scala.collection.JavaConversions.enumerationAsScalaIterator
|
||||
import akka.AkkaApplication
|
||||
import akka.event.Logging
|
||||
import scala.collection.immutable.TreeMap
|
||||
|
||||
class Report(
|
||||
|
|
@ -13,7 +14,8 @@ class Report(
|
|||
resultRepository: BenchResultRepository,
|
||||
compareResultWith: Option[String] = None) {
|
||||
|
||||
private def log = System.getProperty("benchmark.logResult", "true").toBoolean
|
||||
private def doLog = System.getProperty("benchmark.logResult", "true").toBoolean
|
||||
val log = Logging(app, this)
|
||||
|
||||
val dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
|
||||
val legendTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
|
||||
|
|
@ -58,8 +60,8 @@ class Report(
|
|||
val reportName = current.name + "--" + timestamp + ".html"
|
||||
resultRepository.saveHtmlReport(sb.toString, reportName)
|
||||
|
||||
if (log) {
|
||||
app.eventHandler.info(this, resultTable + "Charts in html report: " + resultRepository.htmlReportUrl(reportName))
|
||||
if (doLog) {
|
||||
log.info(resultTable + "Charts in html report: " + resultRepository.htmlReportUrl(reportName))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package akka.routing
|
|||
|
||||
import akka.dispatch.{ KeptPromise, Future }
|
||||
import akka.actor._
|
||||
import akka.testkit.Testing._
|
||||
import akka.testkit.{ TestLatch, filterEvents, EventFilter, filterException }
|
||||
import akka.util.duration._
|
||||
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger }
|
||||
|
|
@ -88,7 +87,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
def instance(p: Props) = actorOf(p.withCreator(new Actor {
|
||||
def receive = {
|
||||
case req: String ⇒ {
|
||||
sleepFor(10 millis)
|
||||
(10 millis).dilated.sleep
|
||||
sender.tell("Response")
|
||||
}
|
||||
}
|
||||
|
|
@ -116,7 +115,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
def instance(p: Props) = actorOf(p.withCreator(new Actor {
|
||||
def receive = {
|
||||
case n: Int ⇒
|
||||
sleepFor(n millis)
|
||||
(n millis).dilated.sleep
|
||||
count.incrementAndGet
|
||||
latch.countDown()
|
||||
}
|
||||
|
|
@ -142,7 +141,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
count.set(0)
|
||||
for (m ← 0 until loops) {
|
||||
pool ? t
|
||||
sleepFor(50 millis)
|
||||
(50 millis).dilated.sleep
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +179,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
def instance(p: Props) = actorOf(p.withCreator(new Actor {
|
||||
def receive = {
|
||||
case n: Int ⇒
|
||||
sleepFor(n millis)
|
||||
(n millis).dilated.sleep
|
||||
count.incrementAndGet
|
||||
latch.countDown()
|
||||
}
|
||||
|
|
@ -291,7 +290,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
def instance(p: Props) = actorOf(p.withCreator(new Actor {
|
||||
def receive = {
|
||||
case n: Int ⇒
|
||||
sleepFor(n millis)
|
||||
(n millis).dilated.sleep
|
||||
latch.countDown()
|
||||
}
|
||||
}))
|
||||
|
|
@ -311,7 +310,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
|
||||
for (m ← 0 to 10) pool ! 250
|
||||
|
||||
sleepFor(5 millis)
|
||||
(5 millis).dilated.sleep
|
||||
|
||||
val z = (pool ? ActorPool.Stat).as[ActorPool.Stats].get.size
|
||||
|
||||
|
|
@ -321,7 +320,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
|
||||
for (m ← 0 to 3) {
|
||||
pool ! 1
|
||||
sleepFor(500 millis)
|
||||
(500 millis).dilated.sleep
|
||||
}
|
||||
|
||||
(pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be <= (z)
|
||||
|
|
@ -416,7 +415,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
pool1 ! "ping"
|
||||
(pool1 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
pool1 ! akka.Die
|
||||
sleepFor(2 seconds)
|
||||
(2 seconds).dilated.sleep
|
||||
(pool1 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
pingCount.get must be(1)
|
||||
|
||||
|
|
@ -427,7 +426,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
pool1 ! "ping"
|
||||
(pool1 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
pool1 ! akka.Die
|
||||
sleepFor(2 seconds)
|
||||
(2 seconds).dilated.sleep
|
||||
(pool1 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(1)
|
||||
pool1 ! "ping"
|
||||
(pool1 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
|
|
@ -440,7 +439,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
pool2 ! "ping"
|
||||
(pool2 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
pool2 ! akka.Die
|
||||
sleepFor(2 seconds)
|
||||
(2 seconds).dilated.sleep
|
||||
(pool2 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
pingCount.get must be(1)
|
||||
|
||||
|
|
@ -451,7 +450,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
pool2 ! "ping"
|
||||
(pool2 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
pool2 ! akka.Die
|
||||
sleepFor(2 seconds)
|
||||
(2 seconds).dilated.sleep
|
||||
(pool2 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(1)
|
||||
pool2 ! "ping"
|
||||
(pool2 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
|
|
@ -463,7 +462,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
pool3 ! "ping"
|
||||
(pool3 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
pool3 ! akka.Die
|
||||
sleepFor(2 seconds)
|
||||
(2 seconds).dilated.sleep
|
||||
(pool3 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(1)
|
||||
pool3 ! "ping"
|
||||
pool3 ! "ping"
|
||||
|
|
@ -474,7 +473,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
}
|
||||
|
||||
"support customizable supervision config of pooled actors" in {
|
||||
filterEvents(EventFilter[IllegalStateException], EventFilter[RuntimeException]) {
|
||||
filterEvents(EventFilter[IllegalStateException](), EventFilter[RuntimeException]()) {
|
||||
val pingCount = new AtomicInteger(0)
|
||||
val deathCount = new AtomicInteger(0)
|
||||
var keepDying = new AtomicBoolean(false)
|
||||
|
|
@ -512,7 +511,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
pool1 ! "ping"
|
||||
(pool1 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
pool1 ! BadState
|
||||
sleepFor(2 seconds)
|
||||
(2 seconds).dilated.sleep
|
||||
(pool1 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
pingCount.get must be(1)
|
||||
|
||||
|
|
@ -522,7 +521,7 @@ class ActorPoolSpec extends AkkaSpec {
|
|||
pool1 ! "ping"
|
||||
(pool1 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
pool1 ! BadState
|
||||
sleepFor(2 seconds)
|
||||
(2 seconds).dilated.sleep
|
||||
(pool1 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(1)
|
||||
pool1 ! "ping"
|
||||
(pool1 ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import org.junit.{ After, Test }
|
|||
class CallingThreadDispatcherModelSpec extends ActorModelSpec {
|
||||
import ActorModelSpec._
|
||||
|
||||
def newInterceptedDispatcher = new CallingThreadDispatcher(app, "test", true) with MessageDispatcherInterceptor
|
||||
def newInterceptedDispatcher = new CallingThreadDispatcher(app, "test") with MessageDispatcherInterceptor
|
||||
def dispatcherType = "Calling Thread Dispatcher"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@ package akka
|
|||
|
||||
import akka.config._
|
||||
import akka.actor._
|
||||
import dispatch._
|
||||
import event._
|
||||
import akka.dispatch._
|
||||
import akka.event._
|
||||
import akka.util.duration._
|
||||
import java.net.InetAddress
|
||||
import com.eaio.uuid.UUID
|
||||
import akka.dispatch.{ Dispatchers, Future }
|
||||
import akka.util.Duration
|
||||
import akka.util.ReflectiveAccess
|
||||
import java.util.concurrent.TimeUnit
|
||||
import akka.routing.Routing
|
||||
import akka.remote.RemoteSupport
|
||||
import akka.serialization.Serialization
|
||||
|
|
@ -20,6 +20,8 @@ import java.net.InetSocketAddress
|
|||
|
||||
object AkkaApplication {
|
||||
|
||||
type AkkaConfig = a.AkkaConfig.type forSome { val a: AkkaApplication }
|
||||
|
||||
val Version = "2.0-SNAPSHOT"
|
||||
|
||||
val envHome = System.getenv("AKKA_HOME") match {
|
||||
|
|
@ -87,21 +89,26 @@ class AkkaApplication(val name: String, val config: Configuration) extends Actor
|
|||
|
||||
val ProviderClass = getString("akka.actor.provider", "akka.actor.LocalActorRefProvider")
|
||||
|
||||
val DefaultTimeUnit = getString("akka.time-unit", "seconds")
|
||||
val DefaultTimeUnit = Duration.timeUnit(getString("akka.time-unit", "seconds"))
|
||||
val ActorTimeout = Timeout(Duration(getInt("akka.actor.timeout", 5), DefaultTimeUnit))
|
||||
val ActorTimeoutMillis = ActorTimeout.duration.toMillis
|
||||
val SerializeAllMessages = getBool("akka.actor.serialize-messages", false)
|
||||
|
||||
val LogLevel = getString("akka.event-handler-level", "INFO")
|
||||
val TestTimeFactor = getDouble("akka.test.timefactor", 1.0)
|
||||
val TestEventFilterLeeway = Duration(getDouble("akka.test.filter-leeway", 0.5), DefaultTimeUnit)
|
||||
|
||||
val LogLevel = getString("akka.loglevel", "INFO")
|
||||
val StdoutLogLevel = getString("akka.stdout-loglevel", LogLevel)
|
||||
val EventHandlers = getList("akka.event-handlers")
|
||||
val AddLoggingReceive = getBool("akka.actor.debug.receive", false)
|
||||
val DebugAutoReceive = getBool("akka.actor.debug.autoreceive", false)
|
||||
val DebugLifecycle = getBool("akka.actor.debug.lifecycle", false)
|
||||
val FsmDebugEvent = getBool("akka.actor.debug.fsm", false)
|
||||
val DebugMainBus = getBool("akka.actor.debug.mainbus", false)
|
||||
|
||||
val DispatcherThroughput = getInt("akka.actor.throughput", 5)
|
||||
val DispatcherDefaultShutdown = getLong("akka.actor.dispatcher-shutdown-timeout").
|
||||
map(time ⇒ Duration(time, DefaultTimeUnit)).getOrElse(Duration(1000, TimeUnit.MILLISECONDS))
|
||||
map(time ⇒ Duration(time, DefaultTimeUnit)).getOrElse(1 second)
|
||||
val MailboxCapacity = getInt("akka.actor.default-dispatcher.mailbox-capacity", -1)
|
||||
val MailboxPushTimeout = Duration(getInt("akka.actor.default-dispatcher.mailbox-push-timeout-time", 10), DefaultTimeUnit)
|
||||
val DispatcherThroughputDeadlineTime = Duration(getInt("akka.actor.throughput-deadline-time", -1), DefaultTimeUnit)
|
||||
|
|
@ -132,6 +139,8 @@ class AkkaApplication(val name: String, val config: Configuration) extends Actor
|
|||
val ExpiredHeaderValue = config.getString("akka.http.expired-header-value", "expired")
|
||||
}
|
||||
|
||||
private[akka] def systemActorOf(props: Props, address: String): ActorRef = provider.actorOf(props, systemGuardian, address, true)
|
||||
|
||||
import AkkaConfig._
|
||||
|
||||
if (ConfigVersion != Version)
|
||||
|
|
@ -158,11 +167,14 @@ class AkkaApplication(val name: String, val config: Configuration) extends Actor
|
|||
|
||||
def port: Int = defaultAddress.getPort
|
||||
|
||||
// this provides basic logging (to stdout) until .start() is called below
|
||||
val mainbus = new MainBus(DebugMainBus)
|
||||
mainbus.startStdoutLogger(AkkaConfig)
|
||||
val log = new BusLogging(mainbus, this)
|
||||
|
||||
// TODO correctly pull its config from the config
|
||||
val dispatcherFactory = new Dispatchers(this)
|
||||
|
||||
implicit val dispatcher = dispatcherFactory.defaultGlobalDispatcher
|
||||
|
||||
def terminationFuture: Future[ExitStatus] = provider.terminationFuture
|
||||
|
||||
// TODO think about memory consistency effects when doing funky stuff inside constructor
|
||||
|
|
@ -171,31 +183,53 @@ class AkkaApplication(val name: String, val config: Configuration) extends Actor
|
|||
// TODO think about memory consistency effects when doing funky stuff inside constructor
|
||||
val provider: ActorRefProvider = reflective.createProvider
|
||||
|
||||
// TODO make this configurable
|
||||
protected[akka] val guardian: ActorRef = {
|
||||
import akka.actor.FaultHandlingStrategy._
|
||||
new LocalActorRef(this,
|
||||
Props(context ⇒ { case _ ⇒ }).withFaultHandler(OneForOneStrategy {
|
||||
case _: ActorKilledException ⇒ Stop
|
||||
case _: ActorInitializationException ⇒ Stop
|
||||
case _: Exception ⇒ Restart
|
||||
}).withDispatcher(dispatcher),
|
||||
provider.theOneWhoWalksTheBubblesOfSpaceTime,
|
||||
"ApplicationSupervisor",
|
||||
true)
|
||||
private class Guardian extends Actor {
|
||||
def receive = {
|
||||
case Terminated(_) ⇒ context.self.stop()
|
||||
}
|
||||
}
|
||||
private class SystemGuardian extends Actor {
|
||||
def receive = {
|
||||
case Terminated(_) ⇒
|
||||
mainbus.stopDefaultLoggers()
|
||||
context.self.stop()
|
||||
}
|
||||
}
|
||||
private val guardianFaultHandlingStrategy = {
|
||||
import akka.actor.FaultHandlingStrategy._
|
||||
OneForOneStrategy {
|
||||
case _: ActorKilledException ⇒ Stop
|
||||
case _: ActorInitializationException ⇒ Stop
|
||||
case _: Exception ⇒ Restart
|
||||
}
|
||||
}
|
||||
private val guardianProps = Props(new Guardian).withFaultHandler(guardianFaultHandlingStrategy)
|
||||
|
||||
// TODO think about memory consistency effects when doing funky stuff inside constructor
|
||||
val eventHandler = new EventHandler(this)
|
||||
private val guardianInChief: ActorRef =
|
||||
provider.actorOf(guardianProps, provider.theOneWhoWalksTheBubblesOfSpaceTime, "GuardianInChief", true)
|
||||
|
||||
// TODO think about memory consistency effects when doing funky stuff inside constructor
|
||||
val log: Logging = new EventHandlerLogging(eventHandler, this)
|
||||
protected[akka] val guardian: ActorRef =
|
||||
provider.actorOf(guardianProps, guardianInChief, "ApplicationSupervisor", true)
|
||||
|
||||
protected[akka] val systemGuardian: ActorRef =
|
||||
provider.actorOf(guardianProps.withCreator(new SystemGuardian), guardianInChief, "SystemSupervisor", true)
|
||||
|
||||
// TODO think about memory consistency effects when doing funky stuff inside constructor
|
||||
val deadLetters = new DeadLetterActorRef(this)
|
||||
|
||||
val deathWatch = provider.createDeathWatch()
|
||||
|
||||
// chain death watchers so that killing guardian stops the application
|
||||
deathWatch.subscribe(systemGuardian, guardian)
|
||||
deathWatch.subscribe(guardianInChief, systemGuardian)
|
||||
|
||||
// this starts the reaper actor and the user-configured logging subscribers, which are also actors
|
||||
mainbus.start(this)
|
||||
mainbus.startDefaultLoggers(this, AkkaConfig)
|
||||
|
||||
// TODO think about memory consistency effects when doing funky stuff inside an ActorRefProvider's constructor
|
||||
val deployer = new Deployer(this)
|
||||
|
||||
// TODO think about memory consistency effects when doing funky stuff inside constructor
|
||||
val typedActor = new TypedActor(this)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import akka.remote.RemoteSupport
|
|||
import akka.cluster.ClusterNode
|
||||
import akka.japi.{ Creator, Procedure }
|
||||
import akka.serialization.{ Serializer, Serialization }
|
||||
import akka.event.EventHandler
|
||||
import akka.event.Logging.Debug
|
||||
import akka.experimental
|
||||
import akka.{ AkkaApplication, AkkaException }
|
||||
|
||||
|
|
@ -154,6 +154,10 @@ object Timeout {
|
|||
implicit def defaultTimeout(implicit app: AkkaApplication) = app.AkkaConfig.ActorTimeout
|
||||
}
|
||||
|
||||
trait ActorLogging { this: Actor ⇒
|
||||
val log = akka.event.Logging(app.mainbus, context.self)
|
||||
}
|
||||
|
||||
object Actor {
|
||||
|
||||
type Receive = PartialFunction[Any, Unit]
|
||||
|
|
@ -164,7 +168,7 @@ object Actor {
|
|||
class LoggingReceive(source: AnyRef, r: Receive)(implicit app: AkkaApplication) extends Receive {
|
||||
def isDefinedAt(o: Any) = {
|
||||
val handled = r.isDefinedAt(o)
|
||||
app.eventHandler.debug(source, "received " + (if (handled) "handled" else "unhandled") + " message " + o)
|
||||
app.mainbus.publish(Debug(source, "received " + (if (handled) "handled" else "unhandled") + " message " + o))
|
||||
handled
|
||||
}
|
||||
def apply(o: Any): Unit = r(o)
|
||||
|
|
@ -410,7 +414,7 @@ trait Actor {
|
|||
private[akka] final def apply(msg: Any) = {
|
||||
|
||||
def autoReceiveMessage(msg: AutoReceivedMessage) {
|
||||
if (app.AkkaConfig.DebugAutoReceive) app.eventHandler.debug(this, "received AutoReceiveMessage " + msg)
|
||||
if (app.AkkaConfig.DebugAutoReceive) app.mainbus.publish(Debug(this, "received AutoReceiveMessage " + msg))
|
||||
|
||||
msg match {
|
||||
case HotSwap(code, discardOld) ⇒ become(code(self), discardOld)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import scala.collection.immutable.{ Stack, TreeMap }
|
|||
import scala.collection.JavaConverters
|
||||
import java.util.concurrent.{ ScheduledFuture, TimeUnit }
|
||||
import akka.AkkaApplication
|
||||
import akka.event.Logging.{ Debug, Warning, Error }
|
||||
|
||||
/**
|
||||
* The actor context - the view of the actor cell from the actor.
|
||||
|
|
@ -21,6 +22,8 @@ private[akka] trait ActorContext extends ActorRefFactory with TypedActorFactory
|
|||
|
||||
def self: ActorRef with ScalaActorRef
|
||||
|
||||
def hasMessages: Boolean
|
||||
|
||||
def receiveTimeout: Option[Long]
|
||||
|
||||
def receiveTimeout_=(timeout: Option[Long]): Unit
|
||||
|
|
@ -90,6 +93,8 @@ private[akka] class ActorCell(
|
|||
@volatile //This must be volatile since it isn't protected by the mailbox status
|
||||
var mailbox: Mailbox = _
|
||||
|
||||
def hasMessages: Boolean = mailbox.hasMessages
|
||||
|
||||
final def start(): Unit = {
|
||||
mailbox = dispatcher.createMailbox(this)
|
||||
|
||||
|
|
@ -155,11 +160,11 @@ private[akka] class ActorCell(
|
|||
actor = created
|
||||
created.preStart()
|
||||
checkReceiveTimeout
|
||||
if (app.AkkaConfig.DebugLifecycle) app.eventHandler.debug(self, "started")
|
||||
if (app.AkkaConfig.DebugLifecycle) app.mainbus.publish(Debug(self, "started (" + actor + ")"))
|
||||
} catch {
|
||||
case e ⇒
|
||||
try {
|
||||
app.eventHandler.error(e, self, "error while creating actor")
|
||||
app.mainbus.publish(Error(e, self, "error while creating actor"))
|
||||
// prevent any further messages to be processed until the actor has been restarted
|
||||
dispatcher.suspend(this)
|
||||
} finally {
|
||||
|
|
@ -169,7 +174,7 @@ private[akka] class ActorCell(
|
|||
|
||||
def recreate(cause: Throwable): Unit = try {
|
||||
val failedActor = actor
|
||||
if (app.AkkaConfig.DebugLifecycle) app.eventHandler.debug(self, "restarting")
|
||||
if (app.AkkaConfig.DebugLifecycle) app.mainbus.publish(Debug(self, "restarting"))
|
||||
val freshActor = newActor()
|
||||
if (failedActor ne null) {
|
||||
val c = currentMessage //One read only plz
|
||||
|
|
@ -183,14 +188,14 @@ private[akka] class ActorCell(
|
|||
}
|
||||
actor = freshActor // assign it here so if preStart fails, we can null out the sef-refs next call
|
||||
freshActor.postRestart(cause)
|
||||
if (app.AkkaConfig.DebugLifecycle) app.eventHandler.debug(self, "restarted")
|
||||
if (app.AkkaConfig.DebugLifecycle) app.mainbus.publish(Debug(self, "restarted"))
|
||||
|
||||
dispatcher.resume(this) //FIXME should this be moved down?
|
||||
|
||||
props.faultHandler.handleSupervisorRestarted(cause, self, children)
|
||||
} catch {
|
||||
case e ⇒ try {
|
||||
app.eventHandler.error(e, self, "error while creating actor")
|
||||
app.mainbus.publish(Error(e, self, "error while creating actor"))
|
||||
// prevent any further messages to be processed until the actor has been restarted
|
||||
dispatcher.suspend(this)
|
||||
} finally {
|
||||
|
|
@ -211,7 +216,7 @@ private[akka] class ActorCell(
|
|||
try {
|
||||
try {
|
||||
val a = actor
|
||||
if (app.AkkaConfig.DebugLifecycle) app.eventHandler.debug(self, "stopping")
|
||||
if (app.AkkaConfig.DebugLifecycle) app.mainbus.publish(Debug(self, "stopping"))
|
||||
if (a ne null) a.postStop()
|
||||
} finally {
|
||||
//Stop supervised actors
|
||||
|
|
@ -236,8 +241,8 @@ private[akka] class ActorCell(
|
|||
val links = _children
|
||||
if (!links.contains(child)) {
|
||||
_children = _children.updated(child, ChildRestartStats())
|
||||
if (app.AkkaConfig.DebugLifecycle) app.eventHandler.debug(self, "now supervising " + child)
|
||||
} else app.eventHandler.warning(self, "Already supervising " + child)
|
||||
if (app.AkkaConfig.DebugLifecycle) app.mainbus.publish(Debug(self, "now supervising " + child))
|
||||
} else app.mainbus.publish(Warning(self, "Already supervising " + child))
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -248,10 +253,10 @@ private[akka] class ActorCell(
|
|||
case Recreate(cause) ⇒ recreate(cause)
|
||||
case Link(subject) ⇒
|
||||
app.deathWatch.subscribe(self, subject)
|
||||
if (app.AkkaConfig.DebugLifecycle) app.eventHandler.debug(self, "now monitoring " + subject)
|
||||
if (app.AkkaConfig.DebugLifecycle) app.mainbus.publish(Debug(self, "now monitoring " + subject))
|
||||
case Unlink(subject) ⇒
|
||||
app.deathWatch.unsubscribe(self, subject)
|
||||
if (app.AkkaConfig.DebugLifecycle) app.eventHandler.debug(self, "stopped monitoring " + subject)
|
||||
if (app.AkkaConfig.DebugLifecycle) app.mainbus.publish(Debug(self, "stopped monitoring " + subject))
|
||||
case Suspend() ⇒ suspend()
|
||||
case Resume() ⇒ resume()
|
||||
case Terminate() ⇒ terminate()
|
||||
|
|
@ -260,7 +265,7 @@ private[akka] class ActorCell(
|
|||
}
|
||||
} catch {
|
||||
case e ⇒ //Should we really catch everything here?
|
||||
app.eventHandler.error(e, self, "error while processing " + message)
|
||||
app.mainbus.publish(Error(e, self, "error while processing " + message))
|
||||
//TODO FIXME How should problems here be handled?
|
||||
throw e
|
||||
}
|
||||
|
|
@ -279,7 +284,7 @@ private[akka] class ActorCell(
|
|||
currentMessage = null // reset current message after successful invocation
|
||||
} catch {
|
||||
case e ⇒
|
||||
app.eventHandler.error(e, self, e.getMessage)
|
||||
app.mainbus.publish(Error(e, self, e.getMessage))
|
||||
|
||||
// prevent any further messages to be processed until the actor has been restarted
|
||||
dispatcher.suspend(this)
|
||||
|
|
@ -299,7 +304,7 @@ private[akka] class ActorCell(
|
|||
}
|
||||
} catch {
|
||||
case e ⇒
|
||||
app.eventHandler.error(e, self, e.getMessage)
|
||||
app.mainbus.publish(Error(e, self, e.getMessage))
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
|
@ -308,7 +313,7 @@ private[akka] class ActorCell(
|
|||
|
||||
final def handleFailure(fail: Failed): Unit = _children.get(fail.actor) match {
|
||||
case Some(stats) ⇒ if (!props.faultHandler.handleFailure(fail, stats, _children)) throw fail.cause
|
||||
case None ⇒ app.eventHandler.warning(self, "dropping " + fail + " from unknown child")
|
||||
case None ⇒ app.mainbus.publish(Warning(self, "dropping " + fail + " from unknown child"))
|
||||
}
|
||||
|
||||
final def handleChildTerminated(child: ActorRef): Unit = {
|
||||
|
|
|
|||
|
|
@ -347,6 +347,9 @@ trait MinimalActorRef extends ActorRef with ScalaActorRef {
|
|||
protected[akka] def sendSystemMessage(message: SystemMessage) {}
|
||||
|
||||
protected[akka] def postMessageToMailbox(msg: Any, sender: ActorRef) {}
|
||||
|
||||
def ?(message: Any)(implicit timeout: Timeout): Future[Any] =
|
||||
throw new UnsupportedOperationException("Not supported for %s".format(getClass.getName))
|
||||
}
|
||||
|
||||
case class DeadLetter(message: Any, sender: ActorRef)
|
||||
|
|
@ -367,10 +370,10 @@ class DeadLetterActorRef(val app: AkkaApplication) extends MinimalActorRef {
|
|||
override def isShutdown(): Boolean = true
|
||||
|
||||
protected[akka] override def postMessageToMailbox(message: Any, sender: ActorRef): Unit =
|
||||
app.eventHandler.notify(DeadLetter(message, sender))
|
||||
app.mainbus.publish(DeadLetter(message, sender))
|
||||
|
||||
def ?(message: Any)(implicit timeout: Timeout): Future[Any] = {
|
||||
app.eventHandler.notify(DeadLetter(message, this))
|
||||
override def ?(message: Any)(implicit timeout: Timeout): Future[Any] = {
|
||||
app.mainbus.publish(DeadLetter(message, this))
|
||||
brokenPromise
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import akka.AkkaApplication
|
|||
import java.util.concurrent.ConcurrentHashMap
|
||||
import com.eaio.uuid.UUID
|
||||
import akka.AkkaException
|
||||
import akka.event.{ ActorClassification, DeathWatch, EventHandler }
|
||||
import akka.event.{ ActorClassification, DeathWatch, Logging }
|
||||
import akka.dispatch._
|
||||
|
||||
/**
|
||||
|
|
@ -100,6 +100,7 @@ class LocalActorRefProvider(val app: AkkaApplication) extends ActorRefProvider {
|
|||
private[akka] val deployer: Deployer = new Deployer(app)
|
||||
|
||||
val terminationFuture = new DefaultPromise[AkkaApplication.ExitStatus](Timeout.never)(app.dispatcher)
|
||||
val log = Logging(app.mainbus, this)
|
||||
|
||||
/**
|
||||
* Top-level anchor for the supervision hierarchy of this actor system. Will
|
||||
|
|
@ -121,14 +122,14 @@ class LocalActorRefProvider(val app: AkkaApplication) extends ActorRefProvider {
|
|||
msg match {
|
||||
case Failed(child, ex) ⇒ child.stop()
|
||||
case ChildTerminated(child) ⇒ terminationFuture.completeWithResult(AkkaApplication.Stopped)
|
||||
case _ ⇒ app.eventHandler.error(this, this + " received unexpected message " + msg)
|
||||
case _ ⇒ log.error(this + " received unexpected message " + msg)
|
||||
}
|
||||
}
|
||||
|
||||
protected[akka] override def sendSystemMessage(message: SystemMessage) {
|
||||
message match {
|
||||
case Supervise(child) ⇒ // TODO register child in some map to keep track of it and enable shutdown after all dead
|
||||
case _ ⇒ app.eventHandler.error(this, this + " received unexpected system message " + message)
|
||||
case _ ⇒ log.error(this + " received unexpected system message " + message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import collection.immutable.Seq
|
|||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
import akka.event.EventHandler
|
||||
import akka.event.Logging
|
||||
import akka.actor.DeploymentConfig._
|
||||
import akka.{ AkkaException, AkkaApplication }
|
||||
import akka.config.{ Configuration, ConfigurationException }
|
||||
|
|
@ -35,6 +35,7 @@ trait ActorDeployer {
|
|||
class Deployer(val app: AkkaApplication) extends ActorDeployer {
|
||||
|
||||
val deploymentConfig = new DeploymentConfig(app)
|
||||
val log = Logging(app.mainbus, this)
|
||||
|
||||
val instance: ActorDeployer = {
|
||||
val deployer = new LocalDeployer()
|
||||
|
|
@ -307,13 +308,13 @@ class Deployer(val app: AkkaApplication) extends ActorDeployer {
|
|||
|
||||
private[akka] def throwDeploymentBoundException(deployment: Deploy): Nothing = {
|
||||
val e = new DeploymentAlreadyBoundException("Address [" + deployment.address + "] already bound to [" + deployment + "]")
|
||||
app.eventHandler.error(e, this, e.getMessage)
|
||||
log.error(e, e.getMessage)
|
||||
throw e
|
||||
}
|
||||
|
||||
private[akka] def thrownNoDeploymentBoundException(address: String): Nothing = {
|
||||
val e = new NoDeploymentBoundException("Address [" + address + "] is not bound to a deployment")
|
||||
app.eventHandler.error(e, this, e.getMessage)
|
||||
log.error(e, e.getMessage)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
package akka.actor
|
||||
|
||||
import akka.util._
|
||||
import akka.event.EventHandler
|
||||
import akka.event.Logging
|
||||
|
||||
import scala.collection.mutable
|
||||
import java.util.concurrent.ScheduledFuture
|
||||
|
|
@ -190,6 +190,8 @@ trait FSM[S, D] extends ListenerManagement {
|
|||
type Timeout = Option[Duration]
|
||||
type TransitionHandler = PartialFunction[(S, S), Unit]
|
||||
|
||||
val log = Logging(app.mainbus, context.self)
|
||||
|
||||
/**
|
||||
* ****************************************
|
||||
* DSL
|
||||
|
|
@ -421,7 +423,7 @@ trait FSM[S, D] extends ListenerManagement {
|
|||
*/
|
||||
private val handleEventDefault: StateFunction = {
|
||||
case Event(value, stateData) ⇒
|
||||
app.eventHandler.warning(context.self, "unhandled event " + value + " in state " + stateName)
|
||||
log.warning("unhandled event " + value + " in state " + stateName)
|
||||
stay
|
||||
}
|
||||
private var handleEvent: StateFunction = handleEventDefault
|
||||
|
|
@ -534,8 +536,8 @@ trait FSM[S, D] extends ListenerManagement {
|
|||
if (!currentState.stopReason.isDefined) {
|
||||
val reason = nextState.stopReason.get
|
||||
reason match {
|
||||
case Failure(ex: Throwable) ⇒ app.eventHandler.error(ex, context.self, "terminating due to Failure")
|
||||
case Failure(msg) ⇒ app.eventHandler.error(context.self, msg)
|
||||
case Failure(ex: Throwable) ⇒ log.error(ex, "terminating due to Failure")
|
||||
case Failure(msg: AnyRef) ⇒ log.error(msg.toString)
|
||||
case _ ⇒
|
||||
}
|
||||
val stopEvent = StopEvent(reason, currentState.stateName, currentState.stateData)
|
||||
|
|
@ -584,13 +586,13 @@ trait LoggingFSM[S, D] extends FSM[S, D] { this: Actor ⇒
|
|||
|
||||
protected[akka] abstract override def setTimer(name: String, msg: Any, timeout: Duration, repeat: Boolean): State = {
|
||||
if (debugEvent)
|
||||
app.eventHandler.debug(context.self, "setting " + (if (repeat) "repeating " else "") + "timer '" + name + "'/" + timeout + ": " + msg)
|
||||
log.debug("setting " + (if (repeat) "repeating " else "") + "timer '" + name + "'/" + timeout + ": " + msg)
|
||||
super.setTimer(name, msg, timeout, repeat)
|
||||
}
|
||||
|
||||
protected[akka] abstract override def cancelTimer(name: String) = {
|
||||
if (debugEvent)
|
||||
app.eventHandler.debug(context.self, "canceling timer '" + name + "'")
|
||||
log.debug("canceling timer '" + name + "'")
|
||||
super.cancelTimer(name)
|
||||
}
|
||||
|
||||
|
|
@ -602,7 +604,7 @@ trait LoggingFSM[S, D] extends FSM[S, D] { this: Actor ⇒
|
|||
case a: ActorRef ⇒ a.toString
|
||||
case _ ⇒ "unknown"
|
||||
}
|
||||
app.eventHandler.debug(context.self, "processing " + event + " from " + srcstr)
|
||||
log.debug("processing " + event + " from " + srcstr)
|
||||
}
|
||||
|
||||
if (logDepth > 0) {
|
||||
|
|
@ -616,7 +618,7 @@ trait LoggingFSM[S, D] extends FSM[S, D] { this: Actor ⇒
|
|||
val newState = stateName
|
||||
|
||||
if (debugEvent && oldState != newState)
|
||||
app.eventHandler.debug(context.self, "transition " + oldState + " -> " + newState)
|
||||
log.debug("transition " + oldState + " -> " + newState)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ package akka.actor
|
|||
|
||||
import akka.util.ByteString
|
||||
import akka.dispatch.Envelope
|
||||
import akka.event.EventHandler
|
||||
import java.net.InetSocketAddress
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ class DefaultScheduler extends Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
private[akka] def shutdown() { service.shutdown() }
|
||||
private[akka] def shutdown() { service.shutdownNow() }
|
||||
}
|
||||
|
||||
private object SchedulerThreadFactory extends ThreadFactory {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ package akka.dispatch
|
|||
|
||||
import java.util.concurrent._
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import akka.event.EventHandler
|
||||
import akka.event.Logging.Error
|
||||
import akka.config.Configuration
|
||||
import akka.util.{ Duration, Switch, ReentrantGuard }
|
||||
import java.util.concurrent.ThreadPoolExecutor.{ AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy, DiscardPolicy }
|
||||
|
|
@ -66,7 +66,7 @@ final case class TaskInvocation(app: AkkaApplication, function: () ⇒ Unit, cle
|
|||
try {
|
||||
function()
|
||||
} catch {
|
||||
case e ⇒ app.eventHandler.error(e, this, e.getMessage)
|
||||
case e ⇒ app.mainbus.publish(Error(e, this, e.getMessage))
|
||||
} finally {
|
||||
cleanup()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,18 @@ class BalancingDispatcher(
|
|||
_timeoutMs: Long)
|
||||
extends Dispatcher(_app, _name, throughput, throughputDeadlineTime, mailboxType, config, _timeoutMs) {
|
||||
|
||||
private val buddies = new ConcurrentSkipListSet[ActorCell](new Comparator[ActorCell] { def compare(a: ActorCell, b: ActorCell) = System.identityHashCode(a) - System.identityHashCode(b) }) //new ConcurrentLinkedQueue[ActorCell]()
|
||||
private val buddies = new ConcurrentSkipListSet[ActorCell](
|
||||
new Comparator[ActorCell] {
|
||||
def compare(a: ActorCell, b: ActorCell): Int = {
|
||||
/*
|
||||
* make sure that there is no overflow or underflow in comparisons, so
|
||||
* that the ordering is actually consistent and you cannot have a
|
||||
* sequence which cyclically is monotone without end.
|
||||
*/
|
||||
val diff = ((System.identityHashCode(a) & 0xffffffffL) - (System.identityHashCode(b) & 0xffffffffL))
|
||||
if (diff > 0) 1 else if (diff < 0) -1 else 0
|
||||
}
|
||||
})
|
||||
|
||||
protected val messageQueue: MessageQueue = mailboxType match {
|
||||
case u: UnboundedMailbox ⇒ new QueueBasedMessageQueue with UnboundedMessageQueueSemantics {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
package akka.dispatch
|
||||
|
||||
import akka.event.EventHandler
|
||||
import akka.event.Logging.Warning
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import java.util.concurrent.{ TimeUnit, ExecutorService, RejectedExecutionException, ConcurrentLinkedQueue }
|
||||
import akka.actor.{ ActorCell, ActorKilledException }
|
||||
|
|
@ -93,7 +93,7 @@ class Dispatcher(
|
|||
executorService.get() execute invocation
|
||||
} catch {
|
||||
case e: RejectedExecutionException ⇒
|
||||
app.eventHandler.warning(this, e.toString)
|
||||
app.mainbus.publish(Warning(this, e.toString))
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
|
@ -105,7 +105,7 @@ class Dispatcher(
|
|||
protected[akka] def shutdown {
|
||||
val old = executorService.getAndSet(new LazyExecutorServiceWrapper(executorServiceFactory.createExecutorService))
|
||||
if (old ne null)
|
||||
old.shutdown()
|
||||
old.shutdownNow()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -120,7 +120,7 @@ class Dispatcher(
|
|||
} catch {
|
||||
case e: RejectedExecutionException ⇒
|
||||
try {
|
||||
app.eventHandler.warning(this, e.toString)
|
||||
app.mainbus.publish(Warning(this, e.toString))
|
||||
} finally {
|
||||
mbox.setAsIdle()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
package akka.dispatch
|
||||
|
||||
import akka.AkkaException
|
||||
import akka.event.EventHandler
|
||||
import akka.actor.{ Timeout }
|
||||
import akka.event.Logging.Error
|
||||
import akka.actor.Timeout
|
||||
import scala.Option
|
||||
import akka.japi.{ Procedure, Function ⇒ JFunc, Option ⇒ JOption }
|
||||
|
||||
|
|
@ -262,7 +262,7 @@ object Future {
|
|||
result completeWithResult currentValue
|
||||
} catch {
|
||||
case e: Exception ⇒
|
||||
dispatcher.app.eventHandler.error(e, this, e.getMessage)
|
||||
dispatcher.app.mainbus.publish(Error(e, this, e.getMessage))
|
||||
result completeWithException e
|
||||
} finally {
|
||||
results.clear
|
||||
|
|
@ -631,7 +631,7 @@ sealed trait Future[+T] extends japi.Future[T] {
|
|||
Right(f(res))
|
||||
} catch {
|
||||
case e: Exception ⇒
|
||||
dispatcher.app.eventHandler.error(e, this, e.getMessage)
|
||||
dispatcher.app.mainbus.publish(Error(e, this, e.getMessage))
|
||||
Left(e)
|
||||
})
|
||||
}
|
||||
|
|
@ -683,7 +683,7 @@ sealed trait Future[+T] extends japi.Future[T] {
|
|||
future.completeWith(f(r))
|
||||
} catch {
|
||||
case e: Exception ⇒
|
||||
dispatcher.app.eventHandler.error(e, this, e.getMessage)
|
||||
dispatcher.app.mainbus.publish(Error(e, this, e.getMessage))
|
||||
future complete Left(e)
|
||||
}
|
||||
}
|
||||
|
|
@ -716,7 +716,7 @@ sealed trait Future[+T] extends japi.Future[T] {
|
|||
if (p(res)) r else Left(new MatchError(res))
|
||||
} catch {
|
||||
case e: Exception ⇒
|
||||
dispatcher.app.eventHandler.error(e, this, e.getMessage)
|
||||
dispatcher.app.mainbus.publish(Error(e, this, e.getMessage))
|
||||
Left(e)
|
||||
})
|
||||
}
|
||||
|
|
@ -811,7 +811,7 @@ trait Promise[T] extends Future[T] {
|
|||
fr completeWith cont(f)
|
||||
} catch {
|
||||
case e: Exception ⇒
|
||||
dispatcher.app.eventHandler.error(e, this, e.getMessage)
|
||||
dispatcher.app.mainbus.publish(Error(e, this, e.getMessage))
|
||||
fr completeWithException e
|
||||
}
|
||||
}
|
||||
|
|
@ -825,7 +825,7 @@ trait Promise[T] extends Future[T] {
|
|||
fr completeWith cont(f)
|
||||
} catch {
|
||||
case e: Exception ⇒
|
||||
dispatcher.app.eventHandler.error(e, this, e.getMessage)
|
||||
dispatcher.app.mainbus.publish(Error(e, this, e.getMessage))
|
||||
fr completeWithException e
|
||||
}
|
||||
}
|
||||
|
|
@ -1017,7 +1017,7 @@ class DefaultPromise[T](val timeout: Timeout)(implicit val dispatcher: MessageDi
|
|||
} else this
|
||||
|
||||
private def notifyCompleted(func: Future[T] ⇒ Unit) {
|
||||
try { func(this) } catch { case e ⇒ dispatcher.app.eventHandler.error(e, this, "Future onComplete-callback raised an exception") } //TODO catch, everything? Really?
|
||||
try { func(this) } catch { case e ⇒ dispatcher.app.mainbus.publish(Error(e, this, "Future onComplete-callback raised an exception")) } //TODO catch, everything? Really?
|
||||
}
|
||||
|
||||
@inline
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.dispatch
|
||||
|
||||
import akka.AkkaException
|
||||
|
|
@ -12,6 +11,7 @@ import akka.actor.{ ActorContext, ActorCell }
|
|||
import java.util.concurrent._
|
||||
import atomic.{ AtomicInteger, AtomicReferenceFieldUpdater }
|
||||
import annotation.tailrec
|
||||
import akka.event.Logging.Error
|
||||
|
||||
class MessageQueueAppendFailedException(message: String, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
|
||||
|
|
@ -205,7 +205,7 @@ abstract class Mailbox(val actor: ActorCell) extends AbstractMailbox with Messag
|
|||
}
|
||||
} catch {
|
||||
case e ⇒
|
||||
actor.app.eventHandler.error(e, this, "exception during processing system messages, dropping " + SystemMessage.size(nextMessage) + " messages!")
|
||||
actor.app.mainbus.publish(Error(e, this, "exception during processing system messages, dropping " + SystemMessage.size(nextMessage) + " messages!"))
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import java.util.concurrent._
|
|||
import atomic.{ AtomicLong, AtomicInteger }
|
||||
import ThreadPoolExecutor.CallerRunsPolicy
|
||||
import akka.util.Duration
|
||||
import akka.event.EventHandler
|
||||
import akka.event.Logging.{ Warning, Error }
|
||||
import akka.AkkaApplication
|
||||
|
||||
object ThreadPoolConfig {
|
||||
|
|
@ -227,10 +227,10 @@ class BoundedExecutorDecorator(val app: AkkaApplication, val executor: ExecutorS
|
|||
})
|
||||
} catch {
|
||||
case e: RejectedExecutionException ⇒
|
||||
app.eventHandler.warning(this, e.toString)
|
||||
app.mainbus.publish(Warning(this, e.toString))
|
||||
semaphore.release
|
||||
case e: Throwable ⇒
|
||||
app.eventHandler.error(e, this, e.getMessage)
|
||||
app.mainbus.publish(Error(e, this, e.getMessage))
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ trait EventBus {
|
|||
*/
|
||||
trait ActorEventBus extends EventBus {
|
||||
type Subscriber = ActorRef
|
||||
protected def compareSubscribers(a: ActorRef, b: ActorRef) = a compareTo b
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -254,9 +255,9 @@ trait ActorClassification { self: ActorEventBus with ActorClassifier ⇒
|
|||
*/
|
||||
protected def mapSize: Int
|
||||
|
||||
def publish(event: Event): Unit = mappings.get(classify(event)) match {
|
||||
case null ⇒
|
||||
case raw: Vector[_] ⇒ raw.asInstanceOf[Vector[ActorRef]] foreach { _ ! event }
|
||||
def publish(event: Event): Unit = {
|
||||
val receivers = mappings.get(classify(event))
|
||||
if (receivers ne null) receivers foreach { _ ! event }
|
||||
}
|
||||
|
||||
def subscribe(subscriber: Subscriber, to: Classifier): Boolean = associate(to, subscriber)
|
||||
|
|
|
|||
|
|
@ -1,296 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.event
|
||||
|
||||
import akka.actor._
|
||||
import akka.dispatch.Dispatchers
|
||||
import akka.config.ConfigurationException
|
||||
import akka.util.{ ListenerManagement, ReflectiveAccess }
|
||||
import akka.serialization._
|
||||
import akka.AkkaException
|
||||
import akka.AkkaApplication
|
||||
|
||||
object EventHandler {
|
||||
|
||||
val ErrorLevel = 1
|
||||
val WarningLevel = 2
|
||||
val InfoLevel = 3
|
||||
val DebugLevel = 4
|
||||
|
||||
val errorFormat = "[ERROR] [%s] [%s] [%s] %s\n%s".intern
|
||||
val warningFormat = "[WARN] [%s] [%s] [%s] %s".intern
|
||||
val infoFormat = "[INFO] [%s] [%s] [%s] %s".intern
|
||||
val debugFormat = "[DEBUG] [%s] [%s] [%s] %s".intern
|
||||
val genericFormat = "[GENERIC] [%s] [%s]".intern
|
||||
|
||||
class EventHandlerException extends AkkaException
|
||||
|
||||
lazy val StandardOutLogger = new StandardOutLogger {}
|
||||
|
||||
sealed trait Event {
|
||||
@transient
|
||||
val thread: Thread = Thread.currentThread
|
||||
def level: Int
|
||||
}
|
||||
|
||||
case class Error(cause: Throwable, instance: AnyRef, message: Any = "") extends Event {
|
||||
def level = ErrorLevel
|
||||
}
|
||||
|
||||
case class Warning(instance: AnyRef, message: Any = "") extends Event {
|
||||
def level = WarningLevel
|
||||
}
|
||||
|
||||
case class Info(instance: AnyRef, message: Any = "") extends Event {
|
||||
def level = InfoLevel
|
||||
}
|
||||
|
||||
case class Debug(instance: AnyRef, message: Any = "") extends Event {
|
||||
def level = DebugLevel
|
||||
}
|
||||
|
||||
trait StandardOutLogger {
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
||||
val dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss.S")
|
||||
|
||||
def timestamp = dateFormat.format(new Date)
|
||||
|
||||
def print(event: Any) {
|
||||
event match {
|
||||
case e: Error ⇒ error(e)
|
||||
case e: Warning ⇒ warning(e)
|
||||
case e: Info ⇒ info(e)
|
||||
case e: Debug ⇒ debug(e)
|
||||
case e ⇒ generic(e)
|
||||
}
|
||||
}
|
||||
|
||||
def error(event: Error) =
|
||||
println(errorFormat.format(
|
||||
timestamp,
|
||||
event.thread.getName,
|
||||
instanceName(event.instance),
|
||||
event.message,
|
||||
stackTraceFor(event.cause)))
|
||||
|
||||
def warning(event: Warning) =
|
||||
println(warningFormat.format(
|
||||
timestamp,
|
||||
event.thread.getName,
|
||||
instanceName(event.instance),
|
||||
event.message))
|
||||
|
||||
def info(event: Info) =
|
||||
println(infoFormat.format(
|
||||
timestamp,
|
||||
event.thread.getName,
|
||||
instanceName(event.instance),
|
||||
event.message))
|
||||
|
||||
def debug(event: Debug) =
|
||||
println(debugFormat.format(
|
||||
timestamp,
|
||||
event.thread.getName,
|
||||
instanceName(event.instance),
|
||||
event.message))
|
||||
|
||||
def generic(event: Any) =
|
||||
println(genericFormat.format(timestamp, event.toString))
|
||||
|
||||
def instanceName(instance: AnyRef): String = instance match {
|
||||
case null ⇒ "NULL"
|
||||
case a: ActorRef ⇒ a.address
|
||||
case _ ⇒ simpleName(instance)
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultListener extends Actor with StandardOutLogger {
|
||||
def receive = { case event ⇒ print(event) }
|
||||
}
|
||||
|
||||
def stackTraceFor(e: Throwable) = {
|
||||
import java.io.{ StringWriter, PrintWriter }
|
||||
val sw = new StringWriter
|
||||
val pw = new PrintWriter(sw)
|
||||
e.printStackTrace(pw)
|
||||
sw.toString
|
||||
}
|
||||
|
||||
private def levelFor(eventClass: Class[_ <: Event]) = {
|
||||
if (classOf[Error].isAssignableFrom(eventClass)) ErrorLevel
|
||||
else if (classOf[Warning].isAssignableFrom(eventClass)) WarningLevel
|
||||
else if (classOf[Info].isAssignableFrom(eventClass)) InfoLevel
|
||||
else if (classOf[Debug].isAssignableFrom(eventClass)) DebugLevel
|
||||
else DebugLevel
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler.
|
||||
* <p/>
|
||||
* Create, add and remove a listener:
|
||||
* <pre>
|
||||
* val eventHandlerListener = Actor.actorOf(new Actor {
|
||||
* self.dispatcher = EventHandler.EventHandlerDispatcher
|
||||
*
|
||||
* def receive = {
|
||||
* case EventHandler.Error(cause, instance, message) ⇒ ...
|
||||
* case EventHandler.Warning(instance, message) ⇒ ...
|
||||
* case EventHandler.Info(instance, message) ⇒ ...
|
||||
* case EventHandler.Debug(instance, message) ⇒ ...
|
||||
* case genericEvent ⇒ ...
|
||||
* }
|
||||
* })
|
||||
*
|
||||
* EventHandler.addListener(eventHandlerListener)
|
||||
* ...
|
||||
* EventHandler.removeListener(eventHandlerListener)
|
||||
* </pre>
|
||||
* <p/>
|
||||
* However best is probably to register the listener in the 'akka.conf'
|
||||
* configuration file.
|
||||
* <p/>
|
||||
* Log an error event:
|
||||
* <pre>
|
||||
* EventHandler.notify(EventHandler.Error(exception, this, message))
|
||||
* </pre>
|
||||
* Or use the direct methods (better performance):
|
||||
* <pre>
|
||||
* EventHandler.error(exception, this, message)
|
||||
* </pre>
|
||||
*
|
||||
* Shut down the EventHandler:
|
||||
* <pre>
|
||||
* EventHandler.shutdown()
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class EventHandler(app: AkkaApplication) extends ListenerManagement {
|
||||
|
||||
import EventHandler._
|
||||
|
||||
val synchronousLogging: Boolean = System.getProperty("akka.event.force-sync") match {
|
||||
case null | "" ⇒ false
|
||||
case _ ⇒ true
|
||||
}
|
||||
|
||||
lazy val EventHandlerDispatcher =
|
||||
app.dispatcherFactory.fromConfig("akka.event-handler-dispatcher", app.dispatcherFactory.newDispatcher("event-handler-dispatcher").setCorePoolSize(2).build)
|
||||
|
||||
implicit object defaultListenerFormat extends StatelessActorFormat[DefaultListener]
|
||||
|
||||
@volatile
|
||||
var level: Int = app.AkkaConfig.LogLevel match {
|
||||
case "ERROR" | "error" ⇒ ErrorLevel
|
||||
case "WARNING" | "warning" ⇒ WarningLevel
|
||||
case "INFO" | "info" ⇒ InfoLevel
|
||||
case "DEBUG" | "debug" ⇒ DebugLevel
|
||||
case unknown ⇒ throw new ConfigurationException(
|
||||
"Configuration option 'akka.event-handler-level' is invalid [" + unknown + "]")
|
||||
}
|
||||
|
||||
def start() {
|
||||
try {
|
||||
val defaultListeners = app.AkkaConfig.EventHandlers match {
|
||||
case Nil ⇒ "akka.event.EventHandler$DefaultListener" :: Nil
|
||||
case listeners ⇒ listeners
|
||||
}
|
||||
defaultListeners foreach { listenerName ⇒
|
||||
try {
|
||||
ReflectiveAccess.getClassFor[Actor](listenerName) match {
|
||||
case Right(actorClass) ⇒ addListener(new LocalActorRef(app, Props(actorClass).withDispatcher(EventHandlerDispatcher), app.guardian, Props.randomAddress, systemService = true))
|
||||
case Left(exception) ⇒ throw exception
|
||||
}
|
||||
} catch {
|
||||
case e: Exception ⇒
|
||||
throw new ConfigurationException(
|
||||
"Event Handler specified in config can't be loaded [" + listenerName +
|
||||
"] due to [" + e.toString + "]", e)
|
||||
}
|
||||
}
|
||||
info(this, "Starting up EventHandler")
|
||||
} catch {
|
||||
case e: Exception ⇒
|
||||
System.err.println("error while starting up EventHandler")
|
||||
e.printStackTrace()
|
||||
throw new ConfigurationException("Could not start Event Handler due to [" + e.toString + "]")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down all event handler listeners including the event handle dispatcher.
|
||||
*/
|
||||
def shutdown() {
|
||||
foreachListener { l ⇒
|
||||
removeListener(l)
|
||||
l.stop()
|
||||
}
|
||||
}
|
||||
|
||||
def notify(event: Any) {
|
||||
if (event.isInstanceOf[Event]) {
|
||||
if (level >= event.asInstanceOf[Event].level) log(event)
|
||||
} else log(event)
|
||||
}
|
||||
|
||||
def notify[T <: Event: ClassManifest](event: ⇒ T) {
|
||||
if (level >= levelFor(classManifest[T].erasure.asInstanceOf[Class[_ <: Event]])) log(event)
|
||||
}
|
||||
|
||||
def error(cause: Throwable, instance: AnyRef, message: ⇒ String) {
|
||||
if (level >= ErrorLevel) log(Error(cause, instance, message))
|
||||
}
|
||||
|
||||
def error(cause: Throwable, instance: AnyRef, message: Any) {
|
||||
if (level >= ErrorLevel) log(Error(cause, instance, message))
|
||||
}
|
||||
|
||||
def error(instance: AnyRef, message: ⇒ String) {
|
||||
if (level >= ErrorLevel) log(Error(new EventHandlerException, instance, message))
|
||||
}
|
||||
|
||||
def error(instance: AnyRef, message: Any) {
|
||||
if (level >= ErrorLevel) log(Error(new EventHandlerException, instance, message))
|
||||
}
|
||||
|
||||
def warning(instance: AnyRef, message: ⇒ String) {
|
||||
if (level >= WarningLevel) log(Warning(instance, message))
|
||||
}
|
||||
|
||||
def warning(instance: AnyRef, message: Any) {
|
||||
if (level >= WarningLevel) log(Warning(instance, message))
|
||||
}
|
||||
|
||||
def info(instance: AnyRef, message: ⇒ String) {
|
||||
if (level >= InfoLevel) log(Info(instance, message))
|
||||
}
|
||||
|
||||
def info(instance: AnyRef, message: Any) {
|
||||
if (level >= InfoLevel) log(Info(instance, message))
|
||||
}
|
||||
|
||||
def debug(instance: AnyRef, message: ⇒ String) {
|
||||
if (level >= DebugLevel) log(Debug(instance, message))
|
||||
}
|
||||
|
||||
def debug(instance: AnyRef, message: Any) {
|
||||
if (level >= DebugLevel) log(Debug(instance, message))
|
||||
}
|
||||
|
||||
def isInfoEnabled = level >= InfoLevel
|
||||
|
||||
def isDebugEnabled = level >= DebugLevel
|
||||
|
||||
private def log(event: Any) {
|
||||
if (synchronousLogging) StandardOutLogger.print(event)
|
||||
else notifyListeners(event)
|
||||
}
|
||||
|
||||
start()
|
||||
}
|
||||
|
|
@ -2,13 +2,396 @@
|
|||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.event
|
||||
import akka.actor.Actor
|
||||
|
||||
import akka.actor.{ Actor, ActorRef, MinimalActorRef, LocalActorRef, Props }
|
||||
import akka.{ AkkaException, AkkaApplication }
|
||||
import akka.AkkaApplication.AkkaConfig
|
||||
import akka.util.ReflectiveAccess
|
||||
import akka.config.ConfigurationException
|
||||
import akka.util.ReentrantGuard
|
||||
|
||||
/**
|
||||
* This trait brings log level handling to the MainBus: it reads the log
|
||||
* levels for the initial logging (StandardOutLogger) and the loggers&level
|
||||
* for after-init logging, possibly keeping the StandardOutLogger enabled if
|
||||
* it is part of the configured loggers. All configured loggers are treated as
|
||||
* system services and managed by this trait, i.e. subscribed/unsubscribed in
|
||||
* response to changes of LoggingBus.logLevel.
|
||||
*/
|
||||
trait LoggingBus extends ActorEventBus {
|
||||
|
||||
type Event >: Logging.LogEvent
|
||||
type Classifier >: Class[_]
|
||||
|
||||
import Logging._
|
||||
|
||||
private val guard = new ReentrantGuard
|
||||
private var loggers = Seq.empty[ActorRef]
|
||||
private var _logLevel: LogLevel = _
|
||||
|
||||
/**
|
||||
* Query currently set log level. See object Logging for more information.
|
||||
*/
|
||||
def logLevel = guard.withGuard { _logLevel }
|
||||
|
||||
/**
|
||||
* Change log level: default loggers (i.e. from configuration file) are
|
||||
* subscribed/unsubscribed as necessary so that they listen to all levels
|
||||
* which are at least as severe as the given one. See object Logging for
|
||||
* more information.
|
||||
*
|
||||
* NOTE: if the StandardOutLogger is configured also as normal logger, it
|
||||
* will not participate in the automatic management of log level
|
||||
* subscriptions!
|
||||
*/
|
||||
def logLevel_=(level: LogLevel): Unit = guard.withGuard {
|
||||
for {
|
||||
l ← AllLogLevels
|
||||
// subscribe if previously ignored and now requested
|
||||
if l > _logLevel && l <= level
|
||||
log ← loggers
|
||||
} subscribe(log, classFor(l))
|
||||
for {
|
||||
l ← AllLogLevels
|
||||
// unsubscribe if previously registered and now ignored
|
||||
if l <= _logLevel && l > level
|
||||
log ← loggers
|
||||
} unsubscribe(log, classFor(l))
|
||||
_logLevel = level
|
||||
}
|
||||
|
||||
private[akka] def startStdoutLogger(config: AkkaConfig) {
|
||||
val level = levelFor(config.StdoutLogLevel) getOrElse {
|
||||
StandardOutLogger.print(Error(new EventHandlerException, this, "unknown akka.stdout-loglevel " + config.StdoutLogLevel))
|
||||
ErrorLevel
|
||||
}
|
||||
AllLogLevels filter (level >= _) foreach (l ⇒ subscribe(StandardOutLogger, classFor(l)))
|
||||
guard.withGuard {
|
||||
loggers = Seq(StandardOutLogger)
|
||||
_logLevel = level
|
||||
}
|
||||
publish(Info(this, "StandardOutLogger started"))
|
||||
}
|
||||
|
||||
private[akka] def startDefaultLoggers(app: AkkaApplication, config: AkkaConfig) {
|
||||
val level = levelFor(config.LogLevel) getOrElse {
|
||||
StandardOutLogger.print(Error(new EventHandlerException, this, "unknown akka.stdout-loglevel " + config.LogLevel))
|
||||
ErrorLevel
|
||||
}
|
||||
try {
|
||||
val defaultLoggers = config.EventHandlers match {
|
||||
case Nil ⇒ "akka.event.Logging$DefaultLogger" :: Nil
|
||||
case loggers ⇒ loggers
|
||||
}
|
||||
val myloggers = for {
|
||||
loggerName ← defaultLoggers
|
||||
if loggerName != StandardOutLoggerName
|
||||
} yield {
|
||||
try {
|
||||
ReflectiveAccess.getClassFor[Actor](loggerName) match {
|
||||
case Right(actorClass) ⇒ addLogger(app, actorClass, level)
|
||||
case Left(exception) ⇒ throw exception
|
||||
}
|
||||
} catch {
|
||||
case e: Exception ⇒
|
||||
throw new ConfigurationException(
|
||||
"Event Handler specified in config can't be loaded [" + loggerName +
|
||||
"] due to [" + e.toString + "]", e)
|
||||
}
|
||||
}
|
||||
guard.withGuard {
|
||||
loggers = myloggers
|
||||
_logLevel = level
|
||||
}
|
||||
publish(Info(this, "Default Loggers started"))
|
||||
if (!(defaultLoggers contains StandardOutLoggerName)) {
|
||||
unsubscribe(StandardOutLogger)
|
||||
}
|
||||
} catch {
|
||||
case e: Exception ⇒
|
||||
System.err.println("error while starting up EventHandler")
|
||||
e.printStackTrace()
|
||||
throw new ConfigurationException("Could not start Event Handler due to [" + e.toString + "]")
|
||||
}
|
||||
}
|
||||
|
||||
private[akka] def stopDefaultLoggers() {
|
||||
val level = _logLevel // volatile access before reading loggers
|
||||
if (!(loggers contains StandardOutLogger)) {
|
||||
AllLogLevels filter (level >= _) foreach (l ⇒ subscribe(StandardOutLogger, classFor(l)))
|
||||
publish(Info(this, "shutting down: StandardOutLogger started"))
|
||||
}
|
||||
for {
|
||||
logger ← loggers
|
||||
if logger != StandardOutLogger
|
||||
} logger.stop()
|
||||
publish(Info(this, "all default loggers stopped"))
|
||||
}
|
||||
|
||||
private def addLogger(app: AkkaApplication, clazz: Class[_ <: Actor], level: LogLevel): ActorRef = {
|
||||
val actor = app.systemActorOf(Props(clazz), Props.randomAddress)
|
||||
actor ! InitializeLogger(this)
|
||||
AllLogLevels filter (level >= _) foreach (l ⇒ subscribe(actor, classFor(l)))
|
||||
publish(Info(this, "logger " + clazz.getName + " started"))
|
||||
actor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point for Akka logging: log levels and message types (aka
|
||||
* channels) defined for the main transport medium, the main event bus. The
|
||||
* recommended use is to obtain an implementation of the Logging trait with
|
||||
* suitable and efficient methods for generating log events:
|
||||
*
|
||||
* <pre><code>
|
||||
* val log = Logging(<bus>, <source object>)
|
||||
* ...
|
||||
* log.info("hello world!")
|
||||
* </code></pre>
|
||||
*
|
||||
* Loggers are attached to the level-specific channels <code>Error</code>,
|
||||
* <code>Warning</code>, <code>Info</code> and <code>Debug</code> as
|
||||
* appropriate for the configured (or set) log level. If you want to implement
|
||||
* your own, make sure to handle these four event types plus the <code>InitializeLogger</code>
|
||||
* message which is sent before actually attaching it to the logging bus.
|
||||
*
|
||||
* Logging is configured in <code>akka.conf</code> by setting (some of) the following:
|
||||
*
|
||||
* <pre><code>
|
||||
* akka {
|
||||
* event-handlers = ["akka.slf4j.Slf4jEventHandler"] # for example
|
||||
* loglevel = "INFO" # used when normal logging ("event-handlers") has been started
|
||||
* stdout-loglevel = "WARN" # used during application start-up until normal logging is available
|
||||
* }
|
||||
* </code></pre>
|
||||
*/
|
||||
object Logging {
|
||||
|
||||
/**
|
||||
* Marker trait for annotating LogLevel, which must be Int after erasure.
|
||||
*/
|
||||
trait LogLevelType
|
||||
/**
|
||||
* Log level in numeric form, used when deciding whether a certain log
|
||||
* statement should generate a log event. Predefined levels are ErrorLevel (1)
|
||||
* to DebugLevel (4). In case you want to add more levels, loggers need to
|
||||
* be subscribed to their event bus channels manually.
|
||||
*/
|
||||
type LogLevel = Int with LogLevelType
|
||||
final val ErrorLevel = 1.asInstanceOf[Int with LogLevelType]
|
||||
final val WarningLevel = 2.asInstanceOf[Int with LogLevelType]
|
||||
final val InfoLevel = 3.asInstanceOf[Int with LogLevelType]
|
||||
final val DebugLevel = 4.asInstanceOf[Int with LogLevelType]
|
||||
|
||||
def levelFor(s: String): Option[LogLevel] = s match {
|
||||
case "ERROR" | "error" ⇒ Some(ErrorLevel)
|
||||
case "WARNING" | "warning" ⇒ Some(WarningLevel)
|
||||
case "INFO" | "info" ⇒ Some(InfoLevel)
|
||||
case "DEBUG" | "debug" ⇒ Some(DebugLevel)
|
||||
case unknown ⇒ None
|
||||
}
|
||||
|
||||
def levelFor(eventClass: Class[_ <: LogEvent]) = {
|
||||
if (classOf[Error].isAssignableFrom(eventClass)) ErrorLevel
|
||||
else if (classOf[Warning].isAssignableFrom(eventClass)) WarningLevel
|
||||
else if (classOf[Info].isAssignableFrom(eventClass)) InfoLevel
|
||||
else if (classOf[Debug].isAssignableFrom(eventClass)) DebugLevel
|
||||
else DebugLevel
|
||||
}
|
||||
|
||||
def classFor(level: LogLevel): Class[_ <: LogEvent] = level match {
|
||||
case ErrorLevel ⇒ classOf[Error]
|
||||
case WarningLevel ⇒ classOf[Warning]
|
||||
case InfoLevel ⇒ classOf[Info]
|
||||
case DebugLevel ⇒ classOf[Debug]
|
||||
}
|
||||
|
||||
// these type ascriptions/casts are necessary to avoid CCEs during construction while retaining correct type
|
||||
val AllLogLevels = Seq(ErrorLevel: AnyRef, WarningLevel, InfoLevel, DebugLevel).asInstanceOf[Seq[LogLevel]]
|
||||
|
||||
val errorFormat = "[ERROR] [%s] [%s] [%s] %s\n%s".intern
|
||||
val warningFormat = "[WARN] [%s] [%s] [%s] %s".intern
|
||||
val infoFormat = "[INFO] [%s] [%s] [%s] %s".intern
|
||||
val debugFormat = "[DEBUG] [%s] [%s] [%s] %s".intern
|
||||
val genericFormat = "[GENERIC] [%s] [%s]".intern
|
||||
|
||||
/**
|
||||
* Obtain LoggingAdapter for the given application and source object. The
|
||||
* source object is used to identify the source of this logging channel.
|
||||
*/
|
||||
def apply(app: AkkaApplication, source: AnyRef): LoggingAdapter = new BusLogging(app.mainbus, source)
|
||||
/**
|
||||
* Java API: Obtain LoggingAdapter for the given application and source object. The
|
||||
* source object is used to identify the source of this logging channel.
|
||||
*/
|
||||
def getLogger(app: AkkaApplication, source: AnyRef): LoggingAdapter = apply(app, source)
|
||||
/**
|
||||
* Obtain LoggingAdapter for the given event bus and source object. The
|
||||
* source object is used to identify the source of this logging channel.
|
||||
*/
|
||||
def apply(bus: LoggingBus, source: AnyRef): LoggingAdapter = new BusLogging(bus, source)
|
||||
/**
|
||||
* Java API: Obtain LoggingAdapter for the given event bus and source object. The
|
||||
* source object is used to identify the source of this logging channel.
|
||||
*/
|
||||
def getLogger(bus: LoggingBus, source: AnyRef): LoggingAdapter = apply(bus, source)
|
||||
|
||||
/**
|
||||
* Artificial exception injected into Error events if no Throwable is
|
||||
* supplied; used for getting a stack dump of error locations.
|
||||
*/
|
||||
class EventHandlerException extends AkkaException
|
||||
|
||||
sealed trait LogEvent {
|
||||
@transient
|
||||
val thread: Thread = Thread.currentThread
|
||||
def level: LogLevel
|
||||
}
|
||||
|
||||
case class Error(cause: Throwable, instance: AnyRef, message: Any = "") extends LogEvent {
|
||||
def level = ErrorLevel
|
||||
}
|
||||
object Error {
|
||||
def apply(instance: AnyRef, message: Any) = new Error(new EventHandlerException, instance, message)
|
||||
}
|
||||
|
||||
case class Warning(instance: AnyRef, message: Any = "") extends LogEvent {
|
||||
def level = WarningLevel
|
||||
}
|
||||
|
||||
case class Info(instance: AnyRef, message: Any = "") extends LogEvent {
|
||||
def level = InfoLevel
|
||||
}
|
||||
|
||||
case class Debug(instance: AnyRef, message: Any = "") extends LogEvent {
|
||||
def level = DebugLevel
|
||||
}
|
||||
|
||||
/**
|
||||
* Message which is sent to each default logger (i.e. from configuration file)
|
||||
* after its creation but before attaching it to the logging bus. The logger
|
||||
* actor should handle this message, e.g. to register for more channels.
|
||||
*/
|
||||
case class InitializeLogger(bus: LoggingBus)
|
||||
|
||||
trait StdOutLogger {
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
||||
val dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss.S")
|
||||
|
||||
def timestamp = dateFormat.format(new Date)
|
||||
|
||||
def print(event: Any) {
|
||||
event match {
|
||||
case e: Error ⇒ error(e)
|
||||
case e: Warning ⇒ warning(e)
|
||||
case e: Info ⇒ info(e)
|
||||
case e: Debug ⇒ debug(e)
|
||||
case e ⇒ generic(e)
|
||||
}
|
||||
}
|
||||
|
||||
def error(event: Error) =
|
||||
println(errorFormat.format(
|
||||
timestamp,
|
||||
event.thread.getName,
|
||||
instanceName(event.instance),
|
||||
event.message,
|
||||
stackTraceFor(event.cause)))
|
||||
|
||||
def warning(event: Warning) =
|
||||
println(warningFormat.format(
|
||||
timestamp,
|
||||
event.thread.getName,
|
||||
instanceName(event.instance),
|
||||
event.message))
|
||||
|
||||
def info(event: Info) =
|
||||
println(infoFormat.format(
|
||||
timestamp,
|
||||
event.thread.getName,
|
||||
instanceName(event.instance),
|
||||
event.message))
|
||||
|
||||
def debug(event: Debug) =
|
||||
println(debugFormat.format(
|
||||
timestamp,
|
||||
event.thread.getName,
|
||||
instanceName(event.instance),
|
||||
event.message))
|
||||
|
||||
def generic(event: Any) =
|
||||
println(genericFormat.format(timestamp, event.toString))
|
||||
|
||||
def instanceName(instance: AnyRef): String = instance match {
|
||||
case null ⇒ "NULL"
|
||||
case a: ActorRef ⇒ a.address
|
||||
case _ ⇒ instance.getClass.getSimpleName
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actor-less logging implementation for synchronous logging to standard
|
||||
* output. This logger is always attached first in order to be able to log
|
||||
* failures during application start-up, even before normal logging is
|
||||
* started. Its log level can be configured by setting
|
||||
* <code>akka.stdout-loglevel</code> in <code>akka.conf</code>.
|
||||
*/
|
||||
class StandardOutLogger extends MinimalActorRef with StdOutLogger {
|
||||
override val toString = "StandardOutLogger"
|
||||
override def postMessageToMailbox(obj: Any, sender: ActorRef) { print(obj) }
|
||||
}
|
||||
val StandardOutLogger = new StandardOutLogger
|
||||
val StandardOutLoggerName = StandardOutLogger.getClass.getName
|
||||
|
||||
/**
|
||||
* Actor wrapper around the standard output logger. If
|
||||
* <code>akka.event-handlers</code> is not set, it defaults to just this
|
||||
* logger.
|
||||
*/
|
||||
class DefaultLogger extends Actor with StdOutLogger {
|
||||
def receive = {
|
||||
case InitializeLogger(_) ⇒
|
||||
case event: LogEvent ⇒ print(event)
|
||||
}
|
||||
}
|
||||
|
||||
def stackTraceFor(e: Throwable) = {
|
||||
if (e ne null) {
|
||||
import java.io.{ StringWriter, PrintWriter }
|
||||
val sw = new StringWriter
|
||||
val pw = new PrintWriter(sw)
|
||||
e.printStackTrace(pw)
|
||||
sw.toString
|
||||
} else {
|
||||
"[NO STACK TRACE]"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Logging wrapper to make nicer and optimize: provide template versions which
|
||||
* evaluate .toString only if the log level is actually enabled.
|
||||
* evaluate .toString only if the log level is actually enabled. Typically used
|
||||
* by obtaining an implementation from the Logging object:
|
||||
*
|
||||
* <code><pre>
|
||||
* val log = Logging(<bus>, <source object>)
|
||||
* ...
|
||||
* log.info("hello world!")
|
||||
* </pre></code>
|
||||
*
|
||||
* All log-level methods support simple interpolation templates with up to four
|
||||
* arguments placed by using <code>{}</code> within the template (first string
|
||||
* argument):
|
||||
*
|
||||
* <code><pre>
|
||||
* log.error(exception, "Exception while processing {} in state {}", msg, state)
|
||||
* </pre></code>
|
||||
*/
|
||||
trait Logging {
|
||||
trait LoggingAdapter {
|
||||
|
||||
/*
|
||||
* implement these as precisely as needed/possible: always returning true
|
||||
|
|
@ -23,6 +406,7 @@ trait Logging {
|
|||
* These actually implement the passing on of the messages to be logged.
|
||||
* Will not be called if is...Enabled returned false.
|
||||
*/
|
||||
protected def notifyError(message: String)
|
||||
protected def notifyError(cause: Throwable, message: String)
|
||||
protected def notifyWarning(message: String)
|
||||
protected def notifyInfo(message: String)
|
||||
|
|
@ -38,7 +422,7 @@ trait Logging {
|
|||
def error(cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any) { if (isErrorEnabled) error(cause, format(template, arg1, arg2, arg3)) }
|
||||
def error(cause: Throwable, template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any) { if (isErrorEnabled) error(cause, format(template, arg1, arg2, arg3, arg4)) }
|
||||
|
||||
def error(message: String) { if (isErrorEnabled) error(null: Throwable, message) }
|
||||
def error(message: String) { if (isErrorEnabled) notifyError(message) }
|
||||
def error(template: String, arg1: Any) { if (isErrorEnabled) error(format(template, arg1)) }
|
||||
def error(template: String, arg1: Any, arg2: Any) { if (isErrorEnabled) error(format(template, arg1, arg2)) }
|
||||
def error(template: String, arg1: Any, arg2: Any, arg3: Any) { if (isErrorEnabled) error(format(template, arg1, arg2, arg3)) }
|
||||
|
|
@ -62,47 +446,39 @@ trait Logging {
|
|||
def debug(template: String, arg1: Any, arg2: Any, arg3: Any) { if (isDebugEnabled) debug(format(template, arg1, arg2, arg3)) }
|
||||
def debug(template: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any) { if (isDebugEnabled) debug(format(template, arg1, arg2, arg3, arg4)) }
|
||||
|
||||
def format(t: String, arg1: Any) = t.replaceFirst("{}", arg1.asInstanceOf[AnyRef].toString)
|
||||
def format(t: String, arg1: Any, arg2: Any) = t.replaceFirst("{}", arg1.asInstanceOf[AnyRef].toString).replaceFirst("{}", arg2.asInstanceOf[AnyRef].toString)
|
||||
def format(t: String, arg1: Any, arg2: Any, arg3: Any) = t.replaceFirst("{}", arg1.asInstanceOf[AnyRef].toString).replaceFirst("{}", arg2.asInstanceOf[AnyRef].toString).replaceFirst("{}", arg3.asInstanceOf[AnyRef].toString)
|
||||
def format(t: String, arg1: Any, arg2: Any, arg3: Any, arg4: Any) = t.replaceFirst("{}", arg1.asInstanceOf[AnyRef].toString).replaceFirst("{}", arg2.asInstanceOf[AnyRef].toString).replaceFirst("{}", arg3.asInstanceOf[AnyRef].toString).replaceFirst("{}", arg4.asInstanceOf[AnyRef].toString)
|
||||
|
||||
def format(t: String, arg: Any*) = {
|
||||
val sb = new StringBuilder
|
||||
var p = 0
|
||||
var rest = t
|
||||
while (p < arg.length) {
|
||||
val index = rest.indexOf("{}")
|
||||
sb.append(rest.substring(0, index))
|
||||
sb.append(arg(p))
|
||||
rest = rest.substring(index + 2)
|
||||
p += 1
|
||||
}
|
||||
sb.append(rest)
|
||||
sb.toString
|
||||
}
|
||||
}
|
||||
|
||||
trait ActorLogging extends Logging { this: Actor ⇒
|
||||
class BusLogging(val bus: LoggingBus, val loggingInstance: AnyRef) extends LoggingAdapter {
|
||||
|
||||
import EventHandler._
|
||||
import Logging._
|
||||
|
||||
def isErrorEnabled = app.eventHandler.level >= ErrorLevel
|
||||
def isWarningEnabled = app.eventHandler.level >= WarningLevel
|
||||
def isInfoEnabled = app.eventHandler.level >= InfoLevel
|
||||
def isDebugEnabled = app.eventHandler.level >= DebugLevel
|
||||
def isErrorEnabled = bus.logLevel >= ErrorLevel
|
||||
def isWarningEnabled = bus.logLevel >= WarningLevel
|
||||
def isInfoEnabled = bus.logLevel >= InfoLevel
|
||||
def isDebugEnabled = bus.logLevel >= DebugLevel
|
||||
|
||||
protected def notifyError(cause: Throwable, message: String) { app.eventHandler.notifyListeners(Error(cause, context.self, message)) }
|
||||
protected def notifyError(message: String) { bus.publish(Error(loggingInstance, message)) }
|
||||
|
||||
protected def notifyWarning(message: String) { app.eventHandler.notifyListeners(Warning(context.self, message)) }
|
||||
protected def notifyError(cause: Throwable, message: String) { bus.publish(Error(cause, loggingInstance, message)) }
|
||||
|
||||
protected def notifyInfo(message: String) { app.eventHandler.notifyListeners(Info(context.self, message)) }
|
||||
protected def notifyWarning(message: String) { bus.publish(Warning(loggingInstance, message)) }
|
||||
|
||||
protected def notifyDebug(message: String) { app.eventHandler.notifyListeners(Debug(context.self, message)) }
|
||||
|
||||
}
|
||||
|
||||
class EventHandlerLogging(val eventHandler: EventHandler, val loggingInstance: AnyRef) extends Logging {
|
||||
|
||||
import EventHandler._
|
||||
|
||||
def isErrorEnabled = eventHandler.level >= ErrorLevel
|
||||
def isWarningEnabled = eventHandler.level >= WarningLevel
|
||||
def isInfoEnabled = eventHandler.level >= InfoLevel
|
||||
def isDebugEnabled = eventHandler.level >= DebugLevel
|
||||
|
||||
protected def notifyError(cause: Throwable, message: String) { eventHandler.notifyListeners(Error(cause, loggingInstance, message)) }
|
||||
|
||||
protected def notifyWarning(message: String) { eventHandler.notifyListeners(Warning(loggingInstance, message)) }
|
||||
|
||||
protected def notifyInfo(message: String) { eventHandler.notifyListeners(Info(loggingInstance, message)) }
|
||||
|
||||
protected def notifyDebug(message: String) { eventHandler.notifyListeners(Debug(loggingInstance, message)) }
|
||||
protected def notifyInfo(message: String) { bus.publish(Info(loggingInstance, message)) }
|
||||
|
||||
protected def notifyDebug(message: String) { bus.publish(Debug(loggingInstance, message)) }
|
||||
|
||||
}
|
||||
|
|
|
|||
56
akka-actor/src/main/scala/akka/event/MainBus.scala
Normal file
56
akka-actor/src/main/scala/akka/event/MainBus.scala
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.event
|
||||
|
||||
import akka.actor.{ ActorRef, Actor, Props }
|
||||
import akka.AkkaApplication
|
||||
import akka.actor.Terminated
|
||||
|
||||
class MainBus(debug: Boolean = false) extends LoggingBus with LookupClassification {
|
||||
|
||||
type Event = AnyRef
|
||||
type Classifier = Class[_]
|
||||
|
||||
@volatile
|
||||
private var reaper: ActorRef = _
|
||||
|
||||
protected def mapSize = 16
|
||||
|
||||
protected def classify(event: AnyRef): Class[_] = event.getClass
|
||||
|
||||
protected def publish(event: AnyRef, subscriber: ActorRef) = subscriber ! event
|
||||
|
||||
override def subscribe(subscriber: ActorRef, channel: Class[_]): Boolean = {
|
||||
if (debug) publish(Logging.Debug(this, "subscribing " + subscriber + " to channel " + channel))
|
||||
if (reaper ne null) reaper ! subscriber
|
||||
super.subscribe(subscriber, channel)
|
||||
}
|
||||
|
||||
override def unsubscribe(subscriber: ActorRef, channel: Class[_]): Boolean = {
|
||||
if (debug) publish(Logging.Debug(this, "unsubscribing " + subscriber + " from channel " + channel))
|
||||
super.unsubscribe(subscriber, channel)
|
||||
}
|
||||
|
||||
override def unsubscribe(subscriber: ActorRef) {
|
||||
if (debug) publish(Logging.Debug(this, "unsubscribing " + subscriber + " from all channels"))
|
||||
super.unsubscribe(subscriber)
|
||||
}
|
||||
|
||||
def start(app: AkkaApplication) {
|
||||
reaper = app.systemActorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case ref: ActorRef ⇒ watch(ref)
|
||||
case Terminated(ref) ⇒ unsubscribe(ref)
|
||||
}
|
||||
}), "MainBusReaper")
|
||||
subscribers.values foreach (reaper ! _)
|
||||
}
|
||||
|
||||
def printSubscribers: String = {
|
||||
val sb = new StringBuilder
|
||||
for (c ← subscribers.keys) sb.append(c + " -> " + subscribers.valueIterator(c).mkString("[", ", ", "]"))
|
||||
sb.toString
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ import java.lang.reflect.InvocationTargetException
|
|||
class RemoteException(message: String) extends AkkaException(message)
|
||||
|
||||
trait RemoteModule {
|
||||
protected[akka] def notifyListeners(message: ⇒ Any): Unit
|
||||
protected[akka] def notifyListeners(message: RemoteLifeCycleEvent): Unit
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -122,7 +122,7 @@ abstract class RemoteSupport(val app: AkkaApplication) extends RemoteServerModul
|
|||
this.shutdownServerModule()
|
||||
}
|
||||
|
||||
protected[akka] override def notifyListeners(message: ⇒ Any): Unit = app.eventHandler.notify(message)
|
||||
protected[akka] override def notifyListeners(message: RemoteLifeCycleEvent): Unit = app.mainbus.publish(message)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -173,4 +173,4 @@ trait RemoteClientModule extends RemoteModule { self: RemoteSupport ⇒
|
|||
remoteAddress: InetSocketAddress,
|
||||
recipient: ActorRef,
|
||||
loader: Option[ClassLoader]): Unit
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ package akka.routing
|
|||
|
||||
import akka.AkkaException
|
||||
import akka.actor._
|
||||
import akka.event.EventHandler
|
||||
import akka.config.ConfigurationException
|
||||
import akka.dispatch.{ Future, MessageDispatcher }
|
||||
import akka.AkkaApplication
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package akka.util
|
|||
import java.util.concurrent.TimeUnit
|
||||
import TimeUnit._
|
||||
import java.lang.{ Long ⇒ JLong, Double ⇒ JDouble }
|
||||
import akka.AkkaApplication
|
||||
|
||||
class TimerException(message: String) extends RuntimeException(message)
|
||||
|
||||
|
|
@ -119,15 +120,6 @@ object Duration {
|
|||
case "ns" | "nano" | "nanos" | "nanosecond" | "nanoseconds" ⇒ NANOSECONDS
|
||||
}
|
||||
|
||||
/*
|
||||
* Testing facilities
|
||||
*/
|
||||
val timeFactor: Double = {
|
||||
val factor = System.getProperty("akka.test.timefactor", "1.0")
|
||||
try { factor.toDouble }
|
||||
catch { case e: java.lang.NumberFormatException ⇒ 1.0 }
|
||||
}
|
||||
|
||||
val Zero: Duration = new FiniteDuration(0, NANOSECONDS)
|
||||
|
||||
trait Infinite {
|
||||
|
|
@ -272,9 +264,10 @@ abstract class Duration extends Serializable {
|
|||
def /(other: Duration): Double
|
||||
def unary_- : Duration
|
||||
def finite_? : Boolean
|
||||
def dilated: Duration = this * Duration.timeFactor
|
||||
def dilated(implicit app: AkkaApplication): Duration = this * app.AkkaConfig.TestTimeFactor
|
||||
def min(other: Duration): Duration = if (this < other) this else other
|
||||
def max(other: Duration): Duration = if (this > other) this else other
|
||||
def sleep(): Unit = Thread.sleep(toMillis)
|
||||
|
||||
// Java API
|
||||
def lt(other: Duration) = this < other
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.util
|
||||
|
||||
import akka.event.EventHandler
|
||||
import java.io.{ PrintWriter, StringWriter }
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import annotation.tailrec
|
|||
|
||||
import java.util.concurrent.{ ConcurrentSkipListSet, ConcurrentHashMap }
|
||||
import java.util.{ Comparator, Set ⇒ JSet }
|
||||
import scala.collection.mutable
|
||||
|
||||
/**
|
||||
* An implementation of a ConcurrentMultiMap
|
||||
|
|
@ -98,6 +99,24 @@ class Index[K, V](val mapSize: Int, val valueComparator: Comparator[V]) {
|
|||
container.entrySet foreach { e ⇒ e.getValue.foreach(fun(e.getKey, _)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the union of all value sets.
|
||||
*/
|
||||
def values: Set[V] = {
|
||||
import scala.collection.JavaConversions._
|
||||
val builder = mutable.Set.empty[V]
|
||||
for {
|
||||
entry ← container.entrySet
|
||||
v ← entry.getValue
|
||||
} builder += v
|
||||
builder.toSet
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key set.
|
||||
*/
|
||||
def keys = scala.collection.JavaConversions.collectionAsScalaIterable(container.keySet)
|
||||
|
||||
/**
|
||||
* Disassociates the value of type V from the key of type K
|
||||
* @return true if the value was disassociated from the key and false if it wasn't previously associated with the key
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
package akka.util
|
||||
|
||||
import akka.event.EventHandler
|
||||
import akka.event.Logging.Error
|
||||
import java.lang.management.ManagementFactory
|
||||
import javax.management.{ ObjectInstance, ObjectName, InstanceAlreadyExistsException, InstanceNotFoundException }
|
||||
import akka.AkkaApplication
|
||||
|
|
@ -24,7 +24,7 @@ object JMX {
|
|||
case e: InstanceAlreadyExistsException ⇒
|
||||
Some(mbeanServer.getObjectInstance(name))
|
||||
case e: Exception ⇒
|
||||
app.eventHandler.error(e, this, "Error when registering mbean [%s]".format(mbean))
|
||||
app.mainbus.publish(Error(e, this, "Error when registering mbean [%s]".format(mbean)))
|
||||
None
|
||||
}
|
||||
|
||||
|
|
@ -32,6 +32,6 @@ object JMX {
|
|||
mbeanServer.unregisterMBean(mbean)
|
||||
} catch {
|
||||
case e: InstanceNotFoundException ⇒ {}
|
||||
case e: Exception ⇒ app.eventHandler.error(e, this, "Error while unregistering mbean [%s]".format(mbean))
|
||||
case e: Exception ⇒ app.mainbus.publish(Error(e, this, "Error while unregistering mbean [%s]".format(mbean)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ package akka.util
|
|||
|
||||
import java.util.concurrent.locks.{ ReentrantReadWriteLock, ReentrantLock }
|
||||
import java.util.concurrent.atomic.{ AtomicBoolean }
|
||||
import akka.event.EventHandler
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
*/
|
||||
|
||||
package akka.util
|
||||
|
||||
import akka.dispatch.Envelope
|
||||
import akka.actor._
|
||||
import DeploymentConfig.ReplicationScheme
|
||||
import akka.config.ModuleNotAvailableException
|
||||
import akka.event.Logging.Debug
|
||||
import akka.cluster.ClusterNode
|
||||
import akka.routing.{ RoutedProps, Router }
|
||||
import akka.AkkaApplication
|
||||
|
|
|
|||
|
|
@ -7,20 +7,22 @@ import akka.util.duration._
|
|||
|
||||
//#imports
|
||||
import akka.actor.Actor
|
||||
import akka.event.EventHandler
|
||||
import akka.event.Logging
|
||||
import akka.config.Configuration
|
||||
|
||||
//#imports
|
||||
|
||||
//#my-actor
|
||||
class MyActor extends Actor {
|
||||
val log = Logging(app, this)
|
||||
def receive = {
|
||||
case "test" ⇒ app.eventHandler.info(this, "received test")
|
||||
case _ ⇒ app.eventHandler.info(this, "received unknown message")
|
||||
case "test" ⇒ log.info("received test")
|
||||
case _ ⇒ log.info("received unknown message")
|
||||
}
|
||||
}
|
||||
//#my-actor
|
||||
|
||||
class ActorDocSpec extends AkkaSpec {
|
||||
class ActorDocSpec extends AkkaSpec(Configuration("akka.loglevel" -> "INFO")) {
|
||||
|
||||
"creating actor with AkkaSpec.actorOf" in {
|
||||
//#creating-actorOf
|
||||
|
|
@ -30,23 +32,21 @@ class ActorDocSpec extends AkkaSpec {
|
|||
// testing the actor
|
||||
|
||||
// TODO: convert docs to AkkaSpec(Configuration(...))
|
||||
app.eventHandler.notify(TestEvent.Mute(EventFilter.custom {
|
||||
case e: EventHandler.Info ⇒ true
|
||||
case _ ⇒ false
|
||||
}))
|
||||
app.eventHandler.addListener(testActor)
|
||||
val eventLevel = app.eventHandler.level
|
||||
app.eventHandler.level = EventHandler.InfoLevel
|
||||
val filter = EventFilter.custom {
|
||||
case e: Logging.Info ⇒ true
|
||||
case _ ⇒ false
|
||||
}
|
||||
app.mainbus.publish(TestEvent.Mute(filter))
|
||||
app.mainbus.subscribe(testActor, classOf[Logging.Info])
|
||||
|
||||
myActor ! "test"
|
||||
expectMsgPF(1 second) { case EventHandler.Info(_, "received test") ⇒ true }
|
||||
expectMsgPF(1 second) { case Logging.Info(_, "received test") ⇒ true }
|
||||
|
||||
myActor ! "unknown"
|
||||
expectMsgPF(1 second) { case EventHandler.Info(_, "received unknown message") ⇒ true }
|
||||
expectMsgPF(1 second) { case Logging.Info(_, "received unknown message") ⇒ true }
|
||||
|
||||
app.eventHandler.level = eventLevel
|
||||
app.eventHandler.removeListener(testActor)
|
||||
app.eventHandler.notify(TestEvent.UnMuteAll)
|
||||
app.mainbus.unsubscribe(testActor)
|
||||
app.mainbus.publish(TestEvent.UnMute(filter))
|
||||
|
||||
myActor.stop()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ package akka.remote
|
|||
|
||||
import akka.actor.{ Actor, BootableActorLoaderService }
|
||||
import akka.util.{ ReflectiveAccess, Bootable }
|
||||
import akka.event.EventHandler
|
||||
|
||||
// TODO: remove me - remoting is enabled through the RemoteActorRefProvider
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package akka.remote
|
|||
import akka.AkkaApplication
|
||||
import akka.actor._
|
||||
import akka.actor.Status._
|
||||
import akka.event.Logging
|
||||
import akka.util.duration._
|
||||
import akka.remote.RemoteProtocol._
|
||||
import akka.remote.RemoteProtocol.RemoteSystemDaemonMessageType._
|
||||
|
|
@ -104,6 +105,7 @@ class Gossiper(remote: Remote) {
|
|||
nodeMembershipChangeListeners: Set[NodeMembershipChangeListener] = Set.empty[NodeMembershipChangeListener])
|
||||
|
||||
private val app = remote.app
|
||||
private val log = Logging(app, this)
|
||||
private val failureDetector = remote.failureDetector
|
||||
private val connectionManager = new RemoteConnectionManager(app, remote, Map.empty[InetSocketAddress, ActorRef])
|
||||
private val seeds = Set(address) // FIXME read in list of seeds from config
|
||||
|
|
@ -241,18 +243,18 @@ class Gossiper(remote: Remote) {
|
|||
try {
|
||||
(connection ? (toRemoteMessage(newGossip), remote.remoteSystemDaemonAckTimeout)).as[Status] match {
|
||||
case Some(Success(receiver)) ⇒
|
||||
app.eventHandler.debug(this, "Gossip sent to [%s] was successfully received".format(receiver))
|
||||
log.debug("Gossip sent to [{}] was successfully received", receiver)
|
||||
|
||||
case Some(Failure(cause)) ⇒
|
||||
app.eventHandler.error(cause, this, cause.toString)
|
||||
log.error(cause, cause.toString)
|
||||
|
||||
case None ⇒
|
||||
val error = new RemoteException("Gossip to [%s] timed out".format(connection.address))
|
||||
app.eventHandler.error(error, this, error.toString)
|
||||
log.error(error, error.toString)
|
||||
}
|
||||
} catch {
|
||||
case e: Exception ⇒
|
||||
app.eventHandler.error(e, this, "Could not gossip to [%s] due to: %s".format(connection.address, e.toString))
|
||||
log.error(e, "Could not gossip to [{}] due to: {}", connection.address, e.toString)
|
||||
}
|
||||
|
||||
seeds exists (peer == _)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ package akka.remote
|
|||
|
||||
import akka.AkkaApplication
|
||||
import akka.actor._
|
||||
import akka.event.EventHandler
|
||||
import akka.event.Logging
|
||||
import akka.actor.Status._
|
||||
import akka.util._
|
||||
import akka.util.duration._
|
||||
|
|
@ -29,10 +29,14 @@ import akka.dispatch.{ Terminate, Dispatchers, Future, PinnedDispatcher }
|
|||
*/
|
||||
class Remote(val app: AkkaApplication) {
|
||||
|
||||
val log = Logging(app, this)
|
||||
|
||||
import app._
|
||||
import app.config
|
||||
import app.AkkaConfig._
|
||||
|
||||
val nodename = app.nodename
|
||||
|
||||
// TODO move to AkkaConfig?
|
||||
val shouldCompressData = config.getBool("akka.remote.use-compression", false)
|
||||
val remoteSystemDaemonAckTimeout = Duration(config.getInt("akka.remote.remote-daemon-ack-timeout", 30), DefaultTimeUnit).toMillis.toInt
|
||||
|
|
@ -70,8 +74,8 @@ class Remote(val app: AkkaApplication) {
|
|||
val remote = new akka.remote.netty.NettyRemoteSupport(app)
|
||||
remote.start() //TODO FIXME Any application loader here?
|
||||
|
||||
app.eventHandler.addListener(eventStream.sender)
|
||||
app.eventHandler.addListener(remoteClientLifeCycleHandler)
|
||||
app.mainbus.subscribe(eventStream.sender, classOf[RemoteLifeCycleEvent])
|
||||
app.mainbus.subscribe(remoteClientLifeCycleHandler, classOf[RemoteLifeCycleEvent])
|
||||
|
||||
// TODO actually register this provider in app in remote mode
|
||||
//provider.register(ActorRefProvider.RemoteProvider, new RemoteActorRefProvider)
|
||||
|
|
@ -81,7 +85,7 @@ class Remote(val app: AkkaApplication) {
|
|||
def start(): Unit = {
|
||||
val serverAddress = server.app.defaultAddress //Force init of server
|
||||
val daemonAddress = remoteDaemon.address //Force init of daemon
|
||||
eventHandler.info(this, "Starting remote server on [%s] and starting remoteDaemon with address [%s]".format(serverAddress, daemonAddress))
|
||||
log.info("Starting remote server on [{}] and starting remoteDaemon with address [{}]", serverAddress, daemonAddress)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,15 +99,14 @@ class Remote(val app: AkkaApplication) {
|
|||
class RemoteSystemDaemon(remote: Remote) extends Actor {
|
||||
|
||||
import remote._
|
||||
import remote.app._
|
||||
|
||||
override def preRestart(reason: Throwable, msg: Option[Any]) {
|
||||
eventHandler.debug(this, "RemoteSystemDaemon failed due to [%s] - restarting...".format(reason))
|
||||
log.debug("RemoteSystemDaemon failed due to [{}] - restarting...", reason)
|
||||
}
|
||||
|
||||
def receive: Actor.Receive = {
|
||||
case message: RemoteSystemDaemonMessageProtocol ⇒
|
||||
eventHandler.debug(this, "Received command [\n%s] to RemoteSystemDaemon on [%s]".format(message.getMessageType, nodename))
|
||||
log.debug("Received command [\n{}] to RemoteSystemDaemon on [{}]", message.getMessageType, nodename)
|
||||
|
||||
message.getMessageType match {
|
||||
case USE ⇒ handleUse(message)
|
||||
|
|
@ -121,7 +124,7 @@ class RemoteSystemDaemon(remote: Remote) extends Actor {
|
|||
//TODO: should we not deal with unrecognized message types?
|
||||
}
|
||||
|
||||
case unknown ⇒ eventHandler.warning(this, "Unknown message to RemoteSystemDaemon [%s]".format(unknown))
|
||||
case unknown ⇒ log.warning("Unknown message to RemoteSystemDaemon [{}]", unknown)
|
||||
}
|
||||
|
||||
def handleUse(message: RemoteSystemDaemonMessageProtocol) {
|
||||
|
|
@ -132,14 +135,14 @@ class RemoteSystemDaemon(remote: Remote) extends Actor {
|
|||
if (shouldCompressData) LZF.uncompress(message.getPayload.toByteArray) else message.getPayload.toByteArray
|
||||
|
||||
val actorFactory =
|
||||
serialization.deserialize(actorFactoryBytes, classOf[() ⇒ Actor], None) match {
|
||||
app.serialization.deserialize(actorFactoryBytes, classOf[() ⇒ Actor], None) match {
|
||||
case Left(error) ⇒ throw error
|
||||
case Right(instance) ⇒ instance.asInstanceOf[() ⇒ Actor]
|
||||
}
|
||||
|
||||
app.actorOf(Props(creator = actorFactory), message.getActorAddress)
|
||||
} else {
|
||||
eventHandler.error(this, "Actor 'address' for actor to instantiate is not defined, ignoring remote system daemon command [%s]".format(message))
|
||||
log.error("Actor 'address' for actor to instantiate is not defined, ignoring remote system daemon command [{}]", message)
|
||||
}
|
||||
|
||||
sender ! Success(app.defaultAddress)
|
||||
|
|
@ -213,7 +216,7 @@ class RemoteSystemDaemon(remote: Remote) extends Actor {
|
|||
}
|
||||
|
||||
private def payloadFor[T](message: RemoteSystemDaemonMessageProtocol, clazz: Class[T]): T = {
|
||||
serialization.deserialize(message.getPayload.toByteArray, clazz, None) match {
|
||||
app.serialization.deserialize(message.getPayload.toByteArray, clazz, None) match {
|
||||
case Left(error) ⇒ throw error
|
||||
case Right(instance) ⇒ instance.asInstanceOf[T]
|
||||
}
|
||||
|
|
@ -244,7 +247,7 @@ class RemoteMessage(input: RemoteMessageProtocol, remote: RemoteSupport, classLo
|
|||
.newInstance(exception.getMessage).asInstanceOf[Throwable]
|
||||
} catch {
|
||||
case problem: Exception ⇒
|
||||
remote.app.eventHandler.error(problem, remote, problem.getMessage)
|
||||
remote.app.mainbus.publish(Logging.Error(problem, remote, problem.getMessage))
|
||||
CannotInstantiateRemoteExceptionDueToRemoteProtocolParsingErrorException(problem, classname, exception.getMessage)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import akka.routing._
|
|||
import akka.dispatch._
|
||||
import akka.util.duration._
|
||||
import akka.config.ConfigurationException
|
||||
import akka.event.{ DeathWatch, EventHandler }
|
||||
import akka.event.{ DeathWatch, Logging }
|
||||
import akka.serialization.{ Serialization, Serializer, Compression }
|
||||
import akka.serialization.Compression.LZF
|
||||
import akka.remote.RemoteProtocol._
|
||||
|
|
@ -31,6 +31,8 @@ import java.util.concurrent.atomic.AtomicBoolean
|
|||
*/
|
||||
class RemoteActorRefProvider(val app: AkkaApplication) extends ActorRefProvider {
|
||||
|
||||
val log = Logging(app, this)
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import akka.dispatch.Promise
|
||||
|
||||
|
|
@ -171,7 +173,7 @@ class RemoteActorRefProvider(val app: AkkaApplication) extends ActorRefProvider
|
|||
local.actorFor(actor.address)
|
||||
} else {
|
||||
val remoteInetSocketAddress = new InetSocketAddress(actor.hostname, actor.port) //FIXME Drop the InetSocketAddresses and use RemoteAddress
|
||||
app.eventHandler.debug(this, "%s: Creating RemoteActorRef with address [%s] connected to [%s]".format(app.defaultAddress, actor.address, remoteInetSocketAddress))
|
||||
log.debug("{}: Creating RemoteActorRef with address [{}] connected to [{}]", app.defaultAddress, actor.address, remoteInetSocketAddress)
|
||||
Some(RemoteActorRef(remote.server, remoteInetSocketAddress, actor.address, None)) //Should it be None here
|
||||
}
|
||||
}
|
||||
|
|
@ -180,7 +182,7 @@ class RemoteActorRefProvider(val app: AkkaApplication) extends ActorRefProvider
|
|||
* Using (checking out) actor on a specific node.
|
||||
*/
|
||||
def useActorOnNode(remoteAddress: InetSocketAddress, actorAddress: String, actorFactory: () ⇒ Actor) {
|
||||
app.eventHandler.debug(this, "[%s] Instantiating Actor [%s] on node [%s]".format(app.defaultAddress, actorAddress, remoteAddress))
|
||||
log.debug("[{}] Instantiating Actor [{}] on node [{}]", app.defaultAddress, actorAddress, remoteAddress)
|
||||
|
||||
val actorFactoryBytes =
|
||||
app.serialization.serialize(actorFactory) match {
|
||||
|
|
@ -208,20 +210,20 @@ class RemoteActorRefProvider(val app: AkkaApplication) extends ActorRefProvider
|
|||
val f = connection ? (command, remote.remoteSystemDaemonAckTimeout)
|
||||
(try f.await.value catch { case _: FutureTimeoutException ⇒ None }) match {
|
||||
case Some(Right(receiver)) ⇒
|
||||
app.eventHandler.debug(this, "Remote system command sent to [%s] successfully received".format(receiver))
|
||||
log.debug("Remote system command sent to [{}] successfully received", receiver)
|
||||
|
||||
case Some(Left(cause)) ⇒
|
||||
app.eventHandler.error(cause, this, cause.toString)
|
||||
log.error(cause, cause.toString)
|
||||
throw cause
|
||||
|
||||
case None ⇒
|
||||
val error = new RemoteException("Remote system command to [%s] timed out".format(connection.address))
|
||||
app.eventHandler.error(error, this, error.toString)
|
||||
log.error(error, error.toString)
|
||||
throw error
|
||||
}
|
||||
} catch {
|
||||
case e: Exception ⇒
|
||||
app.eventHandler.error(e, this, "Could not send remote system command to [%s] due to: %s".format(connection.address, e.toString))
|
||||
log.error(e, "Could not send remote system command to [{}] due to: {}", connection.address, e.toString)
|
||||
throw e
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package akka.remote
|
|||
import akka.actor._
|
||||
import akka.routing._
|
||||
import akka.AkkaApplication
|
||||
import akka.event.Logging
|
||||
|
||||
import scala.collection.immutable.Map
|
||||
import scala.annotation.tailrec
|
||||
|
|
@ -25,6 +26,8 @@ class RemoteConnectionManager(
|
|||
initialConnections: Map[InetSocketAddress, ActorRef] = Map.empty[InetSocketAddress, ActorRef])
|
||||
extends ConnectionManager {
|
||||
|
||||
val log = Logging(app, this)
|
||||
|
||||
// FIXME is this VersionedIterable really needed? It is not used I think. Complicates API. See 'def connections' etc.
|
||||
case class State(version: Long, connections: Map[InetSocketAddress, ActorRef])
|
||||
extends VersionedIterable[ActorRef] {
|
||||
|
|
@ -62,7 +65,7 @@ class RemoteConnectionManager(
|
|||
|
||||
@tailrec
|
||||
final def failOver(from: InetSocketAddress, to: InetSocketAddress) {
|
||||
app.eventHandler.debug(this, "Failing over connection from [%s] to [%s]".format(from, to))
|
||||
log.debug("Failing over connection from [{}] to [{}]", from, to)
|
||||
|
||||
val oldState = state.get
|
||||
var changed = false
|
||||
|
|
@ -113,7 +116,7 @@ class RemoteConnectionManager(
|
|||
if (!state.compareAndSet(oldState, newState)) {
|
||||
remove(faultyConnection) // recur
|
||||
} else {
|
||||
app.eventHandler.debug(this, "Removing connection [%s]".format(faultyAddress))
|
||||
log.debug("Removing connection [{}]", faultyAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -140,7 +143,7 @@ class RemoteConnectionManager(
|
|||
putIfAbsent(address, newConnectionFactory) // recur
|
||||
} else {
|
||||
// we succeeded
|
||||
app.eventHandler.debug(this, "Adding connection [%s]".format(address))
|
||||
log.debug("Adding connection [{}]", address)
|
||||
newConnection // return new connection actor
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import java.util.concurrent._
|
|||
import java.util.concurrent.atomic._
|
||||
import akka.AkkaException
|
||||
import akka.AkkaApplication
|
||||
import akka.event.Logging
|
||||
|
||||
class RemoteClientMessageBufferException(message: String, cause: Throwable = null) extends AkkaException(message, cause) {
|
||||
def this(msg: String) = this(msg, null)
|
||||
|
|
@ -126,6 +127,8 @@ abstract class RemoteClient private[akka] (
|
|||
val module: NettyRemoteClientModule,
|
||||
val remoteAddress: InetSocketAddress) extends RemoteMarshallingOps {
|
||||
|
||||
val log = Logging(app, this)
|
||||
|
||||
val name = simpleName(this) + "@" +
|
||||
remoteAddress.getAddress.getHostAddress + "::" +
|
||||
remoteAddress.getPort
|
||||
|
|
@ -134,7 +137,7 @@ abstract class RemoteClient private[akka] (
|
|||
|
||||
private[remote] def isRunning = runSwitch.isOn
|
||||
|
||||
protected def notifyListeners(msg: ⇒ Any): Unit
|
||||
protected def notifyListeners(msg: RemoteLifeCycleEvent): Unit
|
||||
|
||||
protected def currentChannel: Channel
|
||||
|
||||
|
|
@ -154,9 +157,8 @@ abstract class RemoteClient private[akka] (
|
|||
*/
|
||||
def send(request: RemoteMessageProtocol) {
|
||||
if (isRunning) { //TODO FIXME RACY
|
||||
app.eventHandler.debug(this, "Sending message: " + new RemoteMessage(request, remoteSupport))
|
||||
log.debug("Sending message: " + new RemoteMessage(request, remoteSupport))
|
||||
|
||||
// tell
|
||||
try {
|
||||
val payload = createMessageSendEnvelope(request);
|
||||
currentChannel.write(payload).addListener(
|
||||
|
|
@ -188,21 +190,21 @@ class PassiveRemoteClient(_app: AkkaApplication,
|
|||
module: NettyRemoteClientModule,
|
||||
remoteAddress: InetSocketAddress,
|
||||
val loader: Option[ClassLoader] = None,
|
||||
notifyListenersFun: (⇒ Any) ⇒ Unit)
|
||||
notifyListenersFun: RemoteLifeCycleEvent ⇒ Unit)
|
||||
extends RemoteClient(_app, remoteSupport, module, remoteAddress) {
|
||||
|
||||
def notifyListeners(msg: ⇒ Any): Unit = notifyListenersFun(msg)
|
||||
override def notifyListeners(msg: RemoteLifeCycleEvent) { app.mainbus.publish(msg) }
|
||||
|
||||
def connect(reconnectIfAlreadyConnected: Boolean = false): Boolean = runSwitch switchOn {
|
||||
notifyListeners(RemoteClientStarted(module, remoteAddress))
|
||||
app.eventHandler.debug(this, "Starting remote client connection to [%s]".format(remoteAddress))
|
||||
log.debug("Starting remote client connection to [{}]", remoteAddress)
|
||||
}
|
||||
|
||||
def shutdown() = runSwitch switchOff {
|
||||
app.eventHandler.debug(this, "Shutting down remote client [%s]".format(name))
|
||||
log.debug("Shutting down remote client [{}]", name)
|
||||
|
||||
notifyListeners(RemoteClientShutdown(module, remoteAddress))
|
||||
app.eventHandler.debug(this, "[%s] has been shut down".format(name))
|
||||
log.debug("[{}] has been shut down", name)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -213,12 +215,12 @@ class PassiveRemoteClient(_app: AkkaApplication,
|
|||
*/
|
||||
class ActiveRemoteClient private[akka] (
|
||||
_app: AkkaApplication,
|
||||
remoteSupport: RemoteSupport,
|
||||
module: NettyRemoteClientModule,
|
||||
remoteAddress: InetSocketAddress,
|
||||
_remoteSupport: RemoteSupport,
|
||||
_module: NettyRemoteClientModule,
|
||||
_remoteAddress: InetSocketAddress,
|
||||
val loader: Option[ClassLoader] = None,
|
||||
notifyListenersFun: (⇒ Any) ⇒ Unit)
|
||||
extends RemoteClient(_app, remoteSupport, module, remoteAddress) {
|
||||
notifyListenersFun: (RemoteLifeCycleEvent) ⇒ Unit)
|
||||
extends RemoteClient(_app, _remoteSupport, _module, _remoteAddress) {
|
||||
|
||||
val settings = new RemoteClientSettings(app)
|
||||
import settings._
|
||||
|
|
@ -235,7 +237,7 @@ class ActiveRemoteClient private[akka] (
|
|||
@volatile
|
||||
private var reconnectionTimeWindowStart = 0L
|
||||
|
||||
def notifyListeners(msg: ⇒ Any): Unit = notifyListenersFun(msg)
|
||||
def notifyListeners(msg: RemoteLifeCycleEvent): Unit = notifyListenersFun(msg)
|
||||
|
||||
def currentChannel = connection.getChannel
|
||||
|
||||
|
|
@ -258,7 +260,7 @@ class ActiveRemoteClient private[akka] (
|
|||
}
|
||||
|
||||
def attemptReconnect(): Boolean = {
|
||||
app.eventHandler.debug(this, "Remote client reconnecting to [%s]".format(remoteAddress))
|
||||
log.debug("Remote client reconnecting to [{}]", remoteAddress)
|
||||
|
||||
val connection = bootstrap.connect(remoteAddress)
|
||||
openChannels.add(connection.awaitUninterruptibly.getChannel) // Wait until the connection attempt succeeds or fails.
|
||||
|
|
@ -281,7 +283,7 @@ class ActiveRemoteClient private[akka] (
|
|||
bootstrap.setOption("tcpNoDelay", true)
|
||||
bootstrap.setOption("keepAlive", true)
|
||||
|
||||
app.eventHandler.debug(this, "Starting remote client connection to [%s]".format(remoteAddress))
|
||||
log.debug("Starting remote client connection to [{}]", remoteAddress)
|
||||
|
||||
connection = bootstrap.connect(remoteAddress)
|
||||
|
||||
|
|
@ -301,7 +303,7 @@ class ActiveRemoteClient private[akka] (
|
|||
case false if reconnectIfAlreadyConnected ⇒
|
||||
closeChannel(connection)
|
||||
|
||||
app.eventHandler.debug(this, "Remote client reconnecting to [%s]".format(remoteAddress))
|
||||
log.debug("Remote client reconnecting to [{}]", remoteAddress)
|
||||
attemptReconnect()
|
||||
|
||||
case false ⇒ false
|
||||
|
|
@ -310,7 +312,7 @@ class ActiveRemoteClient private[akka] (
|
|||
|
||||
// Please note that this method does _not_ remove the ARC from the NettyRemoteClientModule's map of clients
|
||||
def shutdown() = runSwitch switchOff {
|
||||
app.eventHandler.debug(this, "Shutting down remote client [%s]".format(name))
|
||||
log.debug("Shutting down remote client [{}]", name)
|
||||
|
||||
notifyListeners(RemoteClientShutdown(module, remoteAddress))
|
||||
timer.stop()
|
||||
|
|
@ -321,7 +323,7 @@ class ActiveRemoteClient private[akka] (
|
|||
bootstrap = null
|
||||
connection = null
|
||||
|
||||
app.eventHandler.debug(this, "[%s] has been shut down".format(name))
|
||||
log.debug("[{}] has been shut down", name)
|
||||
}
|
||||
|
||||
private[akka] def isWithinReconnectionTimeWindow: Boolean = {
|
||||
|
|
@ -331,7 +333,7 @@ class ActiveRemoteClient private[akka] (
|
|||
} else {
|
||||
val timeLeft = (RECONNECTION_TIME_WINDOW - (System.currentTimeMillis - reconnectionTimeWindowStart)) > 0
|
||||
if (timeLeft)
|
||||
app.eventHandler.debug(this, "Will try to reconnect to remote server for another [%s] milliseconds".format(timeLeft))
|
||||
log.info("Will try to reconnect to remote server for another [{}] milliseconds", timeLeft)
|
||||
|
||||
timeLeft
|
||||
}
|
||||
|
|
@ -459,6 +461,8 @@ class NettyRemoteSupport(_app: AkkaApplication) extends RemoteSupport(_app) with
|
|||
|
||||
class NettyRemoteServer(val app: AkkaApplication, serverModule: NettyRemoteServerModule, val loader: Option[ClassLoader]) extends RemoteMarshallingOps {
|
||||
|
||||
val log = Logging(app, this)
|
||||
|
||||
val settings = new RemoteServerSettings(app)
|
||||
import settings._
|
||||
|
||||
|
|
@ -504,6 +508,8 @@ class NettyRemoteServer(val app: AkkaApplication, serverModule: NettyRemoteServe
|
|||
trait NettyRemoteServerModule extends RemoteServerModule {
|
||||
self: RemoteSupport ⇒
|
||||
|
||||
val log = Logging(app, this)
|
||||
|
||||
def app: AkkaApplication
|
||||
def remoteSupport = self
|
||||
|
||||
|
|
@ -595,6 +601,8 @@ class RemoteServerHandler(
|
|||
val applicationLoader: Option[ClassLoader],
|
||||
val server: NettyRemoteServerModule) extends SimpleChannelUpstreamHandler with RemoteMarshallingOps {
|
||||
|
||||
val log = Logging(app, this)
|
||||
|
||||
import settings._
|
||||
|
||||
implicit def app = server.app
|
||||
|
|
|
|||
|
|
@ -6,16 +6,15 @@ package akka.event.slf4j
|
|||
|
||||
import org.slf4j.{ Logger ⇒ SLFLogger, LoggerFactory ⇒ SLFLoggerFactory }
|
||||
|
||||
import akka.event.EventHandler
|
||||
import akka.event.Logging._
|
||||
import akka.actor._
|
||||
import Actor._
|
||||
|
||||
/**
|
||||
* Base trait for all classes that wants to be able use the SLF4J logging infrastructure.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
trait Logging {
|
||||
trait SLF4JLogging {
|
||||
@transient
|
||||
lazy val log = Logger(this.getClass.getName)
|
||||
}
|
||||
|
|
@ -31,8 +30,7 @@ object Logger {
|
|||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class Slf4jEventHandler extends Actor with Logging {
|
||||
import EventHandler._
|
||||
class Slf4jEventHandler extends Actor with SLF4JLogging {
|
||||
|
||||
def receive = {
|
||||
case event @ Error(cause, instance, message) ⇒
|
||||
|
|
@ -51,10 +49,11 @@ class Slf4jEventHandler extends Actor with Logging {
|
|||
logger(instance).debug("[{}] [{}]",
|
||||
event.thread.getName, message.asInstanceOf[AnyRef])
|
||||
|
||||
case event ⇒ log.debug("[{}]", event.toString)
|
||||
case InitializeLogger(_) ⇒ log.info("Slf4jEventHandler started")
|
||||
}
|
||||
|
||||
def logger(instance: AnyRef): SLFLogger = instance match {
|
||||
// TODO make sure that this makes sense (i.e. should be the full path after Peter’s changes)
|
||||
case a: ActorRef ⇒ Logger(a.address)
|
||||
case _ ⇒ Logger(instance.getClass)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import akka.actor.Props;
|
|||
import akka.actor.UntypedActor;
|
||||
import akka.actor.UntypedActorFactory;
|
||||
import akka.dispatch.Future;
|
||||
import akka.event.EventHandler;
|
||||
import akka.testkit.EventFilter;
|
||||
import akka.testkit.ErrorFilter;
|
||||
import akka.testkit.TestEvent;
|
||||
|
|
@ -72,7 +71,7 @@ public class UntypedCoordinatedIncrementTest {
|
|||
EventFilter expectedFailureFilter = (EventFilter) new ErrorFilter(ExpectedFailureException.class);
|
||||
EventFilter coordinatedFilter = (EventFilter) new ErrorFilter(CoordinatedTransactionException.class);
|
||||
Seq<EventFilter> ignoreExceptions = seq(expectedFailureFilter, coordinatedFilter);
|
||||
application.eventHandler().notify(new TestEvent.Mute(ignoreExceptions));
|
||||
application.mainbus().publish(new TestEvent.Mute(ignoreExceptions));
|
||||
CountDownLatch incrementLatch = new CountDownLatch(numCounters);
|
||||
List<ActorRef> actors = new ArrayList<ActorRef>(counters);
|
||||
actors.add(failer);
|
||||
|
|
@ -85,7 +84,7 @@ public class UntypedCoordinatedIncrementTest {
|
|||
Future future = counter.ask("GetCount", askTimeout);
|
||||
assertEquals(0, ((Integer)future.get()).intValue());
|
||||
}
|
||||
application.eventHandler().notify(new TestEvent.UnMute(ignoreExceptions));
|
||||
application.mainbus().publish(new TestEvent.UnMute(ignoreExceptions));
|
||||
}
|
||||
|
||||
public <A> Seq<A> seq(A... args) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import akka.actor.Props;
|
|||
import akka.actor.UntypedActor;
|
||||
import akka.actor.UntypedActorFactory;
|
||||
import akka.dispatch.Future;
|
||||
import akka.event.EventHandler;
|
||||
import akka.testkit.EventFilter;
|
||||
import akka.testkit.ErrorFilter;
|
||||
import akka.testkit.TestEvent;
|
||||
|
|
@ -76,7 +75,7 @@ public class UntypedTransactorTest {
|
|||
EventFilter expectedFailureFilter = (EventFilter) new ErrorFilter(ExpectedFailureException.class);
|
||||
EventFilter coordinatedFilter = (EventFilter) new ErrorFilter(CoordinatedTransactionException.class);
|
||||
Seq<EventFilter> ignoreExceptions = seq(expectedFailureFilter, coordinatedFilter);
|
||||
application.eventHandler().notify(new TestEvent.Mute(ignoreExceptions));
|
||||
application.mainbus().publish(new TestEvent.Mute(ignoreExceptions));
|
||||
CountDownLatch incrementLatch = new CountDownLatch(numCounters);
|
||||
List<ActorRef> actors = new ArrayList<ActorRef>(counters);
|
||||
actors.add(failer);
|
||||
|
|
@ -97,7 +96,7 @@ public class UntypedTransactorTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
application.eventHandler().notify(new TestEvent.UnMute(ignoreExceptions));
|
||||
application.mainbus().publish(new TestEvent.UnMute(ignoreExceptions));
|
||||
}
|
||||
|
||||
public <A> Seq<A> seq(A... args) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import akka.transactor.Coordinated
|
|||
import akka.actor._
|
||||
import akka.stm.{ Ref, TransactionFactory }
|
||||
import akka.util.duration._
|
||||
import akka.event.EventHandler
|
||||
import akka.transactor.CoordinatedTransactionException
|
||||
import akka.testkit._
|
||||
|
||||
|
|
@ -83,20 +82,20 @@ class CoordinatedIncrementSpec extends AkkaSpec with BeforeAndAfterAll {
|
|||
|
||||
"increment no counters with a failing transaction" in {
|
||||
val ignoreExceptions = Seq(
|
||||
EventFilter[ExpectedFailureException],
|
||||
EventFilter[CoordinatedTransactionException],
|
||||
EventFilter[ActorTimeoutException])
|
||||
app.eventHandler.notify(TestEvent.Mute(ignoreExceptions))
|
||||
val (counters, failer) = actorOfs
|
||||
val coordinated = Coordinated()
|
||||
counters(0) ! Coordinated(Increment(counters.tail :+ failer))
|
||||
coordinated.await
|
||||
for (counter ← counters) {
|
||||
(counter ? GetCount).as[Int].get must be === 0
|
||||
EventFilter[ExpectedFailureException](),
|
||||
EventFilter[CoordinatedTransactionException](),
|
||||
EventFilter[ActorTimeoutException]())
|
||||
filterEvents(ignoreExceptions) {
|
||||
val (counters, failer) = actorOfs
|
||||
val coordinated = Coordinated()
|
||||
counters(0) ! Coordinated(Increment(counters.tail :+ failer))
|
||||
coordinated.await
|
||||
for (counter ← counters) {
|
||||
(counter ? GetCount).as[Int].get must be === 0
|
||||
}
|
||||
counters foreach (_.stop())
|
||||
failer.stop()
|
||||
}
|
||||
counters foreach (_.stop())
|
||||
failer.stop()
|
||||
app.eventHandler.notify(TestEvent.UnMute(ignoreExceptions))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import akka.transactor.Coordinated
|
|||
import akka.actor._
|
||||
import akka.stm._
|
||||
import akka.util.duration._
|
||||
import akka.event.EventHandler
|
||||
import akka.transactor.CoordinatedTransactionException
|
||||
import akka.testkit._
|
||||
|
||||
|
|
@ -116,21 +115,21 @@ class FickleFriendsSpec extends AkkaSpec with BeforeAndAfterAll {
|
|||
"Coordinated fickle friends" should {
|
||||
"eventually succeed to increment all counters by one" in {
|
||||
val ignoreExceptions = Seq(
|
||||
EventFilter[ExpectedFailureException],
|
||||
EventFilter[CoordinatedTransactionException],
|
||||
EventFilter[ActorTimeoutException])
|
||||
app.eventHandler.notify(TestEvent.Mute(ignoreExceptions))
|
||||
val (counters, coordinator) = actorOfs
|
||||
val latch = new CountDownLatch(1)
|
||||
coordinator ! FriendlyIncrement(counters, latch)
|
||||
latch.await // this could take a while
|
||||
(coordinator ? GetCount).as[Int].get must be === 1
|
||||
for (counter ← counters) {
|
||||
(counter ? GetCount).as[Int].get must be === 1
|
||||
EventFilter[ExpectedFailureException](),
|
||||
EventFilter[CoordinatedTransactionException](),
|
||||
EventFilter[ActorTimeoutException]())
|
||||
filterEvents(ignoreExceptions) {
|
||||
val (counters, coordinator) = actorOfs
|
||||
val latch = new CountDownLatch(1)
|
||||
coordinator ! FriendlyIncrement(counters, latch)
|
||||
latch.await // this could take a while
|
||||
(coordinator ? GetCount).as[Int].get must be === 1
|
||||
for (counter ← counters) {
|
||||
(counter ? GetCount).as[Int].get must be === 1
|
||||
}
|
||||
counters foreach (_.stop())
|
||||
coordinator.stop()
|
||||
}
|
||||
counters foreach (_.stop())
|
||||
coordinator.stop()
|
||||
app.eventHandler.notify(TestEvent.UnMute(ignoreExceptions))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import akka.transactor.Transactor
|
|||
import akka.actor._
|
||||
import akka.stm._
|
||||
import akka.util.duration._
|
||||
import akka.event.EventHandler
|
||||
import akka.transactor.CoordinatedTransactionException
|
||||
import akka.testkit._
|
||||
|
||||
|
|
@ -106,20 +105,20 @@ class TransactorSpec extends AkkaSpec {
|
|||
|
||||
"increment no counters with a failing transaction" in {
|
||||
val ignoreExceptions = Seq(
|
||||
EventFilter[ExpectedFailureException],
|
||||
EventFilter[CoordinatedTransactionException],
|
||||
EventFilter[ActorTimeoutException])
|
||||
app.eventHandler.notify(TestEvent.Mute(ignoreExceptions))
|
||||
val (counters, failer) = createTransactors
|
||||
val failLatch = TestLatch(numCounters)
|
||||
counters(0) ! Increment(counters.tail :+ failer, failLatch)
|
||||
failLatch.await
|
||||
for (counter ← counters) {
|
||||
(counter ? GetCount).as[Int].get must be === 0
|
||||
EventFilter[ExpectedFailureException](),
|
||||
EventFilter[CoordinatedTransactionException](),
|
||||
EventFilter[ActorTimeoutException]())
|
||||
filterEvents(ignoreExceptions) {
|
||||
val (counters, failer) = createTransactors
|
||||
val failLatch = TestLatch(numCounters)
|
||||
counters(0) ! Increment(counters.tail :+ failer, failLatch)
|
||||
failLatch.await
|
||||
for (counter ← counters) {
|
||||
(counter ? GetCount).as[Int].get must be === 0
|
||||
}
|
||||
counters foreach (_.stop())
|
||||
failer.stop()
|
||||
}
|
||||
counters foreach (_.stop())
|
||||
failer.stop()
|
||||
app.eventHandler.notify(TestEvent.UnMute(ignoreExceptions))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
package akka.testkit
|
||||
|
||||
import akka.event.EventHandler
|
||||
import akka.event.Logging.{ Warning, Error }
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import java.util.LinkedList
|
||||
import java.util.concurrent.RejectedExecutionException
|
||||
|
|
@ -104,7 +104,7 @@ private[testkit] object CallingThreadDispatcher {
|
|||
* @author Roland Kuhn
|
||||
* @since 1.1
|
||||
*/
|
||||
class CallingThreadDispatcher(_app: AkkaApplication, val name: String = "calling-thread", val warnings: Boolean = true) extends MessageDispatcher(_app) {
|
||||
class CallingThreadDispatcher(_app: AkkaApplication, val name: String = "calling-thread") extends MessageDispatcher(_app) {
|
||||
import CallingThreadDispatcher._
|
||||
|
||||
protected[akka] override def createMailbox(actor: ActorCell) = new CallingThreadMailbox(this, actor)
|
||||
|
|
@ -211,12 +211,12 @@ class CallingThreadDispatcher(_app: AkkaApplication, val name: String = "calling
|
|||
true
|
||||
} catch {
|
||||
case ie: InterruptedException ⇒
|
||||
app.eventHandler.error(this, ie)
|
||||
app.mainbus.publish(Error(this, ie))
|
||||
Thread.currentThread().interrupt()
|
||||
intex = ie
|
||||
true
|
||||
case e ⇒
|
||||
app.eventHandler.error(this, e)
|
||||
app.mainbus.publish(Error(this, e))
|
||||
queue.leave
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ package akka.testkit
|
|||
|
||||
import akka.actor._
|
||||
import akka.util.ReflectiveAccess
|
||||
import akka.event.EventHandler
|
||||
import com.eaio.uuid.UUID
|
||||
import akka.actor.Props._
|
||||
import akka.AkkaApplication
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package akka.testkit
|
|||
|
||||
import akka.util.Duration
|
||||
import java.util.concurrent.{ CyclicBarrier, TimeUnit, TimeoutException }
|
||||
import akka.AkkaApplication
|
||||
|
||||
class TestBarrierTimeoutException(message: String) extends RuntimeException(message)
|
||||
|
||||
|
|
@ -24,14 +25,15 @@ object TestBarrier {
|
|||
class TestBarrier(count: Int) {
|
||||
private val barrier = new CyclicBarrier(count)
|
||||
|
||||
def await(): Unit = await(TestBarrier.DefaultTimeout)
|
||||
def await()(implicit app: AkkaApplication): Unit = await(TestBarrier.DefaultTimeout)
|
||||
|
||||
def await(timeout: Duration) {
|
||||
def await(timeout: Duration)(implicit app: AkkaApplication) {
|
||||
try {
|
||||
barrier.await(Testing.testTime(timeout.toNanos), TimeUnit.NANOSECONDS)
|
||||
barrier.await(timeout.dilated.toNanos, TimeUnit.NANOSECONDS)
|
||||
} catch {
|
||||
case e: TimeoutException ⇒
|
||||
throw new TestBarrierTimeoutException("Timeout of %s and time factor of %s" format (timeout.toString, Duration.timeFactor))
|
||||
throw new TestBarrierTimeoutException("Timeout of %s and time factor of %s"
|
||||
format (timeout.toString, app.AkkaConfig.TestTimeFactor))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,36 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.testkit
|
||||
|
||||
import akka.event.EventHandler
|
||||
import akka.event.EventHandler.{ Event, Error }
|
||||
import akka.actor.Actor
|
||||
import scala.util.matching.Regex
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.event.Logging._
|
||||
import akka.event.Logging
|
||||
import akka.util.Duration
|
||||
import akka.AkkaApplication
|
||||
|
||||
/**
|
||||
* Implementation helpers of the EventFilter facilities: send `Mute`
|
||||
* to the TestEventListener to install a filter, and `UnMute` to
|
||||
* deinstall it.
|
||||
*
|
||||
* You should always prefer the filter methods in the package object
|
||||
* (see [[akka.testkit]] `filterEvents` and `filterException`) or on the
|
||||
* EventFilter implementations.
|
||||
*/
|
||||
sealed trait TestEvent
|
||||
|
||||
/**
|
||||
* Implementation helpers of the EventFilter facilities: send <code>Mute</code>
|
||||
* to the TestEventFilter to install a filter, and <code>UnMute</code> to
|
||||
* deinstall it.
|
||||
*
|
||||
* You should always prefer the filter methods in the package object
|
||||
* (see [[akka.testkit]] `filterEvents` and `filterException`) or on the
|
||||
* EventFilter implementations.
|
||||
*/
|
||||
object TestEvent {
|
||||
object Mute {
|
||||
def apply(filter: EventFilter, filters: EventFilter*): Mute = new Mute(filter +: filters.toSeq)
|
||||
|
|
@ -15,84 +40,416 @@ object TestEvent {
|
|||
def apply(filter: EventFilter, filters: EventFilter*): UnMute = new UnMute(filter +: filters.toSeq)
|
||||
}
|
||||
case class UnMute(filters: Seq[EventFilter]) extends TestEvent
|
||||
case object UnMuteAll extends TestEvent
|
||||
}
|
||||
|
||||
trait EventFilter {
|
||||
def apply(event: Event): Boolean
|
||||
/**
|
||||
* Facilities for selectively filtering out expected events from logging so
|
||||
* that you can keep your test run’s console output clean and do not miss real
|
||||
* error messages.
|
||||
*
|
||||
* See the companion object for convenient factory methods.
|
||||
*
|
||||
* If the `occurrences` is set to Int.MaxValue, no tracking is done.
|
||||
*/
|
||||
abstract class EventFilter(occurrences: Int) {
|
||||
|
||||
@volatile // JMM does not guarantee visibility for non-final fields
|
||||
private var todo = occurrences
|
||||
|
||||
/**
|
||||
* This method decides whether to filter the event (<code>true</code>) or not
|
||||
* (<code>false</code>).
|
||||
*/
|
||||
protected def matches(event: LogEvent): Boolean
|
||||
|
||||
final def apply(event: LogEvent): Boolean = {
|
||||
if (matches(event)) {
|
||||
if (todo != Int.MaxValue) todo -= 1
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
def awaitDone(max: Duration): Boolean = {
|
||||
if (todo != Int.MaxValue && todo > 0) TestKit.awaitCond(todo == 0, max, noThrow = true)
|
||||
todo == Int.MaxValue || todo == 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this filter while executing the given code block. Care is taken to
|
||||
* remove the filter when the block is finished or aborted.
|
||||
*/
|
||||
def intercept[T](code: ⇒ T)(implicit app: AkkaApplication): T = {
|
||||
app.mainbus publish TestEvent.Mute(this)
|
||||
try {
|
||||
val result = code
|
||||
if (!awaitDone(app.AkkaConfig.TestEventFilterLeeway))
|
||||
if (todo > 0)
|
||||
throw new AssertionError("Timeout waiting for " + todo + " messages on " + this)
|
||||
else
|
||||
throw new AssertionError("Received " + (-todo) + " messages too many on " + this)
|
||||
result
|
||||
} finally app.mainbus publish TestEvent.UnMute(this)
|
||||
}
|
||||
|
||||
/*
|
||||
* these default values are just there for easier subclassing
|
||||
*/
|
||||
protected val source: Option[AnyRef] = None
|
||||
protected val message: Either[String, Regex] = Left("")
|
||||
protected val complete: Boolean = false
|
||||
/**
|
||||
* internal implementation helper, no guaranteed API
|
||||
*/
|
||||
protected def doMatch(src: AnyRef, msg: Any) = {
|
||||
val msgstr = if (msg != null) msg.toString else "null"
|
||||
(source.isDefined && sourceMatch(src) || source.isEmpty) &&
|
||||
(message match {
|
||||
case Left(s) ⇒ if (complete) msgstr == s else msgstr.startsWith(s)
|
||||
case Right(p) ⇒ p.findFirstIn(msgstr).isDefined
|
||||
})
|
||||
}
|
||||
private def sourceMatch(src: AnyRef) = {
|
||||
source.get match {
|
||||
case c: Class[_] ⇒ c isInstance src
|
||||
case s ⇒ src == s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Facilities for selectively filtering out expected events from logging so
|
||||
* that you can keep your test run’s console output clean and do not miss real
|
||||
* error messages.
|
||||
*
|
||||
* '''Also have a look at the [[akka.testkit]] package object’s `filterEvents` and
|
||||
* `filterException` methods.'''
|
||||
*
|
||||
* The source filters do accept `Class[_]` arguments, matching any
|
||||
* object which is an instance of the given class, e.g.
|
||||
*
|
||||
* {{{
|
||||
* EventFilter.info(source = classOf[MyActor]) // will match Info events from any MyActor instance
|
||||
* }}}
|
||||
*
|
||||
* The message object will be converted to a string before matching (`"null"` if it is `null`).
|
||||
*/
|
||||
object EventFilter {
|
||||
|
||||
def apply[A <: Throwable: Manifest](): EventFilter =
|
||||
ErrorFilter(manifest[A].erasure)
|
||||
/**
|
||||
* Create a filter for Error events. Give up to one of <code>start</code> and <code>pattern</code>:
|
||||
*
|
||||
* {{{
|
||||
* EventFilter[MyException]() // filter only on exception type
|
||||
* EventFilter[MyException]("message") // filter on exactly matching message
|
||||
* EventFilter[MyException](source = obj) // filter on event source
|
||||
* EventFilter[MyException](start = "Expected") // filter on start of message
|
||||
* EventFilter[MyException](source = obj, pattern = "weird.*message") // filter on pattern and message
|
||||
* }}}
|
||||
*
|
||||
* ''Please note that filtering on the `source` being
|
||||
* `null` does NOT work (passing `null` disables the
|
||||
* source filter).''
|
||||
*/
|
||||
def apply[A <: Throwable: Manifest](message: String = null, source: AnyRef = null, start: String = "", pattern: String = null, occurrences: Int = Int.MaxValue): EventFilter =
|
||||
ErrorFilter(manifest[A].erasure, Option(source),
|
||||
if (message ne null) Left(message) else Option(pattern) map (new Regex(_)) toRight start,
|
||||
message ne null)(occurrences)
|
||||
|
||||
def apply[A <: Throwable: Manifest](message: String): EventFilter =
|
||||
ErrorMessageFilter(manifest[A].erasure, message)
|
||||
/**
|
||||
* Create a filter for Warning events. Give up to one of <code>start</code> and <code>pattern</code>:
|
||||
*
|
||||
* {{{
|
||||
* EventFilter.warning() // filter only on exception type
|
||||
* EventFilter.warning(source = obj) // filter on event source
|
||||
* EventFilter.warning(start = "Expected") // filter on start of message
|
||||
* EventFilter.warning(source = obj, pattern = "weird.*message") // filter on pattern and message
|
||||
* }}}
|
||||
*
|
||||
* ''Please note that filtering on the `source` being
|
||||
* `null` does NOT work (passing `null` disables the
|
||||
* source filter).''
|
||||
*/
|
||||
def warning(message: String = null, source: AnyRef = null, start: String = "", pattern: String = null, occurrences: Int = Int.MaxValue): EventFilter =
|
||||
WarningFilter(Option(source),
|
||||
if (message ne null) Left(message) else Option(pattern) map (new Regex(_)) toRight start,
|
||||
message ne null)(occurrences)
|
||||
|
||||
def apply[A <: Throwable: Manifest](source: AnyRef): EventFilter =
|
||||
ErrorSourceFilter(manifest[A].erasure, source)
|
||||
/**
|
||||
* Create a filter for Info events. Give up to one of <code>start</code> and <code>pattern</code>:
|
||||
*
|
||||
* {{{
|
||||
* EventFilter.info() // filter only on exception type
|
||||
* EventFilter.info(source = obj) // filter on event source
|
||||
* EventFilter.info(start = "Expected") // filter on start of message
|
||||
* EventFilter.info(source = obj, pattern = "weird.*message") // filter on pattern and message
|
||||
* }}}
|
||||
*
|
||||
* ''Please note that filtering on the `source` being
|
||||
* `null` does NOT work (passing `null` disables the
|
||||
* source filter).''
|
||||
*/
|
||||
def info(message: String = null, source: AnyRef = null, start: String = "", pattern: String = null, occurrences: Int = Int.MaxValue): EventFilter =
|
||||
InfoFilter(Option(source),
|
||||
if (message ne null) Left(message) else Option(pattern) map (new Regex(_)) toRight start,
|
||||
message ne null)(occurrences)
|
||||
|
||||
def apply[A <: Throwable: Manifest](source: AnyRef, message: String): EventFilter =
|
||||
ErrorSourceMessageFilter(manifest[A].erasure, source, message)
|
||||
/**
|
||||
* Create a filter for Debug events. Give up to one of <code>start</code> and <code>pattern</code>:
|
||||
*
|
||||
* {{{
|
||||
* EventFilter.debug() // filter only on exception type
|
||||
* EventFilter.debug(source = obj) // filter on event source
|
||||
* EventFilter.debug(start = "Expected") // filter on start of message
|
||||
* EventFilter.debug(source = obj, pattern = "weird.*message") // filter on pattern and message
|
||||
* }}}
|
||||
*
|
||||
* ''Please note that filtering on the `source` being
|
||||
* `null` does NOT work (passing `null` disables the
|
||||
* source filter).''
|
||||
*/
|
||||
def debug(message: String = null, source: AnyRef = null, start: String = "", pattern: String = null, occurrences: Int = Int.MaxValue): EventFilter =
|
||||
DebugFilter(Option(source),
|
||||
if (message ne null) Left(message) else Option(pattern) map (new Regex(_)) toRight start,
|
||||
message ne null)(occurrences)
|
||||
|
||||
def custom(test: (Event) ⇒ Boolean): EventFilter =
|
||||
CustomEventFilter(test)
|
||||
/**
|
||||
* Create a custom event filter. The filter will affect those events for
|
||||
* which the supplied partial function is defined and returns
|
||||
* `true`.
|
||||
*
|
||||
* {{{
|
||||
* EventFilter.custom {
|
||||
* case Warning(ref, "my warning") if ref == actor || ref == null => true
|
||||
* }
|
||||
* }}}
|
||||
*/
|
||||
def custom(test: PartialFunction[LogEvent, Boolean], occurrences: Int = Int.MaxValue): EventFilter =
|
||||
CustomEventFilter(test)(occurrences)
|
||||
}
|
||||
|
||||
case class ErrorFilter(throwable: Class[_]) extends EventFilter {
|
||||
def apply(event: Event) = event match {
|
||||
case Error(cause, _, _) ⇒ throwable isInstance cause
|
||||
case _ ⇒ false
|
||||
/**
|
||||
* Filter which matches Error events, if they satisfy the given criteria:
|
||||
* <ul>
|
||||
* <li><code>throwable</code> applies an upper bound on the type of exception contained in the Error event</li>
|
||||
* <li><code>source</code>, if given, applies a filter on the event’s origin</li>
|
||||
* <li><code>message</code> applies a filter on the event’s message (either
|
||||
* with String.startsWith or Regex.findFirstIn().isDefined); if the message
|
||||
* itself does not match, the match is retried with the contained Exception’s
|
||||
* message; if both are <code>null</code>, the filter always matches if at
|
||||
* the same time the Exception’s stack trace is empty (this catches
|
||||
* JVM-omitted “fast-throw” exceptions)</li>
|
||||
* </ul>
|
||||
* If you want to match all Error events, the most efficient is to use <code>Left("")</code>.
|
||||
*/
|
||||
case class ErrorFilter(
|
||||
throwable: Class[_],
|
||||
override val source: Option[AnyRef],
|
||||
override val message: Either[String, Regex],
|
||||
override val complete: Boolean)(occurrences: Int) extends EventFilter(occurrences) {
|
||||
|
||||
def matches(event: LogEvent) = {
|
||||
event match {
|
||||
case Error(cause, src, msg) if throwable isInstance cause ⇒
|
||||
(msg == null && cause.getMessage == null && cause.getStackTrace.length == 0) ||
|
||||
doMatch(src, msg) || doMatch(src, cause.getMessage)
|
||||
case _ ⇒ false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Java API
|
||||
*
|
||||
* @param source
|
||||
* apply this filter only to events from the given source; do not filter on source if this is given as <code>null</code>
|
||||
* @param message
|
||||
* apply this filter only to events whose message matches; do not filter on message if this is given as <code>null</code>
|
||||
* @param pattern
|
||||
* if <code>false</code>, the message string must start with the given
|
||||
* string, otherwise the <code>message</code> argument is treated as
|
||||
* regular expression which is matched against the message (may match only
|
||||
* a substring to filter)
|
||||
* @param complete
|
||||
* whether the event’s message must match the given message string or pattern completely
|
||||
*/
|
||||
def this(throwable: Class[_], source: AnyRef, message: String, pattern: Boolean, complete: Boolean, occurrences: Int) =
|
||||
this(throwable, Option(source),
|
||||
if (message eq null) Left("")
|
||||
else if (pattern) Right(new Regex(message))
|
||||
else Left(message),
|
||||
complete)(occurrences)
|
||||
|
||||
/**
|
||||
* Java API: filter only on the given type of exception
|
||||
*/
|
||||
def this(throwable: Class[_]) = this(throwable, null, null, false, false, Int.MaxValue)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter which matches Warning events, if they satisfy the given criteria:
|
||||
* <ul>
|
||||
* <li><code>source</code>, if given, applies a filter on the event’s origin</li>
|
||||
* <li><code>message</code> applies a filter on the event’s message (either with String.startsWith or Regex.findFirstIn().isDefined)</li>
|
||||
* </ul>
|
||||
* If you want to match all Warning events, the most efficient is to use <code>Left("")</code>.
|
||||
*/
|
||||
case class WarningFilter(
|
||||
override val source: Option[AnyRef],
|
||||
override val message: Either[String, Regex],
|
||||
override val complete: Boolean)(occurrences: Int) extends EventFilter(occurrences) {
|
||||
|
||||
def matches(event: LogEvent) = {
|
||||
event match {
|
||||
case Warning(src, msg) ⇒ doMatch(src, msg)
|
||||
case _ ⇒ false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Java API
|
||||
*
|
||||
* @param source
|
||||
* apply this filter only to events from the given source; do not filter on source if this is given as <code>null</code>
|
||||
* @param message
|
||||
* apply this filter only to events whose message matches; do not filter on message if this is given as <code>null</code>
|
||||
* @param pattern
|
||||
* if <code>false</code>, the message string must start with the given
|
||||
* string, otherwise the <code>message</code> argument is treated as
|
||||
* regular expression which is matched against the message (may match only
|
||||
* a substring to filter)
|
||||
* @param complete
|
||||
* whether the event’s message must match the given message string or pattern completely
|
||||
*/
|
||||
def this(source: AnyRef, message: String, pattern: Boolean, complete: Boolean, occurrences: Int) =
|
||||
this(Option(source),
|
||||
if (message eq null) Left("")
|
||||
else if (pattern) Right(new Regex(message))
|
||||
else Left(message),
|
||||
complete)(occurrences)
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter which matches Info events, if they satisfy the given criteria:
|
||||
* <ul>
|
||||
* <li><code>source</code>, if given, applies a filter on the event’s origin</li>
|
||||
* <li><code>message</code> applies a filter on the event’s message (either with String.startsWith or Regex.findFirstIn().isDefined)</li>
|
||||
* </ul>
|
||||
* If you want to match all Info events, the most efficient is to use <code>Left("")</code>.
|
||||
*/
|
||||
case class InfoFilter(
|
||||
override val source: Option[AnyRef],
|
||||
override val message: Either[String, Regex],
|
||||
override val complete: Boolean)(occurrences: Int) extends EventFilter(occurrences) {
|
||||
|
||||
def matches(event: LogEvent) = {
|
||||
event match {
|
||||
case Info(src, msg) ⇒ doMatch(src, msg)
|
||||
case _ ⇒ false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Java API
|
||||
*
|
||||
* @param source
|
||||
* apply this filter only to events from the given source; do not filter on source if this is given as <code>null</code>
|
||||
* @param message
|
||||
* apply this filter only to events whose message matches; do not filter on message if this is given as <code>null</code>
|
||||
* @param pattern
|
||||
* if <code>false</code>, the message string must start with the given
|
||||
* string, otherwise the <code>message</code> argument is treated as
|
||||
* regular expression which is matched against the message (may match only
|
||||
* a substring to filter)
|
||||
* @param complete
|
||||
* whether the event’s message must match the given message string or pattern completely
|
||||
*/
|
||||
def this(source: AnyRef, message: String, pattern: Boolean, complete: Boolean, occurrences: Int) =
|
||||
this(Option(source),
|
||||
if (message eq null) Left("")
|
||||
else if (pattern) Right(new Regex(message))
|
||||
else Left(message),
|
||||
complete)(occurrences)
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter which matches Debug events, if they satisfy the given criteria:
|
||||
* <ul>
|
||||
* <li><code>source</code>, if given, applies a filter on the event’s origin</li>
|
||||
* <li><code>message</code> applies a filter on the event’s message (either with String.startsWith or Regex.findFirstIn().isDefined)</li>
|
||||
* </ul>
|
||||
* If you want to match all Debug events, the most efficient is to use <code>Left("")</code>.
|
||||
*/
|
||||
case class DebugFilter(
|
||||
override val source: Option[AnyRef],
|
||||
override val message: Either[String, Regex],
|
||||
override val complete: Boolean)(occurrences: Int) extends EventFilter(occurrences) {
|
||||
|
||||
def matches(event: LogEvent) = {
|
||||
event match {
|
||||
case Debug(src, msg) ⇒ doMatch(src, msg)
|
||||
case _ ⇒ false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Java API
|
||||
*
|
||||
* @param source
|
||||
* apply this filter only to events from the given source; do not filter on source if this is given as <code>null</code>
|
||||
* @param message
|
||||
* apply this filter only to events whose message matches; do not filter on message if this is given as <code>null</code>
|
||||
* @param pattern
|
||||
* if <code>false</code>, the message string must start with the given
|
||||
* string, otherwise the <code>message</code> argument is treated as
|
||||
* regular expression which is matched against the message (may match only
|
||||
* a substring to filter)
|
||||
* @param complete
|
||||
* whether the event’s message must match the given message string or pattern completely
|
||||
*/
|
||||
def this(source: AnyRef, message: String, pattern: Boolean, complete: Boolean, occurrences: Int) =
|
||||
this(Option(source),
|
||||
if (message eq null) Left("")
|
||||
else if (pattern) Right(new Regex(message))
|
||||
else Left(message),
|
||||
complete)(occurrences)
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom event filter when the others do not fit the bill.
|
||||
*
|
||||
* If the partial function is defined and returns true, filter the event.
|
||||
*/
|
||||
case class CustomEventFilter(test: PartialFunction[LogEvent, Boolean])(occurrences: Int) extends EventFilter(occurrences) {
|
||||
def matches(event: LogEvent) = {
|
||||
test.isDefinedAt(event) && test(event)
|
||||
}
|
||||
}
|
||||
|
||||
case class ErrorMessageFilter(throwable: Class[_], message: String) extends EventFilter {
|
||||
def apply(event: Event) = event match {
|
||||
case Error(cause, _, _) if !(throwable isInstance cause) ⇒ false
|
||||
case Error(cause, _, null) if cause.getMessage eq null ⇒ cause.getStackTrace.length == 0
|
||||
case Error(cause, _, null) ⇒ cause.getMessage startsWith message
|
||||
case Error(cause, _, msg) ⇒
|
||||
(msg.toString startsWith message) || (cause.getMessage startsWith message)
|
||||
case _ ⇒ false
|
||||
}
|
||||
}
|
||||
|
||||
case class ErrorSourceFilter(throwable: Class[_], source: AnyRef) extends EventFilter {
|
||||
def apply(event: Event) = event match {
|
||||
case Error(cause, instance, _) ⇒ (throwable isInstance cause) && (source eq instance)
|
||||
case _ ⇒ false
|
||||
}
|
||||
}
|
||||
|
||||
case class ErrorSourceMessageFilter(throwable: Class[_], source: AnyRef, message: String) extends EventFilter {
|
||||
def apply(event: Event) = event match {
|
||||
case Error(cause, instance, _) if !((throwable isInstance cause) && (source eq instance)) ⇒ false
|
||||
case Error(cause, _, null) if cause.getMessage eq null ⇒ cause.getStackTrace.length == 0
|
||||
case Error(cause, _, null) ⇒ cause.getMessage startsWith message
|
||||
case Error(cause, _, msg) ⇒
|
||||
(msg.toString startsWith message) || (cause.getMessage startsWith message)
|
||||
case _ ⇒ false
|
||||
}
|
||||
}
|
||||
|
||||
case class CustomEventFilter(test: (Event) ⇒ Boolean) extends EventFilter {
|
||||
def apply(event: Event) = test(event)
|
||||
}
|
||||
|
||||
class TestEventListener extends EventHandler.DefaultListener {
|
||||
/**
|
||||
* EventListener for running tests, which allows selectively filtering out
|
||||
* expected messages. To use it, include something like this into
|
||||
* <code>akka.test.conf</code> and run your tests with system property
|
||||
* <code>"akka.mode"</code> set to <code>"test"</code>:
|
||||
*
|
||||
* <pre><code>
|
||||
* akka {
|
||||
* event-handlers = ["akka.testkit.TestEventListener"]
|
||||
* }
|
||||
* </code></pre>
|
||||
*/
|
||||
class TestEventListener extends Logging.DefaultLogger {
|
||||
import TestEvent._
|
||||
|
||||
var filters: List[EventFilter] = Nil
|
||||
|
||||
override def receive: Actor.Receive = ({
|
||||
case Mute(filters) ⇒ filters foreach addFilter
|
||||
case UnMute(filters) ⇒ filters foreach removeFilter
|
||||
case UnMuteAll ⇒ filters = Nil
|
||||
case event: Event if filter(event) ⇒
|
||||
}: Actor.Receive) orElse super.receive
|
||||
override def receive = {
|
||||
case InitializeLogger(bus) ⇒ Seq(classOf[Mute], classOf[UnMute]) foreach (bus.subscribe(context.self, _))
|
||||
case Mute(filters) ⇒ filters foreach addFilter
|
||||
case UnMute(filters) ⇒ filters foreach removeFilter
|
||||
case event: LogEvent ⇒ if (!filter(event)) print(event)
|
||||
}
|
||||
|
||||
def filter(event: Event): Boolean = filters exists (f ⇒ try { f(event) } catch { case e: Exception ⇒ false })
|
||||
def filter(event: LogEvent): Boolean = filters exists (f ⇒ try { f(event) } catch { case e: Exception ⇒ false })
|
||||
|
||||
def addFilter(filter: EventFilter): Unit = filters ::= filter
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,8 @@ class TestKit(_app: AkkaApplication) {
|
|||
* ActorRef of the test actor. Access is provided to enable e.g.
|
||||
* registration as message target.
|
||||
*/
|
||||
val testActor: ActorRef = new LocalActorRef(app, Props(new TestActor(queue)).copy(dispatcher = new CallingThreadDispatcher(app)), app.guardian, "testActor" + TestKit.testActorId.incrementAndGet(), true)
|
||||
val testActor: ActorRef = app.systemActorOf(Props(new TestActor(queue)).copy(dispatcher = new CallingThreadDispatcher(app)),
|
||||
"testActor" + TestKit.testActorId.incrementAndGet)
|
||||
|
||||
private var end: Duration = Duration.Inf
|
||||
|
||||
|
|
@ -451,12 +452,6 @@ class TestKit(_app: AkkaApplication) {
|
|||
lastWasNoMsg = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `receiveWhile(remaining)(f)`, but correctly treating the timeFactor.
|
||||
*/
|
||||
@deprecated("insert empty first parameter list", "1.2")
|
||||
def receiveWhile[T](f: PartialFunction[AnyRef, T]): Seq[T] = receiveWhile(remaining / Duration.timeFactor)(f)
|
||||
|
||||
/**
|
||||
* Receive a series of messages until one does not match the given partial
|
||||
* function or the idle timeout is met (disabled by default) or the overall
|
||||
|
|
@ -476,7 +471,7 @@ class TestKit(_app: AkkaApplication) {
|
|||
* </pre>
|
||||
*/
|
||||
def receiveWhile[T](max: Duration = Duration.MinusInf, idle: Duration = Duration.Inf, messages: Int = Int.MaxValue)(f: PartialFunction[AnyRef, T]): Seq[T] = {
|
||||
val stop = now + (if (max == Duration.MinusInf) remaining else max.dilated)
|
||||
val stop = now + (if (max eq Duration.MinusInf) remaining else max.dilated)
|
||||
var msg: Message = NullMessage
|
||||
|
||||
@tailrec
|
||||
|
|
@ -554,6 +549,41 @@ class TestKit(_app: AkkaApplication) {
|
|||
|
||||
object TestKit {
|
||||
private[testkit] val testActorId = new AtomicInteger(0)
|
||||
|
||||
/**
|
||||
* Block until the given condition evaluates to `true` or the timeout
|
||||
* expires, whichever comes first.
|
||||
*
|
||||
* If no timeout is given, take it from the innermost enclosing `within`
|
||||
* block.
|
||||
*
|
||||
* Note that the timeout is scaled using Duration.timeFactor.
|
||||
*/
|
||||
def awaitCond(p: ⇒ Boolean, max: Duration, interval: Duration = 100.millis, noThrow: Boolean = false): Boolean = {
|
||||
val stop = now + max
|
||||
|
||||
@tailrec
|
||||
def poll(): Boolean = {
|
||||
if (!p) {
|
||||
val toSleep = stop - now
|
||||
if (toSleep <= Duration.Zero) {
|
||||
if (noThrow) false
|
||||
else throw new AssertionError("timeout " + max + " expired")
|
||||
} else {
|
||||
Thread.sleep((toSleep min interval).toMillis)
|
||||
poll()
|
||||
}
|
||||
} else true
|
||||
}
|
||||
|
||||
poll()
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain current timestamp as Duration for relative measurements (using System.nanoTime).
|
||||
*/
|
||||
def now: Duration = System.nanoTime().nanos
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package akka.testkit
|
|||
|
||||
import akka.util.Duration
|
||||
import java.util.concurrent.{ CountDownLatch, TimeUnit }
|
||||
import akka.AkkaApplication
|
||||
|
||||
class TestLatchTimeoutException(message: String) extends RuntimeException(message)
|
||||
class TestLatchNoTimeoutException(message: String) extends RuntimeException(message)
|
||||
|
|
@ -20,10 +21,10 @@ class TestLatchNoTimeoutException(message: String) extends RuntimeException(mess
|
|||
object TestLatch {
|
||||
val DefaultTimeout = Duration(5, TimeUnit.SECONDS)
|
||||
|
||||
def apply(count: Int = 1) = new TestLatch(count)
|
||||
def apply(count: Int = 1)(implicit app: AkkaApplication) = new TestLatch(count)
|
||||
}
|
||||
|
||||
class TestLatch(count: Int = 1) {
|
||||
class TestLatch(count: Int = 1)(implicit app: AkkaApplication) {
|
||||
private var latch = new CountDownLatch(count)
|
||||
|
||||
def countDown() = latch.countDown()
|
||||
|
|
@ -33,9 +34,9 @@ class TestLatch(count: Int = 1) {
|
|||
def await(): Boolean = await(TestLatch.DefaultTimeout)
|
||||
|
||||
def await(timeout: Duration): Boolean = {
|
||||
val opened = latch.await(Testing.testTime(timeout.toNanos), TimeUnit.NANOSECONDS)
|
||||
val opened = latch.await(timeout.dilated.toNanos, TimeUnit.NANOSECONDS)
|
||||
if (!opened) throw new TestLatchTimeoutException(
|
||||
"Timeout of %s with time factor of %s" format (timeout.toString, Duration.timeFactor))
|
||||
"Timeout of %s with time factor of %s" format (timeout.toString, app.AkkaConfig.TestTimeFactor))
|
||||
opened
|
||||
}
|
||||
|
||||
|
|
@ -43,9 +44,9 @@ class TestLatch(count: Int = 1) {
|
|||
* Timeout is expected. Throws exception if latch is opened before timeout.
|
||||
*/
|
||||
def awaitTimeout(timeout: Duration = TestLatch.DefaultTimeout) = {
|
||||
val opened = latch.await(Testing.testTime(timeout.toNanos), TimeUnit.NANOSECONDS)
|
||||
val opened = latch.await(timeout.dilated.toNanos, TimeUnit.NANOSECONDS)
|
||||
if (opened) throw new TestLatchNoTimeoutException(
|
||||
"Latch opened before timeout of %s with time factor of %s" format (timeout.toString, Duration.timeFactor))
|
||||
"Latch opened before timeout of %s with time factor of %s" format (timeout.toString, app.AkkaConfig.TestTimeFactor))
|
||||
opened
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.testkit
|
||||
|
||||
import akka.util.Duration
|
||||
import Duration.timeFactor
|
||||
|
||||
/**
|
||||
* Multiplying numbers used in test timeouts by a factor, set by system property.
|
||||
* Useful for Jenkins builds (where the machine may need more time).
|
||||
*/
|
||||
object Testing {
|
||||
def testTime(t: Int): Int = (timeFactor * t).toInt
|
||||
def testTime(t: Long): Long = (timeFactor * t).toLong
|
||||
def testTime(t: Float): Float = (timeFactor * t).toFloat
|
||||
def testTime(t: Double): Double = timeFactor * t
|
||||
|
||||
def sleepFor(duration: Duration) = Thread.sleep(testTime(duration.toMillis))
|
||||
}
|
||||
|
|
@ -1,18 +1,28 @@
|
|||
package akka
|
||||
|
||||
import akka.event.EventHandler
|
||||
import akka.util.Duration
|
||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||
|
||||
package object testkit {
|
||||
def filterEvents[T](eventFilters: Iterable[EventFilter])(block: ⇒ T)(implicit app: AkkaApplication): T = {
|
||||
app.eventHandler.notify(TestEvent.Mute(eventFilters.toSeq))
|
||||
def now = System.currentTimeMillis
|
||||
|
||||
app.mainbus.publish(TestEvent.Mute(eventFilters.toSeq))
|
||||
try {
|
||||
block
|
||||
val result = block
|
||||
|
||||
val stop = now + app.AkkaConfig.TestEventFilterLeeway.toMillis
|
||||
val failed = eventFilters filterNot (_.awaitDone(Duration(stop - now, MILLISECONDS))) map ("Timeout waiting for " + _)
|
||||
if (failed.nonEmpty)
|
||||
throw new AssertionError("Filter completion error:\n" + failed.mkString("\n"))
|
||||
|
||||
result
|
||||
} finally {
|
||||
app.eventHandler.notify(TestEvent.UnMute(eventFilters.toSeq))
|
||||
app.mainbus.publish(TestEvent.UnMute(eventFilters.toSeq))
|
||||
}
|
||||
}
|
||||
|
||||
def filterEvents[T](eventFilters: EventFilter*)(block: ⇒ T)(implicit app: AkkaApplication): T = filterEvents(eventFilters.toSeq)(block)
|
||||
|
||||
def filterException[T <: Throwable](block: ⇒ Unit)(implicit app: AkkaApplication, m: Manifest[T]): Unit = filterEvents(Seq(EventFilter[T]))(block)
|
||||
def filterException[T <: Throwable](block: ⇒ Unit)(implicit app: AkkaApplication, m: Manifest[T]): Unit = EventFilter[T]() intercept (block)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,16 +9,24 @@ import org.scalatest.matchers.MustMatchers
|
|||
import akka.AkkaApplication
|
||||
import akka.actor.{ Actor, ActorRef, Props }
|
||||
import akka.dispatch.MessageDispatcher
|
||||
import akka.event.{ Logging, LoggingAdapter }
|
||||
import akka.util.duration._
|
||||
import akka.dispatch.FutureTimeoutException
|
||||
|
||||
abstract class AkkaSpec(_application: AkkaApplication = AkkaApplication())
|
||||
extends TestKit(_application) with WordSpec with MustMatchers with BeforeAndAfterAll {
|
||||
|
||||
val log: LoggingAdapter = Logging(app.mainbus, this)
|
||||
|
||||
final override def beforeAll {
|
||||
atStartup()
|
||||
}
|
||||
|
||||
final override def afterAll {
|
||||
app.stop()
|
||||
try app.terminationFuture.await(5 seconds) catch {
|
||||
case _: FutureTimeoutException ⇒ app.log.warning("failed to stop within 5 seconds")
|
||||
}
|
||||
atTermination()
|
||||
}
|
||||
|
||||
|
|
@ -40,3 +48,21 @@ abstract class AkkaSpec(_application: AkkaApplication = AkkaApplication())
|
|||
actorOf(Props(ctx ⇒ { case "go" ⇒ try body finally ctx.self.stop() }).withDispatcher(dispatcher)) ! "go"
|
||||
}
|
||||
}
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
class AkkaSpecSpec extends WordSpec with MustMatchers {
|
||||
"An AkkaSpec" must {
|
||||
"terminate all actors" in {
|
||||
import AkkaApplication.defaultConfig
|
||||
val app = AkkaApplication("test", defaultConfig ++ Configuration(
|
||||
"akka.actor.debug.lifecycle" -> true, "akka.loglevel" -> "DEBUG"))
|
||||
val spec = new AkkaSpec(app) {
|
||||
val ref = Seq(testActor, app.actorOf(Props.empty, "name"))
|
||||
}
|
||||
spec.ref foreach (_ must not be 'shutdown)
|
||||
app.stop()
|
||||
spec.awaitCond(spec.ref forall (_.isShutdown), 2 seconds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ package akka.testkit
|
|||
import org.scalatest.matchers.MustMatchers
|
||||
import org.scalatest.{ BeforeAndAfterEach, WordSpec }
|
||||
import akka.actor._
|
||||
import akka.event.EventHandler
|
||||
import akka.event.Logging.Warning
|
||||
import akka.dispatch.{ Future, Promise }
|
||||
import akka.util.duration._
|
||||
import akka.AkkaApplication
|
||||
|
|
@ -77,7 +77,6 @@ object TestActorRefSpec {
|
|||
}
|
||||
|
||||
class Logger extends Actor {
|
||||
import EventHandler._
|
||||
var count = 0
|
||||
var msg: String = _
|
||||
def receive = {
|
||||
|
|
@ -154,7 +153,7 @@ class TestActorRefSpec extends AkkaSpec with BeforeAndAfterEach {
|
|||
}
|
||||
|
||||
"stop when sent a poison pill" in {
|
||||
filterEvents(EventFilter[ActorKilledException]) {
|
||||
EventFilter[ActorKilledException]() intercept {
|
||||
val a = TestActorRef(Props[WorkerActor])
|
||||
testActor startsMonitoring a
|
||||
a.!(PoisonPill)(testActor)
|
||||
|
|
@ -167,7 +166,7 @@ class TestActorRefSpec extends AkkaSpec with BeforeAndAfterEach {
|
|||
}
|
||||
|
||||
"restart when Kill:ed" in {
|
||||
filterEvents(EventFilter[ActorKilledException]) {
|
||||
EventFilter[ActorKilledException]() intercept {
|
||||
counter = 2
|
||||
|
||||
val boss = TestActorRef(Props(new TActor {
|
||||
|
|
@ -190,9 +189,10 @@ class TestActorRefSpec extends AkkaSpec with BeforeAndAfterEach {
|
|||
|
||||
"support futures" in {
|
||||
val a = TestActorRef[WorkerActor]
|
||||
val f = a ? "work" mapTo manifest[String]
|
||||
val f = a ? "work"
|
||||
// CallingThreadDispatcher means that there is no delay
|
||||
f must be('completed)
|
||||
f.get must equal("workDone")
|
||||
f.as[String] must equal(Some("workDone"))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import org.scalatest.WordSpec
|
|||
import org.scalatest.matchers.MustMatchers
|
||||
import org.scalatest.{ BeforeAndAfterEach, WordSpec }
|
||||
import akka.actor._
|
||||
import akka.event.EventHandler
|
||||
import akka.dispatch.Future
|
||||
import akka.util.duration._
|
||||
|
||||
|
|
|
|||
|
|
@ -3,23 +3,10 @@ package akka.testkit
|
|||
import org.scalatest.matchers.MustMatchers
|
||||
import org.scalatest.{ BeforeAndAfterEach, WordSpec }
|
||||
import akka.util.Duration
|
||||
import akka.config.Configuration
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
class TestTimeSpec extends AkkaSpec with BeforeAndAfterEach {
|
||||
|
||||
val tf = Duration.timeFactor
|
||||
|
||||
override def beforeEach {
|
||||
val f = Duration.getClass.getDeclaredField("timeFactor")
|
||||
f.setAccessible(true)
|
||||
f.setDouble(Duration, 2.0)
|
||||
}
|
||||
|
||||
override def afterEach {
|
||||
val f = Duration.getClass.getDeclaredField("timeFactor")
|
||||
f.setAccessible(true)
|
||||
f.setDouble(Duration, tf)
|
||||
}
|
||||
class TestTimeSpec extends AkkaSpec(Configuration("akka.test.timefactor" -> 2.0)) with BeforeAndAfterEach {
|
||||
|
||||
"A TestKit" must {
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
package akka.tutorial.second
|
||||
|
||||
import akka.actor.Actor._
|
||||
import akka.event.EventHandler
|
||||
import akka.event.Logging
|
||||
import System.{ currentTimeMillis ⇒ now }
|
||||
import akka.routing.Routing.Broadcast
|
||||
import akka.routing._
|
||||
|
|
@ -15,6 +15,7 @@ import akka.actor.{ ActorRef, Timeout, Actor, PoisonPill }
|
|||
object Pi extends App {
|
||||
|
||||
val app = AkkaApplication()
|
||||
val log = Logging(app, this)
|
||||
|
||||
calculate(nrOfWorkers = 4, nrOfElements = 10000, nrOfMessages = 10000)
|
||||
|
||||
|
|
@ -109,9 +110,9 @@ object Pi extends App {
|
|||
master.?(Calculate, Timeout(60000)).
|
||||
await.resultOrException match { //wait for the result, with a 60 seconds timeout
|
||||
case Some(pi) ⇒
|
||||
app.eventHandler.info(this, "\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis".format(pi, (now - start)))
|
||||
log.info("\n\tPi estimate: \t\t{}\n\tCalculation time: \t{} millis", pi, now - start)
|
||||
case None ⇒
|
||||
app.eventHandler.error(this, "Pi calculation did not complete within the timeout.")
|
||||
log.error("Pi calculation did not complete within the timeout.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,11 @@ akka {
|
|||
|
||||
time-unit = "seconds" # Time unit for all timeout properties throughout the config
|
||||
|
||||
event-handlers = ["akka.event.EventHandler$DefaultListener"] # Event handlers to register at boot time (EventHandler$DefaultListener logs to STDOUT)
|
||||
event-handler-level = "INFO" # Options: ERROR, WARNING, INFO, DEBUG
|
||||
event-handlers = ["akka.event.Logging$DefaultLogger"] # Event handlers to register at boot time (Logging$DefaultLogger logs to STDOUT)
|
||||
loglevel = "INFO" # Options: ERROR, WARNING, INFO, DEBUG
|
||||
# this level is used by the configured loggers (see "event-handlers") as soon
|
||||
# as they have been started; before that, see "stdout-loglevel"
|
||||
stdout-loglevel = "WARNING" # Loglevel for the very basic logger activated during AkkaApplication startup
|
||||
|
||||
event-handler-dispatcher {
|
||||
type = "Dispatcher" # Must be one of the following
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ include "akka-reference.conf"
|
|||
|
||||
akka {
|
||||
event-handlers = ["akka.testkit.TestEventListener"]
|
||||
event-handler-level = "WARNING"
|
||||
loglevel = "WARNING"
|
||||
actor {
|
||||
default-dispatcher {
|
||||
core-pool-size = 4
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue