diff --git a/akka-actor-tests/src/test/scala/akka/actor/ActorFireForgetRequestReplySpec.scala b/akka-actor-tests/src/test/scala/akka/actor/ActorFireForgetRequestReplySpec.scala index 727d2bbbec..dd27b9fe68 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/ActorFireForgetRequestReplySpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/ActorFireForgetRequestReplySpec.scala @@ -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() } diff --git a/akka-actor-tests/src/test/scala/akka/actor/ActorRefSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/ActorRefSpec.scala index b445c09e1c..b485aa0931 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/ActorRefSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/ActorRefSpec.scala @@ -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" diff --git a/akka-actor-tests/src/test/scala/akka/actor/DeathWatchSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/DeathWatchSpec.scala index db51993b34..9e407c97bf 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/DeathWatchSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/DeathWatchSpec.scala @@ -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)) { diff --git a/akka-actor-tests/src/test/scala/akka/actor/FSMActorSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/FSMActorSpec.scala index 6d85c6997c..05716ea04b 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/FSMActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/FSMActorSpec.scala @@ -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) } } diff --git a/akka-actor-tests/src/test/scala/akka/actor/FSMTimingSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/FSMTimingSpec.scala index 094bd4f196..bfda48fba9 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/FSMTimingSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/FSMTimingSpec.scala @@ -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) } diff --git a/akka-actor-tests/src/test/scala/akka/actor/LoggingReceiveSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/LoggingReceiveSpec.scala index 170183d2c8..891b09ce5b 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/LoggingReceiveSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/LoggingReceiveSpec.scala @@ -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() } } diff --git a/akka-actor-tests/src/test/scala/akka/actor/RestartStrategySpec.scala b/akka-actor-tests/src/test/scala/akka/actor/RestartStrategySpec.scala index 38a223d29a..027e614c49 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/RestartStrategySpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/RestartStrategySpec.scala @@ -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 diff --git a/akka-actor-tests/src/test/scala/akka/actor/SchedulerSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/SchedulerSpec.scala index 9d4f0ebd4e..7ab547cf1f 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/SchedulerSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/SchedulerSpec.scala @@ -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)) } } diff --git a/akka-actor-tests/src/test/scala/akka/actor/SupervisorSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/SupervisorSpec.scala index 4e3d495b94..6c09a2ab80 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/SupervisorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/SupervisorSpec.scala @@ -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() = { diff --git a/akka-actor-tests/src/test/scala/akka/actor/SupervisorTreeSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/SupervisorTreeSpec.scala index f2c3d36081..ecacb57a81 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/SupervisorTreeSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/SupervisorTreeSpec.scala @@ -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 } diff --git a/akka-actor-tests/src/test/scala/akka/actor/dispatch/ActorModelSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/dispatch/ActorModelSpec.scala index 299f5fb338..a441ca8651 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/dispatch/ActorModelSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/dispatch/ActorModelSpec.scala @@ -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() diff --git a/akka-actor-tests/src/test/scala/akka/actor/dispatch/PinnedActorSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/dispatch/PinnedActorSpec.scala index fc0e240a50..d2b410a7bf 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/dispatch/PinnedActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/dispatch/PinnedActorSpec.scala @@ -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 { diff --git a/akka-actor-tests/src/test/scala/akka/event/EventBusSpec.scala b/akka-actor-tests/src/test/scala/akka/event/EventBusSpec.scala index 4861dd9ea5..afd255a27c 100644 --- a/akka-actor-tests/src/test/scala/akka/event/EventBusSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/event/EventBusSpec.scala @@ -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 } diff --git a/akka-actor-tests/src/test/scala/akka/event/MainBusSpec.scala b/akka-actor-tests/src/test/scala/akka/event/MainBusSpec.scala new file mode 100644 index 0000000000..16e735542b --- /dev/null +++ b/akka-actor-tests/src/test/scala/akka/event/MainBusSpec.scala @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2009-2011 Typesafe Inc. + */ +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)) + } + +} \ No newline at end of file diff --git a/akka-actor-tests/src/test/scala/akka/performance/trading/system/MatchingEngine.scala b/akka-actor-tests/src/test/scala/akka/performance/trading/system/MatchingEngine.scala index fa2f5f2493..cabf890488 100644 --- a/akka-actor-tests/src/test/scala/akka/performance/trading/system/MatchingEngine.scala +++ b/akka-actor-tests/src/test/scala/akka/performance/trading/system/MatchingEngine.scala @@ -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) } } diff --git a/akka-actor-tests/src/test/scala/akka/performance/trading/system/OrderReceiver.scala b/akka-actor-tests/src/test/scala/akka/performance/trading/system/OrderReceiver.scala index b6d15e1185..e20966405f 100644 --- a/akka-actor-tests/src/test/scala/akka/performance/trading/system/OrderReceiver.scala +++ b/akka-actor-tests/src/test/scala/akka/performance/trading/system/OrderReceiver.scala @@ -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) } } diff --git a/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingLatencyPerformanceSpec.scala b/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingLatencyPerformanceSpec.scala index 83c75c41a2..95249cfe29 100644 --- a/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingLatencyPerformanceSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingLatencyPerformanceSpec.scala @@ -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) diff --git a/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingThroughputPerformanceSpec.scala b/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingThroughputPerformanceSpec.scala index a59f164e16..4e5e108e0a 100644 --- a/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingThroughputPerformanceSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingThroughputPerformanceSpec.scala @@ -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) { diff --git a/akka-actor-tests/src/test/scala/akka/performance/workbench/BenchResultRepository.scala b/akka-actor-tests/src/test/scala/akka/performance/workbench/BenchResultRepository.scala index a21cced8e8..f7cda91ce9 100644 --- a/akka-actor-tests/src/test/scala/akka/performance/workbench/BenchResultRepository.scala +++ b/akka-actor-tests/src/test/scala/akka/performance/workbench/BenchResultRepository.scala @@ -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) diff --git a/akka-actor-tests/src/test/scala/akka/performance/workbench/PerformanceSpec.scala b/akka-actor-tests/src/test/scala/akka/performance/workbench/PerformanceSpec.scala index b1c5612024..0a09f05c8e 100644 --- a/akka-actor-tests/src/test/scala/akka/performance/workbench/PerformanceSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/performance/workbench/PerformanceSpec.scala @@ -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) } } diff --git a/akka-actor-tests/src/test/scala/akka/performance/workbench/Report.scala b/akka-actor-tests/src/test/scala/akka/performance/workbench/Report.scala index 232bdef36f..c534ae543f 100644 --- a/akka-actor-tests/src/test/scala/akka/performance/workbench/Report.scala +++ b/akka-actor-tests/src/test/scala/akka/performance/workbench/Report.scala @@ -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)) } } diff --git a/akka-actor-tests/src/test/scala/akka/routing/ActorPoolSpec.scala b/akka-actor-tests/src/test/scala/akka/routing/ActorPoolSpec.scala index 5ea6902b97..5e34a3c2ed 100644 --- a/akka-actor-tests/src/test/scala/akka/routing/ActorPoolSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/routing/ActorPoolSpec.scala @@ -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) diff --git a/akka-actor-tests/src/test/scala/akka/testkit/CallingThreadDispatcherModelSpec.scala b/akka-actor-tests/src/test/scala/akka/testkit/CallingThreadDispatcherModelSpec.scala index c977709cbc..bd3816417e 100644 --- a/akka-actor-tests/src/test/scala/akka/testkit/CallingThreadDispatcherModelSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/testkit/CallingThreadDispatcherModelSpec.scala @@ -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" } diff --git a/akka-actor/src/main/scala/akka/AkkaApplication.scala b/akka-actor/src/main/scala/akka/AkkaApplication.scala index 72010323b9..7ec88972fe 100644 --- a/akka-actor/src/main/scala/akka/AkkaApplication.scala +++ b/akka-actor/src/main/scala/akka/AkkaApplication.scala @@ -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) diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index a0d62bc76e..c7f93b493f 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -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) diff --git a/akka-actor/src/main/scala/akka/actor/ActorCell.scala b/akka-actor/src/main/scala/akka/actor/ActorCell.scala index 319d65a963..16f1ec7535 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorCell.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorCell.scala @@ -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 = { diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index d25572c3e0..6a3392f910 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -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 } diff --git a/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala b/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala index ce5c90c110..b667188f79 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala @@ -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) } } } diff --git a/akka-actor/src/main/scala/akka/actor/Deployer.scala b/akka-actor/src/main/scala/akka/actor/Deployer.scala index f550c34bfa..e68cde7cd8 100644 --- a/akka-actor/src/main/scala/akka/actor/Deployer.scala +++ b/akka-actor/src/main/scala/akka/actor/Deployer.scala @@ -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 } } diff --git a/akka-actor/src/main/scala/akka/actor/FSM.scala b/akka-actor/src/main/scala/akka/actor/FSM.scala index 2e1202365f..a6a119cef6 100644 --- a/akka-actor/src/main/scala/akka/actor/FSM.scala +++ b/akka-actor/src/main/scala/akka/actor/FSM.scala @@ -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) } /** diff --git a/akka-actor/src/main/scala/akka/actor/IO.scala b/akka-actor/src/main/scala/akka/actor/IO.scala index 600d3a334a..164321ade9 100644 --- a/akka-actor/src/main/scala/akka/actor/IO.scala +++ b/akka-actor/src/main/scala/akka/actor/IO.scala @@ -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 diff --git a/akka-actor/src/main/scala/akka/actor/Scheduler.scala b/akka-actor/src/main/scala/akka/actor/Scheduler.scala index 5d64eec5f1..ae9b86695d 100644 --- a/akka-actor/src/main/scala/akka/actor/Scheduler.scala +++ b/akka-actor/src/main/scala/akka/actor/Scheduler.scala @@ -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 { diff --git a/akka-actor/src/main/scala/akka/dispatch/AbstractDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/AbstractDispatcher.scala index baf972a5b2..7faf91455c 100644 --- a/akka-actor/src/main/scala/akka/dispatch/AbstractDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/AbstractDispatcher.scala @@ -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() } diff --git a/akka-actor/src/main/scala/akka/dispatch/BalancingDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/BalancingDispatcher.scala index ccd7faf471..b25e090fbb 100644 --- a/akka-actor/src/main/scala/akka/dispatch/BalancingDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/BalancingDispatcher.scala @@ -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 { diff --git a/akka-actor/src/main/scala/akka/dispatch/Dispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/Dispatcher.scala index 06e6c6442a..94f9a70078 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Dispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Dispatcher.scala @@ -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() } diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index b51e4baf65..8051bd2336 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -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 diff --git a/akka-actor/src/main/scala/akka/dispatch/Mailbox.scala b/akka-actor/src/main/scala/akka/dispatch/Mailbox.scala index d03655489b..04dd2f8f0f 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Mailbox.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Mailbox.scala @@ -1,7 +1,6 @@ /** * Copyright (C) 2009-2011 Typesafe Inc. */ - 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 } } diff --git a/akka-actor/src/main/scala/akka/dispatch/ThreadPoolBuilder.scala b/akka-actor/src/main/scala/akka/dispatch/ThreadPoolBuilder.scala index ac529342bd..0d579e6e1e 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ThreadPoolBuilder.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ThreadPoolBuilder.scala @@ -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 } } diff --git a/akka-actor/src/main/scala/akka/event/EventBus.scala b/akka-actor/src/main/scala/akka/event/EventBus.scala index e66b68c7e8..a337fd1cbf 100644 --- a/akka-actor/src/main/scala/akka/event/EventBus.scala +++ b/akka-actor/src/main/scala/akka/event/EventBus.scala @@ -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) diff --git a/akka-actor/src/main/scala/akka/event/EventHandler.scala b/akka-actor/src/main/scala/akka/event/EventHandler.scala deleted file mode 100644 index 3acdf52a0b..0000000000 --- a/akka-actor/src/main/scala/akka/event/EventHandler.scala +++ /dev/null @@ -1,296 +0,0 @@ -/** - * Copyright (C) 2009-2011 Typesafe Inc. - */ - -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. - *

- * Create, add and remove a listener: - *

- * 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)
- * 
- *

- * However best is probably to register the listener in the 'akka.conf' - * configuration file. - *

- * Log an error event: - *

- * EventHandler.notify(EventHandler.Error(exception, this, message))
- * 
- * Or use the direct methods (better performance): - *
- * EventHandler.error(exception, this, message)
- * 
- * - * Shut down the EventHandler: - *
- * EventHandler.shutdown()
- * 
- * - * @author Jonas Bonér - */ -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() -} diff --git a/akka-actor/src/main/scala/akka/event/Logging.scala b/akka-actor/src/main/scala/akka/event/Logging.scala index 38964d3f43..4fd9b86325 100644 --- a/akka-actor/src/main/scala/akka/event/Logging.scala +++ b/akka-actor/src/main/scala/akka/event/Logging.scala @@ -2,13 +2,396 @@ * Copyright (C) 2009-2011 Typesafe Inc. */ 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: + * + *

+ * val log = Logging(<bus>, <source object>)
+ * ...
+ * log.info("hello world!")
+ * 
+ * + * Loggers are attached to the level-specific channels Error, + * Warning, Info and Debug 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 InitializeLogger + * message which is sent before actually attaching it to the logging bus. + * + * Logging is configured in akka.conf by setting (some of) the following: + * + *

+ * 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
+ * }
+ * 
+ */ +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 + * akka.stdout-loglevel in akka.conf. + */ + 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 + * akka.event-handlers 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: + * + *
+ * val log = Logging(<bus>, <source object>)
+ * ...
+ * log.info("hello world!")
+ * 
+ * + * All log-level methods support simple interpolation templates with up to four + * arguments placed by using {} within the template (first string + * argument): + * + *
+ * log.error(exception, "Exception while processing {} in state {}", msg, state)
+ * 
*/ -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)) } } diff --git a/akka-actor/src/main/scala/akka/event/MainBus.scala b/akka-actor/src/main/scala/akka/event/MainBus.scala new file mode 100644 index 0000000000..8a628e6edb --- /dev/null +++ b/akka-actor/src/main/scala/akka/event/MainBus.scala @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2009-2011 Typesafe Inc. + */ +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 + } + +} \ No newline at end of file diff --git a/akka-actor/src/main/scala/akka/remote/RemoteInterface.scala b/akka-actor/src/main/scala/akka/remote/RemoteInterface.scala index 6737db52bb..926995bb1b 100644 --- a/akka-actor/src/main/scala/akka/remote/RemoteInterface.scala +++ b/akka-actor/src/main/scala/akka/remote/RemoteInterface.scala @@ -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 -} \ No newline at end of file +} diff --git a/akka-actor/src/main/scala/akka/routing/Routing.scala b/akka-actor/src/main/scala/akka/routing/Routing.scala index 899dcb7f5a..88026249bb 100644 --- a/akka-actor/src/main/scala/akka/routing/Routing.scala +++ b/akka-actor/src/main/scala/akka/routing/Routing.scala @@ -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 diff --git a/akka-actor/src/main/scala/akka/util/Duration.scala b/akka-actor/src/main/scala/akka/util/Duration.scala index cdf4347c30..3993a8a363 100644 --- a/akka-actor/src/main/scala/akka/util/Duration.scala +++ b/akka-actor/src/main/scala/akka/util/Duration.scala @@ -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 diff --git a/akka-actor/src/main/scala/akka/util/Helpers.scala b/akka-actor/src/main/scala/akka/util/Helpers.scala index da44abbe25..0975081d97 100644 --- a/akka-actor/src/main/scala/akka/util/Helpers.scala +++ b/akka-actor/src/main/scala/akka/util/Helpers.scala @@ -1,10 +1,8 @@ /** * Copyright (C) 2009-2011 Typesafe Inc. */ - package akka.util -import akka.event.EventHandler import java.io.{ PrintWriter, StringWriter } /** diff --git a/akka-actor/src/main/scala/akka/util/Index.scala b/akka-actor/src/main/scala/akka/util/Index.scala index c8155f235c..d87103b93c 100644 --- a/akka-actor/src/main/scala/akka/util/Index.scala +++ b/akka-actor/src/main/scala/akka/util/Index.scala @@ -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 diff --git a/akka-actor/src/main/scala/akka/util/JMX.scala b/akka-actor/src/main/scala/akka/util/JMX.scala index d4e287c2e3..ad735ae39b 100644 --- a/akka-actor/src/main/scala/akka/util/JMX.scala +++ b/akka-actor/src/main/scala/akka/util/JMX.scala @@ -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))) } } diff --git a/akka-actor/src/main/scala/akka/util/LockUtil.scala b/akka-actor/src/main/scala/akka/util/LockUtil.scala index f39eb9e237..b5009d1c3f 100644 --- a/akka-actor/src/main/scala/akka/util/LockUtil.scala +++ b/akka-actor/src/main/scala/akka/util/LockUtil.scala @@ -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 Jonas Bonér diff --git a/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala b/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala index 7d26a1c489..d54a6fe72d 100644 --- a/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala +++ b/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala @@ -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 diff --git a/akka-docs/scala/code/ActorDocSpec.scala b/akka-docs/scala/code/ActorDocSpec.scala index a8998d7691..47f9fe0a89 100644 --- a/akka-docs/scala/code/ActorDocSpec.scala +++ b/akka-docs/scala/code/ActorDocSpec.scala @@ -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() } diff --git a/akka-remote/src/main/scala/akka/remote/BootableRemoteActorService.scala b/akka-remote/src/main/scala/akka/remote/BootableRemoteActorService.scala index 6004e7c5ad..07bc74ad30 100644 --- a/akka-remote/src/main/scala/akka/remote/BootableRemoteActorService.scala +++ b/akka-remote/src/main/scala/akka/remote/BootableRemoteActorService.scala @@ -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 diff --git a/akka-remote/src/main/scala/akka/remote/Gossiper.scala b/akka-remote/src/main/scala/akka/remote/Gossiper.scala index f834137b88..4a5efc4f40 100644 --- a/akka-remote/src/main/scala/akka/remote/Gossiper.scala +++ b/akka-remote/src/main/scala/akka/remote/Gossiper.scala @@ -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 == _) diff --git a/akka-remote/src/main/scala/akka/remote/Remote.scala b/akka-remote/src/main/scala/akka/remote/Remote.scala index f669bcad83..2b66ae0687 100644 --- a/akka-remote/src/main/scala/akka/remote/Remote.scala +++ b/akka-remote/src/main/scala/akka/remote/Remote.scala @@ -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) } } diff --git a/akka-remote/src/main/scala/akka/remote/RemoteActorRefProvider.scala b/akka-remote/src/main/scala/akka/remote/RemoteActorRefProvider.scala index dc6b18c6d9..45427995e9 100644 --- a/akka-remote/src/main/scala/akka/remote/RemoteActorRefProvider.scala +++ b/akka-remote/src/main/scala/akka/remote/RemoteActorRefProvider.scala @@ -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 { diff --git a/akka-remote/src/main/scala/akka/remote/RemoteConnectionManager.scala b/akka-remote/src/main/scala/akka/remote/RemoteConnectionManager.scala index a2a5ef4a93..f76f9d072a 100644 --- a/akka-remote/src/main/scala/akka/remote/RemoteConnectionManager.scala +++ b/akka-remote/src/main/scala/akka/remote/RemoteConnectionManager.scala @@ -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 } } diff --git a/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala b/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala index 98568a3b3c..723a830dd4 100644 --- a/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala +++ b/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala @@ -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 diff --git a/akka-slf4j/src/main/scala/akka/event/slf4j/SLF4J.scala b/akka-slf4j/src/main/scala/akka/event/slf4j/SLF4J.scala index 4db40e6c28..8253dcd248 100644 --- a/akka-slf4j/src/main/scala/akka/event/slf4j/SLF4J.scala +++ b/akka-slf4j/src/main/scala/akka/event/slf4j/SLF4J.scala @@ -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 Jonas Bonér */ -trait Logging { +trait SLF4JLogging { @transient lazy val log = Logger(this.getClass.getName) } @@ -31,8 +30,7 @@ object Logger { * * @author Jonas Bonér */ -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) } diff --git a/akka-stm/src/test/java/akka/transactor/test/UntypedCoordinatedIncrementTest.java b/akka-stm/src/test/java/akka/transactor/test/UntypedCoordinatedIncrementTest.java index f975c5e429..4524734198 100644 --- a/akka-stm/src/test/java/akka/transactor/test/UntypedCoordinatedIncrementTest.java +++ b/akka-stm/src/test/java/akka/transactor/test/UntypedCoordinatedIncrementTest.java @@ -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 ignoreExceptions = seq(expectedFailureFilter, coordinatedFilter); - application.eventHandler().notify(new TestEvent.Mute(ignoreExceptions)); + application.mainbus().publish(new TestEvent.Mute(ignoreExceptions)); CountDownLatch incrementLatch = new CountDownLatch(numCounters); List actors = new ArrayList(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 Seq seq(A... args) { diff --git a/akka-stm/src/test/java/akka/transactor/test/UntypedTransactorTest.java b/akka-stm/src/test/java/akka/transactor/test/UntypedTransactorTest.java index 83845a940a..a3fb39b474 100644 --- a/akka-stm/src/test/java/akka/transactor/test/UntypedTransactorTest.java +++ b/akka-stm/src/test/java/akka/transactor/test/UntypedTransactorTest.java @@ -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 ignoreExceptions = seq(expectedFailureFilter, coordinatedFilter); - application.eventHandler().notify(new TestEvent.Mute(ignoreExceptions)); + application.mainbus().publish(new TestEvent.Mute(ignoreExceptions)); CountDownLatch incrementLatch = new CountDownLatch(numCounters); List actors = new ArrayList(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 Seq seq(A... args) { diff --git a/akka-stm/src/test/scala/transactor/CoordinatedIncrementSpec.scala b/akka-stm/src/test/scala/transactor/CoordinatedIncrementSpec.scala index f0cc8e5401..903a7cbd66 100644 --- a/akka-stm/src/test/scala/transactor/CoordinatedIncrementSpec.scala +++ b/akka-stm/src/test/scala/transactor/CoordinatedIncrementSpec.scala @@ -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)) } } } diff --git a/akka-stm/src/test/scala/transactor/FickleFriendsSpec.scala b/akka-stm/src/test/scala/transactor/FickleFriendsSpec.scala index 2d54032413..95014b17b3 100644 --- a/akka-stm/src/test/scala/transactor/FickleFriendsSpec.scala +++ b/akka-stm/src/test/scala/transactor/FickleFriendsSpec.scala @@ -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)) } } } diff --git a/akka-stm/src/test/scala/transactor/TransactorSpec.scala b/akka-stm/src/test/scala/transactor/TransactorSpec.scala index 0e50ebce5e..0cef37376e 100644 --- a/akka-stm/src/test/scala/transactor/TransactorSpec.scala +++ b/akka-stm/src/test/scala/transactor/TransactorSpec.scala @@ -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)) } } diff --git a/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala b/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala index b65af2be6c..bf8c62e995 100644 --- a/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala +++ b/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala @@ -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 } diff --git a/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala b/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala index 3402429ddd..c6827a3547 100644 --- a/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala @@ -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 diff --git a/akka-testkit/src/main/scala/akka/testkit/TestBarrier.scala b/akka-testkit/src/main/scala/akka/testkit/TestBarrier.scala index 4c6c59f5f5..8e9a6eebcd 100644 --- a/akka-testkit/src/main/scala/akka/testkit/TestBarrier.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestBarrier.scala @@ -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)) } } diff --git a/akka-testkit/src/main/scala/akka/testkit/TestEventListener.scala b/akka-testkit/src/main/scala/akka/testkit/TestEventListener.scala index c7773bdf80..1180fc52f8 100644 --- a/akka-testkit/src/main/scala/akka/testkit/TestEventListener.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestEventListener.scala @@ -1,11 +1,36 @@ +/** + * Copyright (C) 2009-2011 Typesafe Inc. + */ 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 Mute + * to the TestEventFilter 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. + */ 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 (true) or not + * (false). + */ + 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 start and pattern: + * + * {{{ + * 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 start and pattern: + * + * {{{ + * 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 start and pattern: + * + * {{{ + * 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 start and pattern: + * + * {{{ + * 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: + *
    + *
  • throwable applies an upper bound on the type of exception contained in the Error event
  • + *
  • source, if given, applies a filter on the event’s origin
  • + *
  • message 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 null, the filter always matches if at + * the same time the Exception’s stack trace is empty (this catches + * JVM-omitted “fast-throw” exceptions)
  • + *
+ * If you want to match all Error events, the most efficient is to use Left(""). + */ +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 null + * @param message + * apply this filter only to events whose message matches; do not filter on message if this is given as null + * @param pattern + * if false, the message string must start with the given + * string, otherwise the message 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: + *
    + *
  • source, if given, applies a filter on the event’s origin
  • + *
  • message applies a filter on the event’s message (either with String.startsWith or Regex.findFirstIn().isDefined)
  • + *
+ * If you want to match all Warning events, the most efficient is to use Left(""). + */ +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 null + * @param message + * apply this filter only to events whose message matches; do not filter on message if this is given as null + * @param pattern + * if false, the message string must start with the given + * string, otherwise the message 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: + *
    + *
  • source, if given, applies a filter on the event’s origin
  • + *
  • message applies a filter on the event’s message (either with String.startsWith or Regex.findFirstIn().isDefined)
  • + *
+ * If you want to match all Info events, the most efficient is to use Left(""). + */ +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 null + * @param message + * apply this filter only to events whose message matches; do not filter on message if this is given as null + * @param pattern + * if false, the message string must start with the given + * string, otherwise the message 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: + *
    + *
  • source, if given, applies a filter on the event’s origin
  • + *
  • message applies a filter on the event’s message (either with String.startsWith or Regex.findFirstIn().isDefined)
  • + *
+ * If you want to match all Debug events, the most efficient is to use Left(""). + */ +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 null + * @param message + * apply this filter only to events whose message matches; do not filter on message if this is given as null + * @param pattern + * if false, the message string must start with the given + * string, otherwise the message 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 + * akka.test.conf and run your tests with system property + * "akka.mode" set to "test": + * + *

+ * akka {
+ *   event-handlers = ["akka.testkit.TestEventListener"]
+ * }
+ * 
+ */ +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 diff --git a/akka-testkit/src/main/scala/akka/testkit/TestKit.scala b/akka-testkit/src/main/scala/akka/testkit/TestKit.scala index c3df4e1177..18ab673c89 100644 --- a/akka-testkit/src/main/scala/akka/testkit/TestKit.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestKit.scala @@ -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) { * */ 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 + } /** diff --git a/akka-testkit/src/main/scala/akka/testkit/TestLatch.scala b/akka-testkit/src/main/scala/akka/testkit/TestLatch.scala index 3c852bf75f..fa32257b0e 100644 --- a/akka-testkit/src/main/scala/akka/testkit/TestLatch.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestLatch.scala @@ -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 } diff --git a/akka-testkit/src/main/scala/akka/testkit/Testing.scala b/akka-testkit/src/main/scala/akka/testkit/Testing.scala deleted file mode 100644 index 70e4929b54..0000000000 --- a/akka-testkit/src/main/scala/akka/testkit/Testing.scala +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2009-2011 Typesafe Inc. - */ - -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)) -} diff --git a/akka-testkit/src/main/scala/akka/testkit/package.scala b/akka-testkit/src/main/scala/akka/testkit/package.scala index 54c6ed8182..975b426297 100644 --- a/akka-testkit/src/main/scala/akka/testkit/package.scala +++ b/akka-testkit/src/main/scala/akka/testkit/package.scala @@ -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) } diff --git a/akka-testkit/src/test/scala/akka/testkit/AkkaSpec.scala b/akka-testkit/src/test/scala/akka/testkit/AkkaSpec.scala index a3a7c86beb..aaa89e5fa8 100644 --- a/akka-testkit/src/test/scala/akka/testkit/AkkaSpec.scala +++ b/akka-testkit/src/test/scala/akka/testkit/AkkaSpec.scala @@ -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) + } + } +} + diff --git a/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala b/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala index b994d6a956..7177165963 100644 --- a/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala +++ b/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala @@ -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")) } } diff --git a/akka-testkit/src/test/scala/akka/testkit/TestProbeSpec.scala b/akka-testkit/src/test/scala/akka/testkit/TestProbeSpec.scala index c2842df0d6..e428769d74 100644 --- a/akka-testkit/src/test/scala/akka/testkit/TestProbeSpec.scala +++ b/akka-testkit/src/test/scala/akka/testkit/TestProbeSpec.scala @@ -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._ diff --git a/akka-testkit/src/test/scala/akka/testkit/TestTimeSpec.scala b/akka-testkit/src/test/scala/akka/testkit/TestTimeSpec.scala index ab3101b827..a8b50bc43a 100644 --- a/akka-testkit/src/test/scala/akka/testkit/TestTimeSpec.scala +++ b/akka-testkit/src/test/scala/akka/testkit/TestTimeSpec.scala @@ -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 { diff --git a/akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala b/akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala index 51d98bf7f9..50791f6d0d 100644 --- a/akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala +++ b/akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala @@ -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.") } } } diff --git a/config/akka-reference.conf b/config/akka-reference.conf index 0d6f0dd9bf..0abc87bdf5 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -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 diff --git a/config/akka.test.conf b/config/akka.test.conf index 4755e1f11f..8e21a7d184 100644 --- a/config/akka.test.conf +++ b/config/akka.test.conf @@ -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