From 03ae6100cd9a92b21b80a10d8890a4da244dd45a Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Sun, 27 Mar 2011 10:25:01 +0200 Subject: [PATCH 001/100] document CTD adaption of ActorModelSpec --- .../testkit/CallingThreadDispatcherModelSpec.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/akka-testkit/src/test/scala/akka/testkit/CallingThreadDispatcherModelSpec.scala b/akka-testkit/src/test/scala/akka/testkit/CallingThreadDispatcherModelSpec.scala index 22e16abdd9..8b52fb3fc4 100644 --- a/akka-testkit/src/test/scala/akka/testkit/CallingThreadDispatcherModelSpec.scala +++ b/akka-testkit/src/test/scala/akka/testkit/CallingThreadDispatcherModelSpec.scala @@ -7,8 +7,15 @@ class CallingThreadDispatcherModelSpec extends ActorModelSpec { import ActorModelSpec._ def newInterceptedDispatcher = new CallingThreadDispatcher with MessageDispatcherInterceptor + // A CallingThreadDispatcher can by design not process messages in parallel, + // so disable this test override def dispatcherShouldProcessMessagesInParallel {} + // This test needs to be adapted: CTD runs the flood completely sequentially + // with start, invocation, stop, schedule shutdown, abort shutdown, repeat; + // add "keeper" actor to lock down the dispatcher instance, since the + // frequent attempted shutdown seems rather costly (random timing failures + // without this fix) override def dispatcherShouldHandleWavesOfActors { implicit val dispatcher = newInterceptedDispatcher @@ -27,6 +34,7 @@ class CallingThreadDispatcherModelSpec extends ActorModelSpec { assertDispatcher(dispatcher)(starts = run, stops = run) } } + } -// vim: set ts=4 sw=4 et: +// vim: set ts=2 sw=2 et: From b169f356434696532dc82ea43c2289153b40f9dd Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Sun, 27 Mar 2011 18:15:40 +0200 Subject: [PATCH 002/100] fix error handling in TestKit.within restore outer deadline in case the code block does throw an exception. This is done in order to reduce the number of follow-on test failures. --- akka-testkit/src/main/scala/akka/testkit/TestKit.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/akka-testkit/src/main/scala/akka/testkit/TestKit.scala b/akka-testkit/src/main/scala/akka/testkit/TestKit.scala index a2d26ac4a8..5ce90509ba 100644 --- a/akka-testkit/src/main/scala/akka/testkit/TestKit.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestKit.scala @@ -157,7 +157,7 @@ trait TestKit { val prev_end = end end = start + max_diff - val ret = f + val ret = try f finally end = prev_end val diff = now - start assert (min <= diff, "block took "+format(min.unit, diff)+", should at least have been "+min) @@ -170,7 +170,6 @@ trait TestKit { lastSoftTimeout -= 5.millis } - end = prev_end ret } From 9c539e96fe04604fecbbc99e13bdb8b969803081 Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Sat, 16 Apr 2011 22:20:04 +0200 Subject: [PATCH 003/100] add TestActorRef - add adaptation of ActorRefSpec test suite - add some more specific tests --- .../testkit/CallingThreadDispatcher.scala | 9 +- .../scala/akka/testkit/TestActorRef.scala | 71 +++++ .../scala/akka/testkit/TestActorRefSpec.scala | 245 ++++++++++++++++++ project/build/AkkaProject.scala | 4 +- 4 files changed, 326 insertions(+), 3 deletions(-) create mode 100644 akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala create mode 100644 akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala diff --git a/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala b/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala index e6fd8ebbce..971ee2e89f 100644 --- a/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala +++ b/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala @@ -28,6 +28,11 @@ import scala.annotation.tailrec */ object CallingThreadDispatcher { + + lazy val global = new CallingThreadDispatcher + + // PRIVATE DATA + private var queues = Map[CallingThreadMailbox, Set[WeakReference[NestingQueue]]]() // we have to forget about long-gone threads sometime @@ -35,7 +40,7 @@ object CallingThreadDispatcher { queues = queues mapValues (_ filter (_.get ne null)) filter (!_._2.isEmpty) } - def registerQueue(mbox : CallingThreadMailbox, q : NestingQueue) : Unit = synchronized { + private[akka] def registerQueue(mbox : CallingThreadMailbox, q : NestingQueue) : Unit = synchronized { if (queues contains mbox) { val newSet = queues(mbox) + new WeakReference(q) queues += mbox -> newSet @@ -50,7 +55,7 @@ object CallingThreadDispatcher { * given mailbox. When this method returns, the queue will be entered * (active). */ - def gatherFromAllInactiveQueues(mbox : CallingThreadMailbox, own : NestingQueue) : Unit = synchronized { + private[akka] def gatherFromAllInactiveQueues(mbox : CallingThreadMailbox, own : NestingQueue) : Unit = synchronized { if (!own.isActive) own.enter if (queues contains mbox) { for { diff --git a/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala b/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala new file mode 100644 index 0000000000..b3c197cf7a --- /dev/null +++ b/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala @@ -0,0 +1,71 @@ +package akka.testkit + +import akka.actor._ +import akka.util.ReflectiveAccess +import akka.event.EventHandler + +/** + * This special ActorRef is exclusively for use during unit testing in a single-threaded environment. Therefore, it + * overrides the dispatcher to CallingThreadDispatcher and sets the receiveTimeout to None. Otherwise, + * it acts just like a normal ActorRef. You may retrieve a reference to the underlying actor to test internal logic. + * + * + * @author Roland Kuhn + * @since 1.1 + */ +class TestActorRef[T <: Actor](factory: () => T) extends LocalActorRef(factory, None) { + + dispatcher = CallingThreadDispatcher.global + receiveTimeout = None + + /** + * Retrieve reference to the underlying actor, where the static type matches the factory used inside the + * constructor. Beware that this reference is discarded by the ActorRef upon restarting the actor (should this + * reference be linked to a supervisor). The old Actor may of course still be used in post-mortem assertions. + */ + def underlyingActor : T = actor.asInstanceOf[T] + + /** + * Override to return the more specific static type. + */ + override def start() = { + super.start() + this + } + + override def toString = "TestActor[" + id + ":" + uuid + "]" + + override def equals(other : Any) = + other.isInstanceOf[TestActorRef[_]] && + other.asInstanceOf[TestActorRef[_]].uuid == uuid + + /** + * Override to check whether the new supervisor is running on the CallingThreadDispatcher, + * as it should be. This can of course be tricked by linking before setting the dispatcher before starting the + * supervisor, but then you just asked for trouble. + */ + override def supervisor_=(a : Option[ActorRef]) { + for (ref <- a) { + if (!ref.dispatcher.isInstanceOf[CallingThreadDispatcher]) + EventHandler.warning(this, "supervisor "+ref+" does not use CallingThreadDispatcher") + } + super.supervisor_=(a) + } + +} + +object TestActorRef { + + def apply[T <: Actor](factory: => T) = new TestActorRef(() => factory) + + def apply[T <: Actor : Manifest] : TestActorRef[T] = new TestActorRef[T] ({ () => + import ReflectiveAccess.{ createInstance, noParams, noArgs } + createInstance[T](manifest[T].erasure, noParams, noArgs).getOrElse( + throw new ActorInitializationException( + "Could not instantiate Actor" + + "\nMake sure Actor is NOT defined inside a class/trait," + + "\nif so put it outside the class/trait, f.e. in a companion object," + + "\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.")) + }) + +} \ No newline at end of file diff --git a/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala b/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala new file mode 100644 index 0000000000..1d79100b71 --- /dev/null +++ b/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala @@ -0,0 +1,245 @@ +package akka.testkit + +import org.scalatest.matchers.MustMatchers +import org.scalatest.{BeforeAndAfterEach, WordSpec} +import akka.actor._ +import akka.config.Supervision.OneForOneStrategy +import akka.event.EventHandler +import akka.dispatch.Future + +/** + * Test whether TestActorRef behaves as an ActorRef should, besides its own spec. + * + * @author Roland Kuhn + */ + +object TestActorRefSpec { + + var counter = 4 + val thread = Thread.currentThread + var otherthread : Thread = null + + trait TActor extends Actor { + def receive = new Receive { + val recv = receiveT + def isDefinedAt(o : Any) = recv.isDefinedAt(o) + def apply(o : Any) { + if (Thread.currentThread ne thread) + otherthread = Thread.currentThread + recv(o) + } + } + def receiveT : Receive + } + + class ReplyActor extends TActor { + var replyTo: Channel[Any] = null + + def receiveT = { + case "complexRequest" => { + replyTo = self.channel + val worker = TestActorRef[WorkerActor].start() + worker ! "work" + } + case "complexRequest2" => + val worker = TestActorRef[WorkerActor].start() + worker ! self.channel + case "workDone" => replyTo ! "complexReply" + case "simpleRequest" => self.reply("simpleReply") + } + } + + class WorkerActor() extends TActor { + def receiveT = { + case "work" => { + self.reply("workDone") + self.stop() + } + case replyTo: Channel[Any] => { + replyTo ! "complexReply" + } + } + } + + class SenderActor(replyActor: ActorRef) extends TActor { + + def receiveT = { + case "complex" => replyActor ! "complexRequest" + case "complex2" => replyActor ! "complexRequest2" + case "simple" => replyActor ! "simpleRequest" + case "complexReply" => { + counter -= 1 + } + case "simpleReply" => { + counter -= 1 + } + } + } + + class Logger extends Actor { + import EventHandler._ + var count = 0 + var msg : String = _ + def receive = { + case Warning(_, m : String) => count += 1; msg = m + } + } + +} + +class TestActorRefSpec extends WordSpec with MustMatchers with BeforeAndAfterEach { + + import TestActorRefSpec._ + + override def beforeEach { + otherthread = null + } + + private def assertThread { + otherthread must (be (null) or equal (thread)) + } + + "A TestActorRef must be an ActorRef, hence it" must { + + "support nested Actor creation" when { + + "used with TestActorRef" in { + val a = TestActorRef(new Actor { + val nested = TestActorRef(new Actor { def receive = { case _ => } }).start() + def receive = { case _ => self reply nested } + }).start() + a must not be (null) + val nested = (a !! "any").get.asInstanceOf[ActorRef] + nested must not be (null) + a must not be theSameInstanceAs (nested) + } + + "used with ActorRef" in { + val a = TestActorRef(new Actor { + val nested = Actor.actorOf(new Actor { def receive = { case _ => } }).start() + def receive = { case _ => self reply nested } + }).start() + a must not be (null) + val nested = (a !! "any").get.asInstanceOf[ActorRef] + nested must not be (null) + a must not be theSameInstanceAs (nested) + } + + } + + "support reply via channel" in { + val serverRef = TestActorRef[ReplyActor].start() + val clientRef = TestActorRef(new SenderActor(serverRef)).start() + + counter = 4 + + clientRef ! "complex" + clientRef ! "simple" + clientRef ! "simple" + clientRef ! "simple" + + counter must be (0) + + counter = 4 + + clientRef ! "complex2" + clientRef ! "simple" + clientRef ! "simple" + clientRef ! "simple" + + counter must be (0) + + assertThread + } + + "stop when sent a poison pill" in { + val a = TestActorRef[WorkerActor].start() + intercept[ActorKilledException] { + a !! PoisonPill + } + a must not be ('running) + a must be ('shutdown) + assertThread + } + + "restart when Kill:ed" in { + counter = 2 + + val boss = TestActorRef(new TActor { + self.faultHandler = OneForOneStrategy(List(classOf[Throwable]), Some(2), Some(1000)) + val ref = TestActorRef(new TActor { + def receiveT = { case _ => } + override def preRestart(reason: Throwable) { counter -= 1 } + override def postRestart(reason: Throwable) { counter -= 1 } + }).start() + self link ref + def receiveT = { case "sendKill" => ref ! Kill } + }).start() + + val l = stopLog() + boss ! "sendKill" + startLog(l) + + counter must be (0) + assertThread + } + + "support futures" in { + val a = TestActorRef[WorkerActor].start() + val f : Future[String] = a !!! "work" + f must be ('completed) + f.get must equal ("workDone") + } + + } + + "A TestActorRef" must { + + "allow access to internals" in { + val ref = TestActorRef(new TActor { + var s : String = _ + def receiveT = { + case x : String => s = x + } + }).start() + ref ! "hallo" + val actor = ref.underlyingActor + actor.s must equal ("hallo") + } + + "set receiveTimeout to None" in { + val a = TestActorRef[WorkerActor] + a.receiveTimeout must be (None) + } + + "set CallingThreadDispatcher" in { + val a = TestActorRef[WorkerActor] + a.dispatcher.getClass must be (classOf[CallingThreadDispatcher]) + } + + "warn about scheduled supervisor" in { + val boss = Actor.actorOf(new Actor { def receive = { case _ => } }).start() + val ref = TestActorRef[WorkerActor].start() + + val log = TestActorRef[Logger] + EventHandler.addListener(log) + boss link ref + val la = log.underlyingActor + la.count must be (1) + la.msg must (include ("supervisor") and include ("CallingThreadDispatcher")) + EventHandler.removeListener(log) + } + + } + + private def stopLog() = { + val l = Actor.registry.actorsFor[EventHandler.DefaultListener] + l foreach (EventHandler.removeListener(_)) + l + } + + private def startLog(l : Array[ActorRef]) { + l foreach {a => EventHandler.addListener(Actor.actorOf[EventHandler.DefaultListener])} + } + +} \ No newline at end of file diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 72598969ba..7666cc7543 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -422,7 +422,9 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // akka-testkit subproject // ------------------------------------------------------------------------------------------------------------------- - class AkkaTestkitProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) + class AkkaTestkitProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + val scalatest = Dependencies.scalatest + } // ------------------------------------------------------------------------------------------------------------------- // akka-actor-tests subproject From 4868f726c54bd17e2b0b154fd60a37e9aa0b5119 Mon Sep 17 00:00:00 2001 From: Roland Date: Thu, 21 Apr 2011 21:29:57 +0200 Subject: [PATCH 004/100] add Future.channel() for obtaining a completable channel --- akka-actor/src/main/scala/akka/dispatch/Future.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 1f86613b47..315da653f0 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -6,7 +6,7 @@ package akka.dispatch import akka.AkkaException import akka.event.EventHandler -import akka.actor.Actor +import akka.actor.{Actor, Channel} import akka.routing.Dispatcher import akka.japi.{ Procedure, Function => JFunc } @@ -217,6 +217,14 @@ object Future { dispatcher.dispatchFuture(FutureInvocation(f.asInstanceOf[CompletableFuture[Any]], () => body)) f } + + /** + * Construct a completable channel + */ + def channel(timeout: Long = Actor.TIMEOUT) = new Channel[Any] { + val future = new DefaultCompletableFuture[Any](timeout) + def !(msg: Any) = future << msg + } } sealed trait Future[+T] { From b6446f50dd4fece98730ab67e567dd78491b2c4a Mon Sep 17 00:00:00 2001 From: Roland Date: Thu, 21 Apr 2011 21:31:24 +0200 Subject: [PATCH 005/100] proxy isDefinedAt/apply through TestActorRef --- .../src/main/scala/akka/testkit/TestActorRef.scala | 14 +++++++++++++- .../test/scala/akka/testkit/TestActorRefSpec.scala | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala b/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala index b3c197cf7a..a31582ac3a 100644 --- a/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala @@ -18,6 +18,18 @@ class TestActorRef[T <: Actor](factory: () => T) extends LocalActorRef(factory, dispatcher = CallingThreadDispatcher.global receiveTimeout = None + /** + * Query actor's current receive behavior. + */ + override def isDefinedAt(o : Any) = actor.isDefinedAt(o) + + /** + * Directly inject messages into actor receive behavior. Any exceptions + * thrown will be available to you, while still being able to use + * become/unbecome and their message counterparts. + */ + def apply(o : Any) { actor(o) } + /** * Retrieve reference to the underlying actor, where the static type matches the factory used inside the * constructor. Beware that this reference is discarded by the ActorRef upon restarting the actor (should this @@ -68,4 +80,4 @@ object TestActorRef { "\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.")) }) -} \ No newline at end of file +} diff --git a/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala b/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala index 1d79100b71..f95b78d028 100644 --- a/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala +++ b/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala @@ -172,6 +172,7 @@ class TestActorRefSpec extends WordSpec with MustMatchers with BeforeAndAfterEac override def preRestart(reason: Throwable) { counter -= 1 } override def postRestart(reason: Throwable) { counter -= 1 } }).start() + self.dispatcher = CallingThreadDispatcher.global self link ref def receiveT = { case "sendKill" => ref ! Kill } }).start() @@ -230,6 +231,17 @@ class TestActorRefSpec extends WordSpec with MustMatchers with BeforeAndAfterEac EventHandler.removeListener(log) } + "proxy isDefinedAt/apply for the underlying actor" in { + val ref = TestActorRef[WorkerActor].start() + ref.isDefinedAt("work") must be (true) + ref.isDefinedAt("sleep") must be (false) + val ch = Future.channel() + ref ! ch + val f = ch.future + f must be ('completed) + f() must be ("complexReply") + } + } private def stopLog() = { @@ -242,4 +254,4 @@ class TestActorRefSpec extends WordSpec with MustMatchers with BeforeAndAfterEac l foreach {a => EventHandler.addListener(Actor.actorOf[EventHandler.DefaultListener])} } -} \ No newline at end of file +} From cb332b27563e81b4830c82f5998d82c2ade5c2ce Mon Sep 17 00:00:00 2001 From: Roland Date: Thu, 21 Apr 2011 21:31:30 +0200 Subject: [PATCH 006/100] make testActor.dispatcher=CallingThreadDispatcher - it's needed for unit testing, and it does not hurt since testActor does not send anything --- akka-testkit/src/main/scala/akka/testkit/TestKit.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/akka-testkit/src/main/scala/akka/testkit/TestKit.scala b/akka-testkit/src/main/scala/akka/testkit/TestKit.scala index c5cfec6e43..fdad53c4ff 100644 --- a/akka-testkit/src/main/scala/akka/testkit/TestKit.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestKit.scala @@ -20,6 +20,8 @@ class TestActor(queue : BlockingDeque[AnyRef]) extends Actor with FSM[Int, TestA import FSM._ import TestActor._ + self.dispatcher = CallingThreadDispatcher.global + startWith(0, None) when(0, stateTimeout = 5 seconds) { case Ev(SetTimeout(d)) => From df9be277188e23e42588887741886c586e24cceb Mon Sep 17 00:00:00 2001 From: Roland Date: Thu, 21 Apr 2011 21:59:12 +0200 Subject: [PATCH 007/100] test exception reception on TestActorRef.apply() --- akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala b/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala index f95b78d028..47c4908148 100644 --- a/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala +++ b/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala @@ -235,6 +235,7 @@ class TestActorRefSpec extends WordSpec with MustMatchers with BeforeAndAfterEac val ref = TestActorRef[WorkerActor].start() ref.isDefinedAt("work") must be (true) ref.isDefinedAt("sleep") must be (false) + intercept[IllegalActorStateException] { ref("work") } val ch = Future.channel() ref ! ch val f = ch.future From 1715e3ca582e866062354bc6899618e3d144514f Mon Sep 17 00:00:00 2001 From: Roland Date: Thu, 21 Apr 2011 22:06:46 +0200 Subject: [PATCH 008/100] add testing doc (scala) --- akka-docs/scala/fsm.rst | 36 ++-- akka-docs/scala/index.rst | 1 + akka-docs/scala/testing.rst | 350 ++++++++++++++++++++++++++++++++++++ 3 files changed, 361 insertions(+), 26 deletions(-) create mode 100644 akka-docs/scala/testing.rst diff --git a/akka-docs/scala/fsm.rst b/akka-docs/scala/fsm.rst index a23fd9edf4..b982f89a4b 100644 --- a/akka-docs/scala/fsm.rst +++ b/akka-docs/scala/fsm.rst @@ -1,5 +1,6 @@ +### FSM -=== +### .. sidebar:: Contents @@ -14,7 +15,7 @@ FSM Module stability: **STABLE** Overview -++++++++ +======== The FSM (Finite State Machine) is available as a mixin for the akka Actor and is best described in the `Erlang design principles @@ -29,7 +30,7 @@ These relations are interpreted as meaning: *If we are in state S and the event E occurs, we should perform the actions A and make a transition to the state S'.* A Simple Example -++++++++++++++++ +================ To demonstrate the usage of states we start with a simple FSM without state data. The state can be of any type so for this example we create the states A, @@ -94,7 +95,7 @@ FSM is finished by calling the :func:`initialize` method as last part of the ABC constructor. State Data -++++++++++ +========== The FSM can also hold state data associated with the internal state of the state machine. The state data can be of any type but to demonstrate let's look @@ -152,7 +153,7 @@ This encapsulation is what makes state machines a powerful abstraction, e.g. for handling socket states in a network server application. Reference -+++++++++ +========= This section describes the DSL in a more formal way, refer to `Examples`_ for more sample material. @@ -194,24 +195,7 @@ The :class:`FSM` trait takes two type parameters: Defining Timeouts ----------------- -The :class:`FSM` module uses :class:`akka.util.Duration` for all timing -configuration, which includes a mini-DSL: - -.. code-block:: scala - - import akka.util.duration._ // notice the small d - - val fivesec = 5.seconds - val threemillis = 3.millis - val diff = fivesec - threemillis - -.. note:: - - You may leave out the dot if the expression is clearly delimited (e.g. - within parentheses or in an argument list), but it is recommended to use it - if the time unit is the last token on a line, otherwise semi-colon inference - might go wrong, depending on what starts the next line. - +The :class:`FSM` module uses :ref:`Duration` for all timing configuration. Several methods, like :func:`when()` and :func:`startWith()` take a :class:`FSM.Timeout`, which is an alias for :class:`Option[Duration]`. There is an implicit conversion available in the :obj:`FSM` object which makes this @@ -344,7 +328,7 @@ state variable, as everything within the FSM actor is running single-threaded anyway. Internal Monitoring -******************* +^^^^^^^^^^^^^^^^^^^ Up to this point, the FSM DSL has been centered on states and events. The dual view is to describe it as a series of transitions. This is enabled by the @@ -398,7 +382,7 @@ are still executed in declaration order, though. a certain state cannot be forgot when adding new target states. External Monitoring -******************* +^^^^^^^^^^^^^^^^^^^ External actors may be registered to be notified of state transitions by sending a message :class:`SubscribeTransitionCallBack(actorRef)`. The named @@ -476,7 +460,7 @@ As for the :func:`whenUnhandled` case, this handler is not stacked, so each invocation of :func:`onTermination` replaces the previously installed handler. Examples -++++++++ +======== A bigger FSM example can be found in the sources: diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index 645efccf41..e54c88b979 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -6,3 +6,4 @@ Scala API actors fsm + testing diff --git a/akka-docs/scala/testing.rst b/akka-docs/scala/testing.rst new file mode 100644 index 0000000000..2860bd2cef --- /dev/null +++ b/akka-docs/scala/testing.rst @@ -0,0 +1,350 @@ +##################### +Testing Actor Systems +##################### + +.. sidebar:: Contents + + .. contents:: :local: + +.. module:: akka-testkit + :synopsis: Tools for Testing Actor Systems +.. moduleauthor:: Roland Kuhn +.. versionadded:: 1.0 +.. versionchanged:: 1.1 + added :class:`TestActorRef` + +As with any piece of software, automated tests are a very important part of the +development cycle. The actor model presents a different view on how units of +code are delimited and how they interact, which has an influence on how to +perform tests. + +Akka comes with a dedicated module :mod:`akka-testkit` for supporting tests at +different levels, which fall into two clearly distinct categories: + + - Testing isolated pieces of code without involving the actor model, meaning + without multiple threads; this implies completely deterministic behavior + concerning the ordering of events and no concurrency concerns and will be + called **Unit Testing** in the following. + - Testing (multiple) encapsulated actors including multi-threaded scheduling; + this implies non-deterministic order of events but shielding from + concurrency concerns by the actor model and will be called **Integration + Testing** in the following. + +There are of course variations on the granularity of tests in both categories, +where unit testing reaches down to white-box tests and integration testing can +encompass functional tests of complete actor networks. The important +distinction lies in whether concurrency concerns are part of the test or not. +The tools offered are described in detail in the following sections. + +Unit Testing with :class:`TestActorRef` +======================================= + +Testing the business logic inside :class:`Actor` classes can be divided into +two parts: first, each atomic operation must work in isolation, then sequences +of incoming events must be processed correctly, even in the presence of some +possible variability in the ordering of events. The former is the primary use +case for single-threaded unit testing, while the latter can only be verified in +integration tests. + +Normally, the :class:`ActorRef` shields the underlying :class:`Actor` instance +from the outside, the only communications channel is the actor's mailbox. This +restriction is an impediment to unit testing, which led to the inception of the +:class:`TestActorRef`. This special type of reference is designed specifically +for test purposes and allows access to the actor in two ways: either by +obtaining a reference to the underlying actor instance, or by invoking or +querying the actor's behaviour (:meth:`receive`). Each one warrants its own +section below. + +Obtaining a Reference to an :class:`Actor` +------------------------------------------ + +Having access to the actual :class:`Actor` object allows application of all +traditional unit testing techniques on the contained methods. Obtaining a +reference is done like this: + +.. code-block:: scala + + val actorRef = TestActorRef[MyActor] + val actor = actorRef.underlyingActor + +Since :class:`TestActorRef` is generic in the actor type it returns the +underlying actor with its proper static type. From this point on you may bring +any unit testing tool to bear on your actor as usual. + +Testing the Actor's Behavior +---------------------------- + +When the dispatcher invokes the processing behavior of an actor on a message, +it actually calls :meth:`apply` on the current behavior registered for the +actor. This starts out with the return value of the declared :meth:`receive` +method, but it may also be changed using :meth:`become` and :meth:`unbecome`, +both of which have corresponding message equivalents, meaning that the behavior +may be changed from the outside. All of this contributes to the overall actor +behavior and it does not lend itself to easy testing on the :class:`Actor` +itself. Therefore the :class:`TestActorRef` offers a different mode of +operation to complement the :class:`Actor` testing: it supports all operations +also valid on normal :class:`ActorRef`. Messages sent to the actor are +processed synchronously on the current thread and answers may be sent back as +usual. This trick is made possible by the :class:`CallingThreadDispatcher` +described below; this dispatcher is set implicitly for any actor instantiated +into a :class:`TestActorRef`. + +.. code-block:: scala + + val actorRef = TestActorRef(new MyActor) + val result = actorRef !! msg + result must be (expected) + +As the :class:`TestActorRef` is a subclass of :class:`LocalActorRef` with a few +special extras, also aspects like linking to a supervisor and restarting work +properly, as long as all actors involved use the +:class:`CallingThreadDispatcher`. As soon as you add elements which include +more sophisticated scheduling you leave the realm of unit testing as you then +need to think about proper synchronization again (in most cases the problem of +waiting until the desired effect had a chance to happen). + +One more special aspect which is overridden for single-threaded tests is the +:meth:`receiveTimeout`, as including that would entail asynchronous queuing of +:obj:`ReceiveTimeout` messages, violating the synchronous contract. + +.. warning:: + + To summarize: :class:`TestActorRef` overwrites two fields: it sets the + dispatcher to :obj:`CallingThreadDispatcher.global` and it sets the + :obj:`receiveTimeout` to zero. + +The Way In-Between +------------------ + +If you want to test the actor behavior, including hotswapping, but without +involving a dispatcher and without having the :class:`TestActorRef` swallow +any thrown exceptions, then there is another mode available for you: just use +the :class:`TestActorRef` as a partial function, the calls to +:meth:`isDefinedAt` and :meth:`apply` will be forwarded to the underlying +actor: + +.. code-block:: scala + + val ref = TestActorRef[MyActor] + ref.isDefinedAt('unknown) must be (false) + intercept[IllegalActorStateException] { ref(RequestReply) } + +Use Cases +--------- + +You may of course mix and match both modi operandi of :class:`TestActorRef` as +suits your test needs: + + - one common use case is setting up the actor into a specific internal state + before sending the test message + - another is to verify correct internal state transitions after having sent + the test message + +Feel free to experiment with the possibilities, and if you find useful +patterns, don't hesitate to let the Akka forums know about them! Who knows, +common operations might even be worked into nice DSLs. + +Integration Testing with :class:`TestKit` +========================================= + +When you are reasonably sure that your actor's business logic is correct, the +next step is verifying that it works correctly within its intended environment +(if the individual actors are simple enough, possibly because they use the +:mod:`FSM` module, this might also be the first step). The definition of the +environment depends of course very much on the problem at hand and the level at +which you intend to test, ranging for functional/integration tests to full +system tests. The minimal setup consists of the test procedure, which provides +the desired stimuli, the actor under test, and an actor receiving replies. +Bigger systems replace the actor under test with a network of actors, apply +stimuli at varying injection points and arrange results to be sent from +different emission points, but the basic principle stays the same in that a +single procedure drives the test. + +The :class:`TestKit` trait contains a collection of tools which makes this +common task easy: + +.. code-block:: scala + + class MySpec extends WordSpec with MustMatchers with TestKit { + + "An Echo actor" must { + + "send back messages unchanged" in { + + val echo = Actor.actorOf[EchoActor].start() + echo ! "hello world" + expectMsg("hello world") + + } + + } + + } + +The :class:`TestKit` contains an actor named :obj:`testActor` which is +implicitly used as sender reference when dispatching messages from the test +procedure. This enables replies to be received by this internal actor, whose +only function is to queue them so that interrogation methods like +:meth:`expectMsg` can examine them. The :obj:`testActor` may also be passed to +other actors as usual, usually subscribing it as notification listener. There +is a whole set of examination methods, e.g. receiving all consecutive messages +matching certain criteria, receiving a whole sequence of fixed messages or +classes, receiving nothing for some time, etc. + +.. note:: + + The test actor shuts itself down by default after 5 seconds (configurable) + of inactivity, relieving you of the duty of explicitly managing it. + +Another important part of functional testing concerns timing: certain events +must not happen immediately (like a timer), others need to happen before a +deadline. Therefore, all examination methods accept an upper time limit within +the positive or negative result must be obtained. Lower time limits need to be +checked external to the examination, which is facilitated by a new construct +for managing time constraints: + +.. code-block:: scala + + within([min, ]max) { + ... + } + +The block given to :meth:`within` must complete after a :ref:`Duration` which +is between :obj:`min` and :obj:`max`, where the former defaults to zero. The +deadline calculated by adding the :obj:`max` parameter to the block's start +time is implicitly available within the block to all examination methods, if +you do not specify it, is is inherited from the innermost enclosing +:meth:`within` block. It should be noted that using :meth:`expectNoMsg` will +terminate upon reception of a message or at the deadline, whichever occurs +first; it follows that this examination usually is the last statement in a +:meth:`within` block. + +CallingThreadDispatcher +======================= + +The :class:`CallingThreadDispatcher` serves good purposes in unit testing, as +described above, but originally it was conceived in order to allow contiguous +stack traces to be generated in case of an error. As this special dispatcher +runs everything which would normally be queued directly on the current thread, +the full history of a message's processing chain is recorded on the call stack, +so long as all intervening actors run on this dispatcher. + +How it works +------------ + +When receiving an invocation, the :class:`CallingThreadDispatcher` checks +whether the receiving actor is already active on the current thread. The +simplest example for this situation is an actor which sends a message to +itself. In this case, processing cannot continue immediately as that would +violate the actor model, so the invocation is queued and will be processed when +the active invocation on that actor finishes its processing; thus, it will be +processed on the calling thread, but simply after the actor finishes its +previous work. In the other case, the invocation is simply processed +immediately on the current thread. Futures scheduled via this dispatcher are +also executed immediately. + +This scheme makes the :class:`CallingThreadDispatcher` work like a general +purpose dispatcher for any actors which never block on external events. + +In the presence of multiple threads it may happen that two invocations of an +actor running on this dispatcher happen on two different threads at the same +time. In this case, both will be processed directly on their respective +threads, where both compete for the actor's lock and the loser has to wait. +Thus, the actor model is left intact, but the price is loss of concurrency due +to limited scheduling. In a sense this is equivalent to traditional mutex style +concurrency. + +The other remaining difficulty is correct handling of suspend and resume: when +an actor is suspended, subsequent invocations will be queued in thread-local +queues (the same ones used for queuing in the normal case). The call to +:meth:`resume`, however, is done by one specific thread, and all other threads +in the system will probably not be executing this specific actor, which leads +to the problem that the thread-local queues cannot be emptied by their native +threads. Hence, the thread calling :meth:`resume` will collect all currently +queued invocations from all threads into its own queue and process them. + +Limitations +----------- + +If an actor's behavior blocks on a something which would normally be affected +by the calling actor after having sent the message, this will obviously +dead-lock when using this dispatcher. This is a common scenario in actor tests +based on :class:`CountDownLatch` for synchronization: + +.. code-block:: scala + + val latch = new CountDownLatch(1) + actor ! startWorkAfter(latch) // actor will call latch.await() before proceeding + doSomeSetupStuff() + latch.countDown() + +The example would hang indefinitely within the message processing initiated on +the second line and never reach the fourth line, which would unblock it on a +normal dispatcher. + +Thus, keep in mind that the :class:`CallingThreadDispatcher` is not a +general-purpose replacement for the normal dispatchers. On the other hand it +may be quite useful to run your actor network on it for testing, because if it +runs without dead-locking chances are very high that it will not dead-lock in +production. + +.. warning:: + + The above sentence is unfortunately not a strong guarantee, because your + code might directly or indirectly change its behavior when running on a + different dispatcher. If you are looking for a tool to help you debug + dead-locks, the :class:`CallingThreadDispatcher` may help with certain error + scenarios, but keep in mind that it has may give false negatives as well as + false positives. + +Benefits +-------- + +To summarize, these are the features with the :class:`CallingThreadDispatcher` +has to offer: + + - Deterministic execution of single-threaded tests while retaining nearly full + actor semantics + - Full message processing history leading up to the point of failure in + exception stack traces + - Exclusion of certain classes of dead-lock scenarios + +.. _Duration: + +Duration +======== + +Durations are used throughout the Akka library, wherefore this concept is +represented by a special data type, :class:`Duration`. Values of this type may +represent infinite (:obj:`Duration.Inf`, :obj:`Duration.MinusInf`) or finite +durations, where the latter are constructable using a mini-DSL: + +.. code-block:: scala + + import akka.util.duration._ // notice the small d + + val fivesec = 5.seconds + val threemillis = 3.millis + val diff = fivesec - threemillis + assert (diff < fivesec) + +.. note:: + + You may leave out the dot if the expression is clearly delimited (e.g. + within parentheses or in an argument list), but it is recommended to use it + if the time unit is the last token on a line, otherwise semi-colon inference + might go wrong, depending on what starts the next line. + +Java provides less syntactic sugar, so you have to spell out the operations as +method calls instead: + +.. code-block:: java + + final Duration fivesec = Duration.create(5, "seconds"); + final Duration threemillis = Duration.parse("3 millis"); + final Duration diff = fivesec.minus(threemillis); + assert (diff.lt(fivesec)); + assert (Duration.Zero().lt(Duration.Inf())); + + + From 78250478a770205e2ee83298bd58968c8c862756 Mon Sep 17 00:00:00 2001 From: Roland Date: Thu, 21 Apr 2011 22:20:43 +0200 Subject: [PATCH 009/100] add references to testkit example from Ray --- .../scala/migration-guide-0.7.x-0.8.x.rst | 94 ---------- .../scala/migration-guide-0.8.x-0.9.x.rst | 170 ------------------ .../scala/migration-guide-0.9.x-0.10.x.rst | 45 ----- .../scala/migration-guide-1.0.x-1.1.x.rst | 37 ---- akka-docs/scala/migration-guides.rst | 8 - akka-docs/scala/testing.rst | 23 +++ akka-docs/scala/testkit-example.rst | 145 +++++++++++++++ 7 files changed, 168 insertions(+), 354 deletions(-) delete mode 100644 akka-docs/scala/migration-guide-0.7.x-0.8.x.rst delete mode 100644 akka-docs/scala/migration-guide-0.8.x-0.9.x.rst delete mode 100644 akka-docs/scala/migration-guide-0.9.x-0.10.x.rst delete mode 100644 akka-docs/scala/migration-guide-1.0.x-1.1.x.rst delete mode 100644 akka-docs/scala/migration-guides.rst create mode 100644 akka-docs/scala/testkit-example.rst diff --git a/akka-docs/scala/migration-guide-0.7.x-0.8.x.rst b/akka-docs/scala/migration-guide-0.7.x-0.8.x.rst deleted file mode 100644 index 5c45eb76c1..0000000000 --- a/akka-docs/scala/migration-guide-0.7.x-0.8.x.rst +++ /dev/null @@ -1,94 +0,0 @@ -Migrate from 0.7.x to 0.8.x -=========================== - -This is a case-by-case migration guide from Akka 0.7.x (on Scala 2.7.7) to Akka 0.8.x (on Scala 2.8.x) ------------------------------------------------------------------------------------------------------- - -Cases: -====== - -Actor.send is removed and replaced in full with Actor.! -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: scala - - myActor send "test" - -becomes - -.. code-block:: scala - - myActor ! "test" - -Actor.! now has it's implicit sender defaulted to None -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: scala - - def !(message: Any)(implicit sender: Option[Actor] = None) - -"import Actor.Sender.Self" has been removed because it's not needed anymore -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Remove - -.. code-block:: scala - - import Actor.Sender.Self - -Actor.spawn now uses manifests instead of concrete class types -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: scala - - val someActor = spawn(classOf[MyActor]) - -becomes - -.. code-block:: scala - - val someActor = spawn[MyActor] - -Actor.spawnRemote now uses manifests instead of concrete class types -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: scala - - val someActor = spawnRemote(classOf[MyActor],"somehost",1337) - -becomes - -.. code-block:: scala - - val someActor = spawnRemote[MyActor]("somehost",1337) - -Actor.spawnLink now uses manifests instead of concrete class types -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: scala - - val someActor = spawnLink(classOf[MyActor]) - -becomes - -.. code-block:: scala - - val someActor = spawnLink[MyActor] - -Actor.spawnLinkRemote now uses manifests instead of concrete class types -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: scala - - val someActor = spawnLinkRemote(classOf[MyActor],"somehost",1337) - -becomes - -.. code-block:: scala - - val someActor = spawnLinkRemote[MyActor]("somehost",1337) - -**Transaction.atomic and friends are moved into Transaction.Local._ and Transaction.Global._** -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -We now make a difference between transaction management that are local within a thread and global across many threads (and actors). diff --git a/akka-docs/scala/migration-guide-0.8.x-0.9.x.rst b/akka-docs/scala/migration-guide-0.8.x-0.9.x.rst deleted file mode 100644 index 359cb01602..0000000000 --- a/akka-docs/scala/migration-guide-0.8.x-0.9.x.rst +++ /dev/null @@ -1,170 +0,0 @@ -**This document describes between the 0.8.x and the 0.9 release.** - -Background for the new ActorRef -=============================== - -In the work towards 0.9 release we have now done a major change to how Actors are created. In short we have separated identity and value, created an 'ActorRef' that holds the actual Actor instance. This allows us to do many great things such as for example: - -* Create serializable, immutable, network-aware Actor references that can be freely shared across the network. They "remember" their origin and will always work as expected. -* Not only kill and restart the same supervised Actor instance when it has crashed (as we do now), but dereference it, throw it away and make it eligible for garbage collection. -* etc. much more - -These work very much like the 'PID' (process id) in Erlang. - -These changes means that there is no difference in defining Actors. You still use the old Actor trait, all methods are there etc. But you can't just new this Actor up and send messages to it since all its public API methods are gone. They now reside in a new class; 'ActorRef' and use need to use instances of this class to interact with the Actor (sending messages etc.). - -Here is a short migration guide with the things that you have to change. It is a big conceptual change but in practice you don't have to change much. - -Migration Guide -=============== - -Creating Actors with default constructor ----------------------------------------- - -From: - -.. code-block:: scala - - val a = new MyActor - a ! msg - -To: - -.. code-block:: scala - - import Actor._ - val a = actorOf[MyActor] - a ! msg - -You can also start it in the same statement: - -.. code-block:: scala - - val a = actorOf[MyActor].start - -Creating Actors with non-default constructor --------------------------------------------- - -From: - -.. code-block:: scala - - val a = new MyActor(..) - a ! msg - -To: - -.. code-block:: scala - - import Actor._ - val a = actorOf(new MyActor(..)) - a ! msg - -Use of 'self' ActorRef API --------------------------- - -Where you have used 'this' to refer to the Actor from within itself now use 'self': - -.. code-block:: scala - - self ! MessageToMe - -Now the Actor trait only has the callbacks you can implement: -* receive -* postRestart/preRestart -* init/shutdown - -It has no state at all. - -All API has been moved to ActorRef. The Actor is given its ActorRef through the 'self' member variable. -Here you find functions like: -* !, !!, !!! and forward -* link, unlink, startLink, spawnLink etc -* makeTransactional, makeRemote etc. -* start, stop -* etc. - -Here you also find fields like -* dispatcher = ... -* id = ... -* lifeCycle = ... -* faultHandler = ... -* trapExit = ... -* etc. - -This means that to use them you have to prefix them with 'self', like this: - -.. code-block:: scala - - self ! Message - -However, for convenience you can import these functions and fields like below, which will allow you do drop the 'self' prefix: - -.. code-block:: scala - - class MyActor extends Actor { - import self._ - id = ... - dispatcher = ... - spawnLink[OtherActor] - ... - } - -Serialization -============= - -If you want to serialize it yourself, here is how to do it: - -.. code-block:: scala - - val actorRef1 = actorOf[MyActor] - - val bytes = actorRef1.toBinary - - val actorRef2 = ActorRef.fromBinary(bytes) - -If you are also using Protobuf then you can use the methods that work with Protobuf's Messages directly. - -.. code-block:: scala - - val actorRef1 = actorOf[MyActor] - - val protobufMessage = actorRef1.toProtocol - - val actorRef2 = ActorRef.fromProtocol(protobufMessage) - -Camel -====== - -Some methods of the se.scalablesolutions.akka.camel.Message class have been deprecated in 0.9. These are - -.. code-block:: scala - - package se.scalablesolutions.akka.camel - - case class Message(...) { - // ... - @deprecated def bodyAs[T](clazz: Class[T]): T - @deprecated def setBodyAs[T](clazz: Class[T]): Message - // ... - } - -They will be removed in 1.0. Instead use - -.. code-block:: scala - - package se.scalablesolutions.akka.camel - - case class Message(...) { - // ... - def bodyAs[T](implicit m: Manifest[T]): T = - def setBodyAs[T](implicit m: Manifest[T]): Message - // ... - } - -Usage example: -.. code-block:: scala - - val m = Message(1.4) - val b = m.bodyAs[String] - diff --git a/akka-docs/scala/migration-guide-0.9.x-0.10.x.rst b/akka-docs/scala/migration-guide-0.9.x-0.10.x.rst deleted file mode 100644 index 68ec0cb087..0000000000 --- a/akka-docs/scala/migration-guide-0.9.x-0.10.x.rst +++ /dev/null @@ -1,45 +0,0 @@ -Migration Guide from Akka 0.9.x to Akka 0.10.x -============================================== - -Module akka-camel ------------------ - -The following list summarizes the breaking changes since Akka 0.9.1. - -* CamelService moved from package se.scalablesolutions.akka.camel.service one level up to se.scalablesolutions.akka.camel. -* CamelService.newInstance removed. For starting and stopping a CamelService, applications should use -** CamelServiceManager.startCamelService and -** CamelServiceManager.stopCamelService. -* Existing def receive = produce method definitions from Producer implementations must be removed (resolves compile error: method receive needs override modifier). -* The Producer.async method and the related Sync trait have been removed. This is now fully covered by Camel's `asynchronous routing engine `_. -* @consume annotation can not placed any longer on actors (i.e. on type-level), only on typed actor methods. Consumer actors must mixin the Consumer trait. -* @consume annotation moved to package se.scalablesolutions.akka.camel. - -Logging -------- - -We've switched to Logback (SLF4J compatible) for the logging, if you're having trouble seeing your log output you'll need to make sure that there's a logback.xml available on the classpath or you'll need to specify the location of the logback.xml file via the system property, ex: -Dlogback.configurationFile=/path/to/logback.xml - -Configuration -------------- - -* The configuration is now JSON-style (see below). -* Now you can define the time-unit to be used throughout the config file: - -.. code-block:: ruby - - akka { - version = "0.10" - time-unit = "seconds" # default timeout time unit for all timeout properties throughout the config - - actor { - timeout = 5 # default timeout for future based invocations - throughput = 5 # default throughput for ExecutorBasedEventDrivenDispatcher - } - ... - } - -RemoteClient events -------------------- - -All events now has a reference to the RemoteClient instance instead of 'hostname' and 'port'. This is more flexible. Enables simpler reconnecting etc. diff --git a/akka-docs/scala/migration-guide-1.0.x-1.1.x.rst b/akka-docs/scala/migration-guide-1.0.x-1.1.x.rst deleted file mode 100644 index c32b2545ac..0000000000 --- a/akka-docs/scala/migration-guide-1.0.x-1.1.x.rst +++ /dev/null @@ -1,37 +0,0 @@ -Akka has now moved to Scala 2.9.x -^^^^^^^^^^^^^^^^^^^^ - -Akka HTTP -========= - -# akka.servlet.Initializer has been moved to ``akka-kernel`` to be able to have ``akka-http`` not depend on ``akka-remote``, if you don't want to use the class for kernel, just create your own version of ``akka.servlet.Initializer``, it's just a couple of lines of code and there is instructions here: `Akka Http Docs `_ -# akka.http.ListWriter has been removed in full, if you use it and want to keep using it, here's the code: `ListWriter `_ -# Jersey-server is now a "provided" dependency for ``akka-http``, so you'll need to add the dependency to your project, it's built against Jersey 1.3 - -Akka Actor -========== - -# is now dependency free, with the exception of the dependency on the ``scala-library.jar`` -# does not bundle any logging anymore, but you can subscribe to events within Akka by registering an event handler on akka.aevent.EventHandler or by specifying the ``FQN`` of an Actor in the akka.conf under akka.event-handlers; there is an ``akka-slf4j`` module which still provides the Logging trait and a default ``SLF4J`` logger adapter. -Don't forget to add a SLF4J backend though, we recommend: - -.. code-block:: scala - lazy val logback = "ch.qos.logback" % "logback-classic" % "0.9.28" - -# If you used HawtDispatcher and want to continue using it, you need to include akka-dispatcher-extras.jar from Akka Modules, in your akka.conf you need to specify: ``akka.dispatch.HawtDispatcherConfigurator`` instead of ``HawtDispatcher`` -# FSM: the onTransition method changed from Function1 to PartialFunction; there is an implicit conversion for the precise types in place, but it may be necessary to add an underscore if you are passing an eta-expansion (using a method as function value). - -Akka Typed Actor -================ - -All methods starting with 'get*' are deprecated and will be removed in post 1.1 release. - -Akka Remote -=========== - -# ``UnparsebleException`` has been renamed to ``CannotInstantiateRemoteExceptionDueToRemoteProtocolParsingErrorException(exception, classname, message)`` - -Akka Testkit -============ - -The TestKit moved into the akka-testkit subproject and correspondingly into the ``akka.testkit` package. diff --git a/akka-docs/scala/migration-guides.rst b/akka-docs/scala/migration-guides.rst deleted file mode 100644 index 361f8e3c7a..0000000000 --- a/akka-docs/scala/migration-guides.rst +++ /dev/null @@ -1,8 +0,0 @@ -Here are migration guides for the latest releases -================================================= - -* `Migrate 0.7.x -> 0.8.x `_ -* `Migrate 0.8.x -> 0.9.x `_ -* `Migrate 0.9.x -> 0.10.x `_ -* `Migrate 0.10.x -> 1.0.x `_ -* `Migrate 1.0.x -> 1.1.x `_ diff --git a/akka-docs/scala/testing.rst b/akka-docs/scala/testing.rst index 2860bd2cef..ecf86720db 100644 --- a/akka-docs/scala/testing.rst +++ b/akka-docs/scala/testing.rst @@ -2,6 +2,10 @@ Testing Actor Systems ##################### +.. toctree:: + + testkit-example + .. sidebar:: Contents .. contents:: :local: @@ -219,6 +223,25 @@ terminate upon reception of a message or at the deadline, whichever occurs first; it follows that this examination usually is the last statement in a :meth:`within` block. +.. code-block:: scala + + class SomeSpec extends WordSpec with MustMatchers with TestKit { + "A Worker" must { + "send timely replies" in { + val worker = actorOf(...) + within (50 millis) { + worker ! "some work" + expectMsg("some result") + expectNoMsg + } + } + } + } + +Ray Roestenburg has written a great article on using the TestKit: +``_. +His full example is also available :ref:`here `. + CallingThreadDispatcher ======================= diff --git a/akka-docs/scala/testkit-example.rst b/akka-docs/scala/testkit-example.rst new file mode 100644 index 0000000000..f53543e474 --- /dev/null +++ b/akka-docs/scala/testkit-example.rst @@ -0,0 +1,145 @@ +.. _testkit-example: + +############### +TestKit Example +############### + +Ray Roestenburg's example code from `his blog `_. + +.. code-block:: scala + + package unit.akka + + import org.scalatest.matchers.ShouldMatchers + import org.scalatest.{WordSpec, BeforeAndAfterAll} + import akka.actor.Actor._ + import akka.util.duration._ + import akka.util.TestKit + import java.util.concurrent.TimeUnit + import akka.actor.{ActorRef, Actor} + import util.Random + + /** + * a Test to show some TestKit examples + */ + + class TestKitUsageSpec extends WordSpec with BeforeAndAfterAll with ShouldMatchers with TestKit { + val echoRef = actorOf(new EchoActor).start() + val forwardRef = actorOf(new ForwardingActor(testActor)).start() + val filterRef = actorOf(new FilteringActor(testActor)).start() + val randomHead = Random.nextInt(6) + val randomTail = Random.nextInt(10) + val headList = List().padTo(randomHead, "0") + val tailList = List().padTo(randomTail, "1") + val seqRef = actorOf(new SequencingActor(testActor, headList, tailList)).start() + + override protected def afterAll(): scala.Unit = { + stopTestActor + echoRef.stop() + forwardRef.stop() + filterRef.stop() + seqRef.stop() + } + + "An EchoActor" should { + "Respond with the same message it receives" in { + within(100 millis) { + echoRef ! "test" + expectMsg("test") + } + } + } + "A ForwardingActor" should { + "Forward a message it receives" in { + within(100 millis) { + forwardRef ! "test" + expectMsg("test") + } + } + } + "A FilteringActor" should { + "Filter all messages, except expected messagetypes it receives" in { + var messages = List[String]() + within(100 millis) { + filterRef ! "test" + expectMsg("test") + filterRef ! 1 + expectNoMsg + filterRef ! "some" + filterRef ! "more" + filterRef ! 1 + filterRef ! "text" + filterRef ! 1 + + receiveWhile(500 millis) { + case msg: String => messages = msg :: messages + } + } + messages.length should be(3) + messages.reverse should be(List("some", "more", "text")) + } + } + "A SequencingActor" should { + "receive an interesting message at some point " in { + within(100 millis) { + seqRef ! "something" + ignoreMsg { + case msg: String => msg != "something" + } + expectMsg("something") + ignoreMsg { + case msg: String => msg == "1" + } + expectNoMsg + } + } + } + } + + /** + * An Actor that echoes everything you send to it + */ + class EchoActor extends Actor { + def receive = { + case msg => { + self.reply(msg) + } + } + } + + /** + * An Actor that forwards every message to a next Actor + */ + class ForwardingActor(next: ActorRef) extends Actor { + def receive = { + case msg => { + next ! msg + } + } + } + + /** + * An Actor that only forwards certain messages to a next Actor + */ + class FilteringActor(next: ActorRef) extends Actor { + def receive = { + case msg: String => { + next ! msg + } + case _ => None + } + } + + /** + * An actor that sends a sequence of messages with a random head list, an interesting value and a random tail list + * The idea is that you would like to test that the interesting value is received and that you cant be bothered with the rest + */ + class SequencingActor(next: ActorRef, head: List[String], tail: List[String]) extends Actor { + def receive = { + case msg => { + head map (next ! _) + next ! msg + tail map (next ! _) + } + } + } From a60bbc5f598cba6c2de02c54d0a8d5674f1d14b3 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Sat, 23 Apr 2011 08:11:31 +0200 Subject: [PATCH 010/100] Applied patch from bruce.mitchener, with minor adjustment --- ...cutorBasedEventDrivenDispatcherActorsSpec.scala | 2 +- akka-actor/src/main/scala/akka/actor/Actor.scala | 4 ++-- .../src/main/scala/akka/actor/ActorRef.scala | 6 +++--- akka-actor/src/main/scala/akka/actor/FSM.scala | 2 +- .../src/main/scala/akka/actor/UntypedActor.scala | 4 ++-- .../ExecutorBasedEventDrivenDispatcher.scala | 2 +- .../src/main/scala/akka/dispatch/Future.scala | 4 ++-- .../main/scala/akka/util/ListenerManagement.scala | 2 +- akka-docs/general/migration-guide-1.0.x-1.1.x.rst | 2 +- akka-docs/intro/building-akka.rst | 6 +++--- akka-docs/pending/companies-using-akka.rst | 4 ++-- akka-docs/pending/dispatchers-java.rst | 2 +- akka-docs/pending/dispatchers-scala.rst | 2 +- akka-docs/pending/fault-tolerance-java.rst | 8 ++++---- akka-docs/pending/fault-tolerance-scala.rst | 8 ++++---- akka-docs/pending/futures-scala.rst | 6 +++--- akka-docs/pending/getting-started.rst | 2 +- akka-docs/pending/guice-integration.rst | 2 +- akka-docs/pending/http.rst | 4 ++-- akka-docs/pending/remote-actors-java.rst | 4 ++-- akka-docs/pending/remote-actors-scala.rst | 6 +++--- akka-docs/pending/security.rst | 2 +- akka-docs/pending/serialization-scala.rst | 14 +++++++------- akka-docs/pending/sponsors.rst | 2 +- akka-docs/pending/stm.rst | 2 +- akka-docs/pending/testkit.rst | 4 ++-- akka-docs/pending/transactors-java.rst | 2 +- akka-docs/pending/transactors-scala.rst | 2 +- akka-docs/pending/tutorial-chat-server-scala.rst | 2 +- akka-docs/pending/typed-actors-java.rst | 4 ++-- akka-docs/pending/typed-actors-scala.rst | 4 ++-- akka-docs/pending/untyped-actors-java.rst | 14 +++++++------- akka-docs/scala/actors.rst | 2 +- akka-docs/scala/fsm.rst | 2 +- akka-docs/scala/migration-guide-1.0.x-1.1.x.rst | 2 +- .../akka/remote/netty/NettyRemoteSupport.scala | 2 +- .../src/test/scala/ticket/Ticket434Spec.scala | 2 +- .../main/scala/akka/transactor/Transactor.scala | 2 +- .../actor/typed-actor/TypedActorRegistrySpec.scala | 10 +++++----- 39 files changed, 78 insertions(+), 78 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorsSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorsSpec.scala index dfdaf9794d..a97238bf7e 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorsSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorsSpec.scala @@ -8,7 +8,7 @@ import akka.actor.Actor import Actor._ /** - * Tests the behaviour of the executor based event driven dispatcher when multiple actors are being dispatched on it. + * Tests the behavior of the executor based event driven dispatcher when multiple actors are being dispatched on it. * * @author Jan Van Besien */ diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index ee3c48374f..14acfbb3e1 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -322,7 +322,7 @@ trait Actor { *

* For example fields like: *

-   * self.dispactcher = ...
+   * self.dispatcher = ...
    * self.trapExit = ...
    * self.faultHandler = ...
    * self.lifeCycle = ...
@@ -417,7 +417,7 @@ trait Actor {
   }
 
   /**
-   * Changes tha Actor's behavior to become the new 'Receive' (PartialFunction[Any, Unit]) handler.
+   * Changes the Actor's behavior to become the new 'Receive' (PartialFunction[Any, Unit]) handler.
    * Puts the behavior on top of the hotswap stack.
    * If "discardOld" is true, an unbecome will be issued prior to pushing the new behavior to the stack
    */
diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala
index 6fa44452e0..12e2b5949a 100644
--- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala
+++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala
@@ -279,7 +279,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal
    * Akka Java API. 

* Sends a one-way asynchronous message. E.g. fire-and-forget semantics. *

- * Allows you to pass along the sender of the messag. + * Allows you to pass along the sender of the message. *

*

    * actor.sendOneWay(message, context);
@@ -291,14 +291,14 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal
   /**
    * Akka Java API. 

* @see sendRequestReply(message: AnyRef, timeout: Long, sender: ActorRef) - * Uses the defualt timeout of the Actor (setTimeout()) and omits the sender reference + * Uses the default timeout of the Actor (setTimeout()) and omits the sender reference */ def sendRequestReply(message: AnyRef): AnyRef = sendRequestReply(message, timeout, null) /** * Akka Java API.

* @see sendRequestReply(message: AnyRef, timeout: Long, sender: ActorRef) - * Uses the defualt timeout of the Actor (setTimeout()) + * Uses the default timeout of the Actor (setTimeout()) */ def sendRequestReply(message: AnyRef, sender: ActorRef): AnyRef = sendRequestReply(message, timeout, sender) diff --git a/akka-actor/src/main/scala/akka/actor/FSM.scala b/akka-actor/src/main/scala/akka/actor/FSM.scala index 815ab1076c..f4fff52035 100644 --- a/akka-actor/src/main/scala/akka/actor/FSM.scala +++ b/akka-actor/src/main/scala/akka/actor/FSM.scala @@ -104,7 +104,7 @@ object FSM { * different concerns in different places; you may choose to use * when for describing the properties of a state, including of * course initiating transitions, but you can describe the transitions using - * onTransision to avoid having to duplicate that code among + * onTransition to avoid having to duplicate that code among * multiple paths which lead to a transition: * *

diff --git a/akka-actor/src/main/scala/akka/actor/UntypedActor.scala b/akka-actor/src/main/scala/akka/actor/UntypedActor.scala
index 77500d4059..41bf7ac048 100644
--- a/akka-actor/src/main/scala/akka/actor/UntypedActor.scala
+++ b/akka-actor/src/main/scala/akka/actor/UntypedActor.scala
@@ -24,7 +24,7 @@ import akka.japi.{Creator, Procedure}
  *
  *        } else if (msg.equals("UseSender") && getContext().getSender().isDefined()) {
  *          // Reply to original sender of message using the sender reference
- *          // also passing along my own refererence (the context)
+ *          // also passing along my own reference (the context)
  *          getContext().getSender().get().sendOneWay(msg, context);
  *
  *        } else if (msg.equals("UseSenderFuture") && getContext().getSenderFuture().isDefined()) {
@@ -36,7 +36,7 @@ import akka.japi.{Creator, Procedure}
  *          getContext().sendOneWay(msg)
  *
  *        } else if (msg.equals("ForwardMessage")) {
- *          // Retreive an actor from the ActorRegistry by ID and get an ActorRef back
+ *          // Retrieve an actor from the ActorRegistry by ID and get an ActorRef back
  *          ActorRef actorRef = Actor.registry.actorsFor("some-actor-id").head();
  *
  *        } else throw new IllegalArgumentException("Unknown message: " + message);
diff --git a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala
index 261a4c8170..105028f693 100644
--- a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala
+++ b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala
@@ -63,7 +63,7 @@ import java.util.concurrent.{ TimeUnit, ExecutorService, RejectedExecutionExcept
  * @param throughput positive integer indicates the dispatcher will only process so much messages at a time from the
  *                   mailbox, without checking the mailboxes of other actors. Zero or negative means the dispatcher
  *                   always continues until the mailbox is empty.
- *                   Larger values (or zero or negative) increase througput, smaller values increase fairness
+ *                   Larger values (or zero or negative) increase throughput, smaller values increase fairness
  */
 class ExecutorBasedEventDrivenDispatcher(
   _name: String,
diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala
index c69ca82bad..430f7182e4 100644
--- a/akka-actor/src/main/scala/akka/dispatch/Future.scala
+++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala
@@ -357,12 +357,12 @@ sealed trait Future[+T] {
   /**
    * When this Future is completed, apply the provided function to the
    * Future. If the Future has already been completed, this will apply
-   * immediatly.
+   * immediately.
    */
   def onComplete(func: Future[T] => Unit): Future[T]
 
   /**
-   * When the future is compeleted with a valid result, apply the provided
+   * When the future is completed with a valid result, apply the provided
    * PartialFunction to the result.
    * 
    *   val result = future receive {
diff --git a/akka-actor/src/main/scala/akka/util/ListenerManagement.scala b/akka-actor/src/main/scala/akka/util/ListenerManagement.scala
index efeb482377..ede46fc80a 100644
--- a/akka-actor/src/main/scala/akka/util/ListenerManagement.scala
+++ b/akka-actor/src/main/scala/akka/util/ListenerManagement.scala
@@ -45,7 +45,7 @@ trait ListenerManagement {
   def hasListeners: Boolean = !listeners.isEmpty
 
   /**
-   * Checks if a specfic listener is registered. ActorInitializationException leads to removal of listener if that
+   * Checks if a specific listener is registered. ActorInitializationException leads to removal of listener if that
    * one isShutdown.
    */
   def hasListener(listener: ActorRef): Boolean = listeners.contains(listener)
diff --git a/akka-docs/general/migration-guide-1.0.x-1.1.x.rst b/akka-docs/general/migration-guide-1.0.x-1.1.x.rst
index c473c44129..cd3bf5d9f2 100644
--- a/akka-docs/general/migration-guide-1.0.x-1.1.x.rst
+++ b/akka-docs/general/migration-guide-1.0.x-1.1.x.rst
@@ -15,7 +15,7 @@ Akka Actor
 ----------
 
 # is now dependency free, with the exception of the dependency on the ``scala-library.jar``
-# does not bundle any logging anymore, but you can subscribe to events within Akka by registering an event handler on akka.aevent.EventHandler or by specifying the ``FQN`` of an Actor in the akka.conf under akka.event-handlers; there is an ``akka-slf4j`` module which still provides the Logging trait and a default ``SLF4J`` logger adapter.
+# does not bundle any logging anymore, but you can subscribe to events within Akka by registering an event handler on akka.event.EventHandler or by specifying the ``FQN`` of an Actor in the akka.conf under akka.event-handlers; there is an ``akka-slf4j`` module which still provides the Logging trait and a default ``SLF4J`` logger adapter.
 Don't forget to add a SLF4J backend though, we recommend:
 
 .. code-block:: scala
diff --git a/akka-docs/intro/building-akka.rst b/akka-docs/intro/building-akka.rst
index 3d4f4ca1a0..2f2a745eeb 100644
--- a/akka-docs/intro/building-akka.rst
+++ b/akka-docs/intro/building-akka.rst
@@ -167,7 +167,7 @@ download) use the ``dist`` command::
 The distribution zip can be found in the dist directory and is called
 ``akka-modules-{version}.zip``.
 
-To run the mircokernel, unzip the zip file, change into the unzipped directory,
+To run the microkernel, unzip the zip file, change into the unzipped directory,
 set the ``AKKA_HOME`` environment variable, and run the main jar file. For
 example:
 
@@ -282,7 +282,7 @@ akka-camel
 ^^^^^^^^^^
 
 * Depends on akka-actor
-* camel-core-2.5.0.jar
+* camel-core-2.7.0.jar
 * commons-logging-api-1.1.jar
 * commons-management-1.0.jar
 
@@ -290,7 +290,7 @@ akka-camel-typed
 ^^^^^^^^^^^^^^^^
 
 * Depends on akka-typed-actor
-* camel-core-2.5.0.jar
+* camel-core-2.7.0.jar
 * commons-logging-api-1.1.jar
 * commons-management-1.0.jar
 
diff --git a/akka-docs/pending/companies-using-akka.rst b/akka-docs/pending/companies-using-akka.rst
index 3832adab1b..aae679be9d 100644
--- a/akka-docs/pending/companies-using-akka.rst
+++ b/akka-docs/pending/companies-using-akka.rst
@@ -41,7 +41,7 @@ SVT (Swedish Television)
 
 *Our system is highly asynchronous so the actor style of doing things is a perfect fit. I don’t know about how you feel about concurrency in a big system, but rolling your own abstractions is not a very easy thing to do. When using Akka you can almost forget about all that. Synchronizing between threads, locking and protecting access to state etc. Akka is not just about actors, but that’s one of the most pleasurable things to work with. It’s easy to add new ones and it’s easy to design with actors. You can fire up work actors tied to a specific dispatcher etc. I could make the list of benefits much longer, but I’m at work right now. I suggest you try it out and see how it fits your requirements.*
 
-*We saw a perfect businness reson for using Akka. It lets you concentrate on the business logic instead of the low level things. It’s easy to teach others and the business intent is clear just by reading the code. We didn’t chose Akka just for fun. It’s a business critical application that’s used in broadcasting. Even live broadcasting. We wouldn’t have been where we are today in such a short time without using Akka. We’re two developers that have done great things in such a short amount of time and part of this is due to Akka. As I said, it lets us focus on the business logic instead of low level things such as concurrency, locking, performence etc."*
+*We saw a perfect business reason for using Akka. It lets you concentrate on the business logic instead of the low level things. It’s easy to teach others and the business intent is clear just by reading the code. We didn’t chose Akka just for fun. It’s a business critical application that’s used in broadcasting. Even live broadcasting. We wouldn’t have been where we are today in such a short time without using Akka. We’re two developers that have done great things in such a short amount of time and part of this is due to Akka. As I said, it lets us focus on the business logic instead of low level things such as concurrency, locking, performance etc."*
 
 Tapad
 -----
@@ -107,7 +107,7 @@ LShift
 
 * *"Diffa is an open source data analysis tool that automatically establishes data differences between two or more real-time systems.*
 * Diffa will help you compare local or distributed systems for data consistency, without having to stop them running or implement manual cross-system comparisons. The interface provides you with simple visual summary of any consistency breaks and tools to investigate the issues.*
-* Diffa is the ideal tool to use to investigate where or when inconsistencies are occuring, or simply to provide confidence that your systems are running in perfect sync. It can be used operationally as an early warning system, in deployment for release verification, or in development with other enterprise diagnosis tools to help troubleshoot faults."*
+* Diffa is the ideal tool to use to investigate where or when inconsistencies are occurring, or simply to provide confidence that your systems are running in perfect sync. It can be used operationally as an early warning system, in deployment for release verification, or in development with other enterprise diagnosis tools to help troubleshoot faults."*
 
 ``_
 
diff --git a/akka-docs/pending/dispatchers-java.rst b/akka-docs/pending/dispatchers-java.rst
index 7889db30fc..b9d5ee9ee8 100644
--- a/akka-docs/pending/dispatchers-java.rst
+++ b/akka-docs/pending/dispatchers-java.rst
@@ -165,7 +165,7 @@ Creating a PriorityExecutorBasedEventDrivenDispatcher using PriorityGenerator:
       ref.setDispatcher(new PriorityExecutorBasedEventDrivenDispatcher("foo", gen)); 
 
           ref.start(); // Start the actor
-      ref.getDispatcher().suspend(ref); // Suspening the actor so it doesn't start to treat the messages before we have enqueued all of them :-)
+      ref.getDispatcher().suspend(ref); // Suspending the actor so it doesn't start to treat the messages before we have enqueued all of them :-)
           ref.sendOneWay("lowpriority");
           ref.sendOneWay("lowpriority");
           ref.sendOneWay("highpriority");
diff --git a/akka-docs/pending/dispatchers-scala.rst b/akka-docs/pending/dispatchers-scala.rst
index 35df55724f..62584835a4 100644
--- a/akka-docs/pending/dispatchers-scala.rst
+++ b/akka-docs/pending/dispatchers-scala.rst
@@ -153,7 +153,7 @@ Creating a PriorityExecutorBasedEventDrivenDispatcher using PriorityGenerator:
     a.dispatcher = new PriorityExecutorBasedEventDrivenDispatcher("foo", gen) 
     a.start // Start the Actor
 
-    a.dispatcher.suspend(a) // Suspening the actor so it doesn't start to treat the messages before we have enqueued all of them :-)
+    a.dispatcher.suspend(a) // Suspending the actor so it doesn't start to treat the messages before we have enqueued all of them :-)
 
      a ! 'lowpriority
      a ! 'lowpriority
diff --git a/akka-docs/pending/fault-tolerance-java.rst b/akka-docs/pending/fault-tolerance-java.rst
index 18cbb63e9e..96190c7b8e 100644
--- a/akka-docs/pending/fault-tolerance-java.rst
+++ b/akka-docs/pending/fault-tolerance-java.rst
@@ -125,7 +125,7 @@ The Actor’s supervision can be declaratively defined by creating a ‘Supervis
 
 Supervisors created like this are implicitly instantiated and started.
 
-To cofigure a handler function for when the actor underlying the supervisor recieves a MaximumNumberOfRestartsWithinTimeRangeReached message, you can specify
+To configure a handler function for when the actor underlying the supervisor receives a MaximumNumberOfRestartsWithinTimeRangeReached message, you can specify
  a Procedure2 when creating the SupervisorConfig. This handler will be called with the ActorRef of the supervisor and the
 MaximumNumberOfRestartsWithinTimeRangeReached message.
 
@@ -213,7 +213,7 @@ Here is an example:
           true)
        }));
 
-Programmatical linking and supervision of Untyped Actors
+Programmatic linking and supervision of Untyped Actors
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Untyped Actors can at runtime create, spawn, link and supervise other actors. Linking and unlinking is done using one of the 'link' and 'unlink' methods available in the 'ActorRef' (therefore prefixed with getContext() in these examples).
@@ -459,10 +459,10 @@ In the supervised TypedActor you can override the ‘preRestart’ and ‘postRe
     }
   }
 
-Programatical linking and supervision of TypedActors
+Programatic linking and supervision of TypedActors
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-TypedActors can be linked an unlinked just like UntypedActors:
+TypedActors can be linked and unlinked just like UntypedActors:
 
 .. code-block:: java
 
diff --git a/akka-docs/pending/fault-tolerance-scala.rst b/akka-docs/pending/fault-tolerance-scala.rst
index 5e02cf232a..6070f9e01e 100644
--- a/akka-docs/pending/fault-tolerance-scala.rst
+++ b/akka-docs/pending/fault-tolerance-scala.rst
@@ -121,7 +121,7 @@ The Actor's supervision can be declaratively defined by creating a "Supervisor'
 
 Supervisors created like this are implicitly instantiated and started.
 
-To cofigure a handler function for when the actor underlying the supervisor recieves a MaximumNumberOfRestartsWithinTimeRangeReached message, you can specify a function of type
+To configure a handler function for when the actor underlying the supervisor receives a MaximumNumberOfRestartsWithinTimeRangeReached message, you can specify a function of type
 (ActorRef, MaximumNumberOfRestartsWithinTimeRangeReached) => Unit when creating the SupervisorConfig. This handler will be called with the ActorRef of the supervisor and the
 MaximumNumberOfRestartsWithinTimeRangeReached message.
 
@@ -194,7 +194,7 @@ Here is an example:
         **true**)
       :: Nil))
 
-Programmatical linking and supervision of Actors
+Programmatic linking and supervision of Actors
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Actors can at runtime create, spawn, link and supervise other actors. Linking and unlinking is done using one of the 'link' and 'unlink' methods available in the 'ActorRef' (therefore prefixed with 'self' in these examples).
@@ -411,10 +411,10 @@ Then you can retrieve the Typed Actor as follows:
 Restart callbacks
 ^^^^^^^^^^^^^^^^^
 
-Programatical linking and supervision of TypedActors
+Programatic linking and supervision of TypedActors
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-TypedActors can be linked an unlinked just like actors - in fact the linking is done on the underlying actor:
+TypedActors can be linked and unlinked just like actors - in fact the linking is done on the underlying actor:
 
 .. code-block:: scala
 
diff --git a/akka-docs/pending/futures-scala.rst b/akka-docs/pending/futures-scala.rst
index 3426ce0ff6..f03990a1cf 100644
--- a/akka-docs/pending/futures-scala.rst
+++ b/akka-docs/pending/futures-scala.rst
@@ -11,7 +11,7 @@ Use with Actors
 
 There are generally two ways of getting a reply from an ``Actor``: the first is by a sent message (``actor ! msg``), which only works if the original sender was an ``Actor``) and the second is through a ``Future``.
 
-Using an ``Actor``\'s ``!!!`` method to send a message will return a Future. To wait for and retreive the actual result the simplest method is:
+Using an ``Actor``\'s ``!!!`` method to send a message will return a Future. To wait for and retrieve the actual result the simplest method is:
 
 .. code-block:: scala
 
@@ -97,7 +97,7 @@ If we do the opposite:
 
 Our little string has been processed long before our 1 second sleep has finished. Because of this, the dispatcher has moved onto other messages that need processing and can no longer calculate the length of the string for us, instead it gets calculated in the current thread just as if we weren't using a Future.
 
-Normally this works quite well for us as it means there is very little overhead to running a quick Function. If there is a possiblity of the Function taking a non-trivial amount of time to process it might be better to have this done concurrently, and for that we use 'flatMap':
+Normally this works quite well for us as it means there is very little overhead to running a quick Function. If there is a possibility of the Function taking a non-trivial amount of time to process it might be better to have this done concurrently, and for that we use 'flatMap':
 
 .. code-block:: scala
 
@@ -150,7 +150,7 @@ The example for comprehension above is an example of composing Futures. A common
 
 Here we have 2 actors processing a single message each. In the for comprehension we need to add the expected types in order to work with the results. Once the 2 results are available, they are being added together and sent to a third actor, which replies with a String, which we assign to 'result'.
 
-This is fine when dealing with a known amount of Actors, but can grow unwieldly if we have more then a handful. The 'sequence' and 'traverse' helper methods can make it easier to handle more complex use cases. Both of these methods are ways of turning a Traversable[Future[A]] into a Future[Traversable[A]]. For example:
+This is fine when dealing with a known amount of Actors, but can grow unwieldy if we have more then a handful. The 'sequence' and 'traverse' helper methods can make it easier to handle more complex use cases. Both of these methods are ways of turning a Traversable[Future[A]] into a Future[Traversable[A]]. For example:
 
 .. code-block:: scala
 
diff --git a/akka-docs/pending/getting-started.rst b/akka-docs/pending/getting-started.rst
index 8f86f5cfca..d76db9b299 100644
--- a/akka-docs/pending/getting-started.rst
+++ b/akka-docs/pending/getting-started.rst
@@ -18,7 +18,7 @@ Download the release you need (Akka core or Akka Modules) from ``_ annotations (such as ‘@Inject’ etc.).
+All Typed Actors support dependency injection using `Guice `_ annotations (such as ‘@Inject’ etc.).
 The ‘TypedActorManager’ class understands Guice and will do the wiring for you.
 
 External Guice modules
diff --git a/akka-docs/pending/http.rst b/akka-docs/pending/http.rst
index 739f443c1d..a4c7842233 100644
--- a/akka-docs/pending/http.rst
+++ b/akka-docs/pending/http.rst
@@ -179,7 +179,7 @@ Endpoints are actors that handle request messages. Minimally there must be an in
 Preparations
 ^^^^^^^^^^^^
 
-In order to use Mist you have to register the MistServlet in *web.xml* or do the analogous for the embedded server if running in Akka Micrkernel:
+In order to use Mist you have to register the MistServlet in *web.xml* or do the analogous for the embedded server if running in Akka Microkernel:
 
 .. code-block:: xml
 
@@ -419,7 +419,7 @@ As noted above, hook functions are non-exclusive. This means multiple actors can
     def receive = handleHttpRequest
 
     //
-    // this guy completes requests after other actions have occured
+    // this guy completes requests after other actions have occurred
     //
     lazy val complete = actorOf[ActionCompleteActor].start()
   }
diff --git a/akka-docs/pending/remote-actors-java.rst b/akka-docs/pending/remote-actors-java.rst
index 0e654fc698..47f27d6cef 100644
--- a/akka-docs/pending/remote-actors-java.rst
+++ b/akka-docs/pending/remote-actors-java.rst
@@ -15,7 +15,7 @@ Managing the Remote Service
 Starting remote service in user code as a library
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Here is how to start up the server and specify the hostname and port programatically:
+Here is how to start up the server and specify the hostname and port programmatically:
 
 .. code-block:: java
 
@@ -314,7 +314,7 @@ The API for server managed remote actors is really simple. 2 methods only:
 
 Actors created like this are automatically started.
 
-You can also register an actor by its UUD rather than ID or handle. This is done by prefixing the handle with the "uuid:" protocol.
+You can also register an actor by its UUID rather than ID or handle. This is done by prefixing the handle with the "uuid:" protocol.
 
 .. code-block:: scala
 
diff --git a/akka-docs/pending/remote-actors-scala.rst b/akka-docs/pending/remote-actors-scala.rst
index fef71e69e5..9389a5d284 100644
--- a/akka-docs/pending/remote-actors-scala.rst
+++ b/akka-docs/pending/remote-actors-scala.rst
@@ -15,7 +15,7 @@ Starting up the remote service
 Starting remote service in user code as a library
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Here is how to start up the RemoteNode and specify the hostname and port programatically:
+Here is how to start up the RemoteNode and specify the hostname and port programmatically:
 
 .. code-block:: scala
 
@@ -318,7 +318,7 @@ The API for server managed remote actors is really simple. 2 methods only:
 
 Actors created like this are automatically started.
 
-You can also register an actor by its UUD rather than ID or handle. This is done by prefixing the handle with the "uuid:" protocol.
+You can also register an actor by its UUID rather than ID or handle. This is done by prefixing the handle with the "uuid:" protocol.
 
 .. code-block:: scala
 
@@ -645,7 +645,7 @@ So a simple listener actor can look like this:
       case RemoteServerClientConnected(server, clientAddress)    => ... // act upon client connection
       case RemoteServerClientDisconnected(server, clientAddress) => ... // act upon client disconnection
       case RemoteServerClientClosed(server, clientAddress)       => ... // act upon client connection close
-      case RemoteServerWriteFailed(request, casue, server, clientAddress) => ... // act upon server write failure
+      case RemoteServerWriteFailed(request, cause, server, clientAddress) => ... // act upon server write failure
     }
   }).start()
 
diff --git a/akka-docs/pending/security.rst b/akka-docs/pending/security.rst
index 3600c21285..cae23fbdd5 100644
--- a/akka-docs/pending/security.rst
+++ b/akka-docs/pending/security.rst
@@ -89,7 +89,7 @@ How does it work (at least for REST actors)?
 # The browser will send the *service ticket* to the web application encoded in the header value of the *Authorization*header
 # The web application must validate the ticket based on a shared secret between the web application and the kerberos server. As a result the web application will know the name of the user
 
-To activate the kerberos/SPNEGO authentication for your REST actor you need to enable the kerberos/SPNGEOauthentication actor in the akka.conf like this:
+To activate the kerberos/SPNEGO authentication for your REST actor you need to enable the kerberos/SPNEGOauthentication actor in the akka.conf like this:
 
 .. code-block:: ruby
 
diff --git a/akka-docs/pending/serialization-scala.rst b/akka-docs/pending/serialization-scala.rst
index 93b738a176..a0b0e312e6 100644
--- a/akka-docs/pending/serialization-scala.rst
+++ b/akka-docs/pending/serialization-scala.rst
@@ -321,7 +321,7 @@ Each serialization interface/trait in
 Note however that if you are using one of the Serializable interfaces then you don’t have to do anything else in regard to sending remote messages.
 
 The ones currently supported are (besides the default which is regular Java serialization):
-* ScalaJON (Scala only)
+* ScalaJSON (Scala only)
 * JavaJSON (Java but some Scala structures)
 * SBinary (Scala only)
 * Protobuf (Scala and Java)
@@ -439,7 +439,7 @@ Here are the steps that you need to follow:
 Serializer API using reflection
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-You can also use the Serializer abstraction to serialize using reflection based serialization API of sjson. But we recommend using the type class based one, because reflection based serialization has limitations due to type erasure. Here's an example of reflection based serialization:
+You can also use the Serializer abstraction to serialize using the reflection based serialization API of sjson. But we recommend using the type class based one, because reflection based serialization has limitations due to type erasure. Here's an example of reflection based serialization:
 
 .. code-block:: scala
 
@@ -672,7 +672,7 @@ Consider the following Scala class:
   }
 
 Because of erasure, you need to add the type hint declaratively through the annotation @JSONTypeHint that
-SJSON will pick up during serialization. No we can say:
+SJSON will pick up during serialization. Now we can say:
 
 .. code-block:: scala
 
@@ -683,7 +683,7 @@ SJSON will pick up during serialization. No we can say:
     c should equal(serializer.in[Contact](co))
   }
 
-With optional generic data members, we need to provide the hint to SJSON through another annotation@OptionTypeHint.
+With optional generic data members, we need to provide the hint to SJSON through another annotation @OptionTypeHint.
 
 .. code-block:: scala
 
@@ -714,7 +714,7 @@ Serialization works ok with optional members annotated as above.
     }
   }
 
-You can also specify a custom ClassLoader while using SJSON serializer:
+You can also specify a custom ClassLoader while using the SJSON serializer:
 
 .. code-block:: scala
 
@@ -915,7 +915,7 @@ For your POJOs to be able to serialize themselves you have to extend the JavaJSO
   SerializerFactory factory = new SerializerFactory();
   MyMessage messageCopy = factory.getJavaJSON().in(json);
 
-Use the akka.serialization.SerializerFactory.getJavaJSON to do generic JSONserialization, e.g. serialize object that does not extend JavaJSON using the JSON serializer.
+Use the akka.serialization.SerializerFactory.getJavaJSON to do generic JSON serialization, e.g. serialize object that does not extend JavaJSON using the JSON serializer.
 
 .. code-block:: java
 
@@ -948,7 +948,7 @@ If you need to serialize your own user-defined objects then you have to do three
 ## toBytes: Array[Byte]
 # Create an implicit sbinary.Format[T] object for your class. Which means that you have to define its two methods:
 ## reads(in: Input): T; in which you read in all the fields in your object, using read[FieldType](in)and recreate it.
-## writes(out: Output, value: T): Unit; in which you write out all the fields in your object, usingwrite[FieldType](out, value.field).
+## writes(out: Output, value: T): Unit; in which you write out all the fields in your object, using write[FieldType](out, value.field).
 
 Here is an example:
 ``_
diff --git a/akka-docs/pending/sponsors.rst b/akka-docs/pending/sponsors.rst
index 65faac6794..88d35f1f0a 100644
--- a/akka-docs/pending/sponsors.rst
+++ b/akka-docs/pending/sponsors.rst
@@ -4,7 +4,7 @@
 Scalable Solutions
 ==================
 
-Scalable Solutions AB is commercial entity behind Akka, providing support, consulting and training around Akka.
+Scalable Solutions AB is the commercial entity behind Akka, providing support, consulting and training around Akka.
 ``_
 
 YourKit
diff --git a/akka-docs/pending/stm.rst b/akka-docs/pending/stm.rst
index c84ca4e6bb..8666425289 100644
--- a/akka-docs/pending/stm.rst
+++ b/akka-docs/pending/stm.rst
@@ -7,7 +7,7 @@ The Akka Software Transactional Memory implementation
 
 Read consistency is that all value
 
-**Read concistency and MVCC**
+**Read consistency and MVCC**
 *****************************
 
 A lot of STM (like the Clojure STM) implementations are Multi Version Concurrency Control Based (MVCC) based (TL2 of david dice could be seen as MVCC).
diff --git a/akka-docs/pending/testkit.rst b/akka-docs/pending/testkit.rst
index d2d177948f..65aeac00b6 100644
--- a/akka-docs/pending/testkit.rst
+++ b/akka-docs/pending/testkit.rst
@@ -8,7 +8,7 @@ Overview
 
 Testing actors comprises several aspects, which can have different weight according to the concrete project at hand:
 * If you have a collection of actors which performs a certain function, you may want to apply defined stimuli and observe the delivery of the desired result messages to a test actor; in this case the ***TestKit*** trait will likely interest you.
-* If you encounter undesired behaviour (exceptions, dead-locks) and want to nail down the cause, it might help to run the actors in question using the ***CallingThreadDispatcher***; this dispatcher is strictly less powerful than the general purpose ones, but its deterministic behaviour and complete message stack can help debugging, unless your setup depends on concurrent execution for correctness.
+* If you encounter undesired behavior (exceptions, dead-locks) and want to nail down the cause, it might help to run the actors in question using the ***CallingThreadDispatcher***; this dispatcher is strictly less powerful than the general purpose ones, but its deterministic behavior and complete message stack can help debugging, unless your setup depends on concurrent execution for correctness.
 * For real unit tests of one actor body at a time, there soon will be a special ***TestActorRef*** which allows access to the innards and enables running without a dispatcher.
 
 TestKit
@@ -42,7 +42,7 @@ CallingThreadDispatcher
 
 This special purpose dispatcher was conceived to enable collection of the full stack trace accumulated during processing of a complete message chain. The idea is to run invocations always on the calling thread, except when the target actor is already running on the current thread; in that case it is necessary to queue the invocation and run it after the current invocation on that actor has finished processing. This design implies that any invocation which blocks waiting on some future action to be done by the current thread will dead-lock. Hence, the CallingThreadDispatcher offers strictly more possibilities to dead-lock than a standard dispatcher.
 
-One nice property is that this feature can help verify that your design is dead-lock free: if you run only on this dispatcher and utilitze only one thread, then a successful run implies that for the given set of inputs there cannot be a dead-lock. (This is unfortunately not a hard guarantee, as long as your actor behavior depends on the dispatcher used, e.g. you could sabotage it by explicitly dead-locking only if self.dispatcher != CallingThreadDispatcher.)
+One nice property is that this feature can help verify that your design is dead-lock free: if you run only on this dispatcher and utilize only one thread, then a successful run implies that for the given set of inputs there cannot be a dead-lock. (This is unfortunately not a hard guarantee, as long as your actor behavior depends on the dispatcher used, e.g. you could sabotage it by explicitly dead-locking only if self.dispatcher != CallingThreadDispatcher.)
 
 TestActorRef (coming soon ...)
 ------------------------------
diff --git a/akka-docs/pending/transactors-java.rst b/akka-docs/pending/transactors-java.rst
index 6547063703..9cc4d522f4 100644
--- a/akka-docs/pending/transactors-java.rst
+++ b/akka-docs/pending/transactors-java.rst
@@ -190,7 +190,7 @@ Example of coordinating an increment, similar to the explicitly coordinated exam
       }
   }
 
-To exeucte directly before or after the coordinated transaction, override the ``before`` and ``after`` methods. These methods also expect partial functions like the receive method. They do not execute within the transaction.
+To execute directly before or after the coordinated transaction, override the ``before`` and ``after`` methods. These methods also expect partial functions like the receive method. They do not execute within the transaction.
 
 To completely bypass coordinated transactions override the ``normally`` method. Any message matched by ``normally`` will not be matched by the other methods, and will not be involved in coordinated transactions. In this method you can implement normal actor behavior, or use the normal STM atomic for local transactions.
 
diff --git a/akka-docs/pending/transactors-scala.rst b/akka-docs/pending/transactors-scala.rst
index 454fffb6a6..6ee4126f0a 100644
--- a/akka-docs/pending/transactors-scala.rst
+++ b/akka-docs/pending/transactors-scala.rst
@@ -172,7 +172,7 @@ Using ``sendTo`` to coordinate transactions but pass-on a different message than
     case SomeMessage => sendTo(actor1 -> Message1, actor2 -> Message2)
   }
 
-To exeucte directly before or after the coordinated transaction, override the ``before`` and ``after`` methods. These methods also expect partial functions like the receive method. They do not execute within the transaction.
+To execute directly before or after the coordinated transaction, override the ``before`` and ``after`` methods. These methods also expect partial functions like the receive method. They do not execute within the transaction.
 
 To completely bypass coordinated transactions override the ``normally`` method. Any message matched by ``normally`` will not be matched by the other methods, and will not be involved in coordinated transactions. In this method you can implement normal actor behavior, or use the normal STM atomic for local transactions.
 
diff --git a/akka-docs/pending/tutorial-chat-server-scala.rst b/akka-docs/pending/tutorial-chat-server-scala.rst
index 9d35abddd9..830bf75c22 100644
--- a/akka-docs/pending/tutorial-chat-server-scala.rst
+++ b/akka-docs/pending/tutorial-chat-server-scala.rst
@@ -358,7 +358,7 @@ It responds to two different messages; 'ChatMessage' and 'GetChatLog'. The 'Chat
 
 The 'GetChatLog' message handler retrieves all the messages in the chat log storage inside an atomic block, iterates over them using the 'map' combinator transforming them from 'Array[Byte] to 'String'. Then it invokes the 'reply(message)' function that will send the chat log to the original sender; the 'ChatClient'.
 
-You might rememeber that the 'ChatServer' was supervising the 'ChatStorage' actor. When we discussed that we showed you the supervising Actor's view. Now is the time for the supervised Actor's side of things. First, a supervised Actor need to define a life-cycle in which it declares if it should be seen as a:
+You might remember that the 'ChatServer' was supervising the 'ChatStorage' actor. When we discussed that we showed you the supervising Actor's view. Now is the time for the supervised Actor's side of things. First, a supervised Actor need to define a life-cycle in which it declares if it should be seen as a:
 
 * 'Permanent': which means that the actor will always be restarted.
 * 'Temporary': which means that the actor will not be restarted, but it will be shut down through the regular shutdown process so the 'postStop' callback function will called.
diff --git a/akka-docs/pending/typed-actors-java.rst b/akka-docs/pending/typed-actors-java.rst
index 2322698ed1..0f6c9563b5 100644
--- a/akka-docs/pending/typed-actors-java.rst
+++ b/akka-docs/pending/typed-actors-java.rst
@@ -100,7 +100,7 @@ Methods that return void are turned into ‘fire-and-forget’ semantics by asyn
 Request-reply message send
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Methods that return something (e.g. non-void methods) are turned into ‘send-and-recieve-eventually’ semantics by asynchronously firing off the message and wait on the reply using a Future.
+Methods that return something (e.g. non-void methods) are turned into ‘send-and-receive-eventually’ semantics by asynchronously firing off the message and wait on the reply using a Future.
 
 .. code-block:: java
 
@@ -118,7 +118,7 @@ The same holds for the 'request-reply-with-future' described below.
 Request-reply-with-future message send
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Methods that return a 'akka.dispatch.Future' are turned into ‘send-and-recieve-with-future’ semantics by asynchronously firing off the message and returns immediately with a Future. You need to use the 'future(...)' method in the TypedActor base class to resolve the Future that the client code is waiting on.
+Methods that return a 'akka.dispatch.Future' are turned into ‘send-and-receive-with-future’ semantics by asynchronously firing off the message and returns immediately with a Future. You need to use the 'future(...)' method in the TypedActor base class to resolve the Future that the client code is waiting on.
 
 Here is an example:
 
diff --git a/akka-docs/pending/typed-actors-scala.rst b/akka-docs/pending/typed-actors-scala.rst
index 3d03cc93b1..e9aa061672 100644
--- a/akka-docs/pending/typed-actors-scala.rst
+++ b/akka-docs/pending/typed-actors-scala.rst
@@ -95,7 +95,7 @@ Methods that return void are turned into ‘fire-and-forget’ semantics by asyn
 Request-reply message send
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Methods that return something (e.g. non-void methods) are turned into ‘send-and-recieve-eventually’ semantics by asynchronously firing off the message and wait on the reply using a Future.
+Methods that return something (e.g. non-void methods) are turned into ‘send-and-receive-eventually’ semantics by asynchronously firing off the message and wait on the reply using a Future.
 
 .. code-block:: scala
 
@@ -111,7 +111,7 @@ Generally it is preferred to use fire-forget messages as much as possible since
 Request-reply-with-future message send
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Methods that return a 'akka.dispatch.Future' are turned into ‘send-and-recieve-with-future’ semantics by asynchronously firing off the message and returns immediately with a Future. You need to use the 'future(...)' method in the TypedActor base class to resolve the Future that the client code is waiting on.
+Methods that return a 'akka.dispatch.Future' are turned into ‘send-and-receive-with-future’ semantics by asynchronously firing off the message and returns immediately with a Future. You need to use the 'future(...)' method in the TypedActor base class to resolve the Future that the client code is waiting on.
 
 Here is an example:
 
diff --git a/akka-docs/pending/untyped-actors-java.rst b/akka-docs/pending/untyped-actors-java.rst
index 760b5fd324..35e97011af 100644
--- a/akka-docs/pending/untyped-actors-java.rst
+++ b/akka-docs/pending/untyped-actors-java.rst
@@ -51,7 +51,7 @@ You can also create & start the actor in one statement:
 
   ActorRef myActor = actorOf(SampleUntypedActor.class).start();
 
-The call to 'actorOf' returns an instance of 'ActorRef'. This is a handle to the 'UntypedActor' instance which you can use to interact with the Actor, like send messages to it etc. more on this shortly. The 'ActorRef' is immutble and has a one to one relationship with the Actor it represents. The 'ActorRef' is also serializable and network-aware. This means that you can serialize it, send it over the wire and use it on a remote host and it will still be representing the same Actor on the original node, across the network.
+The call to 'actorOf' returns an instance of 'ActorRef'. This is a handle to the 'UntypedActor' instance which you can use to interact with the Actor, like send messages to it etc. more on this shortly. The 'ActorRef' is immutable and has a one to one relationship with the Actor it represents. The 'ActorRef' is also serializable and network-aware. This means that you can serialize it, send it over the wire and use it on a remote host and it will still be representing the same Actor on the original node, across the network.
 
 Creating Actors with non-default constructor
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -103,7 +103,7 @@ Messages are sent to an Actor through one of the 'send' methods.
 * 'sendRequestReply' means “send-and-reply-eventually”, e.g. send a message asynchronously and wait for a reply through a Future. Here you can specify a timeout. Using timeouts is very important. If no timeout is specified then the actor’s default timeout (set by the 'getContext().setTimeout(..)' method in the 'ActorRef') is used. This method throws an 'ActorTimeoutException' if the call timed out.
 * 'sendRequestReplyFuture' sends a message asynchronously and returns a 'Future'.
 
-In all these methods you have the option of passing along your 'ActorRef' context variable. Make it a practive of doing so because it will allow the receiver actors to be able to respond to your message, since the sender reference is sent along with the message.
+In all these methods you have the option of passing along your 'ActorRef' context variable. Make it a practice of doing so because it will allow the receiver actors to be able to respond to your message, since the sender reference is sent along with the message.
 
 Fire-forget
 ^^^^^^^^^^^
@@ -138,7 +138,7 @@ Here are some examples:
 
 .. code-block:: java
 
-  UnypedActorRef actorRef = ...
+  UntypedActorRef actorRef = ...
 
   try {
     Object result = actorRef.sendRequestReply("Hello", getContext(), 1000);
@@ -256,7 +256,7 @@ The 'replyUnsafe' method throws an 'IllegalStateException' if unable to determin
 Reply using the sender reference
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-If the sender reference (the sender's 'ActorRef') is passed into one ofe the 'send*' methods it will be implicitly passed along together with the message and will be available in the 'Option getSender()' method on the 'ActorRef. This means that you can use this field to send a message back to the sender.
+If the sender reference (the sender's 'ActorRef') is passed into one of the 'send*' methods it will be implicitly passed along together with the message and will be available in the 'Option getSender()' method on the 'ActorRef. This means that you can use this field to send a message back to the sender.
 
 On this 'Option' you can invoke 'boolean isDefined()' or 'boolean isEmpty()' to check if the sender is available or not, and if it is call 'get()' to get the reference. It's important to know that 'getSender().get()' will throw an exception if there is no sender in scope. The same pattern holds for using the 'getSenderFuture()' in the section below.
 
@@ -267,7 +267,7 @@ On this 'Option' you can invoke 'boolean isDefined()' or 'boolean isEmpty()' to
       String msg = (String)message;
       if (msg.equals("Hello")) {
         // Reply to original sender of message using the sender reference
-        // also passing along my own refererence (the context)
+        // also passing along my own reference (the context)
         if (getContext().getSender().isDefined)
           getContext().getSender().get().sendOneWay(msg + " from " + getContext().getUuid(), getContext());
       }
@@ -279,7 +279,7 @@ Reply using the sender future
 
 If a message was sent with the 'sendRequestReply' or 'sendRequestReplyFuture' methods, which both implements request-reply semantics using Future's, then you either have the option of replying using the 'reply' method as above. This method will then resolve the Future. But you can also get a reference to the Future directly and resolve it yourself or if you would like to store it away to resolve it later, or pass it on to some other Actor to resolve it.
 
-The reference to the Future resides in the 'ActorRef' instance and can be retreived using 'Option getSenderFuture()'.
+The reference to the Future resides in the 'ActorRef' instance and can be retrieved using 'Option getSenderFuture()'.
 
 CompletableFuture is a future with methods for 'completing the future:
 * completeWithResult(..)
@@ -337,7 +337,7 @@ Actors are started by invoking the ‘start’ method.
   ActorRef actor = actorOf(SampleUntypedActor.class);
   myActor.start();
 
-You can create and start the Actor in a oneliner like this:
+You can create and start the Actor in a one liner like this:
 
 .. code-block:: java
 
diff --git a/akka-docs/scala/actors.rst b/akka-docs/scala/actors.rst
index 70f9e0cfcc..7c9990cd3f 100644
--- a/akka-docs/scala/actors.rst
+++ b/akka-docs/scala/actors.rst
@@ -372,7 +372,7 @@ Actors are started by invoking the ``start`` method.
   val actor = actorOf[MyActor]
   actor.start()
 
-You can create and start the ``Actor`` in a oneliner like this:
+You can create and start the ``Actor`` in a one liner like this:
 
 .. code-block:: scala
 
diff --git a/akka-docs/scala/fsm.rst b/akka-docs/scala/fsm.rst
index a23fd9edf4..c1736bf444 100644
--- a/akka-docs/scala/fsm.rst
+++ b/akka-docs/scala/fsm.rst
@@ -42,7 +42,7 @@ B and C.
   case object B extends ExampleState
   case object C extends ExampleState
 
-Now lets create an object representing the FSM and defining the behaviour.
+Now lets create an object representing the FSM and defining the behavior.
 
 .. code-block:: scala
 
diff --git a/akka-docs/scala/migration-guide-1.0.x-1.1.x.rst b/akka-docs/scala/migration-guide-1.0.x-1.1.x.rst
index c32b2545ac..07014e3f81 100644
--- a/akka-docs/scala/migration-guide-1.0.x-1.1.x.rst
+++ b/akka-docs/scala/migration-guide-1.0.x-1.1.x.rst
@@ -12,7 +12,7 @@ Akka Actor
 ==========
 
 # is now dependency free, with the exception of the dependency on the ``scala-library.jar``
-# does not bundle any logging anymore, but you can subscribe to events within Akka by registering an event handler on akka.aevent.EventHandler or by specifying the ``FQN`` of an Actor in the akka.conf under akka.event-handlers; there is an ``akka-slf4j`` module which still provides the Logging trait and a default ``SLF4J`` logger adapter.
+# does not bundle any logging anymore, but you can subscribe to events within Akka by registering an event handler on akka.event.EventHandler or by specifying the ``FQN`` of an Actor in the akka.conf under akka.event-handlers; there is an ``akka-slf4j`` module which still provides the Logging trait and a default ``SLF4J`` logger adapter.
 Don't forget to add a SLF4J backend though, we recommend:
 
 .. code-block:: scala
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 8781c72ecd..3be65cdea3 100644
--- a/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala
+++ b/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala
@@ -155,7 +155,7 @@ trait NettyRemoteClientModule extends RemoteClientModule { self: ListenerManagem
 
 /**
  * This is the abstract baseclass for netty remote clients, currently there's only an
- * ActiveRemoteClient, but otehrs could be feasible, like a PassiveRemoteClient that
+ * ActiveRemoteClient, but others could be feasible, like a PassiveRemoteClient that
  * reuses an already established connection.
  */
 abstract class RemoteClient private[akka] (
diff --git a/akka-remote/src/test/scala/ticket/Ticket434Spec.scala b/akka-remote/src/test/scala/ticket/Ticket434Spec.scala
index 971f200340..55cc4a2659 100644
--- a/akka-remote/src/test/scala/ticket/Ticket434Spec.scala
+++ b/akka-remote/src/test/scala/ticket/Ticket434Spec.scala
@@ -25,7 +25,7 @@ class Ticket434Spec extends AkkaRemoteTest {
       latch.await(1, unit) must be (true)
     }
 
-    "should be possible to set the acor id and uuuid" in {
+    "should be possible to set the actor id and uuid" in {
       val uuid = newUuid
       val actorInfo = ActorInfoProtocol.newBuilder
         .setUuid(UuidProtocol.newBuilder.setHigh(uuid.getTime).setLow(uuid.getClockSeqAndNode).build)
diff --git a/akka-stm/src/main/scala/akka/transactor/Transactor.scala b/akka-stm/src/main/scala/akka/transactor/Transactor.scala
index 8469aa53dd..57130dec07 100644
--- a/akka-stm/src/main/scala/akka/transactor/Transactor.scala
+++ b/akka-stm/src/main/scala/akka/transactor/Transactor.scala
@@ -82,7 +82,7 @@ case class SendTo(actor: ActorRef, message: Option[Any] = None)
  * }}}
  * 
* - * To exeucte directly before or after the coordinated transaction, override + * To execute directly before or after the coordinated transaction, override * the `before` and `after` methods. These methods also expect partial functions * like the receive method. They do not execute within the transaction. * diff --git a/akka-typed-actor/src/test/scala/actor/typed-actor/TypedActorRegistrySpec.scala b/akka-typed-actor/src/test/scala/actor/typed-actor/TypedActorRegistrySpec.scala index a83dccc18d..fd99974775 100644 --- a/akka-typed-actor/src/test/scala/actor/typed-actor/TypedActorRegistrySpec.scala +++ b/akka-typed-actor/src/test/scala/actor/typed-actor/TypedActorRegistrySpec.scala @@ -16,7 +16,7 @@ class TypedActorRegistrySpec extends WordSpec with MustMatchers { "Typed Actor" should { - "be able to be retreived from the registry by class" in { + "be able to be retrieved from the registry by class" in { Actor.registry.shutdownAll() val my = TypedActor.newInstance[My](classOf[My], classOf[MyImpl], 3000) val actors = Actor.registry.typedActorsFor(classOf[My]) @@ -24,7 +24,7 @@ class TypedActorRegistrySpec extends WordSpec with MustMatchers { Actor.registry.shutdownAll() } - "be able to be retreived from the registry by manifest" in { + "be able to be retrieved from the registry by manifest" in { Actor.registry.shutdownAll() val my = TypedActor.newInstance[My](classOf[My], classOf[MyImpl], 3000) val option = Actor.registry.typedActorFor[My] @@ -33,7 +33,7 @@ class TypedActorRegistrySpec extends WordSpec with MustMatchers { Actor.registry.shutdownAll() } - "be able to be retreived from the registry by class two times" in { + "be able to be retrieved from the registry by class two times" in { Actor.registry.shutdownAll() val my = TypedActor.newInstance[My](classOf[My], classOf[MyImpl], 3000) val actors1 = Actor.registry.typedActorsFor(classOf[My]) @@ -43,7 +43,7 @@ class TypedActorRegistrySpec extends WordSpec with MustMatchers { Actor.registry.shutdownAll() } - "be able to be retreived from the registry by manifest two times" in { + "be able to be retrieved from the registry by manifest two times" in { Actor.registry.shutdownAll() val my = TypedActor.newInstance[My](classOf[My], classOf[MyImpl], 3000) val option1 = Actor.registry.typedActorFor[My] @@ -55,7 +55,7 @@ class TypedActorRegistrySpec extends WordSpec with MustMatchers { Actor.registry.shutdownAll() } - "be able to be retreived from the registry by manifest two times (even when created in supervisor)" in { + "be able to be retrieved from the registry by manifest two times (even when created in supervisor)" in { Actor.registry.shutdownAll() val manager = new TypedActorConfigurator manager.configure( From d55c9cea10279abe368a6466b5b3dea05be1a637 Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Sat, 23 Apr 2011 09:44:59 +0200 Subject: [PATCH 011/100] add Java API to Duration --- .../src/test/java/akka/util/JavaDuration.java | 16 ++++++ .../src/main/scala/akka/util/Duration.scala | 52 ++++++++++++++++--- 2 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 akka-actor-tests/src/test/java/akka/util/JavaDuration.java diff --git a/akka-actor-tests/src/test/java/akka/util/JavaDuration.java b/akka-actor-tests/src/test/java/akka/util/JavaDuration.java new file mode 100644 index 0000000000..1cea685880 --- /dev/null +++ b/akka-actor-tests/src/test/java/akka/util/JavaDuration.java @@ -0,0 +1,16 @@ +package akka.util; + +import org.junit.Test; + +public class JavaDuration { + + @Test void testCreation() { + final Duration fivesec = Duration.create(5, "seconds"); + final Duration threemillis = Duration.parse("3 millis"); + final Duration diff = fivesec.minus(threemillis); + assert diff.lt(fivesec); + assert Duration.Zero().lteq(Duration.Inf()); + assert Duration.Inf().gt(Duration.Zero().neg()); + } + +} diff --git a/akka-actor/src/main/scala/akka/util/Duration.scala b/akka-actor/src/main/scala/akka/util/Duration.scala index 933e3cd9a9..4e105c198f 100644 --- a/akka-actor/src/main/scala/akka/util/Duration.scala +++ b/akka-actor/src/main/scala/akka/util/Duration.scala @@ -96,15 +96,32 @@ object Duration { case "ns" | "nano" | "nanos" | "nanosecond" | "nanoseconds" => NANOSECONDS } + val Zero : Duration = new FiniteDuration(0, NANOSECONDS) + trait Infinite { this : Duration => override def equals(other : Any) = false - def +(other : Duration) : Duration = this - def -(other : Duration) : Duration = this - def *(other : Double) : Duration = this - def /(other : Double) : Duration = this + def +(other : Duration) : Duration = + other match { + case _ : this.type => this + case _ : Infinite => throw new IllegalArgumentException("illegal addition of infinities") + case _ => this + } + def -(other : Duration) : Duration = + other match { + case _ : this.type => throw new IllegalArgumentException("illegal subtraction of infinities") + case _ => this + } + def *(factor : Double) : Duration = this + def /(factor : Double) : Duration = this + def /(other : Duration) : Double = + other match { + case _ : Infinite => throw new IllegalArgumentException("illegal division of infinities") + // maybe questionable but pragmatic: Inf / 0 => Inf + case x => Double.PositiveInfinity * (if ((this > Zero) ^ (other >= Zero)) -1 else 1) + } def finite_? = false @@ -126,7 +143,7 @@ object Duration { * Infinite duration: greater than any other and not equal to any other, * including itself. */ - object Inf extends Duration with Infinite { + val Inf : Duration = new Duration with Infinite { override def toString = "Duration.Inf" def >(other : Duration) = true def >=(other : Duration) = true @@ -139,7 +156,7 @@ object Duration { * Infinite negative duration: lesser than any other and not equal to any other, * including itself. */ - object MinusInf extends Duration with Infinite { + val MinusInf : Duration = new Duration with Infinite { override def toString = "Duration.MinusInf" def >(other : Duration) = false def >=(other : Duration) = false @@ -148,6 +165,11 @@ object Duration { def unary_- : Duration = Inf } + // Java Factories + def create(length : Long, unit : TimeUnit) : Duration = apply(length, unit) + def create(length : Double, unit : TimeUnit) : Duration = apply(length, unit) + def create(length : Long, unit : String) : Duration = apply(length, unit) + def parse(s : String) : Duration = unapply(s).get } /** @@ -195,7 +217,7 @@ object Duration { * val d3 = d2 + 1.millisecond *
*/ -trait Duration { +abstract class Duration { def length : Long def unit : TimeUnit def toNanos : Long @@ -215,8 +237,22 @@ trait Duration { def -(other : Duration) : Duration def *(factor : Double) : Duration def /(factor : Double) : Duration + def /(other : Duration) : Double def unary_- : Duration def finite_? : Boolean + + // Java API + def lt(other : Duration) = this < other + def lteq(other : Duration) = this <= other + def gt(other : Duration) = this > other + def gteq(other : Duration) = this >= other + def plus(other : Duration) = this + other + def minus(other : Duration) = this - other + def mul(factor : Double) = this * factor + def div(factor : Double) = this / factor + def div(other : Duration) = this / other + def neg() = -this + def isFinite() = finite_? } class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration { @@ -306,6 +342,8 @@ class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration { def /(factor : Double) = fromNanos(long2double(toNanos) / factor) + def /(other : Duration) = if (other.finite_?) long2double(toNanos) / other.toNanos else 0 + def unary_- = Duration(-length, unit) def finite_? = true From 2adc45cf2c64d825aaacdc2bde36cc30b01c6175 Mon Sep 17 00:00:00 2001 From: Roland Date: Sat, 23 Apr 2011 11:04:02 +0200 Subject: [PATCH 012/100] move Duration doc to general/util.rst move migration guide links one level down --- akka-docs/general/index.rst | 9 ++--- akka-docs/general/migration-guides.rst | 11 ++++++ akka-docs/general/util.rst | 49 ++++++++++++++++++++++++++ akka-docs/scala/testing.rst | 39 -------------------- 4 files changed, 63 insertions(+), 45 deletions(-) create mode 100644 akka-docs/general/migration-guides.rst create mode 100644 akka-docs/general/util.rst diff --git a/akka-docs/general/index.rst b/akka-docs/general/index.rst index 1d716ed63a..5b0e3c24d6 100644 --- a/akka-docs/general/index.rst +++ b/akka-docs/general/index.rst @@ -2,10 +2,7 @@ General ======= .. toctree:: - :maxdepth: 1 + :maxdepth: 2 - migration-guide-0.7.x-0.8.x - migration-guide-0.8.x-0.9.x - migration-guide-0.9.x-0.10.x - migration-guide-0.10.x-1.0.x - migration-guide-1.0.x-1.1.x + migration-guides + util diff --git a/akka-docs/general/migration-guides.rst b/akka-docs/general/migration-guides.rst new file mode 100644 index 0000000000..204b7a22e5 --- /dev/null +++ b/akka-docs/general/migration-guides.rst @@ -0,0 +1,11 @@ +Migration Guides +================ + +.. toctree:: + :maxdepth: 1 + + migration-guide-0.7.x-0.8.x + migration-guide-0.8.x-0.9.x + migration-guide-0.9.x-0.10.x + migration-guide-0.10.x-1.0.x + migration-guide-1.0.x-1.1.x diff --git a/akka-docs/general/util.rst b/akka-docs/general/util.rst new file mode 100644 index 0000000000..bb0f61e778 --- /dev/null +++ b/akka-docs/general/util.rst @@ -0,0 +1,49 @@ +######### +Utilities +######### + +.. sidebar:: Contents + + .. contents:: :local: + +This section of the manual describes miscellaneous utilities which are provided +by Akka and used in multiple places. + +.. _Duration: + +Duration +======== + +Durations are used throughout the Akka library, wherefore this concept is +represented by a special data type, :class:`Duration`. Values of this type may +represent infinite (:obj:`Duration.Inf`, :obj:`Duration.MinusInf`) or finite +durations, where the latter are constructable using a mini-DSL: + +.. code-block:: scala + + import akka.util.duration._ // notice the small d + + val fivesec = 5.seconds + val threemillis = 3.millis + val diff = fivesec - threemillis + assert (diff < fivesec) + +.. note:: + + You may leave out the dot if the expression is clearly delimited (e.g. + within parentheses or in an argument list), but it is recommended to use it + if the time unit is the last token on a line, otherwise semi-colon inference + might go wrong, depending on what starts the next line. + +Java provides less syntactic sugar, so you have to spell out the operations as +method calls instead: + +.. code-block:: java + + final Duration fivesec = Duration.create(5, "seconds"); + final Duration threemillis = Duration.parse("3 millis"); + final Duration diff = fivesec.minus(threemillis); + assert (diff.lt(fivesec)); + assert (Duration.Zero().lt(Duration.Inf())); + + diff --git a/akka-docs/scala/testing.rst b/akka-docs/scala/testing.rst index ecf86720db..9238cfd198 100644 --- a/akka-docs/scala/testing.rst +++ b/akka-docs/scala/testing.rst @@ -332,42 +332,3 @@ has to offer: exception stack traces - Exclusion of certain classes of dead-lock scenarios -.. _Duration: - -Duration -======== - -Durations are used throughout the Akka library, wherefore this concept is -represented by a special data type, :class:`Duration`. Values of this type may -represent infinite (:obj:`Duration.Inf`, :obj:`Duration.MinusInf`) or finite -durations, where the latter are constructable using a mini-DSL: - -.. code-block:: scala - - import akka.util.duration._ // notice the small d - - val fivesec = 5.seconds - val threemillis = 3.millis - val diff = fivesec - threemillis - assert (diff < fivesec) - -.. note:: - - You may leave out the dot if the expression is clearly delimited (e.g. - within parentheses or in an argument list), but it is recommended to use it - if the time unit is the last token on a line, otherwise semi-colon inference - might go wrong, depending on what starts the next line. - -Java provides less syntactic sugar, so you have to spell out the operations as -method calls instead: - -.. code-block:: java - - final Duration fivesec = Duration.create(5, "seconds"); - final Duration threemillis = Duration.parse("3 millis"); - final Duration diff = fivesec.minus(threemillis); - assert (diff.lt(fivesec)); - assert (Duration.Zero().lt(Duration.Inf())); - - - From 78beaa9e43c007136f28d2a6b921cafb6922fe23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bone=CC=81r?= Date: Sat, 23 Apr 2011 11:31:00 +0200 Subject: [PATCH 013/100] added some generic lookup methods to Configuration --- .../main/scala/akka/config/Configuration.scala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/akka-actor/src/main/scala/akka/config/Configuration.scala b/akka-actor/src/main/scala/akka/config/Configuration.scala index aabdc5962f..d06a9eb88e 100644 --- a/akka-actor/src/main/scala/akka/config/Configuration.scala +++ b/akka-actor/src/main/scala/akka/config/Configuration.scala @@ -64,6 +64,24 @@ class Configuration(val map: Map[String, Any]) { def keys: Iterable[String] = map.keys + def getAny(key: String): Option[Any] = { + try { + Some(map(key)) + } catch { + case _ => None + } + } + + def getAny(key: String, defaultValue: Any): Any = getAny(key).getOrElse(defaultValue) + + def getSeqAny(key: String): Seq[Any] = { + try { + map(key).asInstanceOf[Seq[Any]] + } catch { + case _ => Seq.empty[Any] + } + } + def getString(key: String): Option[String] = map.get(key).map(_.toString) def getString(key: String, defaultValue: String): String = getString(key).getOrElse(defaultValue) From 18bfe152ba1b16655b0ac36e90fcb31873d119f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bone=CC=81r?= Date: Sat, 23 Apr 2011 11:32:19 +0200 Subject: [PATCH 014/100] Changed default event-handler level to INFO --- akka-actor/src/main/scala/akka/event/EventHandler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/event/EventHandler.scala b/akka-actor/src/main/scala/akka/event/EventHandler.scala index 9d53b57787..efda95a951 100644 --- a/akka-actor/src/main/scala/akka/event/EventHandler.scala +++ b/akka-actor/src/main/scala/akka/event/EventHandler.scala @@ -85,7 +85,7 @@ object EventHandler extends ListenerManagement { lazy val EventHandlerDispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher(ID).build - val level: Int = config.getString("akka.event-handler-level", "DEBUG") match { + val level: Int = config.getString("akka.event-handler-level", "INFO") match { case "ERROR" => ErrorLevel case "WARNING" => WarningLevel case "INFO" => InfoLevel From e3a7a2cf6630ba12b7ab8cfc1290d1ae32a3047a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bone=CC=81r?= Date: Sat, 23 Apr 2011 11:34:19 +0200 Subject: [PATCH 015/100] Removed logging ClassNotFoundException to EventHandler in ReflectiveAccess --- .../main/scala/akka/util/ReflectiveAccess.scala | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala b/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala index f4ceba6ebe..cda46c1a95 100644 --- a/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala +++ b/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala @@ -178,9 +178,7 @@ object ReflectiveAccess { val first = try { Option(classloader.loadClass(fqn).asInstanceOf[Class[T]]) } catch { - case c: ClassNotFoundException => - EventHandler.debug(this, c.toString) - None + case c: ClassNotFoundException => None } if (first.isDefined) first @@ -189,9 +187,7 @@ object ReflectiveAccess { val second = try { Option(Thread.currentThread.getContextClassLoader.loadClass(fqn).asInstanceOf[Class[T]]) } catch { - case c: ClassNotFoundException => - EventHandler.debug(this, c.toString) - None + case c: ClassNotFoundException => None } if (second.isDefined) second @@ -201,9 +197,7 @@ object ReflectiveAccess { if (classloader ne loader) Option(loader.loadClass(fqn).asInstanceOf[Class[T]]) else None } catch { - case c: ClassNotFoundException => - EventHandler.debug(this, c.toString) - None + case c: ClassNotFoundException => None } if (third.isDefined) third @@ -212,9 +206,7 @@ object ReflectiveAccess { try { Option(Class.forName(fqn).asInstanceOf[Class[T]]) } catch { - case c: ClassNotFoundException => - EventHandler.debug(this, c.toString) - None + case c: ClassNotFoundException => None } } } From 6e45ab7c17e8a11d593540eb9793e3c7dafceafd Mon Sep 17 00:00:00 2001 From: Roland Date: Sat, 23 Apr 2011 11:35:54 +0200 Subject: [PATCH 016/100] add Future.empty[T] --- akka-actor/src/main/scala/akka/dispatch/Future.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 60d956e07c..0f44fb27c0 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -246,6 +246,11 @@ object Future { def !(msg: Any) = future << msg } + /** + * Create an empty Future with default timeout + */ + def empty[T](timeout : Long = Actor.TIMEOUT) = new DefaultCompletableFuture[T](timeout) + import scala.collection.mutable.Builder import scala.collection.generic.CanBuildFrom From 33f05856c1434dfcd3861b7de9236bf43cc2238c Mon Sep 17 00:00:00 2001 From: Roland Date: Sat, 23 Apr 2011 11:49:03 +0200 Subject: [PATCH 017/100] use Future.empty in Future.channel --- akka-actor/src/main/scala/akka/dispatch/Future.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 0f44fb27c0..2b3fc6425d 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -242,7 +242,7 @@ object Future { * Construct a completable channel */ def channel(timeout: Long = Actor.TIMEOUT) = new Channel[Any] { - val future = new DefaultCompletableFuture[Any](timeout) + val future = empty[Any](timeout) def !(msg: Any) = future << msg } From 06c134ca91d276c4d2322db66fd82bca173cb1b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bone=CC=81r?= Date: Sat, 23 Apr 2011 11:49:33 +0200 Subject: [PATCH 018/100] Added EventHandler.shutdown()' --- .../src/main/scala/akka/event/EventHandler.scala | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/akka-actor/src/main/scala/akka/event/EventHandler.scala b/akka-actor/src/main/scala/akka/event/EventHandler.scala index efda95a951..b29eb0ca72 100644 --- a/akka-actor/src/main/scala/akka/event/EventHandler.scala +++ b/akka-actor/src/main/scala/akka/event/EventHandler.scala @@ -44,6 +44,11 @@ import akka.AkkaException * EventHandler.error(exception, this, message) *
* + * Shut down the EventHandler: + *
+ * EventHandler.shutdown()
+ * 
+ * * @author Jonas Bonér */ object EventHandler extends ListenerManagement { @@ -94,6 +99,14 @@ object EventHandler extends ListenerManagement { "Configuration option 'akka.event-handler-level' is invalid [" + unknown + "]") } + /** + * Shuts down all event handler listeners including the event handle dispatcher. + */ + def shutdown() = { + foreachListener(_.stop) + EventHandlerDispatcher.shutdown + } + def notify(event: Any) { if (event.isInstanceOf[Event]) { if (level >= event.asInstanceOf[Event].level) notifyListeners(event) From 2770f47f27ac12a5e6b44918de987f4a3ab4b7c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bone=CC=81r?= Date: Sat, 23 Apr 2011 11:55:54 +0200 Subject: [PATCH 019/100] Fixed problem in Pi calculation algorithm --- akka-docs/intro/examples/Pi.scala | 2 +- .../intro/getting-started-first-java.rst | 4 ++-- .../getting-started-first-scala-eclipse.rst | 24 +++++++++---------- akka-docs/intro/getting-started-first.rst | 2 -- .../java/akka/tutorial/first/java/Pi.java | 2 +- .../src/main/scala/Pi.scala | 2 +- 6 files changed, 17 insertions(+), 19 deletions(-) diff --git a/akka-docs/intro/examples/Pi.scala b/akka-docs/intro/examples/Pi.scala index 6bf1dea903..1635229802 100644 --- a/akka-docs/intro/examples/Pi.scala +++ b/akka-docs/intro/examples/Pi.scala @@ -36,7 +36,7 @@ object Pi extends App { def calculatePiFor(start: Int, nrOfElements: Int): Double = { var acc = 0.0 for (i <- start until (start + nrOfElements)) - acc += 4 * math.pow(-1, i) / (2 * i + 1) + acc += 4.0 * math.pow(-1, i) / (2 * i + 1) acc } //#calculate-pi diff --git a/akka-docs/intro/getting-started-first-java.rst b/akka-docs/intro/getting-started-first-java.rst index df032a8970..99db6f8c07 100644 --- a/akka-docs/intro/getting-started-first-java.rst +++ b/akka-docs/intro/getting-started-first-java.rst @@ -282,7 +282,7 @@ The only thing missing in our ``Worker`` actor is the implementation on the ``ca private double calculatePiFor(int start, int nrOfElements) { double acc = 0.0; for (int i = start * nrOfElements; i <= ((start + 1) * nrOfElements - 1); i++) { - acc += 4 * (1 - (i % 2) * 2) / (2 * i + 1); + acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1); } return acc; } @@ -550,7 +550,7 @@ Before we package it up and run it, let's take a look at the full code now, with private double calculatePiFor(int start, int nrOfElements) { double acc = 0.0; for (int i = start * nrOfElements; i <= ((start + 1) * nrOfElements - 1); i++) { - acc += 4 * (1 - (i % 2) * 2) / (2 * i + 1); + acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1); } return acc; } diff --git a/akka-docs/intro/getting-started-first-scala-eclipse.rst b/akka-docs/intro/getting-started-first-scala-eclipse.rst index d34f242e7d..ebd0064620 100644 --- a/akka-docs/intro/getting-started-first-scala-eclipse.rst +++ b/akka-docs/intro/getting-started-first-scala-eclipse.rst @@ -4,7 +4,7 @@ Getting Started Tutorial (Scala with Eclipse): First Chapter Introduction ------------ -Welcome to the first tutorial on how to get started with Akka and Scala. We assume that you already know what Akka and Scala are and will now focus on the steps necessary to start your first project. We will be using `Eclipse `_, and the `Scala plugin for Eclipse `_. +Welcome to the first tutorial on how to get started with Akka and Scala. We assume that you already know what Akka and Scala are and will now focus on the steps necessary to start your first project. We will be using `Eclipse `_, and the `Scala plugin for Eclipse `_. The sample application that we will create is using actors to calculate the value of Pi. Calculating Pi is a CPU intensive operation and we will utilize Akka Actors to write a concurrent solution that scales out to multi-core processors. This sample will be extended in future tutorials to use Akka Remote Actors to scale out on multiple machines in a cluster. @@ -109,7 +109,7 @@ Once the installation is finished, you need to restart Eclipse. The first time t Accept the recommended settings, and follow the instructions if you need to increase the heap size of Eclipse. -Check that the installation succeeded by creating a new Scala project (``File/New>Scala Project``), and typing some code. You should have content-assist, hyperlinking to definitions, instant error reporting, and so on. +Check that the installation succeeded by creating a new Scala project (``File/New>Scala Project``), and typing some code. You should have content-assist, hyperlinking to definitions, instant error reporting, and so on. .. image:: example-code.png @@ -147,8 +147,8 @@ Using SBT in Eclipse If you are an `SBT `_ user, you can follow the :doc:`Akka Tutorial in Scala ` and additionally install the ``sbt-eclipse`` plugin. This adds support for generating Eclipse project files from your SBT project. You need to update your SBT plugins definition in ``project/plugins``:: - import sbt._ - + import sbt._ + class TutorialPlugins(info: ProjectInfo) extends PluginDefinition(info) { // eclipsify plugin lazy val eclipse = "de.element34" % "sbt-eclipsify" % "0.7.0" @@ -161,8 +161,8 @@ and then update your SBT project definition by mixing in ``Eclipsify`` in your p import sbt._ import de.element34.sbteclipsify._ - - class MySbtProject(info: ProjectInfo) extends DefaultProject(info) + + class MySbtProject(info: ProjectInfo) extends DefaultProject(info) with Eclipsify with AkkaProject { // the project definition here // akka dependencies @@ -173,14 +173,14 @@ Then run the ``eclipse`` target to generate the Eclipse project:: dragos@dragos-imac pi $ sbt eclipse [info] Building project AkkaPi 1.0 against Scala 2.9.0.RC1 [info] using MySbtProject with sbt 0.7.4 and Scala 2.7.7 - [info] + [info] [info] == eclipse == [info] Creating eclipse project... [info] == eclipse == [success] Successful. - [info] + [info] [info] Total time: 0 s, completed Apr 20, 2011 2:48:03 PM - [info] + [info] [info] Total session time: 1 s, completed Apr 20, 2011 2:48:03 PM [success] Build completed successfully. @@ -195,7 +195,7 @@ Start writing the code The design we are aiming for is to have one ``Master`` actor initiating the computation, creating a set of ``Worker`` actors. Then it splits up the work into discrete chunks, and sends these chunks to the different workers in a round-robin fashion. The master waits until all the workers have completed their work and sent back results for aggregation. When computation is completed the master prints out the result, shuts down all workers and then itself. -With this in mind, let's now create the messages that we want to have flowing in the system. +With this in mind, let's now create the messages that we want to have flowing in the system. Creating the messages --------------------- @@ -245,7 +245,7 @@ The only thing missing in our ``Worker`` actor is the implementation on the ``ca def calculatePiFor(start: Int, nrOfElements: Int): Double = { var acc = 0.0 for (i <- start until (start + nrOfElements)) - acc += 4 * (1 - (i % 2) * 2) / (2 * i + 1) + acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1) acc } @@ -392,7 +392,7 @@ Run it from Eclipse ------------------- Eclipse builds your project on every save when ``Project/Build Automatically`` is set. If not, bring you project up to date by clicking ``Project/Build Project``. If there are no compilation errors, you can right-click in the editor where ``Pi`` is defined, and choose ``Run as.. /Scala application``. If everything works fine, you should see:: - + AKKA_HOME is defined as [/Users/jboner/tools/akka-modules-1.1-M1/] loading config from [/Users/jboner/tools/akka-modules-1.1-M1/config/akka.conf]. diff --git a/akka-docs/intro/getting-started-first.rst b/akka-docs/intro/getting-started-first.rst index aaa466c1dc..79c220d14a 100644 --- a/akka-docs/intro/getting-started-first.rst +++ b/akka-docs/intro/getting-started-first.rst @@ -266,8 +266,6 @@ But before we package it up and run it, let's take a look at the full code now, .. includecode:: examples/Pi.scala - - Run it as a command line application ------------------------------------ diff --git a/akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java b/akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java index 8c0085fb97..034dec5178 100644 --- a/akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java +++ b/akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java @@ -87,7 +87,7 @@ public class Pi { private double calculatePiFor(int start, int nrOfElements) { double acc = 0.0; for (int i = start * nrOfElements; i <= ((start + 1) * nrOfElements - 1); i++) { - acc += 4 * (1 - (i % 2) * 2) / (2 * i + 1); + acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1); } return acc; } diff --git a/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala b/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala index c16c53f995..e6d8b87c14 100644 --- a/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala +++ b/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala @@ -59,7 +59,7 @@ object Pi extends App { def calculatePiFor(start: Int, nrOfElements: Int): Double = { var acc = 0.0 for (i <- start until (start + nrOfElements)) - acc += 4 * (1 - (i % 2) * 2) / (2 * i + 1) + acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1) acc } From b16108b6618227dfbda60ae8302ee57c62f39a1a Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Sun, 24 Apr 2011 08:33:13 +0200 Subject: [PATCH 020/100] Applied the last typo fixes also --- akka-docs/general/migration-guide-1.0.x-1.1.x.rst | 2 +- akka-docs/pending/release-notes.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/akka-docs/general/migration-guide-1.0.x-1.1.x.rst b/akka-docs/general/migration-guide-1.0.x-1.1.x.rst index 4108d323be..3fc555abaf 100644 --- a/akka-docs/general/migration-guide-1.0.x-1.1.x.rst +++ b/akka-docs/general/migration-guide-1.0.x-1.1.x.rst @@ -20,7 +20,7 @@ Don't forget to add a SLF4J backend though, we recommend: .. code-block:: scala - lazy val logback = "ch.qos.logback" % "logback-classic" % "0.9.28" + lazy val logback = "ch.qos.logback" % "logback-classic" % "0.9.28" % "runtime" # If you used HawtDispatcher and want to continue using it, you need to include akka-dispatcher-extras.jar from Akka Modules, in your akka.conf you need to specify: ``akka.dispatch.HawtDispatcherConfigurator`` instead of ``HawtDispatcher`` # FSM: the onTransition method changed from Function1 to PartialFunction; there is an implicit conversion for the precise types in place, but it may be necessary to add an underscore if you are passing an eta-expansion (using a method as function value). diff --git a/akka-docs/pending/release-notes.rst b/akka-docs/pending/release-notes.rst index 2000b5a1d6..692e7ce244 100644 --- a/akka-docs/pending/release-notes.rst +++ b/akka-docs/pending/release-notes.rst @@ -315,7 +315,7 @@ Release 0.10 - Aug 21 2010 ||< **ADD** ||< Added isDefinedAt method to Actor for checking if it can receive a certain message ||< Jonas Bonér || ||< **ADD** ||< Added caching of Active Object generated class bytes, huge perf improvement ||< Jonas Bonér || ||< **ADD** ||< Added RemoteClient Listener API ||< Jonas Bonér || -||< **ADD** ||< Added methods to retreive children from a Supervisor ||< Jonas Bonér || +||< **ADD** ||< Added methods to retrieve children from a Supervisor ||< Jonas Bonér || ||< **ADD** ||< Rewritten Supervisor to become more clear and "correct" ||< Jonas Bonér || ||< **ADD** ||< Added options to configure a blocking mailbox with custom capacity ||< Jonas Bonér || ||< **ADD** ||< Added RemoteClient reconnection time window configuration option ||< Jonas Bonér || From e22113075fe2472af5e0fb2c4b9a88d51cab2cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Sun, 24 Apr 2011 09:01:45 -0700 Subject: [PATCH 021/100] Fixed broken pi calc algo --- akka-docs/intro/getting-started-first-scala.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-docs/intro/getting-started-first-scala.rst b/akka-docs/intro/getting-started-first-scala.rst index 17c4d265c4..d0c8e2c90e 100644 --- a/akka-docs/intro/getting-started-first-scala.rst +++ b/akka-docs/intro/getting-started-first-scala.rst @@ -238,7 +238,7 @@ The only thing missing in our ``Worker`` actor is the implementation on the ``ca def calculatePiFor(start: Int, nrOfElements: Int): Double = { var acc = 0.0 for (i <- start until (start + nrOfElements)) - acc += 4 * (1 - (i % 2) * 2) / (2 * i + 1) + acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1) acc } From 2468f9e68a99ee553e2fcdeb3436cb8dca48ae93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Sun, 24 Apr 2011 09:02:37 -0700 Subject: [PATCH 022/100] Fixed broken pi calc algo --- akka-docs/intro/getting-started-first-scala.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-docs/intro/getting-started-first-scala.rst b/akka-docs/intro/getting-started-first-scala.rst index d0c8e2c90e..c6ea2f4fe1 100644 --- a/akka-docs/intro/getting-started-first-scala.rst +++ b/akka-docs/intro/getting-started-first-scala.rst @@ -404,7 +404,7 @@ But before we package it up and run it, let's take a look at the full code now, def calculatePiFor(start: Int, nrOfElements: Int): Double = { var acc = 0.0 for (i <- start until (start + nrOfElements)) - acc += 4 * (1 - (i % 2) * 2) / (2 * i + 1) + acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1) acc } From b041329b98f84d167c0d6dc4b9bcb157d2f25400 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Sun, 24 Apr 2011 11:24:29 -0600 Subject: [PATCH 023/100] Update Jetty to version 7.4.0 --- project/build/AkkaProject.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 8a93603b0d..b3cb6dba6b 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -114,7 +114,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { lazy val JERSEY_VERSION = "1.3" lazy val MULTIVERSE_VERSION = "0.6.2" lazy val SCALATEST_VERSION = "1.4-SNAPSHOT" - lazy val JETTY_VERSION = "7.2.2.v20101205" + lazy val JETTY_VERSION = "7.4.0.v20110414" lazy val JAVAX_SERVLET_VERSION = "3.0" lazy val SLF4J_VERSION = "1.6.0" From 69ee799dd9e9a8855c60066c54579580a51ba364 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 26 Apr 2011 11:52:17 +0200 Subject: [PATCH 024/100] Removing unused imports, closing ticket #802 --- akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala index 374b3b4b45..14348e9f85 100644 --- a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala +++ b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala @@ -5,11 +5,10 @@ package akka.dispatch import java.util.concurrent._ -import atomic. {AtomicInteger, AtomicBoolean, AtomicReference, AtomicLong} import akka.event.EventHandler import akka.config.Configuration import akka.config.Config.TIME_UNIT -import akka.util.{Duration, Switch, ReentrantGuard, HashCode, ReflectiveAccess} +import akka.util.{Duration, Switch, ReentrantGuard} import java.util.concurrent.ThreadPoolExecutor.{AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy, DiscardPolicy} import akka.actor._ From 8f43d6f0b8b417976f4d01c8a39c68f4a1486ba5 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 26 Apr 2011 12:00:09 +0200 Subject: [PATCH 025/100] Adding parens to preStart and postStop closing #798 --- akka-actor/src/main/scala/akka/actor/Actor.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 14acfbb3e1..778a79f7f4 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -368,14 +368,14 @@ trait Actor { *

* Is called when an Actor is started by invoking 'actor.start()'. */ - def preStart {} + def preStart() {} /** * User overridable callback. *

* Is called when 'actor.stop()' is invoked. */ - def postStop {} + def postStop() {} /** * User overridable callback. From 7446afd51b507b33f0220f2be22d893154860efc Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 26 Apr 2011 12:05:33 +0200 Subject: [PATCH 026/100] Adding the class that failed to instantiate, closing ticket #803 --- akka-actor/src/main/scala/akka/actor/Actor.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 778a79f7f4..ff3cf26d7b 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -161,7 +161,7 @@ object Actor extends ListenerManagement { import ReflectiveAccess.{ createInstance, noParams, noArgs } createInstance[Actor](clazz.asInstanceOf[Class[_]], noParams, noArgs).getOrElse( throw new ActorInitializationException( - "Could not instantiate Actor" + + "Could not instantiate Actor of " + clazz + "\nMake sure Actor is NOT defined inside a class/trait," + "\nif so put it outside the class/trait, f.e. in a companion object," + "\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.")) From 66853294b15ee82d8f5075bc21907360f36b74ef Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 26 Apr 2011 12:12:25 +0200 Subject: [PATCH 027/100] Making ActorRegistry public so it`ll be in the scaladoc, constructor remains private tho, closing #743 --- akka-actor/src/main/scala/akka/actor/ActorRegistry.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/actor/ActorRegistry.scala b/akka-actor/src/main/scala/akka/actor/ActorRegistry.scala index 27057ce8a9..c833c6d360 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRegistry.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRegistry.scala @@ -36,7 +36,7 @@ case class ActorUnregistered(actor: ActorRef) extends ActorRegistryEvent * @author Jonas Bonér */ -private[actor] final class ActorRegistry private[actor] () extends ListenerManagement { +final class ActorRegistry private[actor] () extends ListenerManagement { private val actorsByUUID = new ConcurrentHashMap[Uuid, ActorRef] private val actorsById = new Index[String,ActorRef] From 5ff0165717cfdff93add15910a6776575baf8782 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 26 Apr 2011 13:03:03 +0200 Subject: [PATCH 028/100] Pointing Jersey sample to v1.0 tag, closing #776 --- akka-docs/pending/http.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/akka-docs/pending/http.rst b/akka-docs/pending/http.rst index a4c7842233..801b786da0 100644 --- a/akka-docs/pending/http.rst +++ b/akka-docs/pending/http.rst @@ -138,12 +138,12 @@ If you want to use akka-camel or any other modules that have their own "Bootable Java API: Typed Actors ---------------------- -`Sample module for REST services with Actors in Java `_ +`Sample module for REST services with Actors in Java `_ Scala API: Actors ----------------- -`Sample module for REST services with Actors in Scala `_ +`Sample module for REST services with Actors in Scala `_ Using Akka with the Pinky REST/MVC framework -------------------------------------------- From d9fbefd4521b27e5b96b78d4bddd91abada4d1bf Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 26 Apr 2011 14:16:12 +0200 Subject: [PATCH 029/100] Fixing ticket #805 --- akka-docs/pending/issue-tracking.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/akka-docs/pending/issue-tracking.rst b/akka-docs/pending/issue-tracking.rst index fcff2e2c94..fa81a4d254 100644 --- a/akka-docs/pending/issue-tracking.rst +++ b/akka-docs/pending/issue-tracking.rst @@ -7,6 +7,8 @@ Browsing -------- You can find the Akka tickets here: ``_ +You can find the Akka Modules tickets here: ``_ + The roadmap for each milestone is here: ``_ Creating tickets @@ -16,8 +18,16 @@ In order to create tickets you need to do the following: # Register here: ``_ # Log in + +For Akka tickets: + # Create the ticket: ``_ + +For Akka Modules tickets: + +# Create the ticket: ``_ + Thanks a lot for reporting bugs and suggesting features. Failing test From 60b650133a23105d9313887bf72e705e7fd1be4b Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Sun, 24 Apr 2011 23:06:12 +0200 Subject: [PATCH 030/100] Improved agents doc --- akka-docs/pending/agents-scala.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/akka-docs/pending/agents-scala.rst b/akka-docs/pending/agents-scala.rst index 9adb9e9f81..c6a4ee9b73 100644 --- a/akka-docs/pending/agents-scala.rst +++ b/akka-docs/pending/agents-scala.rst @@ -26,7 +26,7 @@ An Agent will be running until you invoke ``close`` on it. Then it will be eligi .. code-block:: scala - agent.close + agent.close() Updating Agents --------------- @@ -101,6 +101,7 @@ Example of a monadic usage: val agent2 = Agent(5) // uses foreach + var result = 0 for (value <- agent1) { result = value + 1 } @@ -115,7 +116,7 @@ Example of a monadic usage: value2 <- agent2 } yield value1 + value2 - agent1.close - agent2.close - agent3.close - agent4.close + agent1.close() + agent2.close() + agent3.close() + agent4.close() From 371ac01a4b3a41343cdd3591f4af903fa1db7e95 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 14:19:00 +0200 Subject: [PATCH 031/100] Improved stm docs --- akka-docs/pending/stm-scala.rst | 9 ++++---- akka-docs/pending/stm.rst | 41 ++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/akka-docs/pending/stm-scala.rst b/akka-docs/pending/stm-scala.rst index 0e1249fc48..69de2d6266 100644 --- a/akka-docs/pending/stm-scala.rst +++ b/akka-docs/pending/stm-scala.rst @@ -12,15 +12,16 @@ An `STM `_ turns the * Isolated Generally, the STM is not needed very often when working with Akka. Some use-cases (that we can think of) are: -# When you really need composable message flows across many actors updating their **internal local** state but need them to do that atomically in one big transaction. Might not often, but when you do need this then you are screwed without it. -# When you want to share a datastructure across actors. -# When you need to use the persistence modules. + +- When you really need composable message flows across many actors updating their **internal local** state but need them to do that atomically in one big transaction. Might not be often, but when you do need this then you are screwed without it. +- When you want to share a datastructure across actors. +- When you need to use the persistence modules. Akka’s STM implements the concept in `Clojure’s `_ STM view on state in general. Please take the time to read `this excellent document `_ and view `this presentation `_ by Rich Hickey (the genius behind Clojure), since it forms the basis of Akka’s view on STM and state in general. The STM is based on Transactional References (referred to as Refs). Refs are memory cells, holding an (arbitrary) immutable value, that implement CAS (Compare-And-Swap) semantics and are managed and enforced by the STM for coordinated changes across many Refs. They are implemented using the excellent `Multiverse STM `_. -Working with immutable collections can sometimes give bad performance due to extensive copying. Scala provides so-called persistent datastructures which makes working with immutable collections fast. They are immutable but with constant time access and modification. The use of structural sharing and an insert or update does not ruin the old structure, hence “persistent”. Makes working with immutable composite types fast. The persistent datastructures currently consist of a Map and Vector. +Working with immutable collections can sometimes give bad performance due to extensive copying. Scala provides so-called persistent datastructures which makes working with immutable collections fast. They are immutable but with constant time access and modification. They use structural sharing and an insert or update does not ruin the old structure, hence “persistent”. Makes working with immutable composite types fast. The persistent datastructures currently consist of a Map and Vector. Simple example ============== diff --git a/akka-docs/pending/stm.rst b/akka-docs/pending/stm.rst index 8666425289..54984eecf8 100644 --- a/akka-docs/pending/stm.rst +++ b/akka-docs/pending/stm.rst @@ -1,27 +1,29 @@ Akka STM +======== The Akka Software Transactional Memory implementation **Read consistency** ^^^^^^^^^^^^^^^^^^^^ -Read consistency is that all value +Read consistency is that all value TODO??? **Read consistency and MVCC** ***************************** -A lot of STM (like the Clojure STM) implementations are Multi Version Concurrency Control Based (MVCC) based (TL2 of david dice could be seen as MVCC). +A lot of STM (like the Clojure STM) implementations are Multi Version Concurrency Control (MVCC) based (TL2 of david dice could be seen as MVCC). To provide read consistency, every ref is augmented with a version field (a long). There also is a logical clock (an AtomicLong for instance) that is incremented every time a transaction does a commit (there are some optimizations) and on all refs written, the version of the ref is updated to this new clock value. If a transaction begins, it reads the current version of the clock and makes sure that the version of the refs it reads, are equal or lower than the version of the transaction. If the transaction encounters a ref with a higher value, the transaction is aborted and retried. MVCC STM’s are relatively simple to write and have some very nice properties: -# readers don’t block writers -# writers don’t block readers -# persistent data-structures are very easy to write since a log can be added to each ref containing older versions of the data, -The problem with MVCC however is that the central clock forms a contention point that makes independent transactional data-structures not linearly scalable. todo: give example of scalability with MVCC. +- readers don’t block writers +- writers don’t block readers +- persistent data-structures are very easy to write since a log can be added to each ref containing older versions of the data + +The problem with MVCC however is that the central clock forms a contention point that makes independent transactional data-structures not linearly scalable. TODO: give example of scalability with MVCC. So even if you have 2 Threads having their private transactional Ref (so there is no visible contention), underwater the transaction still are going to contend for the clock. @@ -35,26 +37,29 @@ It uses 2 different mechanisms: 2) For longer transactions it uses semi visible reads. Every time a read is done, the surplus of readers is incremented and stored in the ref. Once the transaction aborts or commits, the surplus is lowered again. If a transaction does an update, and sees that there is a surplus of readers, it increments a conflict counter. This conflict counter is checked every time a transaction reads a new ref. If it hasn’t changed, no full conflict scan is needed. If it has changed, a full conflict scan is required. If a conflict is detected, the transaction is aborted and retried. This technique is called a semi visible read (we don’t know which transactions are possibly going to encounter a conflict, but we do know if there is at least one possible conflict). There are 2 important optimizations to this design: -# Eager full conflict scan -# Read biases refs + +- Eager full conflict scan +- Read biases refs **Eager full conflict scan** **************************** -The reasons why short transactions always do a full conflict scan is that doing semi visible reads, relies doing more expensive synchronization operations (e.g. doing a cas to increase the surplus of readers, or doing a cas to decrease it). +The reasons why short transactions always do a full conflict scan is that doing semi visible reads, relies on doing more expensive synchronization operations (e.g. doing a CAS to increase the surplus of readers, or doing a CAS to decrease it). **Read biased vs update biased.** ********************************* -The problem with semi visible reads is that certain structures (e.g. the root of a tree) can form a contention point (because of the arrives/departs) even though it mostly is read. To reduce contention, a ref can become read biased after a certain number of reads by transactions that use semi visible reads is done. Once it has become read biased, no arrives and departs are required any more, but once it the Ref is updated it will always increment the conflict counter because it doesn’t know if there are any conflicting readers. +The problem with semi visible reads is that certain structures (e.g. the root of a tree) can form a contention point (because of the arrives/departs) even though it mostly is read. To reduce contention, a ref can become read biased after a certain number of reads by transactions that use semi visible reads is done. Once it has become read biased, no arrives and departs are required any more, but once the Ref is updated it will always increment the conflict counter because it doesn’t know if there are any conflicting readers. -Visible reads, semi visible reads -Read tracking +TODO: -strict isolation -eager conflict detection -deferred write, no dirty read possible +- Visible reads, semi visible reads +- Read tracking -isolation level -optimistic -various levels of pessimistic behavior +- strict isolation +- eager conflict detection +- deferred write, no dirty read possible + +- isolation level +- optimistic +- various levels of pessimistic behavior From e5cee9faa4b779e1573c67caa57e8b94e6ffe12e Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 16:09:48 +0200 Subject: [PATCH 032/100] Improved stm docs --- akka-docs/pending/stm-java.rst | 244 ++++++++++++++++++-------------- akka-docs/pending/stm-scala.rst | 81 +++++++---- 2 files changed, 190 insertions(+), 135 deletions(-) diff --git a/akka-docs/pending/stm-java.rst b/akka-docs/pending/stm-java.rst index 7873e38a7a..1b06fc94a7 100644 --- a/akka-docs/pending/stm-java.rst +++ b/akka-docs/pending/stm-java.rst @@ -12,9 +12,10 @@ An `STM `_ turns the * Isolated: changes made by concurrent execution transactions are not visible to each other. Generally, the STM is not needed that often when working with Akka. Some use-cases (that we can think of) are: -# When you really need composable message flows across many actors updating their **internal local** state but need them to do that atomically in one big transaction. Might not often, but when you do need this then you are screwed without it. -# When you want to share a datastructure across actors. -# When you need to use the persistence modules. + +- When you really need composable message flows across many actors updating their **internal local** state but need them to do that atomically in one big transaction. Might not often, but when you do need this then you are screwed without it. +- When you want to share a datastructure across actors. +- When you need to use the persistence modules. Akka’s STM implements the concept in `Clojure’s `_ STM view on state in general. Please take the time to read `this excellent document `_ and view `this presentation `_ by Rich Hickey (the genius behind Clojure), since it forms the basis of Akka’s view on STM and state in general. @@ -163,33 +164,36 @@ Configuring transactions with a ``TransactionFactory``: }.execute(); The following settings are possible on a TransactionFactory: -* familyName - Family name for transactions. Useful for debugging because the familyName is shown in exceptions, logging and in the future also will be used for profiling. -* readonly - Sets transaction as readonly. Readonly transactions are cheaper and can be used to prevent modification to transactional objects. -* maxRetries - The maximum number of times a transaction will retry. -* timeout - The maximum time a transaction will block for. -* trackReads - Whether all reads should be tracked. Needed for blocking operations. Readtracking makes a transaction more expensive, but makes subsequent reads cheaper and also lowers the chance of a readconflict. -* writeSkew - Whether writeskew is allowed. Disable with care. -* blockingAllowed - Whether explicit retries are allowed. -* interruptible - Whether a blocking transaction can be interrupted if it is blocked. -* speculative - Whether speculative configuration should be enabled. -* quickRelease - Whether locks should be released as quickly as possible (before whole commit). -* propagation - For controlling how nested transactions behave. -* traceLevel - Transaction trace level. + +- familyName - Family name for transactions. Useful for debugging because the familyName is shown in exceptions, logging and in the future also will be used for profiling. +- readonly - Sets transaction as readonly. Readonly transactions are cheaper and can be used to prevent modification to transactional objects. +- maxRetries - The maximum number of times a transaction will retry. +- timeout - The maximum time a transaction will block for. +- trackReads - Whether all reads should be tracked. Needed for blocking operations. Readtracking makes a transaction more expensive, but makes subsequent reads cheaper and also lowers the chance of a readconflict. +- writeSkew - Whether writeskew is allowed. Disable with care. +- blockingAllowed - Whether explicit retries are allowed. +- interruptible - Whether a blocking transaction can be interrupted if it is blocked. +- speculative - Whether speculative configuration should be enabled. +- quickRelease - Whether locks should be released as quickly as possible (before whole commit). +- propagation - For controlling how nested transactions behave. +- traceLevel - Transaction trace level. You can also specify the default values for some of these options in akka.conf. Here they are with their default values: :: stm { - max-retries = 1000 - timeout = 10 - write-skew = true + fair = on # Should global transactions be fair or non-fair (non fair yield better performance) + max-retries = 1000 + timeout = 5 # Default timeout for blocking transactions and transaction set (in unit defined by + # the time-unit property) + write-skew = true blocking-allowed = false - interruptible = false - speculative = true - quick-release = true - propagation = requires - trace-level = none + interruptible = false + speculative = true + quick-release = true + propagation = "requires" + trace-level = "none" } Transaction lifecycle listeners @@ -232,15 +236,19 @@ Here is an example of using ``retry`` to block until an account has enough money import akka.stm.*; public class Transfer { - public Ref from; - public Ref to; - public double amount; + private final Ref from; + private final Ref to; + private final double amount; - public Transfer(Ref from, Ref to, double amount) { - this.from = from; - this.to = to; - this.amount = amount; - } + public Transfer(Ref from, Ref to, double amount) { + this.from = from; + this.to = to; + this.amount = amount; + } + + public Ref getFrom() { return from; } + public Ref getTo() { return to; } + public double getAmount() { return amount; } } .. code-block:: java @@ -250,6 +258,7 @@ Here is an example of using ``retry`` to block until an account has enough money import akka.actor.*; import akka.util.FiniteDuration; import java.util.concurrent.TimeUnit; + import akka.event.EventHandler; public class Transferer extends UntypedActor { TransactionFactory txFactory = new TransactionFactoryBuilder() @@ -261,16 +270,16 @@ Here is an example of using ``retry`` to block until an account has enough money public void onReceive(Object message) throws Exception { if (message instanceof Transfer) { Transfer transfer = (Transfer) message; - final Ref from = transfer.from; - final Ref to = transfer.to; - final double amount = transfer.amount; + final Ref from = transfer.getFrom(); + final Ref to = transfer.getTo(); + final double amount = transfer.getAmount(); new Atomic(txFactory) { public Object atomically() { if (from.get() < amount) { - System.out.println("Transferer: not enough money - retrying"); + EventHandler.info(this, "not enough money - retrying"); retry(); } - System.out.println("Transferer: transferring"); + EventHandler.info(this, "transferring"); from.set(from.get() - amount); to.set(to.get() + amount); return null; @@ -285,40 +294,48 @@ Here is an example of using ``retry`` to block until an account has enough money import akka.stm.*; import akka.actor.*; - final Ref account1 = new Ref(100.0); - final Ref account2 = new Ref(100.0); + public class Main { + public static void main(String...args) throws Exception { + final Ref account1 = new Ref(100.0); + final Ref account2 = new Ref(100.0); - ActorRef transferer = Actors.actorOf(Transferer.class).start(); + ActorRef transferer = Actors.actorOf(Transferer.class).start(); - transferer.sendOneWay(new Transfer(account1, account2, 500.0)); - // Transferer: not enough money - retrying + transferer.sendOneWay(new Transfer(account1, account2, 500.0)); + // Transferer: not enough money - retrying - new Atomic() { - public Object atomically() { - return account1.set(account1.get() + 2000); - } - }.execute(); - // Transferer: transferring + new Atomic() { + public Object atomically() { + return account1.set(account1.get() + 2000); + } + }.execute(); + // Transferer: transferring - Double acc1 = new Atomic() { - public Double atomically() { - return account1.get(); - } - }.execute(); + Thread.sleep(1000); - Double acc2 = new Atomic() { - public Double atomically() { - return account2.get(); - } - }.execute(); + Double acc1 = new Atomic() { + public Double atomically() { + return account1.get(); + } + }.execute(); - System.out.println("Account 1: " + acc1); - // Account 1: 1600.0 + Double acc2 = new Atomic() { + public Double atomically() { + return account2.get(); + } + }.execute(); - System.out.println("Account 2: " + acc2); - // Account 2: 600.0 - transferer.stop(); + + System.out.println("Account 1: " + acc1); + // Account 1: 1600.0 + + System.out.println("Account 2: " + acc2); + // Account 2: 600.0 + + transferer.stop(); + } + } Alternative blocking transactions --------------------------------- @@ -330,24 +347,31 @@ You can also have two alternative blocking transactions, one of which can succee import akka.stm.*; public class Branch { - public Ref left; - public Ref right; - public int amount; + private final Ref left; + private final Ref right; + private final double amount; - public Branch(Ref left, Ref right, int amount) { - this.left = left; - this.right = right; - this.amount = amount; - } + public Branch(Ref left, Ref right, int amount) { + this.left = left; + this.right = right; + this.amount = amount; + } + + public Ref getLeft() { return left; } + + public Ref getRight() { return right; } + + public double getAmount() { return amount; } } .. code-block:: java + import akka.actor.*; import akka.stm.*; import static akka.stm.StmUtils.retry; - import akka.actor.*; import akka.util.FiniteDuration; import java.util.concurrent.TimeUnit; + import akka.event.EventHandler; public class Brancher extends UntypedActor { TransactionFactory txFactory = new TransactionFactoryBuilder() @@ -359,26 +383,26 @@ You can also have two alternative blocking transactions, one of which can succee public void onReceive(Object message) throws Exception { if (message instanceof Branch) { Branch branch = (Branch) message; - final Ref left = branch.left; - final Ref right = branch.right; - final double amount = branch.amount; + final Ref left = branch.getLeft(); + final Ref right = branch.getRight(); + final double amount = branch.getAmount(); new Atomic(txFactory) { public Integer atomically() { return new EitherOrElse() { public Integer either() { if (left.get() < amount) { - System.out.println("not enough on left - retrying"); + EventHandler.info(this, "not enough on left - retrying"); retry(); } - System.out.println("going left"); + EventHandler.info(this, "going left"); return left.get(); } public Integer orElse() { if (right.get() < amount) { - System.out.println("not enough on right - retrying"); + EventHandler.info(this, "not enough on right - retrying"); retry(); } - System.out.println("going right"); + EventHandler.info(this, "going right"); return right.get(); } }.execute(); @@ -393,23 +417,31 @@ You can also have two alternative blocking transactions, one of which can succee import akka.stm.*; import akka.actor.*; - final Ref left = new Ref(100); - final Ref right = new Ref(100); + public class Main2 { + public static void main(String...args) throws Exception { + final Ref left = new Ref(100); + final Ref right = new Ref(100); - ActorRef brancher = Actors.actorOf(Brancher.class).start(); + ActorRef brancher = Actors.actorOf(Brancher.class).start(); - brancher.sendOneWay(new Branch(left, right, 500)); - // not enough on left - retrying - // not enough on right - retrying + brancher.sendOneWay(new Branch(left, right, 500)); + // not enough on left - retrying + // not enough on right - retrying - new Atomic() { - public Object atomically() { - return right.set(right.get() + 1000); - } - }.execute(); - // going right + Thread.sleep(1000); - brancher.stop(); + new Atomic() { + public Object atomically() { + return right.set(right.get() + 1000); + } + }.execute(); + // going right + + + + brancher.stop(); + } + } ---- @@ -417,8 +449,9 @@ Transactional datastructures ============================ Akka provides two datastructures that are managed by the STM. -* TransactionalMap -* TransactionalVector + +- TransactionalMap +- TransactionalVector TransactionalMap and TransactionalVector look like regular mutable datastructures, they even implement the standard Scala 'Map' and 'RandomAccessSeq' interfaces, but they are implemented using persistent datastructures and managed references under the hood. Therefore they are safe to use in a concurrent environment. Underlying TransactionalMap is HashMap, an immutable Map but with near constant time access and modification operations. Similarly TransactionalVector uses a persistent Vector. See the Persistent Datastructures section below for more details. @@ -483,14 +516,15 @@ Persistent datastructures ========================= Akka's STM should only be used with immutable data. This can be costly if you have large datastructures and are using a naive copy-on-write. In order to make working with immutable datastructures fast enough Scala provides what are called Persistent Datastructures. There are currently two different ones: -* HashMap (`scaladoc `_) -* Vector (`scaladoc `_) + +- HashMap (`scaladoc `_) +- Vector (`scaladoc `_) They are immutable and each update creates a completely new version but they are using clever structural sharing in order to make them almost as fast, for both read and update, as regular mutable datastructures. This illustration is taken from Rich Hickey's presentation. Copyright Rich Hickey 2009. -``_ +.. image:: http://eclipsesource.com/blogs/wp-content/uploads/2009/12/clojure-trees.png ---- @@ -512,11 +546,13 @@ You can enable JTA support in the 'stm' section in the config: You also have to configure which JTA provider to use etc in the 'jta' config section: -``_ - jta { - provider = "from-jndi" # Options: "from-jndi" (means that Akka will try to detect a TransactionManager in the JNDI) - # "atomikos" (means that Akka will use the Atomikos based JTA impl in 'akka-jta', - # e.g. you need the akka-jta JARs on classpath). - timeout = 60 - } -``_ +:: + + jta { + provider = "from-jndi" # Options: "from-jndi" (means that Akka will try to detect a TransactionManager in the JNDI) + # "atomikos" (means that Akka will use the Atomikos based JTA impl in 'akka-jta', + # e.g. you need the akka-jta JARs on classpath). + timeout = 60 + } + +---- diff --git a/akka-docs/pending/stm-scala.rst b/akka-docs/pending/stm-scala.rst index 69de2d6266..1db74895d8 100644 --- a/akka-docs/pending/stm-scala.rst +++ b/akka-docs/pending/stm-scala.rst @@ -187,6 +187,20 @@ A transaction is delimited using ``atomic``. // ... } +All changes made to transactional objects are isolated from other changes, all make it or non make it (so failure atomicity) and are consistent. With the AkkaSTM you automatically have the Oracle version of the SERIALIZED isolation level, lower isolation is not possible. To make it fully serialized, set the writeskew property that checks if a writeskew problem is allowed to happen. + +Retries +------- + +A transaction is automatically retried when it runs into some read or write conflict, until the operation completes, an exception (throwable) is thrown or when there are too many retries. When a read or writeconflict is encountered, the transaction uses a bounded exponential backoff to prevent cause more contention and give other transactions some room to complete. + +If you are using non transactional resources in an atomic block, there could be problems because a transaction can be retried. If you are using print statements or logging, it could be that they are called more than once. So you need to be prepared to deal with this. One of the possible solutions is to work with a deferred or compensating task that is executed after the transaction aborts or commits. + +Unexpected retries +------------------ + +It can happen for the first few executions that you get a few failures of execution that lead to unexpected retries, even though there is not any read or writeconflict. The cause of this is that speculative transaction configuration/selection is used. There are transactions optimized for a single transactional object, for 1..n and for n to unlimited. So based on the execution of the transaction, the system learns; it begins with a cheap one and upgrades to more expensive ones. Once it has learned, it will reuse this knowledge. It can be activated/deactivated using the speculative property on the TransactionFactory. In most cases it is best use the default value (enabled) so you get more out of performance. + Coordinated transactions and Transactors ---------------------------------------- @@ -222,33 +236,36 @@ Configuring transactions with an **explicit** ``TransactionFactory``: } The following settings are possible on a TransactionFactory: -* familyName - Family name for transactions. Useful for debugging. -* readonly - Sets transaction as readonly. Readonly transactions are cheaper. -* maxRetries - The maximum number of times a transaction will retry. -* timeout - The maximum time a transaction will block for. -* trackReads - Whether all reads should be tracked. Needed for blocking operations. -* writeSkew - Whether writeskew is allowed. Disable with care. -* blockingAllowed - Whether explicit retries are allowed. -* interruptible - Whether a blocking transaction can be interrupted. -* speculative - Whether speculative configuration should be enabled. -* quickRelease - Whether locks should be released as quickly as possible (before whole commit). -* propagation - For controlling how nested transactions behave. -* traceLevel - Transaction trace level. + +- familyName - Family name for transactions. Useful for debugging. +- readonly - Sets transaction as readonly. Readonly transactions are cheaper. +- maxRetries - The maximum number of times a transaction will retry. +- timeout - The maximum time a transaction will block for. +- trackReads - Whether all reads should be tracked. Needed for blocking operations. +- writeSkew - Whether writeskew is allowed. Disable with care. +- blockingAllowed - Whether explicit retries are allowed. +- interruptible - Whether a blocking transaction can be interrupted. +- speculative - Whether speculative configuration should be enabled. +- quickRelease - Whether locks should be released as quickly as possible (before whole commit). +- propagation - For controlling how nested transactions behave. +- traceLevel - Transaction trace level. You can also specify the default values for some of these options in akka.conf. Here they are with their default values: :: stm { - max-retries = 1000 - timeout = 10 - write-skew = true + fair = on # Should global transactions be fair or non-fair (non fair yield better performance) + max-retries = 1000 + timeout = 5 # Default timeout for blocking transactions and transaction set (in unit defined by + # the time-unit property) + write-skew = true blocking-allowed = false - interruptible = false - speculative = true - quick-release = true - propagation = requires - trace-level = none + interruptible = false + speculative = true + quick-release = true + propagation = "requires" + trace-level = "none" } You can also determine at which level a transaction factory is shared or not shared, which affects the way in which the STM can optimise transactions. @@ -323,23 +340,23 @@ Here is an example of using ``retry`` to block until an account has enough money import akka.stm._ import akka.actor._ import akka.util.duration._ - import akka.util.Logging + import akka.event.EventHandler type Account = Ref[Double] case class Transfer(from: Account, to: Account, amount: Double) - class Transferer extends Actor with Logging { + class Transferer extends Actor { implicit val txFactory = TransactionFactory(blockingAllowed = true, trackReads = true, timeout = 60 seconds) def receive = { case Transfer(from, to, amount) => atomic { if (from.get < amount) { - log.info("not enough money - retrying") + EventHandler.info(this, "not enough money - retrying") retry } - log.info("transferring") + EventHandler.info(this, "transferring") from alter (_ - amount) to alter (_ + amount) } @@ -375,11 +392,11 @@ You can also have two alternative blocking transactions, one of which can succee import akka.stm._ import akka.actor._ import akka.util.duration._ - import akka.util.Logging + import akka.event.EventHandler case class Branch(left: Ref[Int], right: Ref[Int], amount: Int) - class Brancher extends Actor with Logging { + class Brancher extends Actor { implicit val txFactory = TransactionFactory(blockingAllowed = true, trackReads = true, timeout = 60 seconds) def receive = { @@ -387,13 +404,13 @@ You can also have two alternative blocking transactions, one of which can succee atomic { either { if (left.get < amount) { - log.info("not enough on left - retrying") + EventHandler.info(this, "not enough on left - retrying") retry } log.info("going left") } orElse { if (right.get < amount) { - log.info("not enough on right - retrying") + EventHandler.info(this, "not enough on right - retrying") retry } log.info("going right") @@ -423,8 +440,9 @@ Transactional datastructures ============================ Akka provides two datastructures that are managed by the STM. -* TransactionalMap -* TransactionalVector + +- TransactionalMap +- TransactionalVector TransactionalMap and TransactionalVector look like regular mutable datastructures, they even implement the standard Scala 'Map' and 'RandomAccessSeq' interfaces, but they are implemented using persistent datastructures and managed references under the hood. Therefore they are safe to use in a concurrent environment. Underlying TransactionalMap is HashMap, an immutable Map but with near constant time access and modification operations. Similarly TransactionalVector uses a persistent Vector. See the Persistent Datastructures section below for more details. @@ -506,7 +524,8 @@ They are immutable and each update creates a completely new version but they are This illustration is taken from Rich Hickey's presentation. Copyright Rich Hickey 2009. -``_ +.. image:: http://eclipsesource.com/blogs/wp-content/uploads/2009/12/clojure-trees.png + ---- From c60f46813d6d3f0b8f52062d5f9a73dc0024366e Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 26 Apr 2011 17:19:07 +0200 Subject: [PATCH 033/100] Removing some boiler in Future --- .../src/main/scala/akka/dispatch/Future.scala | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 2b3fc6425d..863d9c1283 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -616,6 +616,9 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com private var _value: Option[Either[Throwable, T]] = None private var _listeners: List[Future[T] => Unit] = Nil + /** + * Must be called inside _lock.lock<->_lock.unlock + */ @tailrec private def awaitUnsafe(wait: Long): Boolean = { if (_value.isEmpty && wait > 0) { @@ -635,7 +638,7 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com def awaitValue: Option[Either[Throwable, T]] = { _lock.lock try { - awaitUnsafe(timeoutInNanos - (currentTimeInNanos - _startTimeInNanos)) + awaitUnsafe(timeLeft()) _value } finally { _lock.unlock @@ -645,7 +648,7 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com def valueWithin(time: Long, unit: TimeUnit): Option[Either[Throwable, T]] = { _lock.lock try { - awaitUnsafe(unit.toNanos(time).min(timeoutInNanos - (currentTimeInNanos - _startTimeInNanos))) + awaitUnsafe(unit toNanos time min timeLeft()) _value } finally { _lock.unlock @@ -654,7 +657,7 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com def await = { _lock.lock - if (try { awaitUnsafe(timeoutInNanos - (currentTimeInNanos - _startTimeInNanos)) } finally { _lock.unlock }) this + if (try { awaitUnsafe(timeLeft()) } finally { _lock.unlock }) this else throw new FutureTimeoutException("Futures timed out after [" + NANOS.toMillis(timeoutInNanos) + "] milliseconds") } @@ -670,7 +673,7 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com } } - def isExpired: Boolean = timeoutInNanos - (currentTimeInNanos - _startTimeInNanos) <= 0 + def isExpired: Boolean = timeLeft() <= 0 def value: Option[Either[Throwable, T]] = { _lock.lock @@ -725,7 +728,8 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com } } - private def currentTimeInNanos: Long = MILLIS.toNanos(System.currentTimeMillis) + @inline private def currentTimeInNanos: Long = MILLIS.toNanos(System.currentTimeMillis) + @inline private def timeLeft(): Long = timeoutInNanos - (currentTimeInNanos - _startTimeInNanos) } /** From d0447c76cb4568eccc5762cd9ebc517bdae66596 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 20:16:31 +0200 Subject: [PATCH 034/100] Minor, added import --- akka-docs/pending/untyped-actors-java.rst | 3 +++ akka-docs/scala/actors.rst | 3 +++ 2 files changed, 6 insertions(+) diff --git a/akka-docs/pending/untyped-actors-java.rst b/akka-docs/pending/untyped-actors-java.rst index 35e97011af..539f0a86a6 100644 --- a/akka-docs/pending/untyped-actors-java.rst +++ b/akka-docs/pending/untyped-actors-java.rst @@ -16,6 +16,9 @@ Here is an example: .. code-block:: java + import akka.actor.UntypedActor; + import akka.event.EventHandler; + public class SampleUntypedActor extends UntypedActor { public void onReceive(Object message) throws Exception { diff --git a/akka-docs/scala/actors.rst b/akka-docs/scala/actors.rst index 2da7f2d57b..62db0ad619 100644 --- a/akka-docs/scala/actors.rst +++ b/akka-docs/scala/actors.rst @@ -26,6 +26,9 @@ Here is an example: .. code-block:: scala + import akka.actor.Actor + import akka.event.EventHandler + class MyActor extends Actor { def receive = { case "test" => EventHandler.info(this, "received test") From bce7d176f419cad9e8cdc1bb6b58edebaed786af Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 20:31:08 +0200 Subject: [PATCH 035/100] Added parens to override of preStart and postStop --- .../src/test/scala/akka/actor/actor/Bench.scala | 2 +- .../scala/akka/actor/supervisor/RestartStrategySpec.scala | 8 ++++---- .../test/scala/akka/actor/supervisor/Ticket669Spec.scala | 2 +- akka-actor/src/main/scala/akka/actor/UntypedActor.scala | 4 ++-- akka-actor/src/main/scala/akka/routing/Pool.scala | 2 +- akka-docs/intro/examples/Pi.scala | 4 ++-- akka-docs/intro/getting-started-first-scala-eclipse.rst | 4 ++-- akka-docs/intro/getting-started-first-scala.rst | 8 ++++---- akka-docs/pending/fault-tolerance-scala.rst | 2 +- akka-docs/pending/http.rst | 4 ++-- akka-docs/pending/tutorial-chat-server-scala.rst | 4 ++-- akka-docs/scala/actors.rst | 4 ++-- akka-http/src/main/scala/akka/http/Mist.scala | 2 +- .../remote/ServerInitiatedRemoteSessionActorSpec.scala | 4 ++-- .../akka-sample-chat/src/main/scala/ChatServer.scala | 4 ++-- .../akka-tutorial-first/src/main/scala/Pi.scala | 4 ++-- .../akka-tutorial-second/src/main/scala/Pi.scala | 2 +- .../src/main/scala/akka/actor/TypedActor.scala | 8 ++++---- 18 files changed, 36 insertions(+), 36 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/Bench.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/Bench.scala index f018de635c..1f121babd5 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/Bench.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/Bench.scala @@ -78,7 +78,7 @@ object Chameneos { var sumMeetings = 0 var numFaded = 0 - override def preStart = { + override def preStart() = { for (i <- 0 until numChameneos) actorOf(new Chameneo(self, colours(i % 3), i)) } diff --git a/akka-actor-tests/src/test/scala/akka/actor/supervisor/RestartStrategySpec.scala b/akka-actor-tests/src/test/scala/akka/actor/supervisor/RestartStrategySpec.scala index f2a3103d08..c2af94ba1a 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/supervisor/RestartStrategySpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/supervisor/RestartStrategySpec.scala @@ -46,7 +46,7 @@ class RestartStrategySpec extends JUnitSuite { secondRestartLatch.open } - override def postStop = { + override def postStop() = { stopLatch.open } }) @@ -131,7 +131,7 @@ class RestartStrategySpec extends JUnitSuite { thirdRestartLatch.open } - override def postStop = { + override def postStop() = { if (restartLatch.isOpen) { secondRestartLatch.open } @@ -189,7 +189,7 @@ class RestartStrategySpec extends JUnitSuite { secondRestartLatch.open } - override def postStop = { + override def postStop() = { stopLatch.open } }) @@ -243,7 +243,7 @@ class RestartStrategySpec extends JUnitSuite { restartLatch.open } - override def postStop = { + override def postStop() = { stopLatch.open } }) diff --git a/akka-actor-tests/src/test/scala/akka/actor/supervisor/Ticket669Spec.scala b/akka-actor-tests/src/test/scala/akka/actor/supervisor/Ticket669Spec.scala index 206d06d1c4..33f7a72434 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/supervisor/Ticket669Spec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/supervisor/Ticket669Spec.scala @@ -65,7 +65,7 @@ object Ticket669Spec { self.reply_?("failure1") } - override def postStop { + override def postStop() { self.reply_?("failure2") } } diff --git a/akka-actor/src/main/scala/akka/actor/UntypedActor.scala b/akka-actor/src/main/scala/akka/actor/UntypedActor.scala index 41bf7ac048..71904288ef 100644 --- a/akka-actor/src/main/scala/akka/actor/UntypedActor.scala +++ b/akka-actor/src/main/scala/akka/actor/UntypedActor.scala @@ -88,14 +88,14 @@ abstract class UntypedActor extends Actor { *

* Is called when an Actor is started by invoking 'actor.start()'. */ - override def preStart {} + override def preStart() {} /** * User overridable callback. *

* Is called when 'actor.stop()' is invoked. */ - override def postStop {} + override def postStop() {} /** * User overridable callback. diff --git a/akka-actor/src/main/scala/akka/routing/Pool.scala b/akka-actor/src/main/scala/akka/routing/Pool.scala index 6ab6aa0c4d..5a906df851 100644 --- a/akka-actor/src/main/scala/akka/routing/Pool.scala +++ b/akka-actor/src/main/scala/akka/routing/Pool.scala @@ -54,7 +54,7 @@ trait DefaultActorPool extends ActorPool { this: Actor => private var _lastCapacityChange = 0 private var _lastSelectorCount = 0 - override def postStop = _delegates foreach { + override def postStop() = _delegates foreach { delegate => try { delegate ! PoisonPill } catch { case e: Exception => } //Ignore any exceptions here diff --git a/akka-docs/intro/examples/Pi.scala b/akka-docs/intro/examples/Pi.scala index 1635229802..41f8e88b9f 100644 --- a/akka-docs/intro/examples/Pi.scala +++ b/akka-docs/intro/examples/Pi.scala @@ -91,11 +91,11 @@ object Pi extends App { } //#master-receive - override def preStart { + override def preStart() { start = now } - override def postStop { + override def postStop() { // tell the world that the calculation is complete println( "\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis" diff --git a/akka-docs/intro/getting-started-first-scala-eclipse.rst b/akka-docs/intro/getting-started-first-scala-eclipse.rst index ebd0064620..5c3987866a 100644 --- a/akka-docs/intro/getting-started-first-scala-eclipse.rst +++ b/akka-docs/intro/getting-started-first-scala-eclipse.rst @@ -307,11 +307,11 @@ Here is the master actor:: def receive = { ... } - override def preStart { + override def preStart() { start = System.currentTimeMillis } - override def postStop { + override def postStop() { // tell the world that the calculation is complete println( "\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis" diff --git a/akka-docs/intro/getting-started-first-scala.rst b/akka-docs/intro/getting-started-first-scala.rst index c6ea2f4fe1..364c0d276b 100644 --- a/akka-docs/intro/getting-started-first-scala.rst +++ b/akka-docs/intro/getting-started-first-scala.rst @@ -291,11 +291,11 @@ Here is the master actor:: def receive = { ... } - override def preStart { + override def preStart() { start = System.currentTimeMillis } - override def postStop { + override def postStop() { // tell the world that the calculation is complete println( "\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis" @@ -451,11 +451,11 @@ But before we package it up and run it, let's take a look at the full code now, if (nrOfResults == nrOfMessages) self.stop() } - override def preStart { + override def preStart() { start = System.currentTimeMillis } - override def postStop { + override def postStop() { // tell the world that the calculation is complete println( "\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis" diff --git a/akka-docs/pending/fault-tolerance-scala.rst b/akka-docs/pending/fault-tolerance-scala.rst index 6070f9e01e..84cc48d549 100644 --- a/akka-docs/pending/fault-tolerance-scala.rst +++ b/akka-docs/pending/fault-tolerance-scala.rst @@ -316,7 +316,7 @@ Supervised actors have the option to reply to the initial sender within preResta self.reply_?(reason.getMessage) } - override def postStop { + override def postStop() { self.reply_?("stopped by supervisor") } } diff --git a/akka-docs/pending/http.rst b/akka-docs/pending/http.rst index 801b786da0..9de34d05e7 100644 --- a/akka-docs/pending/http.rst +++ b/akka-docs/pending/http.rst @@ -269,7 +269,7 @@ Finally, bind the *handleHttpRequest* function of the *Endpoint* trait to the ac // // this is where you want attach your endpoint hooks // - override def preStart = { + override def preStart() = { // // we expect there to be one root and that it's already been started up // obviously there are plenty of other ways to obtaining this actor @@ -397,7 +397,7 @@ As noted above, hook functions are non-exclusive. This means multiple actors can // // this is where you want attach your endpoint hooks // - override def preStart = { + override def preStart() = { // // we expect there to be one root and that it's already been started up // obviously there are plenty of other ways to obtaining this actor diff --git a/akka-docs/pending/tutorial-chat-server-scala.rst b/akka-docs/pending/tutorial-chat-server-scala.rst index 830bf75c22..afec3e948f 100644 --- a/akka-docs/pending/tutorial-chat-server-scala.rst +++ b/akka-docs/pending/tutorial-chat-server-scala.rst @@ -221,7 +221,7 @@ I'll try to show you how we can make use Scala's mixins to decouple the Actor im protected def sessionManagement: Receive protected def shutdownSessions(): Unit - override def postStop = { + override def postStop() = { EventHandler.info(this, "Chat server is shutting down...") shutdownSessions self.unlink(storage) @@ -422,7 +422,7 @@ We have now created the full functionality for the chat server, all nicely decou SessionManagement with ChatManagement with MemoryChatStorageFactory { - override def preStart = { + override def preStart() = { remote.start("localhost", 2552); remote.register("chat:service", self) //Register the actor with the specified service id } diff --git a/akka-docs/scala/actors.rst b/akka-docs/scala/actors.rst index 62db0ad619..a3e0bbd28f 100644 --- a/akka-docs/scala/actors.rst +++ b/akka-docs/scala/actors.rst @@ -385,7 +385,7 @@ When you start the ``Actor`` then it will automatically call the ``def preStart` .. code-block:: scala - override def preStart = { + override def preStart() = { ... // initialization code } @@ -402,7 +402,7 @@ When stop is called then a call to the ``def postStop`` callback method will tak .. code-block:: scala - override def postStop = { + override def postStop() = { ... // clean up resources } diff --git a/akka-http/src/main/scala/akka/http/Mist.scala b/akka-http/src/main/scala/akka/http/Mist.scala index 379cbfb36d..99e717281a 100644 --- a/akka-http/src/main/scala/akka/http/Mist.scala +++ b/akka-http/src/main/scala/akka/http/Mist.scala @@ -269,7 +269,7 @@ class RootEndpoint extends Actor with Endpoint { // adopt the configured id if (RootActorBuiltin) self.id = RootActorID - override def preStart = + override def preStart() = _attachments = Tuple2((uri: String) => {uri eq Root}, (uri: String) => this.actor) :: _attachments def recv: Receive = { diff --git a/akka-remote/src/test/scala/remote/ServerInitiatedRemoteSessionActorSpec.scala b/akka-remote/src/test/scala/remote/ServerInitiatedRemoteSessionActorSpec.scala index c2277200b1..09a5f96bde 100644 --- a/akka-remote/src/test/scala/remote/ServerInitiatedRemoteSessionActorSpec.scala +++ b/akka-remote/src/test/scala/remote/ServerInitiatedRemoteSessionActorSpec.scala @@ -19,8 +19,8 @@ object ServerInitiatedRemoteSessionActorSpec { class RemoteStatefullSessionActorSpec extends Actor { - override def preStart = instantiatedSessionActors.add(self) - override def postStop = instantiatedSessionActors.remove(self) + override def preStart() = instantiatedSessionActors.add(self) + override def postStop() = instantiatedSessionActors.remove(self) var user: String = "anonymous" def receive = { diff --git a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala index a19ed26da0..90f6f2701e 100644 --- a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala +++ b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala @@ -186,7 +186,7 @@ protected def sessionManagement: Receive protected def shutdownSessions(): Unit - override def postStop = { + override def postStop() = { EventHandler.info(this, "Chat server is shutting down...") shutdownSessions self.unlink(storage) @@ -206,7 +206,7 @@ SessionManagement with ChatManagement with MemoryChatStorageFactory { - override def preStart = { + override def preStart() = { remote.start("localhost", 2552); remote.register("chat:service", self) //Register the actor with the specified service id } diff --git a/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala b/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala index e6d8b87c14..41f562791a 100644 --- a/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala +++ b/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala @@ -104,11 +104,11 @@ object Pi extends App { if (nrOfResults == nrOfMessages) self.stop() } - override def preStart { + override def preStart() { start = System.currentTimeMillis } - override def postStop { + override def postStop() { // tell the world that the calculation is complete println( "\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis" 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 e7e10f56ef..35d29f8f6c 100644 --- a/akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala +++ b/akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala @@ -111,7 +111,7 @@ object Pi extends App { def receive = scatter // when we are stopped, stop our team of workers and our router - override def postStop { + override def postStop() { // send a PoisonPill to all workers telling them to shut down themselves router ! Broadcast(PoisonPill) // send a PoisonPill to the router, telling him to shut himself down diff --git a/akka-typed-actor/src/main/scala/akka/actor/TypedActor.scala b/akka-typed-actor/src/main/scala/akka/actor/TypedActor.scala index 7103c3fd5b..591613a203 100644 --- a/akka-typed-actor/src/main/scala/akka/actor/TypedActor.scala +++ b/akka-typed-actor/src/main/scala/akka/actor/TypedActor.scala @@ -83,11 +83,11 @@ import scala.reflect.BeanProperty * * def square(x: Int): Future[Integer] = future(x * x) * - * override def preStart = { + * override def preStart() = { * ... // optional initialization on start * } * - * override def postStop = { + * override def postStop() = { * ... // optional cleanup on stop * } * @@ -160,14 +160,14 @@ abstract class TypedActor extends Actor with Proxyable { *

* Is called when an Actor is started by invoking 'actor.start()'. */ - override def preStart {} + override def preStart() {} /** * User overridable callback. *

* Is called when 'actor.stop()' is invoked. */ - override def postStop {} + override def postStop() {} /** * User overridable callback. From 9c7242f374927c7ad7a6454af125234bb2a0071b Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 20:48:30 +0200 Subject: [PATCH 036/100] Moved untyped-actors from pending --- .../{pending/untyped-actors-java.rst => java/untyped-actors.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename akka-docs/{pending/untyped-actors-java.rst => java/untyped-actors.rst} (100%) diff --git a/akka-docs/pending/untyped-actors-java.rst b/akka-docs/java/untyped-actors.rst similarity index 100% rename from akka-docs/pending/untyped-actors-java.rst rename to akka-docs/java/untyped-actors.rst From 88d400a4e9c41151a48d6743c387386aa2b432a8 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 20:57:05 +0200 Subject: [PATCH 037/100] index for java api --- akka-docs/index.rst | 1 + akka-docs/java/index.rst | 7 +++++++ akka-docs/java/untyped-actors.rst | 17 ++++++++--------- 3 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 akka-docs/java/index.rst diff --git a/akka-docs/index.rst b/akka-docs/index.rst index fbb2506fab..11bfef862a 100644 --- a/akka-docs/index.rst +++ b/akka-docs/index.rst @@ -7,6 +7,7 @@ Contents intro/index general/index scala/index + java/index dev/index Links diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst new file mode 100644 index 0000000000..ca3a84c5de --- /dev/null +++ b/akka-docs/java/index.rst @@ -0,0 +1,7 @@ +Java API +========= + +.. toctree:: + :maxdepth: 2 + + untyped-actors diff --git a/akka-docs/java/untyped-actors.rst b/akka-docs/java/untyped-actors.rst index 539f0a86a6..e7977801a3 100644 --- a/akka-docs/java/untyped-actors.rst +++ b/akka-docs/java/untyped-actors.rst @@ -1,7 +1,5 @@ -Actors (Java) -============= - -= +Actors +====== Module stability: **SOLID** @@ -412,8 +410,9 @@ Actor life-cycle The actor has a well-defined non-circular life-cycle. -``_ -NEW (newly created actor) - can't receive messages (yet) - => STARTED (when 'start' is invoked) - can receive messages - => SHUT DOWN (when 'exit' or 'stop' is invoked) - can't do anything -``_ +:: + + NEW (newly created actor) - can't receive messages (yet) + => STARTED (when 'start' is invoked) - can receive messages + => SHUT DOWN (when 'exit' or 'stop' is invoked) - can't do anything + From a0f52113aaf895a5b0e95afea17ddb828724b0ef Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 21:11:58 +0200 Subject: [PATCH 038/100] Moved actor-registry from pending --- .../actor-registry.rst} | 0 akka-docs/java/index.rst | 1 + akka-docs/java/untyped-actors.rst | 11 ++++++++--- .../actor-registry.rst} | 0 akka-docs/scala/actors.rst | 8 ++++++-- akka-docs/scala/index.rst | 1 + 6 files changed, 16 insertions(+), 5 deletions(-) rename akka-docs/{pending/actor-registry-java.rst => java/actor-registry.rst} (100%) rename akka-docs/{pending/actor-registry-scala.rst => scala/actor-registry.rst} (100%) diff --git a/akka-docs/pending/actor-registry-java.rst b/akka-docs/java/actor-registry.rst similarity index 100% rename from akka-docs/pending/actor-registry-java.rst rename to akka-docs/java/actor-registry.rst diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst index ca3a84c5de..412e76d1cd 100644 --- a/akka-docs/java/index.rst +++ b/akka-docs/java/index.rst @@ -5,3 +5,4 @@ Java API :maxdepth: 2 untyped-actors + actor-registry diff --git a/akka-docs/java/untyped-actors.rst b/akka-docs/java/untyped-actors.rst index e7977801a3..5a29d13ae5 100644 --- a/akka-docs/java/untyped-actors.rst +++ b/akka-docs/java/untyped-actors.rst @@ -1,5 +1,9 @@ -Actors -====== +Actors (Java) +============= + +.. sidebar:: Contents + + .. contents:: :local: Module stability: **SOLID** @@ -21,7 +25,8 @@ Here is an example: public void onReceive(Object message) throws Exception { if (message instanceof String) - EventHandler.info(this, String.format("Received String message: %s", message)); + EventHandler.info(this, String.format("Received String message: %s", + message)); else throw new IllegalArgumentException("Unknown message: " + message); } diff --git a/akka-docs/pending/actor-registry-scala.rst b/akka-docs/scala/actor-registry.rst similarity index 100% rename from akka-docs/pending/actor-registry-scala.rst rename to akka-docs/scala/actor-registry.rst diff --git a/akka-docs/scala/actors.rst b/akka-docs/scala/actors.rst index a3e0bbd28f..ed186e17ba 100644 --- a/akka-docs/scala/actors.rst +++ b/akka-docs/scala/actors.rst @@ -1,5 +1,9 @@ -Actors -====== +Actors (Scala) +============== + +.. sidebar:: Contents + + .. contents:: :local: Module stability: **SOLID** diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index e54c88b979..adc2843f1e 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -5,5 +5,6 @@ Scala API :maxdepth: 2 actors + actor-registry fsm testing From 2efa82fc8e08319e0c68bee1a05d1e019892809a Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 21:27:49 +0200 Subject: [PATCH 039/100] Moved typed-actors from pending --- akka-docs/java/index.rst | 1 + .../{pending/typed-actors-java.rst => java/typed-actors.rst} | 0 akka-docs/scala/index.rst | 1 + .../{pending/typed-actors-scala.rst => scala/typed-actors.rst} | 0 4 files changed, 2 insertions(+) rename akka-docs/{pending/typed-actors-java.rst => java/typed-actors.rst} (100%) rename akka-docs/{pending/typed-actors-scala.rst => scala/typed-actors.rst} (100%) diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst index 412e76d1cd..e9d77b30f6 100644 --- a/akka-docs/java/index.rst +++ b/akka-docs/java/index.rst @@ -5,4 +5,5 @@ Java API :maxdepth: 2 untyped-actors + typed-actors actor-registry diff --git a/akka-docs/pending/typed-actors-java.rst b/akka-docs/java/typed-actors.rst similarity index 100% rename from akka-docs/pending/typed-actors-java.rst rename to akka-docs/java/typed-actors.rst diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index adc2843f1e..ede84e0917 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -5,6 +5,7 @@ Scala API :maxdepth: 2 actors + typed-actors actor-registry fsm testing diff --git a/akka-docs/pending/typed-actors-scala.rst b/akka-docs/scala/typed-actors.rst similarity index 100% rename from akka-docs/pending/typed-actors-scala.rst rename to akka-docs/scala/typed-actors.rst From 89b1814d1c04819e1ec2a21d67f057fb4f738632 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 21:29:08 +0200 Subject: [PATCH 040/100] Added sidebar toc --- akka-docs/java/typed-actors.rst | 4 ++++ akka-docs/scala/typed-actors.rst | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/akka-docs/java/typed-actors.rst b/akka-docs/java/typed-actors.rst index 0f6c9563b5..edbb1d43c6 100644 --- a/akka-docs/java/typed-actors.rst +++ b/akka-docs/java/typed-actors.rst @@ -1,6 +1,10 @@ Typed Actors (Java) =================== +.. sidebar:: Contents + + .. contents:: :local: + Module stability: **SOLID** The Typed Actors are implemented through `Typed Actors `_. It uses AOP through `AspectWerkz `_ to turn regular POJOs into asynchronous non-blocking Actors with semantics of the Actor Model. E.g. each message dispatch is turned into a message that is put on a queue to be processed by the Typed Actor sequentially one by one. diff --git a/akka-docs/scala/typed-actors.rst b/akka-docs/scala/typed-actors.rst index e9aa061672..9fc6d327c1 100644 --- a/akka-docs/scala/typed-actors.rst +++ b/akka-docs/scala/typed-actors.rst @@ -1,6 +1,10 @@ Typed Actors (Scala) ==================== +.. sidebar:: Contents + + .. contents:: :local: + Module stability: **SOLID** The Typed Actors are implemented through `Typed Actors `_. It uses AOP through `AspectWerkz `_ to turn regular POJOs into asynchronous non-blocking Actors with semantics of the Actor Model. E.g. each message dispatch is turned into a message that is put on a queue to be processed by the Typed Actor sequentially one by one. From 054403325d39e7f368f6af6c53d6b4bfa4d9543d Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 21:31:13 +0200 Subject: [PATCH 041/100] Added serialize-messages description to scala typed actors doc --- akka-docs/scala/typed-actors.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/akka-docs/scala/typed-actors.rst b/akka-docs/scala/typed-actors.rst index 9fc6d327c1..912ac234be 100644 --- a/akka-docs/scala/typed-actors.rst +++ b/akka-docs/scala/typed-actors.rst @@ -173,3 +173,15 @@ Messages and immutability ------------------------- **IMPORTANT**: Messages can be any kind of object but have to be immutable (there is a workaround, see next section). Java or Scala can’t enforce immutability (yet) so this has to be by convention. Primitives like String, int, Long are always immutable. Apart from these you have to create your own immutable objects to send as messages. If you pass on a reference to an instance that is mutable then this instance can be modified concurrently by two different Typed Actors and the Actor model is broken leaving you with NO guarantees and most likely corrupt data. + +Akka can help you in this regard. It allows you to turn on an option for serializing all messages, e.g. all parameters to the Typed Actor effectively making a deep clone/copy of the parameters. This will make sending mutable messages completely safe. This option is turned on in the ‘$AKKA_HOME/config/akka.conf’ config file like this: + +.. code-block:: ruby + + akka { + actor { + serialize-messages = on # does a deep clone of messages to ensure immutability + } + } + +This will make a deep clone (using Java serialization) of all parameters. From b19bd275d2e9e204d96dfb279579090a278a8948 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 21:34:04 +0200 Subject: [PATCH 042/100] typo --- akka-docs/java/typed-actors.rst | 2 +- akka-docs/scala/typed-actors.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/akka-docs/java/typed-actors.rst b/akka-docs/java/typed-actors.rst index edbb1d43c6..acd99b7fcf 100644 --- a/akka-docs/java/typed-actors.rst +++ b/akka-docs/java/typed-actors.rst @@ -175,7 +175,7 @@ Here is an example how you can use it to in a 'void' (e.g. fire-forget) method t } } -If the sender, sender future etc. is not available, then these methods will return 'null' so you should have a way of dealing with scenario. +If the sender, sender future etc. is not available, then these methods will return 'null' so you should have a way of dealing with that scenario. Messages and immutability ------------------------- diff --git a/akka-docs/scala/typed-actors.rst b/akka-docs/scala/typed-actors.rst index 912ac234be..7e5a327113 100644 --- a/akka-docs/scala/typed-actors.rst +++ b/akka-docs/scala/typed-actors.rst @@ -167,7 +167,7 @@ Here is an example how you can use it to in a 'void' (e.g. fire-forget) method t } } -If the sender, sender future etc. is not available, then these methods will return 'null' so you should have a way of dealing with scenario. +If the sender, sender future etc. is not available, then these methods will return 'null' so you should have a way of dealing with that scenario. Messages and immutability ------------------------- From 40533a73341a3d8fb832fd4215b4808917bd0337 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 21:39:10 +0200 Subject: [PATCH 043/100] Moved event-handler from pending --- akka-docs/{pending => general}/event-handler.rst | 0 akka-docs/general/index.rst | 1 + 2 files changed, 1 insertion(+) rename akka-docs/{pending => general}/event-handler.rst (100%) diff --git a/akka-docs/pending/event-handler.rst b/akka-docs/general/event-handler.rst similarity index 100% rename from akka-docs/pending/event-handler.rst rename to akka-docs/general/event-handler.rst diff --git a/akka-docs/general/index.rst b/akka-docs/general/index.rst index 5b0e3c24d6..81cee4fcf2 100644 --- a/akka-docs/general/index.rst +++ b/akka-docs/general/index.rst @@ -5,4 +5,5 @@ General :maxdepth: 2 migration-guides + event-handler util From 850536bd2aa4e613f070e40eae3ed9d513cfb30d Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 21:42:11 +0200 Subject: [PATCH 044/100] cleanup --- akka-docs/general/event-handler.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/akka-docs/general/event-handler.rst b/akka-docs/general/event-handler.rst index 18eefefb0a..e43bb20eb0 100644 --- a/akka-docs/general/event-handler.rst +++ b/akka-docs/general/event-handler.rst @@ -12,7 +12,8 @@ You can configure which event handlers should be registered at boot time. That i .. code-block:: ruby akka { - event-handlers = ["akka.event.EventHandler$DefaultListener"] # event handlers to register at boot time (EventHandler$DefaultListener logs to STDOUT) + # event handlers to register at boot time (EventHandler$DefaultListener logs to STDOUT) + event-handlers = ["akka.event.EventHandler$DefaultListener"] event-handler-level = "DEBUG" # Options: ERROR, WARNING, INFO, DEBUG } @@ -88,9 +89,10 @@ The methods take a call-by-name parameter for the message to avoid object alloca From Java you need to nest the call in an if statement to achieve the same thing. -``_ -if (EventHandler.isDebugEnabled()) { - EventHandler.debug(this, String.format("Processing took %s ms", duration)); -} +.. code-block:: java + + if (EventHandler.isDebugEnabled()) { + EventHandler.debug(this, String.format("Processing took %s ms", duration)); + } + -``_ From e2c0d11c101f614773f47c289f40825da7f7146c Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 21:45:33 +0200 Subject: [PATCH 045/100] Moved dispatchers from pending --- akka-docs/{pending/dispatchers-java.rst => java/dispatchers.rst} | 0 akka-docs/java/index.rst | 1 + .../{pending/dispatchers-scala.rst => scala/dispatchers.rst} | 0 akka-docs/scala/index.rst | 1 + 4 files changed, 2 insertions(+) rename akka-docs/{pending/dispatchers-java.rst => java/dispatchers.rst} (100%) rename akka-docs/{pending/dispatchers-scala.rst => scala/dispatchers.rst} (100%) diff --git a/akka-docs/pending/dispatchers-java.rst b/akka-docs/java/dispatchers.rst similarity index 100% rename from akka-docs/pending/dispatchers-java.rst rename to akka-docs/java/dispatchers.rst diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst index e9d77b30f6..70fefd1f53 100644 --- a/akka-docs/java/index.rst +++ b/akka-docs/java/index.rst @@ -7,3 +7,4 @@ Java API untyped-actors typed-actors actor-registry + dispatchers diff --git a/akka-docs/pending/dispatchers-scala.rst b/akka-docs/scala/dispatchers.rst similarity index 100% rename from akka-docs/pending/dispatchers-scala.rst rename to akka-docs/scala/dispatchers.rst diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index ede84e0917..e772d2d926 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -7,5 +7,6 @@ Scala API actors typed-actors actor-registry + dispatchers fsm testing From 884a9ae2ef144e01c0ba6d3fa2bac2cfc9265a37 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 21:52:45 +0200 Subject: [PATCH 046/100] Cleanup --- akka-docs/java/dispatchers.rst | 27 ++++++++++++++++----------- akka-docs/scala/dispatchers.rst | 24 +++++++++++++++--------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/akka-docs/java/dispatchers.rst b/akka-docs/java/dispatchers.rst index b9d5ee9ee8..a7fe7ce19a 100644 --- a/akka-docs/java/dispatchers.rst +++ b/akka-docs/java/dispatchers.rst @@ -1,6 +1,10 @@ Dispatchers (Java) ================== +.. sidebar:: Contents + + .. contents:: :local: + Module stability: **SOLID** The Dispatcher is an important piece that allows you to configure the right semantics and parameters for optimal performance, throughput and scalability. Different Actors have different needs. @@ -128,7 +132,7 @@ If you don't define a the 'throughput' option in the configuration file then the Browse the `ScalaDoc `_ or look at the code for all the options available. Priority event-based -^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^ Sometimes it's useful to be able to specify priority order of messages, that is done by using PriorityExecutorBasedEventDrivenDispatcher and supply a java.util.Comparator[MessageInvocation] or use a akka.dispatch.PriorityGenerator (recommended): @@ -137,7 +141,7 @@ Creating a PriorityExecutorBasedEventDrivenDispatcher using PriorityGenerator: .. code-block:: java - package some.package; + package some.pkg; import akka.actor.*; import akka.dispatch.*; @@ -249,13 +253,14 @@ For the 'ExecutorBasedEventDrivenDispatcher' and the 'ExecutorBasedWorkStealingD For the 'ThreadBasedDispatcher', it is non-shareable between actors, and associates a dedicated Thread with the actor. Making it bounded (by specifying a capacity) is optional, but if you do, you need to provide a pushTimeout (default is 10 seconds). When trying to send a message to the Actor it will throw a MessageQueueAppendFailedException("BlockingMessageTransferQueue transfer timed out") if the message cannot be added to the mailbox within the time specified by the pushTimeout. -``_ -class MyActor extends UntypedActor { - public MyActor() { - int mailboxCapacity = 100; - Duration pushTimeout = new FiniteDuration(10, TimeUnit.SECONDS); - getContext().setDispatcher(Dispatchers.newThreadBasedDispatcher(getContext(), mailboxCapacity, pushTimeout)); +.. code-block:: java + + class MyActor extends UntypedActor { + public MyActor() { + int mailboxCapacity = 100; + Duration pushTimeout = new FiniteDuration(10, TimeUnit.SECONDS); + getContext().setDispatcher(Dispatchers.newThreadBasedDispatcher(getContext(), mailboxCapacity, pushTimeout)); + } + ... } - ... -} -``_ + diff --git a/akka-docs/scala/dispatchers.rst b/akka-docs/scala/dispatchers.rst index 62584835a4..35285c20fa 100644 --- a/akka-docs/scala/dispatchers.rst +++ b/akka-docs/scala/dispatchers.rst @@ -1,6 +1,10 @@ Dispatchers (Scala) =================== +.. sidebar:: Contents + + .. contents:: :local: + Module stability: **SOLID** The Dispatcher is an important piece that allows you to configure the right semantics and parameters for optimal performance, throughput and scalability. Different Actors have different needs. @@ -124,7 +128,7 @@ If you don't define a the 'throughput' option in the configuration file then the Browse the `ScalaDoc `_ or look at the code for all the options available. Priority event-based -^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^ Sometimes it's useful to be able to specify priority order of messages, that is done by using PriorityExecutorBasedEventDrivenDispatcher and supply a java.util.Comparator[MessageInvocation] or use a akka.dispatch.PriorityGenerator (recommended): @@ -231,11 +235,13 @@ For the 'ExecutorBasedEventDrivenDispatcher' and the 'ExecutorBasedWorkStealingD For the 'ThreadBasedDispatcher', it is non-shareable between actors, and associates a dedicated Thread with the actor. Making it bounded (by specifying a capacity) is optional, but if you do, you need to provide a pushTimeout (default is 10 seconds). When trying to send a message to the Actor it will throw a MessageQueueAppendFailedException("BlockingMessageTransferQueue transfer timed out") if the message cannot be added to the mailbox within the time specified by the pushTimeout. -``_ -class MyActor extends Actor { - import akka.util.duration._ - self.dispatcher = Dispatchers.newThreadBasedDispatcher(self, mailboxCapacity = 100, - pushTimeOut = 10 seconds) - ... -} -``_ +.. code-block:: scala + + class MyActor extends Actor { + import akka.util.duration._ + self.dispatcher = Dispatchers.newThreadBasedDispatcher(self, mailboxCapacity = 100, + pushTimeOut = 10 seconds) + ... + } + + From a44031d0782be2be32a6e2089f8697c42c48e468 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 21:55:24 +0200 Subject: [PATCH 047/100] Moved agents from pending --- akka-docs/{pending/agents-scala.rst => scala/agents.rst} | 0 akka-docs/scala/index.rst | 1 + 2 files changed, 1 insertion(+) rename akka-docs/{pending/agents-scala.rst => scala/agents.rst} (100%) diff --git a/akka-docs/pending/agents-scala.rst b/akka-docs/scala/agents.rst similarity index 100% rename from akka-docs/pending/agents-scala.rst rename to akka-docs/scala/agents.rst diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index e772d2d926..1138ad803a 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -7,6 +7,7 @@ Scala API actors typed-actors actor-registry + agents dispatchers fsm testing From e3a5aa724093d71b97982cf39af43e9fd430e56c Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 21:56:34 +0200 Subject: [PATCH 048/100] Sidebar toc --- akka-docs/scala/agents.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/akka-docs/scala/agents.rst b/akka-docs/scala/agents.rst index c6a4ee9b73..1e9ea128a3 100644 --- a/akka-docs/scala/agents.rst +++ b/akka-docs/scala/agents.rst @@ -1,6 +1,10 @@ Agents (Scala) ============== +.. sidebar:: Contents + + .. contents:: :local: + Module stability: **SOLID** Agents in Akka were inspired by `agents in Clojure `_. From 0cc6499a4ae95f1d2bfdc1b5c20dd6e2c6cb3283 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 21:58:36 +0200 Subject: [PATCH 049/100] Moved stm from pending --- akka-docs/java/index.rst | 1 + akka-docs/{pending/stm-java.rst => java/stm.rst} | 0 akka-docs/scala/index.rst | 1 + akka-docs/{pending/stm-scala.rst => scala/stm.rst} | 0 4 files changed, 2 insertions(+) rename akka-docs/{pending/stm-java.rst => java/stm.rst} (100%) rename akka-docs/{pending/stm-scala.rst => scala/stm.rst} (100%) diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst index 70fefd1f53..d4fed805d4 100644 --- a/akka-docs/java/index.rst +++ b/akka-docs/java/index.rst @@ -7,4 +7,5 @@ Java API untyped-actors typed-actors actor-registry + stm dispatchers diff --git a/akka-docs/pending/stm-java.rst b/akka-docs/java/stm.rst similarity index 100% rename from akka-docs/pending/stm-java.rst rename to akka-docs/java/stm.rst diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index 1138ad803a..186cc0b949 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -8,6 +8,7 @@ Scala API typed-actors actor-registry agents + stm dispatchers fsm testing diff --git a/akka-docs/pending/stm-scala.rst b/akka-docs/scala/stm.rst similarity index 100% rename from akka-docs/pending/stm-scala.rst rename to akka-docs/scala/stm.rst From 929f8458ff96fad9626c53e5631016a616ed7ec2 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 22:23:23 +0200 Subject: [PATCH 050/100] Cleanup --- akka-docs/java/stm.rst | 49 +++++++++++++++++++------------------ akka-docs/scala/stm.rst | 53 +++++++++++++++++++---------------------- 2 files changed, 49 insertions(+), 53 deletions(-) diff --git a/akka-docs/java/stm.rst b/akka-docs/java/stm.rst index 1b06fc94a7..221c706183 100644 --- a/akka-docs/java/stm.rst +++ b/akka-docs/java/stm.rst @@ -1,10 +1,14 @@ Software Transactional Memory (Java) ==================================== +.. sidebar:: Contents + + .. contents:: :local: + Module stability: **SOLID** Overview of STM -=============== +--------------- An `STM `_ turns the Java heap into a transactional data set with begin/commit/rollback semantics. Very much like a regular database. It implements the first three letters in ACID; ACI: * (failure) Atomicity: all changes during the execution of a transaction make it, or none make it. This only counts for transactional datastructures. @@ -24,7 +28,7 @@ The STM is based on Transactional References (referred to as Refs). Refs are mem Working with immutable collections can sometimes give bad performance due to extensive copying. Scala provides so-called persistent datastructures which makes working with immutable collections fast. They are immutable but with constant time access and modification. The use of structural sharing and an insert or update does not ruin the old structure, hence “persistent”. Makes working with immutable composite types fast. The persistent datastructures currently consist of a Map and Vector. Simple example -============== +-------------- Here is a simple example of an incremental counter using STM. This shows creating a ``Ref``, a transactional reference, and then modifying it within a transaction, which is delimited by an ``Atomic`` anonymous inner class. @@ -50,15 +54,14 @@ Here is a simple example of an incremental counter using STM. This shows creatin counter(); // -> 2 ----- Ref -=== +--- Refs (transactional references) are mutable references to values and through the STM allow the safe sharing of mutable data. To ensure safety the value stored in a Ref should be immutable. The value referenced by a Ref can only be accessed or swapped within a transaction. Refs separate identity from value. Creating a Ref --------------- +^^^^^^^^^^^^^^ You can create a Ref with or without an initial value. @@ -73,7 +76,7 @@ You can create a Ref with or without an initial value. final Ref ref = new Ref(); Accessing the value of a Ref ----------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use ``get`` to access the value of a Ref. Note that if no initial value has been given then the value is initially ``null``. @@ -91,7 +94,7 @@ Use ``get`` to access the value of a Ref. Note that if no initial value has been // -> value = 0 Changing the value of a Ref ---------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^ To set a new value for a Ref you can use ``set`` (or equivalently ``swap``), which sets the new value and returns the old value. @@ -107,10 +110,9 @@ To set a new value for a Ref you can use ``set`` (or equivalently ``swap``), whi } }.execute(); ----- Transactions -============ +------------ A transaction is delimited using an ``Atomic`` anonymous inner class. @@ -125,24 +127,24 @@ A transaction is delimited using an ``Atomic`` anonymous inner class. All changes made to transactional objects are isolated from other changes, all make it or non make it (so failure atomicity) and are consistent. With the AkkaSTM you automatically have the Oracle version of the SERIALIZED isolation level, lower isolation is not possible. To make it fully serialized, set the writeskew property that checks if a writeskew problem is allowed to happen. Retries -------- +^^^^^^^ A transaction is automatically retried when it runs into some read or write conflict, until the operation completes, an exception (throwable) is thrown or when there are too many retries. When a read or writeconflict is encountered, the transaction uses a bounded exponential backoff to prevent cause more contention and give other transactions some room to complete. If you are using non transactional resources in an atomic block, there could be problems because a transaction can be retried. If you are using print statements or logging, it could be that they are called more than once. So you need to be prepared to deal with this. One of the possible solutions is to work with a deferred or compensating task that is executed after the transaction aborts or commits. Unexpected retries ------------------- +^^^^^^^^^^^^^^^^^^ It can happen for the first few executions that you get a few failures of execution that lead to unexpected retries, even though there is not any read or writeconflict. The cause of this is that speculative transaction configuration/selection is used. There are transactions optimized for a single transactional object, for 1..n and for n to unlimited. So based on the execution of the transaction, the system learns; it begins with a cheap one and upgrades to more expensive ones. Once it has learned, it will reuse this knowledge. It can be activated/deactivated using the speculative property on the TransactionFactoryBuilder. In most cases it is best use the default value (enabled) so you get more out of performance. Coordinated transactions and Transactors ----------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you need coordinated transactions across actors or threads then see `Transactors `_. Configuring transactions ------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^ It's possible to configure transactions. The ``Atomic`` class can take a ``TransactionFactory``, which can determine properties of the transaction. A default transaction factory is used if none is specified. You can create a ``TransactionFactory`` with a ``TransactionFactoryBuilder``. @@ -197,7 +199,7 @@ You can also specify the default values for some of these options in akka.conf. } Transaction lifecycle listeners -------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ It's possible to have code that will only run on the successful commit of a transaction, or when a transaction aborts. You can do this by adding ``deferred`` or ``compensating`` blocks to a transaction. @@ -225,7 +227,7 @@ It's possible to have code that will only run on the successful commit of a tran }.execute(); Blocking transactions ---------------------- +^^^^^^^^^^^^^^^^^^^^^ You can block in a transaction until a condition is met by using an explicit ``retry``. To use ``retry`` you also need to configure the transaction to allow explicit retries. @@ -338,7 +340,7 @@ Here is an example of using ``retry`` to block until an account has enough money } Alternative blocking transactions ---------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can also have two alternative blocking transactions, one of which can succeed first, with ``EitherOrElse``. @@ -443,10 +445,9 @@ You can also have two alternative blocking transactions, one of which can succee } } ----- Transactional datastructures -============================ +---------------------------- Akka provides two datastructures that are managed by the STM. @@ -510,15 +511,14 @@ Here is an example of creating and accessing a TransactionalVector: } }.execute(); ----- Persistent datastructures -========================= +------------------------- Akka's STM should only be used with immutable data. This can be costly if you have large datastructures and are using a naive copy-on-write. In order to make working with immutable datastructures fast enough Scala provides what are called Persistent Datastructures. There are currently two different ones: -- HashMap (`scaladoc `_) -- Vector (`scaladoc `_) +- HashMap (`scaladoc `__) +- Vector (`scaladoc `__) They are immutable and each update creates a completely new version but they are using clever structural sharing in order to make them almost as fast, for both read and update, as regular mutable datastructures. @@ -526,10 +526,9 @@ This illustration is taken from Rich Hickey's presentation. Copyright Rich Hicke .. image:: http://eclipsesource.com/blogs/wp-content/uploads/2009/12/clojure-trees.png ----- JTA integration -=============== +--------------- The STM has JTA (Java Transaction API) integration. This means that it will, if enabled, hook in to JTA and start a JTA transaction when the STM transaction is started. It will also rollback the STM transaction if the JTA transaction has failed and vice versa. This does not mean that the STM is made durable, if you need that you should use one of the `persistence modules `_. It simply means that the STM will participate and interact with and external JTA provider, for example send a message using JMS atomically within an STM transaction, or use Hibernate to persist STM managed data etc. @@ -555,4 +554,4 @@ You also have to configure which JTA provider to use etc in the 'jta' config sec timeout = 60 } ----- + diff --git a/akka-docs/scala/stm.rst b/akka-docs/scala/stm.rst index 1db74895d8..4917a7cd96 100644 --- a/akka-docs/scala/stm.rst +++ b/akka-docs/scala/stm.rst @@ -1,10 +1,14 @@ Software Transactional Memory (Scala) ===================================== +.. sidebar:: Contents + + .. contents:: :local: + Module stability: **SOLID** Overview of STM -=============== +--------------- An `STM `_ turns the Java heap into a transactional data set with begin/commit/rollback semantics. Very much like a regular database. It implements the first three letters in ACID; ACI: * Atomic @@ -24,7 +28,7 @@ The STM is based on Transactional References (referred to as Refs). Refs are mem Working with immutable collections can sometimes give bad performance due to extensive copying. Scala provides so-called persistent datastructures which makes working with immutable collections fast. They are immutable but with constant time access and modification. They use structural sharing and an insert or update does not ruin the old structure, hence “persistent”. Makes working with immutable composite types fast. The persistent datastructures currently consist of a Map and Vector. Simple example -============== +-------------- Here is a simple example of an incremental counter using STM. This shows creating a ``Ref``, a transactional reference, and then modifying it within a transaction, which is delimited by ``atomic``. @@ -44,15 +48,14 @@ Here is a simple example of an incremental counter using STM. This shows creatin counter // -> 2 ----- Ref -=== +--- Refs (transactional references) are mutable references to values and through the STM allow the safe sharing of mutable data. Refs separate identity from value. To ensure safety the value stored in a Ref should be immutable (they can of course contain refs themselves). The value referenced by a Ref can only be accessed or swapped within a transaction. If a transaction is not available, the call will be executed in its own transaction (the call will be atomic). This is a different approach than the Clojure Refs, where a missing transaction results in an error. Creating a Ref --------------- +^^^^^^^^^^^^^^ You can create a Ref with or without an initial value. @@ -67,7 +70,7 @@ You can create a Ref with or without an initial value. val ref = Ref[Int] Accessing the value of a Ref ----------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use ``get`` to access the value of a Ref. Note that if no initial value has been given then the value is initially ``null``. @@ -97,7 +100,7 @@ If there is a chance that the value of a Ref is null then you can use ``opt``, w } Changing the value of a Ref ---------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^ To set a new value for a Ref you can use ``set`` (or equivalently ``swap``), which sets the new value and returns the old value. @@ -138,7 +141,7 @@ You can also use ``alter`` which accepts a function that takes the old value and // -> 6 Refs in for-comprehensions --------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^ Ref is monadic and can be used in for-comprehensions. @@ -174,10 +177,9 @@ Ref is monadic and can be used in for-comprehensions. } // -> Ref[Int] ----- Transactions -============ +------------ A transaction is delimited using ``atomic``. @@ -190,24 +192,24 @@ A transaction is delimited using ``atomic``. All changes made to transactional objects are isolated from other changes, all make it or non make it (so failure atomicity) and are consistent. With the AkkaSTM you automatically have the Oracle version of the SERIALIZED isolation level, lower isolation is not possible. To make it fully serialized, set the writeskew property that checks if a writeskew problem is allowed to happen. Retries -------- +^^^^^^^ A transaction is automatically retried when it runs into some read or write conflict, until the operation completes, an exception (throwable) is thrown or when there are too many retries. When a read or writeconflict is encountered, the transaction uses a bounded exponential backoff to prevent cause more contention and give other transactions some room to complete. If you are using non transactional resources in an atomic block, there could be problems because a transaction can be retried. If you are using print statements or logging, it could be that they are called more than once. So you need to be prepared to deal with this. One of the possible solutions is to work with a deferred or compensating task that is executed after the transaction aborts or commits. Unexpected retries ------------------- +^^^^^^^^^^^^^^^^^^ It can happen for the first few executions that you get a few failures of execution that lead to unexpected retries, even though there is not any read or writeconflict. The cause of this is that speculative transaction configuration/selection is used. There are transactions optimized for a single transactional object, for 1..n and for n to unlimited. So based on the execution of the transaction, the system learns; it begins with a cheap one and upgrades to more expensive ones. Once it has learned, it will reuse this knowledge. It can be activated/deactivated using the speculative property on the TransactionFactory. In most cases it is best use the default value (enabled) so you get more out of performance. Coordinated transactions and Transactors ----------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you need coordinated transactions across actors or threads then see `Transactors `_. Configuring transactions ------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^ It's possible to configure transactions. The ``atomic`` method can take an implicit or explicit ``TransactionFactory``, which can determine properties of the transaction. A default transaction factory is used if none is specified explicitly or there is no implicit ``TransactionFactory`` in scope. @@ -311,7 +313,7 @@ Here's a similar example with an individual transaction factory for each instanc } Transaction lifecycle listeners -------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ It's possible to have code that will only run on the successful commit of a transaction, or when a transaction aborts. You can do this by adding ``deferred`` or ``compensating`` blocks to a transaction. @@ -329,7 +331,7 @@ It's possible to have code that will only run on the successful commit of a tran } Blocking transactions ---------------------- +^^^^^^^^^^^^^^^^^^^^^ You can block in a transaction until a condition is met by using an explicit ``retry``. To use ``retry`` you also need to configure the transaction to allow explicit retries. @@ -383,7 +385,7 @@ Here is an example of using ``retry`` to block until an account has enough money transferer.stop() Alternative blocking transactions ---------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can also have two alternative blocking transactions, one of which can succeed first, with ``either-orElse``. @@ -434,10 +436,9 @@ You can also have two alternative blocking transactions, one of which can succee brancher.stop() ----- Transactional datastructures -============================ +---------------------------- Akka provides two datastructures that are managed by the STM. @@ -511,14 +512,13 @@ Here is the same example using TransactionalMap: } // -> User("bill") ----- Persistent datastructures -========================= +------------------------- Akka's STM should only be used with immutable data. This can be costly if you have large datastructures and are using a naive copy-on-write. In order to make working with immutable datastructures fast enough Scala provides what are called Persistent Datastructures. There are currently two different ones: -* HashMap (`scaladoc `_) -* Vector (`scaladoc `_) +* HashMap (`scaladoc `__) +* Vector (`scaladoc `__) They are immutable and each update creates a completely new version but they are using clever structural sharing in order to make them almost as fast, for both read and update, as regular mutable datastructures. @@ -527,10 +527,8 @@ This illustration is taken from Rich Hickey's presentation. Copyright Rich Hicke .. image:: http://eclipsesource.com/blogs/wp-content/uploads/2009/12/clojure-trees.png ----- - JTA integration -=============== +--------------- The STM has JTA (Java Transaction API) integration. This means that it will, if enabled, hook in to JTA and start a JTA transaction when the STM transaction is started. It will also rollback the STM transaction if the JTA transaction has failed and vice versa. This does not mean that the STM is made durable, if you need that you should use one of the `persistence modules `_. It simply means that the STM will participate and interact with and external JTA provider, for example send a message using JMS atomically within an STM transaction, or use Hibernate to persist STM managed data etc. @@ -556,9 +554,8 @@ You also have to configure which JTA provider to use etc in the 'jta' config sec timeout = 60 } ----- Ants simulation sample -====================== +---------------------- One fun and very enlightening visual demo of STM, actors and transactional references is the `Ant simulation sample `_. I encourage you to run it and read through the code since it's a good example of using actors with STM. From ce99b60060450b4d0d7e0ec04edd6dbb855eedf9 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 22:26:21 +0200 Subject: [PATCH 051/100] Moved tutorial-chat-server from pending --- akka-docs/scala/index.rst | 1 + .../tutorial-chat-server.rst} | 0 2 files changed, 1 insertion(+) rename akka-docs/{pending/tutorial-chat-server-scala.rst => scala/tutorial-chat-server.rst} (100%) diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index 186cc0b949..8897cfc17b 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -12,3 +12,4 @@ Scala API dispatchers fsm testing + tutorial-chat-server diff --git a/akka-docs/pending/tutorial-chat-server-scala.rst b/akka-docs/scala/tutorial-chat-server.rst similarity index 100% rename from akka-docs/pending/tutorial-chat-server-scala.rst rename to akka-docs/scala/tutorial-chat-server.rst From 0b405aa37d6c6c28f54f9d61bbaa4dde616d0329 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 22:30:19 +0200 Subject: [PATCH 052/100] Cleanup --- akka-docs/scala/tutorial-chat-server.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/akka-docs/scala/tutorial-chat-server.rst b/akka-docs/scala/tutorial-chat-server.rst index afec3e948f..4a10af9a45 100644 --- a/akka-docs/scala/tutorial-chat-server.rst +++ b/akka-docs/scala/tutorial-chat-server.rst @@ -1,6 +1,10 @@ Tutorial: write a scalable, fault-tolerant, persistent network chat server and client (Scala) ============================================================================================= +.. sidebar:: Contents + + .. contents:: :local: + Introduction ------------ @@ -44,6 +48,8 @@ Here is a little example before we dive into a more interesting one. .. code-block:: scala + import akka.actor.Actor + class MyActor extends Actor { def receive = { case "test" => println("received test") @@ -118,7 +124,8 @@ Sometimes however, there is a need for sequential logic, sending a message and w def login = chat ! Login(name) def logout = chat ! Logout(name) def post(message: String) = chat ! ChatMessage(name, name + ": " + message) - def chatLog = (chat !! GetChatLog(name)).as[ChatLog].getOrElse(throw new Exception("Couldn't get the chat log from ChatServer")) + def chatLog = (chat !! GetChatLog(name)).as[ChatLog] + .getOrElse(throw new Exception("Couldn't get the chat log from ChatServer")) } As you can see, we are using the 'Actor.remote.actorFor' to lookup the chat server on the remote node. From this call we will get a handle to the remote instance and can use it as it is local. From 735252d12c6dd59aa843111940137cf9248577dd Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Wed, 27 Apr 2011 09:34:11 +1200 Subject: [PATCH 053/100] Update building akka docs --- akka-docs/intro/building-akka.rst | 205 ++++++++---------------------- 1 file changed, 50 insertions(+), 155 deletions(-) diff --git a/akka-docs/intro/building-akka.rst b/akka-docs/intro/building-akka.rst index 2f2a745eeb..02765172d3 100644 --- a/akka-docs/intro/building-akka.rst +++ b/akka-docs/intro/building-akka.rst @@ -1,5 +1,11 @@ -Building Akka -============= + +.. highlightlang:: none + +.. _building-akka: + +############### + Building Akka +############### This page describes how to build and run Akka from the latest source code. @@ -7,16 +13,18 @@ This page describes how to build and run Akka from the latest source code. Get the source code -------------------- +=================== -Akka uses `Git `_ and is hosted at `Github -`_. +Akka uses `Git`_ and is hosted at `Github`_. + +.. _Git: http://git-scm.com +.. _Github: http://github.com You first need Git installed on your machine. You can then clone the source repositories: -- Akka repository from ``_ -- Akka Modules repository from ``_ +- Akka repository from http://github.com/jboner/akka +- Akka Modules repository from http://github.com/jboner/akka-modules For example:: @@ -30,24 +38,27 @@ code with ``git pull``:: SBT - Simple Build Tool ------------------------ +======================= -Akka is using the excellent `SBT `_ -build system. So the first thing you have to do is to download and install -SBT. You can read more about how to do that `here -`_ . +Akka is using the excellent `SBT`_ build system. So the first thing you have to +do is to download and install SBT. You can read more about how to do that in the +`SBT setup`_ documentation. + +.. _SBT: http://code.google.com/p/simple-build-tool +.. _SBT setup: http://code.google.com/p/simple-build-tool/wiki/Setup The SBT commands that you'll need to build Akka are all included below. If you want to find out more about SBT and using it for your own projects do read the -`SBT documentation -`_. +`SBT documentation`_. + +.. _SBT documentation: http://code.google.com/p/simple-build-tool/wiki/RunningSbt The Akka SBT build file is ``project/build/AkkaProject.scala`` with some properties defined in ``project/build.properties``. Building Akka -------------- +============= First make sure that you are in the akka code directory:: @@ -55,7 +66,7 @@ First make sure that you are in the akka code directory:: Fetching dependencies -^^^^^^^^^^^^^^^^^^^^^ +--------------------- SBT does not fetch dependencies automatically. You need to manually do this with the ``update`` command:: @@ -70,7 +81,7 @@ or when the dependencies have changed.* Building -^^^^^^^^ +-------- To compile all the Akka core modules use the ``compile`` command:: @@ -85,7 +96,7 @@ latest Akka development version. Publish to local Ivy repository -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +------------------------------- If you want to deploy the artifacts to your local Ivy repository (for example, to use from an SBT project) use the ``publish-local`` command:: @@ -94,7 +105,7 @@ to use from an SBT project) use the ``publish-local`` command:: Publish to local Maven repository -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +--------------------------------- If you want to deploy the artifacts to your local Maven repository use:: @@ -102,7 +113,7 @@ If you want to deploy the artifacts to your local Maven repository use:: SBT interactive mode -^^^^^^^^^^^^^^^^^^^^ +-------------------- Note that in the examples above we are calling ``sbt compile`` and ``sbt test`` and so on. SBT also has an interactive mode. If you just run ``sbt`` you enter @@ -110,9 +121,7 @@ the interactive SBT prompt and can enter the commands directly. This saves starting up a new JVM instance for each command and can be much faster and more convenient. -For example, building Akka as above is more commonly done like this: - -.. code-block:: none +For example, building Akka as above is more commonly done like this:: % sbt [info] Building project akka 1.1-SNAPSHOT against Scala 2.9.0.RC1 @@ -131,7 +140,7 @@ For example, building Akka as above is more commonly done like this: SBT batch mode -^^^^^^^^^^^^^^ +-------------- It's also possible to combine commands in a single call. For example, updating, testing, and publishing Akka to the local Ivy repository can be done with:: @@ -140,7 +149,7 @@ testing, and publishing Akka to the local Ivy repository can be done with:: Building Akka Modules ---------------------- +===================== To build Akka Modules first build and publish Akka to your local Ivy repository as described above. Or using:: @@ -157,7 +166,7 @@ test, or publish-local as needed. For example:: Microkernel distribution -^^^^^^^^^^^^^^^^^^^^^^^^ +------------------------ To build the Akka Modules microkernel (the same as the Akka Modules distribution download) use the ``dist`` command:: @@ -169,9 +178,7 @@ The distribution zip can be found in the dist directory and is called To run the microkernel, unzip the zip file, change into the unzipped directory, set the ``AKKA_HOME`` environment variable, and run the main jar file. For -example: - -.. code-block:: none +example:: unzip dist/akka-modules-1.1-SNAPSHOT.zip cd akka-modules-1.1-SNAPSHOT @@ -184,10 +191,10 @@ into the ``deploy`` directory as well. Scripts -------- +======= Linux/Unix init script -^^^^^^^^^^^^^^^^^^^^^^ +---------------------- Here is a Linux/Unix init script that can be very useful: @@ -197,7 +204,7 @@ Copy and modify as needed. Simple startup shell script -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +--------------------------- This little script might help a bit. Just make sure you have the Akka distribution in the '$AKKA_HOME/dist' directory and then invoke this script to @@ -210,131 +217,19 @@ Copy and modify as needed. Dependencies ------------- +============ -If you are managing dependencies by hand you can find out what all the compile -dependencies are for each module by looking in the ``lib_managed/compile`` -directories. For example, you can run this to create a listing of dependencies -(providing you have the source code and have run ``sbt update``):: +If you are managing dependencies by hand you can find the dependencies for each +module by looking in the ``lib_managed`` directories. For example, this will +list all compile dependencies (providing you have the source code and have run +``sbt update``):: cd akka ls -1 */lib_managed/compile - -Dependencies used by the Akka core modules ------------------------------------------- - -akka-actor -^^^^^^^^^^ - -* No dependencies - -akka-stm -^^^^^^^^ - -* Depends on akka-actor -* multiverse-alpha-0.6.2.jar - -akka-typed-actor -^^^^^^^^^^^^^^^^ - -* Depends on akka-stm -* aopalliance-1.0.jar -* aspectwerkz-2.2.3.jar -* guice-all-2.0.jar - -akka-remote -^^^^^^^^^^^ - -* Depends on akka-typed-actor -* commons-codec-1.4.jar -* commons-io-2.0.1.jar -* dispatch-json_2.8.1-0.7.8.jar -* guice-all-2.0.jar -* h2-lzf-1.0.jar -* jackson-core-asl-1.7.1.jar -* jackson-mapper-asl-1.7.1.jar -* junit-4.8.1.jar -* netty-3.2.3.Final.jar -* objenesis-1.2.jar -* protobuf-java-2.3.0.jar -* sjson_2.8.1-0.9.1.jar - -akka-http -^^^^^^^^^ - -* Depends on akka-remote -* jsr250-api-1.0.jar -* jsr311-api-1.1.jar - - -Dependencies used by the Akka modules -------------------------------------- - -akka-amqp -^^^^^^^^^ - -* Depends on akka-remote -* commons-cli-1.1.jar -* amqp-client-1.8.1.jar - -akka-camel -^^^^^^^^^^ - -* Depends on akka-actor -* camel-core-2.7.0.jar -* commons-logging-api-1.1.jar -* commons-management-1.0.jar - -akka-camel-typed -^^^^^^^^^^^^^^^^ - -* Depends on akka-typed-actor -* camel-core-2.7.0.jar -* commons-logging-api-1.1.jar -* commons-management-1.0.jar - -akka-spring -^^^^^^^^^^^ - -* Depends on akka-camel -* akka-camel-typed -* commons-logging-1.1.1.jar -* spring-aop-3.0.4.RELEASE.jar -* spring-asm-3.0.4.RELEASE.jar -* spring-beans-3.0.4.RELEASE.jar -* spring-context-3.0.4.RELEASE.jar -* spring-core-3.0.4.RELEASE.jar -* spring-expression-3.0.4.RELEASE.jar - -akka-scalaz -^^^^^^^^^^^ - -* Depends on akka-actor -* hawtdispatch-1.1.jar -* hawtdispatch-scala-1.1.jar -* scalaz-core_2.8.1-6.0-SNAPSHOT.jar - -akka-kernel -^^^^^^^^^^^ - -* Depends on akka-http, akka-amqp, and akka-spring -* activation-1.1.jar -* asm-3.1.jar -* jaxb-api-2.1.jar -* jaxb-impl-2.1.12.jar -* jersey-core-1.3.jar -* jersey-json-1.3.jar -* jersey-scala-1.3.jar -* jersey-server-1.3.jar -* jettison-1.1.jar -* jetty-continuation-7.1.6.v20100715.jar -* jetty-http-7.1.6.v20100715.jar -* jetty-io-7.1.6.v20100715.jar -* jetty-security-7.1.6.v20100715.jar -* jetty-server-7.1.6.v20100715.jar -* jetty-servlet-7.1.6.v20100715.jar -* jetty-util-7.1.6.v20100715.jar -* jetty-xml-7.1.6.v20100715.jar -* servlet-api-2.5.jar -* stax-api-1.0.1.jar +You can also look at the Ivy dependency resolution information that is created +on ``sbt update`` and found in ``~/.ivy2/cache``. For example, the +``.ivy2/cache/se.scalablesolutions.akka-akka-remote-compile.xml`` file contains +the resolution information for the akka-remote module compile dependencies. If +you open this file in a web browser you will get an easy to navigate view of +dependencies. From 2a4e9673538b8f6a9ef74b190f0309d63cda5545 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Wed, 27 Apr 2011 09:37:14 +1200 Subject: [PATCH 054/100] Move building and configuration to general --- akka-docs/{intro => general}/building-akka.rst | 0 akka-docs/{intro => general}/configuration.rst | 0 akka-docs/general/index.rst | 2 ++ akka-docs/intro/index.rst | 2 -- 4 files changed, 2 insertions(+), 2 deletions(-) rename akka-docs/{intro => general}/building-akka.rst (100%) rename akka-docs/{intro => general}/configuration.rst (100%) diff --git a/akka-docs/intro/building-akka.rst b/akka-docs/general/building-akka.rst similarity index 100% rename from akka-docs/intro/building-akka.rst rename to akka-docs/general/building-akka.rst diff --git a/akka-docs/intro/configuration.rst b/akka-docs/general/configuration.rst similarity index 100% rename from akka-docs/intro/configuration.rst rename to akka-docs/general/configuration.rst diff --git a/akka-docs/general/index.rst b/akka-docs/general/index.rst index 81cee4fcf2..367e45b9d5 100644 --- a/akka-docs/general/index.rst +++ b/akka-docs/general/index.rst @@ -5,5 +5,7 @@ General :maxdepth: 2 migration-guides + building-akka + configuration event-handler util diff --git a/akka-docs/intro/index.rst b/akka-docs/intro/index.rst index 8df1a87a5d..6550f1bd79 100644 --- a/akka-docs/intro/index.rst +++ b/akka-docs/intro/index.rst @@ -8,5 +8,3 @@ Introduction getting-started-first-scala getting-started-first-scala-eclipse getting-started-first-java - building-akka - configuration From ad0b55ca7f297d8556a2a5b3a99bb70fc074a3ca Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Wed, 27 Apr 2011 10:04:18 +1200 Subject: [PATCH 055/100] Fix warnings in docs --- akka-docs/conf.py | 2 +- .../{intro => disabled}/examples/Pi.scala | 0 .../getting-started-first.rst | 7 ++++-- .../general/migration-guide-1.0.x-1.1.x.rst | 2 +- akka-docs/{intro => images}/build-path.png | Bin akka-docs/images/clojure-trees.png | Bin 0 -> 72431 bytes .../{intro => images}/diagnostics-window.png | Bin akka-docs/{intro => images}/example-code.png | Bin .../{intro => images}/import-project.png | Bin .../install-beta2-updatesite.png | Bin akka-docs/{intro => images}/pi-formula.png | Bin akka-docs/{intro => images}/quickfix.png | Bin akka-docs/{intro => images}/run-config.png | Bin .../intro/getting-started-first-java.rst | 7 ++++-- .../getting-started-first-scala-eclipse.rst | 21 ++++++++++-------- .../intro/getting-started-first-scala.rst | 7 ++++-- akka-docs/java/stm.rst | 2 +- akka-docs/scala/stm.rst | 2 +- akka-docs/scala/typed-actors.rst | 1 + 19 files changed, 32 insertions(+), 19 deletions(-) rename akka-docs/{intro => disabled}/examples/Pi.scala (100%) rename akka-docs/{intro => disabled}/getting-started-first.rst (98%) rename akka-docs/{intro => images}/build-path.png (100%) create mode 100644 akka-docs/images/clojure-trees.png rename akka-docs/{intro => images}/diagnostics-window.png (100%) rename akka-docs/{intro => images}/example-code.png (100%) rename akka-docs/{intro => images}/import-project.png (100%) rename akka-docs/{intro => images}/install-beta2-updatesite.png (100%) rename akka-docs/{intro => images}/pi-formula.png (100%) rename akka-docs/{intro => images}/quickfix.png (100%) rename akka-docs/{intro => images}/run-config.png (100%) diff --git a/akka-docs/conf.py b/akka-docs/conf.py index 209f747afc..712a3d10c8 100644 --- a/akka-docs/conf.py +++ b/akka-docs/conf.py @@ -13,7 +13,7 @@ extensions = ['sphinx.ext.todo', 'includecode'] templates_path = ['_templates'] source_suffix = '.rst' master_doc = 'index' -exclude_patterns = ['_build', 'pending'] +exclude_patterns = ['_build', 'pending', 'disabled'] project = u'Akka' copyright = u'2009-2011, Scalable Solutions AB' diff --git a/akka-docs/intro/examples/Pi.scala b/akka-docs/disabled/examples/Pi.scala similarity index 100% rename from akka-docs/intro/examples/Pi.scala rename to akka-docs/disabled/examples/Pi.scala diff --git a/akka-docs/intro/getting-started-first.rst b/akka-docs/disabled/getting-started-first.rst similarity index 98% rename from akka-docs/intro/getting-started-first.rst rename to akka-docs/disabled/getting-started-first.rst index 79c220d14a..63683a8c17 100644 --- a/akka-docs/intro/getting-started-first.rst +++ b/akka-docs/disabled/getting-started-first.rst @@ -19,14 +19,17 @@ We will be using an algorithm that is called "embarrassingly parallel" which jus Here is the formula for the algorithm we will use: -.. image:: pi-formula.png +.. image:: ../images/pi-formula.png In this particular algorithm the master splits the series into chunks which are sent out to each worker actor to be processed. When each worker has processed its chunk it sends a result back to the master which aggregates the total result. Tutorial source code -------------------- -If you want don't want to type in the code and/or set up an SBT project then you can check out the full tutorial from the Akka GitHub repository. It is in the ``akka-tutorials/akka-tutorial-first`` module. You can also browse it online `here `_, with the actual source code `here `_. +If you want don't want to type in the code and/or set up an SBT project then you can check out the full tutorial from the Akka GitHub repository. It is in the ``akka-tutorials/akka-tutorial-first`` module. You can also browse it online `here`__, with the actual source code `here`__. + +__ https://github.com/jboner/akka/tree/master/akka-tutorials/akka-tutorial-first +__ https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala Prerequisites ------------- diff --git a/akka-docs/general/migration-guide-1.0.x-1.1.x.rst b/akka-docs/general/migration-guide-1.0.x-1.1.x.rst index 3fc555abaf..e9b27c5032 100644 --- a/akka-docs/general/migration-guide-1.0.x-1.1.x.rst +++ b/akka-docs/general/migration-guide-1.0.x-1.1.x.rst @@ -38,4 +38,4 @@ Akka Remote Akka Testkit ------------ -The TestKit moved into the akka-testkit subproject and correspondingly into the :code:`akka.testkit` package. +The TestKit moved into the akka-testkit subproject and correspondingly into the ``akka.testkit`` package. diff --git a/akka-docs/intro/build-path.png b/akka-docs/images/build-path.png similarity index 100% rename from akka-docs/intro/build-path.png rename to akka-docs/images/build-path.png diff --git a/akka-docs/images/clojure-trees.png b/akka-docs/images/clojure-trees.png new file mode 100644 index 0000000000000000000000000000000000000000..60127d52b2ef65420879616461dda0b0da5ac9d3 GIT binary patch literal 72431 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sU<~76V_;zT>7>)cz`(#*9OUlAuNSs54@I14-?iy0XB4ude`@%$Aj3=9mCC9V-A!TD(=<%vb93;~Im zc_n&&t|1C##(JiDhJMd2eljrpZ}4<+45^5FbC-RFPUu#%2hL)StVcVZ%qbT0^m1c8 zsUR5=5@5hSiK$ESXba=fH4jXaosRNc|1tZq>Fd4M=diBXf3G&?dETsfFJHY|^={V8 zRnK3hpPRXI-L2}k8}@x&yFpy^?p4nPlO5C>{g~#mSaFsJy-?Y(KxIRQ?yr| z8Dhs7X-<|MyT4wG=AIv&w{z)^pU-Arem?*IpY-JU747ryNxr{6@7*gEgXt<47!T|; ziHeQ3hB)hhDnkZiEW-`^e;@lFmGA!@`|;}fx~)6@{d(QqU-xD4%g+=0YnEK}-}8L( z{68rl56l1C@Z)v-|5*&_42M|lTw=dJ?_jZFuyAKvm7NB4=q?tfAAkS8uh;+g=efQ3 z`-wg@U?-JktcBycTA(y$iT@nCuGYSs&k2g3OMukU~aQi%}5KZET0pQqy| zo&H|?zB+#6B`XWw$by zKU!}8H}m6h`+pmM+`j*B+KdD5Ua4FN<1As=uvYo<*441s4%#FCO#i_A8M|}0-!)@g zXL@aa@yC7g^3iv`tiQbc*Uks4Y`d3EkJQ?^NPo|VLyxZS|5uu%xpnt$E6y#UOtK6) zuTPY`eWe0Q2nU#^Gl*-6@y`DC`~LrZKaT3x1;!K}73Iv(4Q5*R{QvQN!K`(RHv0=c zXCAd{Uq9L5<*U`}}$B)alVyjhCO>zOOv$U-xOU2>*@P zxh%J|8u=Kum8R?}D}}^Pqql=n>yrNaf1a5i?XUmy_|dM{>$FquGBrHkTh@QA!Ja{Y zd&l2--#v40C->WS<=6kd-MRG*Hb*t69{8*Mk@@%c)A9c{{Ww>?FL+M*y-Lo6yO~p; zc7Ok0@@r%L-i_`3`)drXcb?K(Et19Z@BROO@0sKdu6vz39ppnhr`X?6A2O&4Y^>k^ z_ieuL@8vf>H?qs^IP$mpvF`G%U5qSjsjT{;)0RY5y6a#ZG_T z1xm)@ZmY7>p~-jx%Z~b^7hK=mPq+VlGpU)^%!A>>kK*k)vOky^@_$_RJZk%X_YRx0 z$#S1(JOU|UlG?a#b+Ip#EW?~^y;f)}{q%YM|2;pBneUsJ^}lc} zQ~0TMKD)no@Be;emTmWUK^T+6f)9@B`BQG~IBRxW$LuU8%FJ=Ve5W=o4A&> zVYyA|>?`L=uSKe>t}6*wZ`|g!fVm-8cILWRh#?URgeEWueXPI#_g#7S`Pz5IM~&zI zJj1f)UB&XJr!L#~W$H56ytcGnT6XXAyze^uexA+OyCC>xm#sLs>@o0)m4=o*4JrzZ zPyFkD1#A8Pv^KwX^^Ysd?LuwdX8K3RFr{4;?c)FUXU8?B0~)NGZ)L4sD(tvx^<;-= z)eDRVw)Od3UkAz3Isu#>41xdsnND2a|L}Gt>UGDjog<gX#i?Ex{&rzb?;L6MOsX_4@cAma3yur|I7Ny!hL$2Km~Tecmw#CWYg$_6dVP z_qUDxwQDNAZJs~3;p(N+I-z_1O1p2l##(bocze_&mQP3Z>k1qD60X19snE#BaIQ7* z)vX(z;EI7&snLRokNL;s`F~V)1~ML)zUPVR(M_lIW*@kE+P*9xoAJTF<7?;N+VON+ zbeY}*R-^RiS?1OZ7UAyevdbX}^w~m|G^U2|xXP!KoG$oR{`?!o$p9`5#CG2?W<0=d z^MFy7VNUz!V!P6n%nhG2O?K~t)G-Ni9t!1*^>4G|y$u`kYaUB?mtK!GZ(QAOS2H<` z=|KPgcSn+@UVS&=R>dJs^%8~xxid$9-f87yIQRVG+h5lp&bQQPv|u|Ixl2fM-L6-w z9_@JC=RI@lfvaxO_t*ZoWAC3U%Te=JTyD~>=SPH6vm35sD=N7|m>3pVHJmyyr|6XC zQM=!7f^B|X?APP7`|*HfLs&%N%}2A|Z+UMrp*?;>Y}S0^?)5t!b%h*ShP9-e)G*op z%5UrMcZ$2+Z5|1Fb2*+~viHd(@2=SLyIXH0b?bJChQ}B->Zs>gRBzXvfBxU!D@ljf zzMFPy+hIOyk@dB2v%5h7!JWafd)+xxP|dooH{$xeN>xV2_rA5yrY_iZ?u<95#e4I0 z-TM1>RJ=)+pKVlE|NHiRHHIJe|Np&zl-=$_?>P%bg!uV z!FRs3k7Vz?eEj#;LfgAnJrkmYe$4;>bN-{V`E|vGZ#EuZw!^O2cpWGszRc&ZJmK8+ zea~~-qoDF%%>K_q{%+~~J%$?_Zs+aJ-FP)DdZ~nU!t0Ov_qK{Me7Ib1yYxoV^J)L$Xx<8NQ{k_k8{0l3|N+%^4+br=KKOGv+mA$p>m;tMZ;R!UX`up@4^(re=%C?_s=T{`glFLPxD_; z-M^YI7q|bFDGmAfywLup@&BNIx2yj}8R^#BKJIzspQ`p$zV7SlD{^lS?P&?hb6~lv zEWU$9=JB^f;`=sOyjaj2`NvcJ`pBBs(f76N zKF>Taay_m(mnr=DoMOMi<<@sI3lF(QmOT|be`nv0FKd5`Oy8HgX@2_N%=eEP-Pc7k z?O!@gq`vN4@w~~uz8>VS+aR#NgY}H#1E%?6vEQRrKvi&t!LDUzYB^b6yH5~^qXW#eH+58$* zO~-xTbzRhJ=NBod=X1;BHr}uMEea~hg%5O}{xy-~(*t(<9TtDTTox9V$V*5#Wqkib zo4JaZz>1j6rBj7IWF1c5`}LakykA$AFN-)@zUQ&*QFXhIokxmZ*{khKzI%Lb;@$7B zr#Y}Qyq=cveGjWH<7^vNjfT08e$D5vU9n@Q4kzP}yL%299ew|ONBGCv&DUo9c;0_| zeb}9#c?(y`{=bp2_|f&H*<8%bOPCsEg?B7i{x)Ln|8wR0CQIzx`|Z|hi_d3_Ctti= z{r8#qe$S(G4&M;CF79Bs|9VW#rOETAY)VhoH4JOe*uVQ>n{-$9x}D1^o@&R>Okz5( zG4pM&`Mn(xl~1R3Kb;GAEy1gaiWl+s>^!NS$|3tR`*!#X#f9L+U>=6g_-XyJAd98M58mJAJ)>zgby+f!& zc};^><2!q8(Z~0_pR$zv{VP|lvG3zd=Pv1dyQMomP77B5xAWzDb@BWAv-%%Bv(}k> z>+|tRi9ahPqg@S?nXI2~DR};NgRS{~`w%HJ9uDzouNTYz&0w1BabHb z*Q78l$X>TItuRE=t*}J$--E~Z_sVms37P)-n|tBC&F2|LYn(i1ZS@L^?>)5noYm@z zUzg{{#gyGlZGHS|$4bV~TJilJeolH;%W7}Tymt5g78Wap8(Ug-Ki8Mw42U=&5}^9; z<@Kf395;3??cx9R?f3WD7B6V$|H60{QWz8PwPeh*>YxENQQXwWz7|=#=h?F zR#@0Pc>eGE{`#WQZ^^%GWD>X+98cxud3sXrBE!3+PT6^umpdgLChzQIH3{F!m=GfM z%s0O(xM+LV&c}Lj$-SmR0ye+jZ0mIZk`mg?WY0ramEGaQbUw0&a-lY3cr&#`i^-7#Xjp{2Q5V zQzvls%(`r9_xtAgWh&j<_rA?Nx;p;vt4Du6pZC|1+5Q34jCyp4TVF?S$AhMQ@;OU{ zOpO25Zo6Ntcc8s;&zDQy!I86s{cQ|Sa(-^--1~Cb?4ZB)zZ?E8FkZ)Yw&mUX%aa_~ z8J4U5e7ET03A@0U@{hEi|#>Xbt zJ=Ko)%(?&E{{K(M{@rKqH+moIEGuCBf4%;1IMbqekN3V}Wonw_JZE2Hr?B#gr+3~n zAJEh`zI;FZ_ul9GUWKmrS`ggS{oQgd3%6!vYj>2-k{Pb+;tz->yi5AuCiUsvGfvM1 z%niaqCEsUNsy7~WJu4Rf@%{d3v;WFR{@ZW&WCF{ke<5;F`v3Bz3hRycem-q)yX=R1 zq`mL&f2(CV{`q)(`6ZoZ zJ;@F4Y?s6cJKWxopkefS>uH(PltiZezpk!V+xBtp`58?|xZ2!G>9>atGQ*L(0>&}$aQCO;Ky!kZ8yzRlC&14UU zbSE;OYgq9oV%toW0|7Tur^g2M+?~Gv&r?ZO(RI=0GlG-8x3Sj!Jgls!V)SO`jH!(e ze?84R%8?{tSP-+ps9@tnJBRcdhVa_6_2FOd&wIMl?v-#+JoEWqY$fj^rbP%{pD{h+ z)wN59BD{;YPM2kvvs(H3*4u!z8^vRLuN&(NV5{FWl|D6eS*sZ7j5VR#tQ5ZKCz(`=Nig zr&q4)FE4QQ{&wg`_VL^I|9yMZz|80IP-MmzzCTwjf8E_YYk~ti!|VxJ-_NkBGj>f< zI>hm3f6&P#KDVDv)7$su^kY!F_v=FDX(`P2Bd&!m-lDmwfLVVBT=siCTO-)D?yl`z z7B$UAJ_f&_TX%OqTgt*x8EiL2<@$wkxsM$z>;CO}H@EBmpU<9aSsLR1o)b(JI`L>$ zLwwD}?n%FUq$BLD%smm+^ zYbu{xYag%TQ|MT`?cat;&(>Nv%76b-$KWb?VB&`>@8_-B-~GKp-(fB9MX^#&{e3?k zxiNe&czpBjZO?=Wpw6v_)7|^loM)66yE-l%P$*FCRR5J2d7Ed$ufyxU=lK4dey?SM z{=e@ZQwv{-23zkBo0q__gK5JJ;ZtipRqy$(_@%;7;qqqQ{D%3T%Tqsv?PajoSQ6oM zb4_US*9O;_`|r%_XGys`T}H7%o2U zv(~$+2_6IhwGVFFSjFCs(q}xyyjg(#TK%uf^Y!X>J)g%J@%PyT)991`7@N)iK3(!C zKYRV0TR(dzZDfhrZE@h5|8~_kGSl}~n7z9;eTr&(+unvh40?}ViWs~rpLE>kyy&}v z4+q(oH=UW>At`#FeZjd9gQ^?<%xmqxZ!ofoc?TMrTPyT}AtBm*S@!ig6Bxo8e|x`V zpV`6J@V{~A-b?@xaZXJH>HyftBFgfXcat5fUGS$y*X3Fke<)uQP zZfV))1t8lOglsSrQa|F*88|8N$7b2G8Ol5lk{Evc_hRVSS#m^7O?biDNt#xRZtPlr zR*7-rl4ldSlbgOqp!haQx<}{U(Mhe!agN>Zc_6rS5a& z*~6F|06n*KYsk;Uo9m z@9oT!7_LXjF#mWO?B|(k&EW9$iWP&!&zHSZt$)2(EPQ&|loJ-yf0ZOL%YWO@zVxSs z&W!JW(`_D0o|KqzEj4%V16F+xPqoq(<$}YLj+^M!7B0CLnLhWb;p@v14e#a}gL?;0 zUZ@x>0(GMA>rKs1VZ8t4d~ozjwtqo0S^xCcr!&^?3-MuC@M8lXL!X6Y+mBh5%5rBn zCLQ!h?A!Uky>RdEzT`V|d>&4EVA!=f*w|Z= z|5!fl8snd`NcInW-LDzy-W=k#X0TZJ^YgXs`%F{M7jdq-Y~JTB`V zUEsVq0_u%5JC~|A*8JY}H&{A9uI%Qcz53OW;>e&>G?%sg+te%Gs2N%L#JY03p&i`esS*Xv_?Yk0q`{QkXx-9|w) zTkiWOowvJp$^14msQY!;xB7%)`xT+j(@!7&B@%seTKDh!g4MZ$JAtF+V*? znBm&|ZM(7yFW>je6Xp2x$-iIo-^N#5N6)7IUZ>yB_q+f9kNznxAMS#N%y|wh=sR<| z((nMYmEyAVA5&sh_FZ26n8UWyViN0-?yG@2Bu-RZ+dOaT4d>LfTNR8M>#MG=mdcz} zT&X&B%iE-iPS&-_J-4rIUuRqF3>vSLetc!?rlmp*QyA0^a5gY4VOaGVG^#mm=`^jp z*i|}P{>*faVr8f=Il8X<=Kf2*d$0a_E$%K?nUYmpF81NgWS=?Dwc~%D3h}Xg^kGVb zD@NpN0rxZ&P<;d8B_Q3>Ew#o?|ptu+b24(GwkNP=$XL784wp^k+fe) zIO5F2&EEDOFSM&ID1Cj&Tl9Y2|GKpajP;+^ycJLXueZ0P(D$UxitlQ9fxHgtem1WH z`?cPc@Bh8DVzZTBf`s#y;Ge5LhWPs{rW8%s`ufvvt46K|pC`22sBEk~_hqf}vrCzZ zMpfk+{);N_|GsA~`jhu_&`CpImD(q{8_aIU`MF6;iobFy3s-OS1eI))r<8qPSEx( zk(=tnr@iwz$vyx7pXbw${AUzApu=#Y^o2~+y0=yb{co!=imCltZgc#k`usVs9`aWI zxs~(xQ_Y$fgW5-*UY%pA{c%`+-i#&NQof#3>o4#<{U`U+qoCurw*B4oOhU#__|-Z; zrpcgAin{8{cebCym;~89W(L0E`jfZ!>$R63eqNtbTlR6fXn0JFc)`u3UG{H^`X7C- zzE}NdzI|@xb&>uRviCJ(FVD`~kyr?7$glDCI{q+c-8gmM^f`*iqD#Mz6@g6<;TjuVBb^S{F%ZH>%+JCSKqokDe>Z$ z)SA}RGp*f?8!r1<>lz9@bDF)|>|8FWIiJ@bfBl!32SdW6V?RIk*Uvep7y6i+W6IO^ z+6w2NyYH{rbSkO5;QVv>+PdrS_a)X}|GqOd=v4jMjcy;imfNUqJ$-Y8mx-#u{%&sq-uf-2Z*=d)Mdl_V$eX3zOefJx#FF zxb)`Gn>`;|_18>&d-y|a;Zf1dH#XJ#8a}B#6E2WXzIf#G>$+tsM!(%-Ra;)=#ed)P zQx!DEy#C{xDq~ho27yWHJA7n+>E-!&PT;S7;jFpuHE2}e5r{^+W5Wln)|8q%3Rr62Z8)3hd?fIqF*k_aGKB=j^ zJo}!>beAc&vU0WfgWY3Z%z0gN{Xr)vT?>nrf8X>|jd9KWPW5>+9v$0Jpz#!xP&LIS zn4Zt|;_O^G`E6%u)u+kx=gc^CH9USVXb7u&v$y%j1^qwPm0pi6e^C2TW~tY;xazke zhrdsm^l|qFoA==-Ia7Ta>>hkMCY|q-w?qE#3wPG(GUiLm!gp$bS~ZW4ZcLRu=zKcD z-LN=UL&qmGNn4patZdt_qSE8HznnE=P0HbstH-=ZNK{{=%31EaCBf#c#$ID$(nRF z;wxx8T3C9;iTBS`waZ_vTrSpiMeCCFi_ce<+iktEBjp?0UGe(wF{!cNZe_23#QOAN z3B!uVCwDBh|MQSv{kr-Fbxp@N^4pctPQGM$CwnZHE#~B`-sDpZ4?MLi-(F3Bz9#AS zEW7NfXEsD;?J&I>etxQN9#3!f?ah(t(H|sE7oWE+U-U-4@@y!>(z`u&*&9)v9mF(c zlS5sqbi<}Qyy6?|3_xX#2yN zZ<{S|X02Y!cJI05yyrXr>9TQ6RuE(i2>sMrE%hUK8(YLt1J^h54ApTj*InC}I8*1Z z*zbx5jqJ;waQ)3Y)w}epgWR;MtOq8&S4k3P%v`|Zo!s+!-yzpm%YQC9@grt`%548% zN4U>y`Dq)jU;OJJe_cV>#kcGHn0!4JFgN&mY^{E_XG#MLlU~djDS@lijO|mlMW`om zNCdu^KKpBS{O>3xm!|LKH&dsx9J}IpnD5t*|2gt`(UMKg?>>L8P%~dY!{orlk0qNF zul{C!V^BY3@eb3j$lb+i+|nHv4;S&xPI(aN^{}8jIKe3NwN+ezp(PEc||a!)wRGLI>V31`EmWC`b?f=OA+JwC#bZXD-M8`!xNeDi1^6 z?zh`ooh~+XHyw`u^GN(M`+_-dA6ri{WxM4%nUd9HW?Kk zmaq(taQjxXLqa-zZt1mI{bl#?``li8(V`1lA}xa3oYL>tE7()9!!_gVhVW`tLU9zb!P` z!#^ST7^}inIR>uH*H8cXD(#RXSHR@Kcbe^4f>4Jt^O*xin>WU9T#mj@5+zV9``E1!6Cj4M*V2n2_aKW{`T9VHjxSxz&Aln`hjI9(CK7o<}db%g0J`e3xb@deSBKz;9va^2>2~ zpFs0?o_>0{%w4m(6wlm#CYxl!7~HTq^FmYU(X1p_(OdKKryJV6+wnL^!>RnLI^*w& zUCh3cDqpv5slF5gYF9izW+(C#g|IhFZ4BTJ2e zW3!!6_E9;$UHbpz&i9p9>X%K>=KO8J^P~IU?>-eFy}$#xF$K%~y&mVpU(<+LYcrSS zK+ob!$0k-@ec#4okbhw3j^lxn=T^OE>6`MR^3P-W`NtCN;-36k9sf6LRc!K)Guyiz zHRm{;T()!8U7P#IQr3N6&fHL3%d-31bv4F!7BlD0n7Qjzhlj)gCAZjJ3EMSJ{r*Py~+@y=CKD~#tw-(rPY z@d--CTVJIWO?>Xt%9C|;QmXy;(_G(p96aC5uc|Yea6C1kxH53w@;OCbO!Ft{&0?DQ z(eHfy_ucpB#(bY-eJs`Ixa@DW{rPq)BA37Tu|@w~P0pv9y)6?ha*tVo+A@zpMa*N5 z^}pZEnci^2qT#`bPcydc;$I&AsWp2eU%78A!-WRM9U>hEJ{9USoXu2jSeGI!-uC)d z_Ih1EtCvf@{7#T=pV<5+{`jlJCnDnruWG+c2b;3*EC1loZISG zkL|k0{35B~c7fEH&GY}h39&DOx5e>-cd9EcGR6a&ho!5 zu)J7hP~MeOytmlA{`2hnGU0!w?(^rnvAkmb|MTS!+zn=gno8adDv4}5tHRY3K1+o6 z-jXwYKG_?$fVwpzoFxneA}dSp&%7*j;6%b7xvwkSO^Z%7%`i(?aqg08DYFCfixRfw zlR_9n{<6(^6e#}jK{LOfVfd+Y3BgCNpM0?PGt)Aa=L{t&-LhvG*%xRmHfykHI2~%% zsXZxq4&ULUvE}#GFSZxnH{{uE{ciWJ_y6a!oT*%>D0Bwo{|zAjZxDL@ZvJMW10fnO zxE%`W%@%I437#Waw(;C*P6m!09XE@@3Z|a^d;kBx@6#J}^6ZbFWo!~;Uh!YT`i-=C zfiRCmgKi`9Z^k3`KTrDmEw}U0KF4=jd!OKg{6+gikEKa{FZilG@yasob5HVuXV1#( z{x#>EsQlFl*}H1*e!i^@nw2Zh*){E|2BUh4M?^r#4!!L;`=7;DKAozmwl1&WCJV!r zx{nQBSI$iP+T$Bs1zvblSR1-2Vr%%eMYpO-cRyyR?2^09p3;?WvTaiCtFouxrIQu< z_ncYUTGhG!NX07co6Ed8nu<4wUrM#Jdn|cgLvPQAL#(L{I!g@mR;`z1$nn~Aw>Vmn zG0!2-+4Re-KGxzlvzD}~-7)lKbZ9=Jy6^Y9-TH+qnF9U(chsGjvGhed>p79jzwdqD zcjVm9e>>jHa1q!lS$?nbdC9WEn0c?CurHr$p&xn4J@{kBJ?m zyR6go&mr-B7q+FD-jjPX-}2|L>-+b8EHBlcVGoLiKa!`{sn2B5(Bx#^{brMQ5{sMV z$z+K}tGmtKn=~ifRAIQ{?=vf65vZZvsIfF+l|gN2ib}9V@r#>xwl9u3S0H z(_q%I=%Vv4m(QPf&Ph(Z;@Ml)9&xP?+?EO3WixLa^Gbn+`VH>O zo(1K`vnRGzzYsPMMW8e^}d^Y?3pJ%G=XNqHgU*Vil zR9SGbNO^U<*=_3+!k5e5?R>uMTW?XN-G(yI)cUNZB%}7T9^ZIop6T14{i|X};4h2b z_a>(1uU>RlD$m>c?nAfLEwh#BCte+YreC6*8uV%2>-R4i9>mBWT=&;o(}Z3xChe30$7w&f4Ix@lA;OoP^CW zPlR1(SKnSCVaB#l@B4cnqkz@C3co+J+s|QG{-9Po!7OJ@VyGqa^oXd!SxGnSzVAGL zmo0(mLm?N>&a1H!d$O!=SIn=>i`ya@;=w1y^5(Y2ZM9oRDugawY&&$v_sja~G+WRJ zt8LMnc}A+74BVUUYkXy}n4HCR;rL#Q&a6L+85}wV53CfHy7!^YJcRAoff;Y^E_9%*Zf^;?|xpc3>uGje|I2VV);TgvIDM(|n9)4@FAMpXsuj-fJLOZ9x%y-H^VWqE&j>Pj_spB2T3E-x zCjNNIL2)&a;+p>IJIXtcUbUQ4BK>Mgq40U5>#r>gmvwGCYJ66=c*VNnJ+pIOFPWDs zXDwWLr+B~m)qC%3CxZsS)b&5V>rM805WfFcXqTe!ocCWkGh-Z1OQz4cXyGkZ`~tkpgq3u=HCaEpvuwhX6kZnAD!Gv^K^N9^gkuG% z+_z_*Dt;)m_{O$i&s$37udmNKw4*#+y)gznmUDQ{1csxvM+DuM7^z-PQO#NAcP_&y z*i2rafo1XX^I5kyKGWa%WRggEgnQeY1qa3Dm(Pn6H4tN+IsfyV=PKJ*NgN5(yI<_K zQTg7L@HMX=9XleG?78}g`L6g+J!dl3ZMjn$8$SEi51Gdr)_*qk*XF2NZr#WqJ3CLi zasJ;o>6$OSI-JW(g*d=L!o7AH!*v;l#@@t(VT?+?r5qBE7uEbc9e?kN_NC&lAFJ>G zjw_mN_3TZmesv^M!=_(qwVB-E+f^nxPIu{IxBqdl^Qg4=lH}^6;a;0|K5UbAlPjOO zr^~&+xKMEabKCcteAaI^uqYgj;EN6}nDgEL*X|jCiekl@^YRw%e!cpP;8vYV|6{SU zL+@R^Yitf00^e!q$GvW9!*Ng>m*vj}LEoutl5QK`*F3ilF3S-h?c9nd~n!H9Tqly2N7Nzpv}{1*e-?D*w27zOKyc9>;?ybBE&*D>z^1JW3Rb+R)Ko zc_vv%fmPx4jxW5QwXO*nOs)CyI{yEzKKJeKBuo2~C(CZh>+vk-oz}2eQgZ6&n+=jW zt?&9KfCjjBUa;cq5Pke=>5tn%7v+?f7ZlCCW~{nerZYm@&`Q7VV|ThtcP5iVFH?=o zX4OI_iSRR$JEWHFIkQ5Tceire1j7$A&(|yylK!zOe6N=1q&zKanGehijE?JqK4mKL z6^G31{bXBQqc?A{cASXu{n)E24|ty2NS@C0^qmtQx7PG-?Byw->1@rv@9u7&*sw!q z=8T4Eo84vy-8<3CNBz9i{HbJE>qybMS8C)#WB z@o0Qo?J$ljSIqQYcX>j4 zg*!vNo?K;n>#=j?;ijAhAyc|{z1eg+Nau5#bl!|fuXbfW1WiwK=bwqb`B?tX1NNhZ z$7QE898)>>eytXZ&fF9h*Y4vxkMwE=H@=j9YOywx@c?MB=VYo%wpf7e+bx&f?BpHJGs@p{MU*mprXQCl=5_3v)|07^iu=Fir?+Q<_wZLw5y+M3)v zlLb!C-fqa5eq-+*`x%Pqr~0B6=LazeJa~GqM`8nL;X&udp29#O*MqS=4@~sm+kd&> zJo#Y3+pX8PtxCI}U-x--iJIwzRdaZzEj?>kdSLR64QAU5XBFq}+j~1It>xZBzH*NT z=hLr-hQD?AQX~C>>+eg31Xjh;?Iy~MT@xqOJTsne8MJn1TGZEi|G%J7GcR?QBDbmE zE#1z3Op=(odfl#7W)8Pa7^b{9__oh;_R5IjFZXwJ*3NcI;@fcO>hDA1`wSZGjvY4X zV_ah-zQw<0)~sGGAMX4a-R~Z1)xLc<(^^PCZ{u0B+d7Q(j7&$*mR&yt8dMa&Rmsx+GU0d&;GATx7EJ- zZuO;a;Kjg}&*vBiUh9Y!y&?R5s`1L0^$o9&ipSs4QZrEFOY`kGe0l!AFCnJ`WsjTf z(24(j>v~_TQu?iIap(Wvw(qaKb#_Ma`}B^8!`W*#9urb{5#gJCzh>^0^mCkf^GtUz zH+;@vhv4W*Hos2xY+|!W+m*8aRXg*Go#m?=mK7db=EtCs*Z1joP+!zB z7p0DU=CU>VVVWu2E@z)O9p9G7+A6f=>I#E@Gr~%R7$ZNeR^FH~rE=*yMe$!I5qAP_ zSzj(sNjS~(e%(V=fu~E~lqNC0uQ+bKY-_Ua&xmZ_nnyps)&K73H{j8S?iyUK=nfzkg znX_}t?^SjN72bQ7xBKn1P1dWgEm*yIu34r-a1dwe^ZRz8lYi*hG z{0eTnU-jf(4oer9{8vNWX2Q(N$tf}0m-apJ*|bIO%+7ATT`O)B9+&O@eBOS4K~DH& z@m71D6?vO=A-*# zoAc#6H}|MtFPMGvzzg+dkwO!zYF=?&tNo&Q)Jk(&;hl}M-~RSH`EVcGCwox8W=d!N zrg&ew+p{*YoiSun5$jvH^=er3T|G&aNlt1%Kc6e#SN!o+_Pm z8d$=xA@Zl*iRH=722xYjUsBKA`|0dc`+tw^xn^IOnLcl3m(2F3NlVvnK4-Q4=90Ob zck+{;Y$}}9e44H5vah-Ck*;lls~>8t%eiqn&3|I@ewHuSYZ+}PIj5w#Grrq(Kk)l! zjqK-I^Q&I1Y&}r(an|E`R}$E1LaPp|Ww~<=DTf3FnhJ80#OYPTd}K z?r!1YVydAWY zW4B~rH;aar*Rh{_U-@i(*)>Bb_fSK@`jwU9g#k$$8kTRHcPGI)Cb3oY5?kf_>igC^ z^FQU>@YdgJqAJ3}&GSl5`JIJAm6?Hfr zbGu2sq)zB#<+GKAVV|Gw`glw_pP3R z_PJ6YPCGuIe!nDQ_D*Y@)i%;Vn_UH;WtynfHaC1DH>2_FiN=T5wpnxlFrQA_gW zf$Zm8d1-283>zeO>UAvdT50v^gfjQm9n0p1dKe}hQ@VM??Re=aRomzX*57B%Sd_Ur z-Pl*+Ez`6e?<3wWpI=wi$T(B2VODMtb4bL4*Xwq_GuzG>JIPREky#AWLqqp=`MMvI zcAU)IqAe7+i0_uc*9!(JDW|^Hz1#U*%f3m{#Adn8U%jW;Y-d60x~-n~05{r{QL!qR7~m96FvEz{rLkov&RQ1{=yPcJMc@g@t?THk7m^*rGP$C=EG zPg^tG=!tp$H8@w}eD%H0L59)qmS#4ouiLnK{XQ#I#RL~N(bluE%gvl`wk)1^KKi=? z`;FwS%wAJ0m-R0S&-Tw*dN!aswfg)f!p@~OW(K75No(wMODKzz^1BjYzecE!I@|NcAnebx1nUCkCd6Z;L3dh+`q;JbPtnrv2l?v^7*}jQXQh2|w&J8;fKBeSf9tkq#pt3%(tG~ajZ^bW?) z#{v)RE)@NqGDp;R`If~2>q^;Vp0yVJ?p>eEks9Q7=z_BR&l6wXn8odkVX*qVJzOtB zn`hZqKfP;fe9Dc2^#g6Bcm7ycdH-tP?zbM3+jlB|Jbb6icu%m#NwM!4vOky^;y)dU zz5HkDjl&MdnWFfwt*A6wTf4S5dqtmFDZ>UnO}&Qn8!kI$_E|jYSSfOS>&4=7&IiYD zoh@^F?{nC3;+j35PMu`kbW&~hk{L%cV*_V3K1zDH?{(~ZFFl(Feg|_>OXdhEzL1wV zl9#lqAx!?jtB3sc6@^cyhA#`Ueso~bge;9Co|~E&_i4&HK4Z9V`@Zu0?S_e&CwIN7 zR^8aa%aZW=gos-AioI@v((=iAqdtcwd-By3GJ8nu}x3NBh z(*eT+b@~q&ld7|~-+e6i-9u;7j)P_f8kQd}_fOzXSf01yiB4O8sh?Piv)-eMldg=3 zIs1c?7+4kvC_E8kQ|OImXAwVNvB}`M#DtX9>z>rU-#sI-`ch}~Rw0JEl|F2d+jP7a z_dbdSFVj>$@W&vYP4Z{x^{VpwN#H&m=Vq@1X;p8xUSD>0#_FwnMGs?FrmBS=)i_=Z{J3KP%M4_gVP0UhJKICYPZt@X`Bgp*0PXUg!Tzw>bR5NMC|= zM&6qR)i(CWuJ6AdSN-<(i5SP?)}@jnkBlV!u6;5&_x~+xouatTyi(2&h6OU)HR`79 z`C@FZe{Eg^FVlkcyI!qQJ^o^D@i|LPZo4jy;~SH>B{?RQ@2Eek>fjYvnX{+#=eKnG z1ydP54 z{a!_{J7%x(DThPObHDCwSR*q<2Q-TJs6*|d`8Dljd@B|oX!L*gI@r%r)lg=A)z#IL zw#p^s&zL{&sf>SN)AX1k&mv7GF<-g0EDI9ScRc>|({U%W#p*SiP6<_7AARyV{{JtP zBR1cxHO{|Yzu&HxbFq53)wLr&o~{R96-h*2p41&X8gJdS zJDB7C=(_d2kA06E`KtnMtyYmPUGAx4_vvDsml%V{cl}EDhtJpV`MGc{OT(Q5(YY1d zulQY++%WASo7w!`IS;DBbJ!VPPklb8W>)VP&am2uqbAN47{$Ie?l z<7;4@Q((#DeXZ5V=5N-kN__)&yJu26lW+GhPZPTxE&AqcS^S*0JONq@WLb+h9$UR` zS60^S^0Q@;#pj-jon9?qu5e#u^Cf@18QDFiH52sh}OsD&3v@q{?mqQOb6KS)~~(dS1jXwu3*ZMq}bBJhSE1V z|G)R}F5vY(N6Y|k#3Eg6LK*<*^jaFuu{6ip|+mhlCbr>yE^SQ&)TRYA^*Nd$a3+# z^_+hg)``5yF%_;hwu*fF>fhbBdF9>-0@@SP_gwp$|F-+h%{s|Ti}i~CUW`95S9)qv zP#uf&i>`l@w9C5ZoSc0p^=a4MRm?xW+8zEQ>fG^llCGw5*tF*cPmS()A6%tg@x8uH>I0g>8~?rd>_a%Lnq>;;OQl6eNQ>9 zxqD1He~#Dcmgp_>ZH~R&zVTV!!U=mX|9>p8%!6S8!`$~$jIVte9L}pmzbpBebH~9f zpmshdOVA_-m-kO2+hxmc2pQb;cdP7~{?E-#UG>OQ_9X=`XRu4GP_z5R-cULJyIb3r z1#fO@C9hjiA!oPY->=u|%g#nA`M>{Ew=w&`-i`fdj;b*vZ0H3o(-PirY}V}fTNCHk z1TW1$cJ9xEQ{w%nen>y_*nhA5$pioDo~>K^J~Eq~%#}EBz50C)gTc3$1B}t9n*}*d z1dVqJFu15RaF`!l!1#;je#xC%tI9KUe>`TYI}Zd^dA)XHN{OATiIsMuB>VUcKivuCTSPCl2G-wckYZFdt2uU6W= z3hehfHUD(*C#QLT+izVzHo-mE=wV&YuAo~Dk{xFbS}{1C;C?Vkz>%@lsL$A9@BE-m zD@|E{Rn5;{vr$io;lwnfYwO|!GS(%jOwH$H^R0e7>#XX1>Bmp+-Je?g;=JnP58FRI z>EEBbJ@(h1!qsapJzsx2@AeJ#dtbMI%Ghr|effbu>rB!fCHxGGZ9Jf3w#fYOv}}uK zMmv+rHr73now>`masQv4JyHu<91_mFPMmR|W-ar1(9Xii;tUa>4M}Zf{LdFiXl%WA zM9_Nb@jKG&f1Y~!{*^fJr+g3Bx9y+}JSRif<#V&LNY(#6>Sp{x(qPl6vk$B9<{k}W zK5$%i_YW5VIn7S(M+ZK;OwbeFVfXe9`_K=KS+Ei>Tke`{$y?*zeUZ zezd?=?f=c%=Vya+e6EBXVhQ{qIA8Qs?#$F_p3?s{rrRa&7m=RoDama*L%_?h>G9Nr zo7LyJaAM}hxV8gsx8A>rO7OOnI{3Hn-&#X{HWmAZ zx!M;h4^O$c_bY3$N}7wR*qjGuyHHwuOuD zAG^7^KANece#)9pXP>Isz3ngiv+PVuuaV_q1_rM$o&GgRT5D>aTi>tb6xb4GP%!Uo z$H6UHp&|@bLJ#!sDSno!PM_Dy&9t`m{M5+mS1Xqkt01 zed+11CHHy8@g=54dOuFT{>#hM@J}x??YL~YPwh#^2Q}veuDw6OkoL#;h2)ZBm+bc6 z(t4Z5AYc3BzhOgnz@G3GOcLzrX6o z+pp@&ZgfZI&w2BF+Naw3A5T1w7N5Jdvf@P|zu!9v$;3zhQh2ydlzr)Q;i;ef_QjT) zM_T_*x~TQT)~}AE^2x!EMk4$-lkYxae{Z+y$Mfg@x~WD#&&S)>7JXdz+;+o_+UaXA zvz@GEUvt~yb3=u+Mi@`QX?{s(w%M#sFUD^c|s<(Sp&S9vZ$Lw-@L4D}Ch4s_YE7cy9KNszq&3|vfj-Nkt)&5(q zsCf_SD`%Wv@71uR_Le0Vhsn1T&%8(fB`hB)PTfB7%7Gn9_dSj3_cC0b#pd*w=W0UL z@xsbq>wX?O!Pp?lbwgtRaiLk-lk{T@-@mOrAphV^i|8BU4-xD?q}v#I8aj@q)EItK z%2d1d(_+)g$kHp{cE~Uo{V+KEO@H2u5-tx(<{#(JZ#QTAaHQGX=k1+UU&Qu+_SJCe z$TF5dm(o}ctn^HciJhY4aB;Q6Cp%{DCmo_pBD4E`O0($oGMc$x+uBg` zpIu7j*q2@AXZszee-B*ZFm>bKLdone*DvK3duy(bez!BI@bqjx-MpWFG%x?IsDJtH z(!BlCKDzw>`rhew;hcLKGuJX3`dScJwH7?^1%K-7e1~nWtQJ@TWaOp-EX(u7GluYb4c#8VZ+yR z$M_hoshqzfn#Q~&Au{fE?#8f>&;94#nRWEr_w)0j-t2z8>!hlHyLM}~o4khEN2{c+ zPu)eGr(d!dl`}57dCtFgvj($50^hfL#or%k=i99jeW1_q@Nxfrqq#d6*0}b}$;nu* z@nl`0>h`@7y;I=WW>-ufL|Q_7bAyV$pMx>lZU>}`Af*ju+O z`4~FtlDBN|e4^nTv7jnA+3Za5*WBAi$AYTQ>}S|rddxSo-{Sh641<1yJ5LUMSh`{3 zL@!<5%m1{#_c1ZNjelnA#;Nd@w|Rvp@0Le~#)b`9_RFjqo>uL6$=kloP2Ka)3H#aW zc4uWRKl7!JF{jL{lwpI~&!`fSMZ6Q1Ds6PW@Oi5L{`%Kqx7g#0918DjJg)NXP@1o2 zhV|1EN&Z_GoqBxf(FrZ?ix-m*&-p9eFhiQlK|on`hs<94Oz|HJzW@Exd|Y(*n;lmt zRRu@%Sx?P792dqj+xqs@-F2Et4dS1dpNp5>`tDU#8&B$1CeglQ3|5*=hu%$>F$%74 zetXb}L;6P=r^UZr?{*#Ce`@FF^?R@B{R`do?SE*SoX_9Ivej#*-db9FU+3=MhPoXG z+EW=fW?pktF;6I8`+3)~Wqr>XzTK$&eBP$#NcXNgbH8=(IZ?hw*k@BjdbVwsG5hQ| z_x+C~n{Vu!`R|;B@HWOR#|4Xc?pYly^nI1G=J**AW{w5j(WY_vHP7PKUiNl)Fh9>? zXGG@FqYpKf&n=5ub?d@WhCWl-w7*5{4A~QM-+fDt++-kn@NU*;-md+1zouN%H7P9r zemifj$nLbwY!_0ZpY}{=)i9f2?OERzDkqY?s1RO?PLeFgkdCvif|+SXfx&aQxl9ph3P= z@7u;ZI;P64+?~I8Lp>wM*Z6~P%=iC1d(t6&MKSC7Cm>;-q&R=e`5QX<ztNw3cxVPYYk?oreXP2#vVYwjw#^EZ%F9z3#Laatt{&c%cR6BS_ zb2H%{P=YIez$rvic{b?G~%I_%#Gw-~7fk!a*l>vV;1w+BpqO5rv)B*LSVkcjc#y zkNGt*>zV66|2)K$^v@uzA$ShUA_n__bGq-p%sASq;4OZ?=D6V6+x_KtKh0ZN8=b$m zlxyxwKPJg7#=YOa^D*?P@Bfm^5HQDg&h8Um56LQR%Mti6!z_lNoYC#TQyO$QrS0h(%6h zxMrcZIidkDfxh*6JNl&bwZdx2eVLOm!RI_Ei?v>UTMeSFDyVwRdlt z9N}6l?re6(O1?~g&XvC@OY)Xa5dQj);ZBUYVb0W_K}&3e_Rp(2Ww!Q`e|+U<=kBMc zS&SG0EDi5TY*p(GE*C2>HeSixkbKHCoy{kx%QbexTJz}TJ9Zx3I*nnTZFz*YX2%>= zh97Jj9hzN^&EgB!DDaGYx7j~($+x-g+p-S7WWC>Ce`DHF?zj(4w|B^_+RCs%!v{%W_0VaNR_F^lJ~`cLoSX|^?w_#&vSvBmWUJDbRf zUFnfZFR#njMQDFZS?H)fWvlg8Z>D*xczQCfN*tLJRsL=HD!G|~sk<-iyUuvP(B0Pn|gJgm=oW1sPJ(%g6@|VFdyJWMkx!%8zJB^e#t3TcE z`@jC`#L~f{-Z4Z%r`=7pL@sux+KDMfT1I$(Xl7}+>Z|Z z!#XuvZ62F49tc_}BCqQ|QBvvO`Mtu0jP-9?9>teODsF6u*YvkCJa$Vya?@tb|1&N> ze5RsrdX?qsbxxmGbz)CX`mU7;ox@=K?$^6sR#uBQwS|w4oIahRkhx{)= z&9VnxYpUnR#uS}Y-OgxphwZV>rn-g*(O+xLA{T%CRPn$2yMCYbyBkl$wr^auSS;`3 z&1IhJe~a%BV7hSa$9{$mzbzbUDt8VvGca-6Bz2$AIQi*J}mZ{JweKZ|1FI>O$MjJhj>rT-tlK zrqYj{;ke96n}7b6Jn#ILy2wS#W!zOOTOA)gulk!`=gAlDoBOq<)vaS^JYk(%`2N~y z=cF@=?J{#js`Kxh%=yI7m2+z2#Zx9r5*U`Q%DH3mZgI?%Uk4wBuHE9l^2x{gq>_E% z$7ZQA>^N%M##S$!%y57Q>X{hg`Hslfi@VAZLjsT@J#7cw7NK6^SX zI&UY-n`e1j0!RZW|Yg zT`Fuo_rC7;?fdhRy4&{_6*WoUm?JK(Wyu>5zkN+NXNhiTo7$lze zkav{p=}DEB9lMPBZe;2TbsW)7$~gR2`uWLl#(jrRWh&mk56VCG_gR_+1NX1UW!(0c z;lb&7HTxf}*?i86@$i9$H^;Pk!bQz^eyjeF`F-Hkp8wn4vNtqM_+I;f`O?mrT?xh6 zg}=|HRl3#8_@QOEph5X`=p0_NT&V-=URiI9tbV_@oN>dqV>iz{2|MI9fpdG1jM4w* zPc5x14CPUK_c8>iec1Nhi{Zm=)fc^Q4xN}V_w3iZ{13RdZI7G9kXLFOyn8N}(Hh6# zRPQ%)zklQZeZpDp(*zOb*l_t(47&o~2`(zU8XA80+))b+&Q)DGZF}1p4|zPZvfr0- z`8AW}!`_W?Z+#=SrrUjUP`S!b8pf2$^Z6u33~;vRMh-=_?L*6 z^0|nNpmLTw8#b@H^;ec*joNa__emRFGdpgus4~nc|Fvyx`kuI-SMCNIhHq39WH-xm zi!oS~)t~4RS=co7`@O`k*G@mq&bQrDalo!x^m=Uh-M2FuXPP~=vExY)$ydD*)t1X} zd{slvrSF~GlQ+)XoS2w-o`vD7XIsS)D=XJ0>`wzTI2qm*-7jN4@YYM2g+b@>@*YE0 z;f;lx8~+qkvmanzvE#=^qf*AXYV#J~J(c-I%B1;`&FY-apL_ZxjN8sTvdd1{qu~2x z`MGqf+tM7D!hTQLC}aL-Y1f@Q&Seh1Z#0E0k37ApeRSv5(#m}j?%5`OYC`XMV{T49 zx*@LSeQ$wht;#3YU9NsgBF;+-lL+^di+| zXX_4wciT&UOCHeQ_i=;Kj;i@A4~|rZYW52qyf%>~r8%^@oBKwZU1os!ncqj&SRFS@ z%4M5+rzcAWb#-Eos~{So&h`MKJ0 z>Nj53ZvVN4tMPNc;h(}PUWXIf$$km9WLQ_K_+zpXV@@%&9yiN4d%T$ z@(e$=ep2DkGk+^T<Hm4MmD|4W3~6VuTI}}7;JnngxWX5A z_U;MZ6H-`LYPEfLj&K`qf;jt0QN3zu=I7N6lCo{GYhE{tMArZP+6_A1NVEFqrqg=f zs@p@9CVb33ccZ+~KKsnBBd0!jxU4;!n0n!^eFT#%gN)8%$@ec<8zyF*KV|)>@%8q3 zj5T)(qgA#EG4Pb%STJL|zoiN&7HyAtmmzg1?Ib#=yZ8HQ(DkN*37BUyZRL-_vSz$xkR+l{z?v`y}+pZb=W zA>ZcR4pjz&V-*6-8L=)ag+D!VT5{nvla;T{@juBOGx{wT+iF~{z94m;F(Hm$NLpu^ zL`B9T`%>HBLt^F6Z{Pd2jm6>>TSJ6d+g2B0MKgyRDtZZ>(zmC|zxK#;m{PYoEK#m_ zy-~xFY^m+prysqzbI8F^?Oo~m>eyA15zmj`c~JNvXL(JBrwiYn?|a|-9^ni))t%<+ zrIX}vy|V4A%CUrXdlxe|B=<$^Xeeg8z;vth_0?dn+S$g9{I{#;K2e*&!VEfwP-Opq zmz6EiG3*~$`)^z3-Y7L_I48YzBOmh#wc7Wa=Y3tXWW}*m2K&G^K^2CI=b~MYc5PDe zk$%lkw}fY1eBn(NhV}Jll8>b^?A&yVujEYfyJMb44d=w|x0!IfV#+?esXFvs`if3_ zX1iAj42=odvI&KWZK_9nxPII;-?0A9-dE4Q#{KitlkijstUkKg>k{)02FH)>x6ai1 z%O|kg2Je6M%fMwvlVR^SnTJ77*2igG^Vd-;==!Pbt@r6?k<0xV28%bYFJ~z5x%jP{ zG392?riWHX@7v#gcj&lv-p;fl&xoEIr?n&}HXpgV-h{=RdHQMbxc?Kvm=9d$zq@0{ zi$&eX`WUwqGKKLa&No>h{jB!U0ttq{NesVxi}QIBj#%$oUSevQxOk0vph&piQin)CDo^}1t6D7ajNbYB`YV?1jt+49toeu{zEtCFP zeYpN7=M}?)8Ag3cvkM(;yyj**3LW^bz1i#bsS3;G6~A-M^mE#D3_o_SIkan2Lu6r~ zcwnr7^p(@|wnS$?O`KD9ujuQesq1T==87zP(x>sh~b&5aGIX_eZN4#n$p=9lf;$y0N@z~#o_1xlMM z#SAPbito(bdNu5&NKc`oU+{hQBO*)BOWrHIUcRQW?x8*pXHg&%XGX@V_{TQl5jHQE zOzv84`!;jswV10?Gw!W?r~Hk}`{|=)A>u`|)H@{Db5{iIv7h1amh}Ro?&t8f4f`%6 zGA#VZm}2)x^z}cD?}r)|Pn5{&SsCG~u+{sQgz$`zKUM4u+FV)7ZvVQcW)E>u;)4JZ&7ynngXWPzSkK=v{h&P_`tsAc2wn`H_XRHqw{x0cGXKRbDQI9 z>cx08@&W^sn8X30Y-3Ib^EcL8{EwyWIOw*LSweDSzRu&yx@`y9x)~EJkF8~Hh}Up7 zJXOHX@IhTVcgmwVZIX}cvgh2{@$2s4J?p&y)U&g& z=R9&KB)ltCQ!MR&?DZVo2bOOYrI!ag6q$GM9&K8a+kv(oGL zyL}XQFWPFr_u;GfyGWU`8;PC^pO=1IrEz9vPXp+%N6pu{5e3)2{Jm9ufRSA#Ku=I5 zLcr(1g3o5vQ|%%r-c0v4FnLvZpm#p&pNpRk{H~wR{AZ?-n%*&q+0&aB&79&__at%- zi&Nf{HUFUQj}`8=TB()CHkL=ooig(mkVyaiS?2ua`~t-;<;gxDK+97l z?GGH;Q7gsrqV4bQp5S%W4(q0~vbB9V{(kE5Ljt>%%JOb4XxWo`?%n3|b}MVn?s(g| zQPKJQwr!10^0j%d9{}iu+gXLJkzp{6K~e0+OChD z7F%Y>VN>6~Fyg>5#s`$$%+dHdT~D>mQde{iyk?T+Ep z#21}!k5qBqpWw03PSzk=JU}k^sLsz_X19W8B`LJubUwB>Tz<-eq=3@vUqI`a4O8VC z)K9r^EZ{xRysZAu;cYpx4|e|H+^zO~{x5$2iClLa!O%CAVb8q$MLV%&0=`KfBIXm(Xq-s z(w43V%Rec*C$Lu=>D^fQ`nS=PqdN_EDIJct2|r@>t+=>v6Yn?UZ$}vSz4XoZIyS}U zc44y0YnFyR0ym5gNO(MD{8-P%bH076Vc+*;5lOyZePx}i=k!nCsT8tm-A@bo)GH

D&^T*Bjjj1L}lY|v`hQOx3w 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

Fq&-y>j)!~6)%Q~ll| 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

  • k$|=LGzDKol`=ne>A2_fu#VG^P3Q%8 zcI*YS>8DJlJoibuWY4Lⅆ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 literal 0 HcmV?d00001 diff --git a/akka-docs/intro/diagnostics-window.png b/akka-docs/images/diagnostics-window.png similarity index 100% rename from akka-docs/intro/diagnostics-window.png rename to akka-docs/images/diagnostics-window.png diff --git a/akka-docs/intro/example-code.png b/akka-docs/images/example-code.png similarity index 100% rename from akka-docs/intro/example-code.png rename to akka-docs/images/example-code.png diff --git a/akka-docs/intro/import-project.png b/akka-docs/images/import-project.png similarity index 100% rename from akka-docs/intro/import-project.png rename to akka-docs/images/import-project.png diff --git a/akka-docs/intro/install-beta2-updatesite.png b/akka-docs/images/install-beta2-updatesite.png similarity index 100% rename from akka-docs/intro/install-beta2-updatesite.png rename to akka-docs/images/install-beta2-updatesite.png diff --git a/akka-docs/intro/pi-formula.png b/akka-docs/images/pi-formula.png similarity index 100% rename from akka-docs/intro/pi-formula.png rename to akka-docs/images/pi-formula.png diff --git a/akka-docs/intro/quickfix.png b/akka-docs/images/quickfix.png similarity index 100% rename from akka-docs/intro/quickfix.png rename to akka-docs/images/quickfix.png diff --git a/akka-docs/intro/run-config.png b/akka-docs/images/run-config.png similarity index 100% rename from akka-docs/intro/run-config.png rename to akka-docs/images/run-config.png diff --git a/akka-docs/intro/getting-started-first-java.rst b/akka-docs/intro/getting-started-first-java.rst index 99db6f8c07..b907118f15 100644 --- a/akka-docs/intro/getting-started-first-java.rst +++ b/akka-docs/intro/getting-started-first-java.rst @@ -19,14 +19,17 @@ We will be using an algorithm that is called "embarrassingly parallel" which jus Here is the formula for the algorithm we will use: -.. image:: pi-formula.png +.. image:: ../images/pi-formula.png In this particular algorithm the master splits the series into chunks which are sent out to each worker actor to be processed. When each worker has processed its chunk it sends a result back to the master which aggregates the total result. Tutorial source code -------------------- -If you want don't want to type in the code and/or set up a Maven project then you can check out the full tutorial from the Akka GitHub repository. It is in the ``akka-tutorials/akka-tutorial-first`` module. You can also browse it online `here `_, with the actual source code `here `_. +If you want don't want to type in the code and/or set up a Maven project then you can check out the full tutorial from the Akka GitHub repository. It is in the ``akka-tutorials/akka-tutorial-first`` module. You can also browse it online `here`__, with the actual source code `here`__. + +__ https://github.com/jboner/akka/tree/master/akka-tutorials/akka-tutorial-first +__ https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java Prerequisites ------------- diff --git a/akka-docs/intro/getting-started-first-scala-eclipse.rst b/akka-docs/intro/getting-started-first-scala-eclipse.rst index 5c3987866a..5aaee1439d 100644 --- a/akka-docs/intro/getting-started-first-scala-eclipse.rst +++ b/akka-docs/intro/getting-started-first-scala-eclipse.rst @@ -12,14 +12,17 @@ We will be using an algorithm that is called "embarrassingly parallel" which jus Here is the formula for the algorithm we will use: -.. image:: pi-formula.png +.. image:: ../images/pi-formula.png In this particular algorithm the master splits the series into chunks which are sent out to each worker actor to be processed. When each worker has processed its chunk it sends a result back to the master which aggregates the total result. Tutorial source code -------------------- -If you want don't want to type in the code and/or set up an SBT project then you can check out the full tutorial from the Akka GitHub repository. It is in the ``akka-tutorials/akka-tutorial-first`` module. You can also browse it online `here `_, with the actual source code `here `_. +If you want don't want to type in the code and/or set up an SBT project then you can check out the full tutorial from the Akka GitHub repository. It is in the ``akka-tutorials/akka-tutorial-first`` module. You can also browse it online `here`__, with the actual source code `here`__. + +__ https://github.com/jboner/akka/tree/master/akka-tutorials/akka-tutorial-first +__ https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala Prerequisites ------------- @@ -99,19 +102,19 @@ If you want to use Eclipse for coding your Akka tutorial, you need to install th You can install this plugin using the regular update mechanism. First choose a version of the IDE from `http://download.scala-ide.org `_. We recommend you choose 2.0.x, which comes with Scala 2.9. Copy the corresponding URL and then choose ``Help/Install New Software`` and paste the URL you just copied. You should see something similar to the following image. -.. image:: install-beta2-updatesite.png +.. image:: ../images/install-beta2-updatesite.png Make sure you select both the ``JDT Weaving for Scala`` and the ``Scala IDE for Eclipse`` plugins. The other plugin is optional, and contains the source code of the plugin itself. Once the installation is finished, you need to restart Eclipse. The first time the plugin starts it will open a diagnostics window and offer to fix several settings, such as the delay for content assist (code-completion) or the shown completion proposal types. -.. image:: diagnostics-window.png +.. image:: ../images/diagnostics-window.png Accept the recommended settings, and follow the instructions if you need to increase the heap size of Eclipse. Check that the installation succeeded by creating a new Scala project (``File/New>Scala Project``), and typing some code. You should have content-assist, hyperlinking to definitions, instant error reporting, and so on. -.. image:: example-code.png +.. image:: ../images/example-code.png You are ready to code now! @@ -140,7 +143,7 @@ Creating an Akka project in Eclipse If you have not already done so, now is the time to create an Eclipse project for our tutorial. Use the ``New Scala Project`` wizard and accept the default settings. Once the project is open, we need to add the akka libraries to the *build path*. Right click on the project and choose ``Properties``, then click on ``Java Build Path``. Go to ``Libraries`` and click on ``Add External Jars..``, then navigate to the location where you installed akka and choose ``akka-actor.jar``. You should see something similar to this: -.. image:: build-path.png +.. image:: ../images/build-path.png Using SBT in Eclipse ^^^^^^^^^^^^^^^^^^^^ @@ -186,7 +189,7 @@ Then run the ``eclipse`` target to generate the Eclipse project:: Next you need to import this project in Eclipse, by choosing ``Eclipse/Import.. Existing Projects into Workspace``. Navigate to the directory where you defined your SBT project and choose import: -.. image:: import-project.png +.. image:: ../images/import-project.png Now we have the basis for an Akka Eclipse application, so we can.. @@ -234,7 +237,7 @@ Now we can create the worker actor. Create a new class called ``Worker`` as bef The ``Actor`` trait is defined in ``akka.actor`` and you can either import it explicitly, or let Eclipse do it for you when it cannot resolve the ``Actor`` trait. The quick fix option (``Ctrl-F1``) will offer two options: -.. image:: quickfix.png +.. image:: ../images/quickfix.png Choose the Akka Actor and move on. @@ -403,7 +406,7 @@ If you have not defined an the ``AKKA_HOME`` environment variable then Akka can' You can also define a new Run configuration, by going to ``Run/Run Configurations``. Create a new ``Scala application`` and choose the tutorial project and the main class to be ``akkatutorial.Pi``. You can pass additional command line arguments to the JVM on the ``Arguments`` page, for instance to define where ``akka.conf`` is: -.. image:: run-config.png +.. image:: ../images/run-config.png Once you finished your run configuration, click ``Run``. You should see the same output in the ``Console`` window. You can use the same configuration for debugging the application, by choosing ``Run/Debug History`` or just ``Debug As``. diff --git a/akka-docs/intro/getting-started-first-scala.rst b/akka-docs/intro/getting-started-first-scala.rst index 364c0d276b..59d8fd5a82 100644 --- a/akka-docs/intro/getting-started-first-scala.rst +++ b/akka-docs/intro/getting-started-first-scala.rst @@ -19,14 +19,17 @@ We will be using an algorithm that is called "embarrassingly parallel" which jus Here is the formula for the algorithm we will use: -.. image:: pi-formula.png +.. image:: ../images/pi-formula.png In this particular algorithm the master splits the series into chunks which are sent out to each worker actor to be processed. When each worker has processed its chunk it sends a result back to the master which aggregates the total result. Tutorial source code -------------------- -If you want don't want to type in the code and/or set up an SBT project then you can check out the full tutorial from the Akka GitHub repository. It is in the ``akka-tutorials/akka-tutorial-first`` module. You can also browse it online `here `_, with the actual source code `here `_. +If you want don't want to type in the code and/or set up an SBT project then you can check out the full tutorial from the Akka GitHub repository. It is in the ``akka-tutorials/akka-tutorial-first`` module. You can also browse it online `here`__, with the actual source code `here`__. + +__ https://github.com/jboner/akka/tree/master/akka-tutorials/akka-tutorial-first +__ https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala Prerequisites ------------- diff --git a/akka-docs/java/stm.rst b/akka-docs/java/stm.rst index 221c706183..dbc0da5d4a 100644 --- a/akka-docs/java/stm.rst +++ b/akka-docs/java/stm.rst @@ -524,7 +524,7 @@ They are immutable and each update creates a completely new version but they are This illustration is taken from Rich Hickey's presentation. Copyright Rich Hickey 2009. -.. image:: http://eclipsesource.com/blogs/wp-content/uploads/2009/12/clojure-trees.png +.. image:: ../images/clojure-trees.png JTA integration diff --git a/akka-docs/scala/stm.rst b/akka-docs/scala/stm.rst index 4917a7cd96..21b8d7b522 100644 --- a/akka-docs/scala/stm.rst +++ b/akka-docs/scala/stm.rst @@ -524,7 +524,7 @@ They are immutable and each update creates a completely new version but they are This illustration is taken from Rich Hickey's presentation. Copyright Rich Hickey 2009. -.. image:: http://eclipsesource.com/blogs/wp-content/uploads/2009/12/clojure-trees.png +.. image:: ../images/clojure-trees.png JTA integration diff --git a/akka-docs/scala/typed-actors.rst b/akka-docs/scala/typed-actors.rst index 7e5a327113..74c7f22f1f 100644 --- a/akka-docs/scala/typed-actors.rst +++ b/akka-docs/scala/typed-actors.rst @@ -70,6 +70,7 @@ Configuration factory class Using a configuration object: .. code-block:: scala + import akka.actor.TypedActorConfiguration import akka.util.Duration import akka.util.duration._ From fd46de15a642e0d02b0e60420d0ed8838e713a75 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Wed, 27 Apr 2011 15:50:57 +1200 Subject: [PATCH 056/100] Remove akka modules build info --- akka-docs/general/building-akka.rst | 64 +---------------------------- 1 file changed, 1 insertion(+), 63 deletions(-) diff --git a/akka-docs/general/building-akka.rst b/akka-docs/general/building-akka.rst index 02765172d3..3ed3b151bc 100644 --- a/akka-docs/general/building-akka.rst +++ b/akka-docs/general/building-akka.rst @@ -151,69 +151,7 @@ testing, and publishing Akka to the local Ivy repository can be done with:: Building Akka Modules ===================== -To build Akka Modules first build and publish Akka to your local Ivy repository -as described above. Or using:: - - cd akka - sbt update publish-local - -Then you can build Akka Modules using the same steps as building Akka. First -update to get all dependencies (including the Akka core modules), then compile, -test, or publish-local as needed. For example:: - - cd akka-modules - sbt update publish-local - - -Microkernel distribution ------------------------- - -To build the Akka Modules microkernel (the same as the Akka Modules distribution -download) use the ``dist`` command:: - - sbt dist - -The distribution zip can be found in the dist directory and is called -``akka-modules-{version}.zip``. - -To run the microkernel, unzip the zip file, change into the unzipped directory, -set the ``AKKA_HOME`` environment variable, and run the main jar file. For -example:: - - unzip dist/akka-modules-1.1-SNAPSHOT.zip - cd akka-modules-1.1-SNAPSHOT - export AKKA_HOME=`pwd` - java -jar akka-modules-1.1-SNAPSHOT.jar - -The microkernel will boot up and install the sample applications that reside in -the distribution's ``deploy`` directory. You can deploy your own applications -into the ``deploy`` directory as well. - - -Scripts -======= - -Linux/Unix init script ----------------------- - -Here is a Linux/Unix init script that can be very useful: - -http://github.com/jboner/akka/blob/master/scripts/akka-init-script.sh - -Copy and modify as needed. - - -Simple startup shell script ---------------------------- - -This little script might help a bit. Just make sure you have the Akka -distribution in the '$AKKA_HOME/dist' directory and then invoke this script to -start up the kernel. The distribution is created in the './dist' dir for you if -you invoke 'sbt dist'. - -http://github.com/jboner/akka/blob/master/scripts/run_akka.sh - -Copy and modify as needed. +See the Akka Modules documentation. Dependencies From 7a33e9003d60d5612ddb07492de71b6399b89aad Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Wed, 27 Apr 2011 17:06:37 +1200 Subject: [PATCH 057/100] Remove microkernel dist stuff --- project/build/AkkaProject.scala | 131 +++++--------------------------- 1 file changed, 19 insertions(+), 112 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index b3cb6dba6b..5a25f943d1 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -27,42 +27,6 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { override def compileOptions = super.compileOptions ++ scalaCompileSettings.map(CompileOption) override def javaCompileOptions = super.javaCompileOptions ++ javaCompileSettings.map(JavaCompileOption) - // ------------------------------------------------------------------------------------------------------------------- - // Deploy/dist settings - // ------------------------------------------------------------------------------------------------------------------- - val distName = "%s-%s".format(name, version) - val distArchiveName = distName + ".zip" - val deployPath = info.projectPath / "deploy" - val distPath = info.projectPath / "dist" - val distArchive = (distPath ##) / distArchiveName - - lazy override val `package` = task { None } - - //The distribution task, packages Akka into a zipfile and places it into the projectPath/dist directory - lazy val dist = task { - - def transferFile(from: Path, to: Path) = - if ( from.asFile.renameTo(to.asFile) ) None - else Some("Couldn't transfer %s to %s".format(from,to)) - - //Creates a temporary directory where we can assemble the distribution - val genDistDir = Path.fromFile({ - val d = File.createTempFile("akka","dist") - d.delete //delete the file - d.mkdir //Recreate it as a dir - d - }).## //## is needed to make sure that the zipped archive has the correct root folder - - //Temporary directory to hold the dist currently being generated - val currentDist = genDistDir / distName - - FileUtilities.copy(allArtifacts.get, currentDist, log).left.toOption orElse //Copy all needed artifacts into the root archive - FileUtilities.zip(List(currentDist), distArchiveName, true, log) orElse //Compress the root archive into a zipfile - transferFile(info.projectPath / distArchiveName, distArchive) orElse //Move the archive into the dist folder - FileUtilities.clean(genDistDir,log) //Cleanup the generated jars - - } dependsOn (`package`) describedAs("Zips up the distribution.") - // ------------------------------------------------------------------------------------------------------------------- // All repositories *must* go here! See ModuleConigurations below. // ------------------------------------------------------------------------------------------------------------------- @@ -197,14 +161,6 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { override def disableCrossPaths = true - override def packageOptions = - manifestClassPath.map(cp => ManifestAttributes( - (Attributes.Name.CLASS_PATH, cp), - (IMPLEMENTATION_TITLE, "Akka"), - (IMPLEMENTATION_URL, "http://akka.io"), - (IMPLEMENTATION_VENDOR, "Scalable Solutions AB") - )).toList - //Exclude slf4j1.5.11 from the classpath, it's conflicting... override def fullClasspath(config: Configuration): PathFinder = { super.fullClasspath(config) --- @@ -250,7 +206,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { override def deliverProjectDependencies = super.deliverProjectDependencies.toList - akka_samples.projectID - akka_tutorials.projectID - // ------------------------------------------------------------ + // ------------------------------------------------------------ // Build release // ------------------------------------------------------------ @@ -265,15 +221,13 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { publishTask(publishIvyModule, releaseConfiguration) dependsOn (deliver, publishLocal, makePom) } - lazy val buildRelease = task { - FileUtilities.copy(Seq(distArchive), localReleaseDownloads, log).left.toOption - } dependsOn (publishRelease, dist) + lazy val buildRelease = task { None } dependsOn publishRelease // ------------------------------------------------------------------------------------------------------------------- // akka-actor subproject // ------------------------------------------------------------------------------------------------------------------- - class AkkaActorProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with OsgiProject { + class AkkaActorProject(info: ProjectInfo) extends AkkaDefaultProject(info) with OsgiProject { override def bndExportPackage = super.bndExportPackage ++ Seq("com.eaio.*;version=3.2") } @@ -281,7 +235,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // akka-stm subproject // ------------------------------------------------------------------------------------------------------------------- - class AkkaStmProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + class AkkaStmProject(info: ProjectInfo) extends AkkaDefaultProject(info) { val multiverse = Dependencies.multiverse // testing @@ -293,7 +247,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // akka-typed-actor subproject // ------------------------------------------------------------------------------------------------------------------- - class AkkaTypedActorProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + class AkkaTypedActorProject(info: ProjectInfo) extends AkkaDefaultProject(info) { val aopalliance = Dependencies.aopalliance val aspectwerkz = Dependencies.aspectwerkz val guicey = Dependencies.guicey @@ -310,7 +264,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // akka-remote subproject // ------------------------------------------------------------------------------------------------------------------- - class AkkaRemoteProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + class AkkaRemoteProject(info: ProjectInfo) extends AkkaDefaultProject(info) { val commons_codec = Dependencies.commons_codec val commons_io = Dependencies.commons_io val guicey = Dependencies.guicey @@ -337,7 +291,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // akka-http subproject // ------------------------------------------------------------------------------------------------------------------- - class AkkaHttpProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + class AkkaHttpProject(info: ProjectInfo) extends AkkaDefaultProject(info) { val jsr250 = Dependencies.jsr250 val javax_servlet30 = Dependencies.javax_servlet_30 val jetty = Dependencies.jetty @@ -371,13 +325,13 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { } } - class AkkaSampleRemoteProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) + class AkkaSampleRemoteProject(info: ProjectInfo) extends AkkaDefaultProject(info) - class AkkaSampleChatProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) + class AkkaSampleChatProject(info: ProjectInfo) extends AkkaDefaultProject(info) - class AkkaSampleFSMProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) + class AkkaSampleFSMProject(info: ProjectInfo) extends AkkaDefaultProject(info) - class AkkaSampleOsgiProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) with BNDPlugin { + class AkkaSampleOsgiProject(info: ProjectInfo) extends AkkaDefaultProject(info) with BNDPlugin { val osgiCore = Dependencies.osgi_core override protected def bndPrivatePackage = List("sample.osgi.*") override protected def bndBundleActivator = Some("sample.osgi.Activator") @@ -407,9 +361,9 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // Tutorials // ------------------------------------------------------------------------------------------------------------------- - class AkkaTutorialFirstProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) + class AkkaTutorialFirstProject(info: ProjectInfo) extends AkkaDefaultProject(info) - class AkkaTutorialSecondProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) + class AkkaTutorialSecondProject(info: ProjectInfo) extends AkkaDefaultProject(info) class AkkaTutorialsParentProject(info: ProjectInfo) extends ParentProject(info) { override def disableCrossPaths = true @@ -430,7 +384,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // akka-testkit subproject // ------------------------------------------------------------------------------------------------------------------- - class AkkaTestkitProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + class AkkaTestkitProject(info: ProjectInfo) extends AkkaDefaultProject(info) { val scalatest = Dependencies.scalatest } @@ -438,52 +392,26 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // akka-actor-tests subproject // ------------------------------------------------------------------------------------------------------------------- - class AkkaActorTestsProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + class AkkaActorTestsProject(info: ProjectInfo) extends AkkaDefaultProject(info) { // testing val junit = Dependencies.junit val scalatest = Dependencies.scalatest val multiverse_test = Dependencies.multiverse_test // StandardLatch } - + // ------------------------------------------------------------------------------------------------------------------- // akka-slf4j subproject // ------------------------------------------------------------------------------------------------------------------- - class AkkaSlf4jProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + class AkkaSlf4jProject(info: ProjectInfo) extends AkkaDefaultProject(info) { val slf4j = Dependencies.slf4j } // ------------------------------------------------------------------------------------------------------------------- - // Helpers + // Default project // ------------------------------------------------------------------------------------------------------------------- - def removeDupEntries(paths: PathFinder) = Path.lazyPathFinder { - val mapped = paths.get map { p => (p.relativePath, p) } - (Map() ++ mapped).values.toList - } - - def allArtifacts = { - Path.fromFile(buildScalaInstance.libraryJar) +++ - (removeDupEntries(runClasspath filter ClasspathUtilities.isArchive) +++ - ((outputPath ##) / defaultJarName) +++ - mainResources +++ - mainDependencies.scalaJars +++ - descendents(info.projectPath / "scripts", "run_akka.sh") +++ - descendents(info.projectPath / "scripts", "akka-init-script.sh") +++ - descendents(info.projectPath / "dist", "*.jar") +++ - descendents(info.projectPath / "deploy", "*.jar") +++ - descendents(path("lib") ##, "*.jar") +++ - descendents(configurationPath(Configurations.Compile) ##, "*.jar")) - .filter(jar => // remove redundant libs - !jar.toString.endsWith("stax-api-1.0.1.jar") || - !jar.toString.endsWith("scala-library-2.7.7.jar") - ) - } - - def akkaArtifacts = descendents(info.projectPath / "dist", "*-" + version + ".jar") - - // ------------------------------------------------------------ - class AkkaDefaultProject(info: ProjectInfo, val deployPath: Path) extends DefaultProject(info) with DeployProject with McPom { + class AkkaDefaultProject(info: ProjectInfo) extends DefaultProject(info) with McPom { override def disableCrossPaths = true @@ -515,27 +443,6 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { } } -trait DeployProject { self: BasicScalaProject => - // defines where the deployTask copies jars to - def deployPath: Path - - lazy val dist = deployTask(jarPath, packageDocsJar, packageSrcJar, deployPath, true, true, true) dependsOn( - `package`, packageDocs, packageSrc) describedAs("Deploying") - - def deployTask(jar: Path, docs: Path, src: Path, toDir: Path, - genJar: Boolean, genDocs: Boolean, genSource: Boolean) = task { - def gen(jar: Path, toDir: Path, flag: Boolean, msg: String): Option[String] = - if (flag) { - log.info(msg + " " + jar) - FileUtilities.copyFile(jar, toDir / jar.name, log) - } else None - - gen(jar, toDir, genJar, "Deploying bits") orElse - gen(docs, toDir, genDocs, "Deploying docs") orElse - gen(src, toDir, genSource, "Deploying sources") - } -} - trait OsgiProject extends BNDPlugin { self: DefaultProject => override def bndExportPackage = Seq("akka.*;version=%s".format(projectVersion.value)) } From 5068f0d48a3575d0f921d9d9ea46573a3c30a46b Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 27 Apr 2011 12:21:19 +0200 Subject: [PATCH 058/100] Removing blocking dequeues from MailboxConfig due to high risk and no gain --- .../akka/dispatch/MailboxConfigSpec.scala | 42 ++++++------------- .../dispatch/PriorityDispatcherSpec.scala | 4 +- .../ExecutorBasedEventDrivenDispatcher.scala | 26 +++++------- .../scala/akka/dispatch/MailboxHandling.scala | 38 ++++++----------- .../scala/akka/dispatch/MessageHandling.scala | 3 +- .../akka/dispatch/ThreadBasedDispatcher.scala | 6 +-- 6 files changed, 41 insertions(+), 78 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/MailboxConfigSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/MailboxConfigSpec.scala index 9ddbfdc332..15d123867e 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/MailboxConfigSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/MailboxConfigSpec.scala @@ -24,7 +24,7 @@ abstract class MailboxSpec extends name should { "create a !blockDequeue && unbounded mailbox" in { - val config = UnboundedMailbox(false) + val config = UnboundedMailbox() val q = factory(config) ensureInitialMailboxState(config, q) @@ -37,8 +37,8 @@ abstract class MailboxSpec extends f.await.resultOrException must be === Some(null) } - "create a !blockDequeue and bounded mailbox with 10 capacity and with push timeout" in { - val config = BoundedMailbox(false, 10, Duration(10,TimeUnit.MILLISECONDS)) + "create a bounded mailbox with 10 capacity and with push timeout" in { + val config = BoundedMailbox(10, Duration(10,TimeUnit.MILLISECONDS)) val q = factory(config) ensureInitialMailboxState(config, q) @@ -59,30 +59,16 @@ abstract class MailboxSpec extends } "dequeue what was enqueued properly for unbounded mailboxes" in { - testEnqueueDequeue(UnboundedMailbox(false)) + testEnqueueDequeue(UnboundedMailbox()) } "dequeue what was enqueued properly for bounded mailboxes" in { - testEnqueueDequeue(BoundedMailbox(false, 10000, Duration(-1, TimeUnit.MILLISECONDS))) + testEnqueueDequeue(BoundedMailbox(10000, Duration(-1, TimeUnit.MILLISECONDS))) } "dequeue what was enqueued properly for bounded mailboxes with pushTimeout" in { - testEnqueueDequeue(BoundedMailbox(false, 10000, Duration(100, TimeUnit.MILLISECONDS))) + testEnqueueDequeue(BoundedMailbox(10000, Duration(100, TimeUnit.MILLISECONDS))) } - - /** FIXME Adapt test so it works with the last dequeue - - "dequeue what was enqueued properly for unbounded mailboxes with blockDeque" in { - testEnqueueDequeue(UnboundedMailbox(true)) - } - - "dequeue what was enqueued properly for bounded mailboxes with blockDeque" in { - testEnqueueDequeue(BoundedMailbox(true, 1000, Duration(-1, TimeUnit.MILLISECONDS))) - } - - "dequeue what was enqueued properly for bounded mailboxes with blockDeque and pushTimeout" in { - testEnqueueDequeue(BoundedMailbox(true, 1000, Duration(100, TimeUnit.MILLISECONDS))) - }*/ } //CANDIDATE FOR TESTKIT @@ -111,8 +97,8 @@ abstract class MailboxSpec extends q match { case aQueue: BlockingQueue[_] => config match { - case BoundedMailbox(_,capacity,_) => aQueue.remainingCapacity must be === capacity - case UnboundedMailbox(_) => aQueue.remainingCapacity must be === Int.MaxValue + case BoundedMailbox(capacity,_) => aQueue.remainingCapacity must be === capacity + case UnboundedMailbox() => aQueue.remainingCapacity must be === Int.MaxValue } case _ => } @@ -165,10 +151,8 @@ abstract class MailboxSpec extends class DefaultMailboxSpec extends MailboxSpec { lazy val name = "The default mailbox implementation" def factory = { - case UnboundedMailbox(blockDequeue) => - new DefaultUnboundedMessageQueue(blockDequeue) - case BoundedMailbox(blocking, capacity, pushTimeOut) => - new DefaultBoundedMessageQueue(capacity, pushTimeOut, blocking) + case UnboundedMailbox() => new DefaultUnboundedMessageQueue() + case BoundedMailbox(capacity, pushTimeOut) => new DefaultBoundedMessageQueue(capacity, pushTimeOut) } } @@ -176,9 +160,7 @@ class PriorityMailboxSpec extends MailboxSpec { val comparator = PriorityGenerator(_.##) lazy val name = "The priority mailbox implementation" def factory = { - case UnboundedMailbox(blockDequeue) => - new UnboundedPriorityMessageQueue(blockDequeue, comparator) - case BoundedMailbox(blocking, capacity, pushTimeOut) => - new BoundedPriorityMessageQueue(capacity, pushTimeOut, blocking, comparator) + case UnboundedMailbox() => new UnboundedPriorityMessageQueue(comparator) + case BoundedMailbox(capacity, pushTimeOut) => new BoundedPriorityMessageQueue(capacity, pushTimeOut, comparator) } } \ No newline at end of file diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/PriorityDispatcherSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/PriorityDispatcherSpec.scala index f256715b8c..002267a6c7 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/PriorityDispatcherSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/PriorityDispatcherSpec.scala @@ -10,11 +10,11 @@ class PriorityDispatcherSpec extends WordSpec with MustMatchers { "A PriorityExecutorBasedEventDrivenDispatcher" must { "Order it's messages according to the specified comparator using an unbounded mailbox" in { - testOrdering(UnboundedMailbox(false)) + testOrdering(UnboundedMailbox()) } "Order it's messages according to the specified comparator using a bounded mailbox" in { - testOrdering(BoundedMailbox(false,1000)) + testOrdering(BoundedMailbox(1000)) } } diff --git a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala index 105028f693..494fa85f28 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala @@ -117,20 +117,14 @@ class ExecutorBasedEventDrivenDispatcher( def createMailbox(actorRef: ActorRef): AnyRef = mailboxType match { case b: UnboundedMailbox => - if (b.blocking) { - new DefaultUnboundedMessageQueue(true) with ExecutableMailbox { - final def dispatcher = ExecutorBasedEventDrivenDispatcher.this - } - } else { //If we have an unbounded, non-blocking mailbox, we can go lockless - new ConcurrentLinkedQueue[MessageInvocation] with MessageQueue with ExecutableMailbox { - final def dispatcher = ExecutorBasedEventDrivenDispatcher.this - final def enqueue(m: MessageInvocation) = this.add(m) - final def dequeue(): MessageInvocation = this.poll() - } + new ConcurrentLinkedQueue[MessageInvocation] with MessageQueue with ExecutableMailbox { + @inline final def dispatcher = ExecutorBasedEventDrivenDispatcher.this + @inline final def enqueue(m: MessageInvocation) = this.add(m) + @inline final def dequeue(): MessageInvocation = this.poll() } case b: BoundedMailbox => - new DefaultBoundedMessageQueue(b.capacity, b.pushTimeOut, b.blocking) with ExecutableMailbox { - final def dispatcher = ExecutorBasedEventDrivenDispatcher.this + new DefaultBoundedMessageQueue(b.capacity, b.pushTimeOut) with ExecutableMailbox { + @inline final def dispatcher = ExecutorBasedEventDrivenDispatcher.this } } @@ -294,13 +288,13 @@ trait PriorityMailbox { self: ExecutorBasedEventDrivenDispatcher => override def createMailbox(actorRef: ActorRef): AnyRef = self.mailboxType match { case b: UnboundedMailbox => - new UnboundedPriorityMessageQueue(b.blocking, comparator) with ExecutableMailbox { - final def dispatcher = self + new UnboundedPriorityMessageQueue(comparator) with ExecutableMailbox { + @inline final def dispatcher = self } case b: BoundedMailbox => - new BoundedPriorityMessageQueue(b.capacity, b.pushTimeOut, b.blocking, comparator) with ExecutableMailbox { - final def dispatcher = self + new BoundedPriorityMessageQueue(b.capacity, b.pushTimeOut, comparator) with ExecutableMailbox { + @inline final def dispatcher = self } } } diff --git a/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala b/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala index e0586a40a7..cacdefe95c 100644 --- a/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala +++ b/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala @@ -30,9 +30,8 @@ trait MessageQueue { */ sealed trait MailboxType -case class UnboundedMailbox(val blocking: Boolean = false) extends MailboxType +case class UnboundedMailbox() extends MailboxType case class BoundedMailbox( - val blocking: Boolean = false, val capacity: Int = { if (Dispatchers.MAILBOX_CAPACITY < 0) Int.MaxValue else Dispatchers.MAILBOX_CAPACITY }, val pushTimeOut: Duration = Dispatchers.MAILBOX_PUSH_TIME_OUT) extends MailboxType { if (capacity < 0) throw new IllegalArgumentException("The capacity for BoundedMailbox can not be negative") @@ -40,46 +39,35 @@ case class BoundedMailbox( } trait UnboundedMessageQueueSemantics extends MessageQueue { self: BlockingQueue[MessageInvocation] => - def blockDequeue: Boolean - - final def enqueue(handle: MessageInvocation) { - this add handle - } - - final def dequeue(): MessageInvocation = { - if (blockDequeue) this.take() - else this.poll() - } + @inline final def enqueue(handle: MessageInvocation): Unit = this add handle + @inline final def dequeue(): MessageInvocation = this.poll() } trait BoundedMessageQueueSemantics extends MessageQueue { self: BlockingQueue[MessageInvocation] => - def blockDequeue: Boolean def pushTimeOut: Duration final def enqueue(handle: MessageInvocation) { - if (pushTimeOut.length > 0 && pushTimeOut.toMillis > 0) { - if (!this.offer(handle, pushTimeOut.length, pushTimeOut.unit)) - throw new MessageQueueAppendFailedException("Couldn't enqueue message " + handle + " to " + toString) + if (pushTimeOut.length > 0) { + this.offer(handle, pushTimeOut.length, pushTimeOut.unit) || { + throw new MessageQueueAppendFailedException("Couldn't enqueue message " + handle + " to " + toString) } } else this put handle } - final def dequeue(): MessageInvocation = - if (blockDequeue) this.take() - else this.poll() + @inline final def dequeue(): MessageInvocation = this.poll() } -class DefaultUnboundedMessageQueue(val blockDequeue: Boolean) extends +class DefaultUnboundedMessageQueue extends LinkedBlockingQueue[MessageInvocation] with UnboundedMessageQueueSemantics -class DefaultBoundedMessageQueue(capacity: Int, val pushTimeOut: Duration, val blockDequeue: Boolean) extends +class DefaultBoundedMessageQueue(capacity: Int, val pushTimeOut: Duration) extends LinkedBlockingQueue[MessageInvocation](capacity) with BoundedMessageQueueSemantics -class UnboundedPriorityMessageQueue(val blockDequeue: Boolean, cmp: Comparator[MessageInvocation]) extends +class UnboundedPriorityMessageQueue(cmp: Comparator[MessageInvocation]) extends PriorityBlockingQueue[MessageInvocation](11, cmp) with UnboundedMessageQueueSemantics -class BoundedPriorityMessageQueue(capacity: Int, val pushTimeOut: Duration, val blockDequeue: Boolean, cmp: Comparator[MessageInvocation]) extends - BoundedBlockingQueue[MessageInvocation](capacity, new PriorityQueue[MessageInvocation](11, cmp)) with - BoundedMessageQueueSemantics +class BoundedPriorityMessageQueue(capacity: Int, val pushTimeOut: Duration, cmp: Comparator[MessageInvocation]) extends + BoundedBlockingQueue[MessageInvocation](capacity, new PriorityQueue[MessageInvocation](11, cmp)) with + BoundedMessageQueueSemantics diff --git a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala index 14348e9f85..9e53bb09ca 100644 --- a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala +++ b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala @@ -221,9 +221,8 @@ abstract class MessageDispatcherConfigurator { def mailboxType(config: Configuration): MailboxType = { val capacity = config.getInt("mailbox-capacity", Dispatchers.MAILBOX_CAPACITY) - // FIXME how do we read in isBlocking for mailbox? Now set to 'false'. if (capacity < 1) UnboundedMailbox() - else BoundedMailbox(false, capacity, Duration(config.getInt("mailbox-push-timeout-time", Dispatchers.MAILBOX_PUSH_TIME_OUT.toMillis.toInt), TIME_UNIT)) + else BoundedMailbox(capacity, Duration(config.getInt("mailbox-push-timeout-time", Dispatchers.MAILBOX_PUSH_TIME_OUT.toMillis.toInt), TIME_UNIT)) } def configureThreadPool(config: Configuration, createDispatcher: => (ThreadPoolConfig) => MessageDispatcher): ThreadPoolConfigDispatcherBuilder = { diff --git a/akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala index a8dfcf5860..9ed0ce8ef1 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala @@ -25,13 +25,13 @@ class ThreadBasedDispatcher(_actor: ActorRef, _mailboxType: MailboxType) private[akka] val owner = new AtomicReference[ActorRef](_actor) def this(actor: ActorRef) = - this(actor, UnboundedMailbox(true)) // For Java API + this(actor, UnboundedMailbox()) // For Java API def this(actor: ActorRef, capacity: Int) = - this(actor, BoundedMailbox(true, capacity)) //For Java API + this(actor, BoundedMailbox(capacity)) //For Java API def this(actor: ActorRef, capacity: Int, pushTimeOut: Duration) = //For Java API - this(actor, BoundedMailbox(true, capacity, pushTimeOut)) + this(actor, BoundedMailbox(capacity, pushTimeOut)) override def register(actorRef: ActorRef) = { val actor = owner.get() From 71a7a922738fa4691dd5ab0e30da2fbefbf233f7 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 27 Apr 2011 13:49:50 +0200 Subject: [PATCH 059/100] Removing the Client Managed Remote Actor sample from the docs and akka-sample-remote, fixing #804 --- akka-samples/akka-sample-remote/README | 34 +----------------- .../ClientManagedRemoteActorSample.scala | 35 ------------------- 2 files changed, 1 insertion(+), 68 deletions(-) delete mode 100644 akka-samples/akka-sample-remote/src/main/scala/ClientManagedRemoteActorSample.scala diff --git a/akka-samples/akka-sample-remote/README b/akka-samples/akka-sample-remote/README index b20d1c7f4e..f19386e1e3 100644 --- a/akka-samples/akka-sample-remote/README +++ b/akka-samples/akka-sample-remote/README @@ -1,10 +1,5 @@ --------------------------------------------------------- == Akka Remote Sample Application == - -This sample has two different samples: - - Server Managed Remote Actors Sample - - Client Managed Remote Actors Sample - --------------------------------------------------------- = Server Managed Remote Actors Sample = @@ -29,31 +24,4 @@ To run the sample: Now you could test client reconnect by killing the console running the ServerManagedRemoteActorClient and start it up again. See the client reconnect take place in the REPL shell. -That’s it. Have fun. - ---------------------------------------------------------- -= Client Managed Remote Actors Sample = - -To run the sample: - -1. Fire up two shells. For each of them: - - Step down into to the root of the Akka distribution. - - Set 'export AKKA_HOME=. - - Run 'sbt' - - Run 'update' followed by 'compile' if you have not done that before. - - Run 'project akka-sample-remote' - - Run 'console' to start up a REPL (interpreter). -2. In the first REPL you get execute: - - scala> import sample.remote._ - - scala> ClientManagedRemoteActorServer.run - This starts up the RemoteNode and registers the remote actor -3. In the second REPL you get execute: - - scala> import sample.remote._ - - scala> ClientManagedRemoteActorClient.run -4. See the actor conversation. -5. Run it again to see full speed after first initialization. - -Now you could test client reconnect by killing the console running the ClientManagedRemoteActorClient and start it up again. See the client reconnect take place in the REPL shell. - -That’s it. Have fun. - +That’s it. Have fun. \ No newline at end of file diff --git a/akka-samples/akka-sample-remote/src/main/scala/ClientManagedRemoteActorSample.scala b/akka-samples/akka-sample-remote/src/main/scala/ClientManagedRemoteActorSample.scala deleted file mode 100644 index 42450b0b39..0000000000 --- a/akka-samples/akka-sample-remote/src/main/scala/ClientManagedRemoteActorSample.scala +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (C) 2009-2011 Scalable Solutions AB - */ - -package sample.remote - -import akka.actor.Actor._ -import akka.actor. {ActorRegistry, Actor} -import Actor.remote - -class RemoteHelloWorldActor extends Actor { - def receive = { - case "Hello" => - self.reply("World") - } -} - -object ClientManagedRemoteActorServer { - def run = { - remote.start("localhost", 2552) - } - - def main(args: Array[String]) = run -} - -object ClientManagedRemoteActorClient { - - def run = { - val actor = remote.actorOf[RemoteHelloWorldActor]("localhost",2552).start() - val result = actor !! "Hello" - } - - def main(args: Array[String]) = run -} - From 82a11110d3416f13b8c59c648934063eb1e346fb Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 27 Apr 2011 15:34:42 +0200 Subject: [PATCH 060/100] Removing awaitValue and valueWithin, and adding await(atMost: Duration) --- .../test/scala/akka/dispatch/FutureSpec.scala | 20 ------ .../src/main/scala/akka/dispatch/Future.scala | 66 ++++++------------- 2 files changed, 21 insertions(+), 65 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala index e12294a70d..bc60f7762f 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -277,26 +277,6 @@ class FutureSpec extends JUnitSuite { Futures.reduce(List[Future[Int]]())(_ + _).await.resultOrException } - @Test def resultWithinShouldNotThrowExceptions { - val latch = new StandardLatch - - val actors = (1 to 10).toList map { _ => - actorOf(new Actor { - def receive = { case (add: Int, wait: Boolean, latch: StandardLatch) => if (wait) latch.await; self reply_? add } - }).start() - } - - def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!![Int]((idx, idx >= 5, latch)) } - val result = for(f <- futures) yield f.valueWithin(2, TimeUnit.SECONDS) - latch.open - val done = result collect { case Some(Right(x)) => x } - val undone = result collect { case None => None } - val errors = result collect { case Some(Left(t)) => t } - assert(done.size === 5) - assert(undone.size === 5) - assert(errors.size === 0) - } - @Test def receiveShouldExecuteOnComplete { val latch = new StandardLatch val actor = actorOf[TestActor].start() diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 863d9c1283..a2d5a63697 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -7,13 +7,13 @@ package akka.dispatch import akka.AkkaException import akka.event.EventHandler import akka.actor.{Actor, Channel} -import akka.routing.Dispatcher +import akka.util.Duration import akka.japi.{ Procedure, Function => JFunc } import java.util.concurrent.locks.ReentrantLock import java.util.concurrent. {ConcurrentLinkedQueue, TimeUnit, Callable} import java.util.concurrent.TimeUnit.{NANOSECONDS => NANOS, MILLISECONDS => MILLIS} -import java.util.concurrent.atomic. {AtomicBoolean, AtomicInteger} +import java.util.concurrent.atomic. {AtomicBoolean} import java.lang.{Iterable => JIterable} import java.util.{LinkedList => JLinkedList} import annotation.tailrec @@ -298,11 +298,20 @@ sealed trait Future[+T] { */ def await : Future[T] + /** + * Blocks the current thread until the Future has been completed or the + * timeout has expired. The timeout will be the least value of 'atMost' and the timeout + * supplied at the constructuion of this Future. + * In the case of the timeout expiring a FutureTimeoutException will be thrown. + */ + def await(atMost: Duration) : Future[T] + /** * Blocks the current thread until the Future has been completed. Use * caution with this method as it ignores the timeout and will block * indefinitely if the Future is never completed. */ + @deprecated("Will be removed after 1.1, it's dangerous and can cause deadlocks, agony and insanity.") def awaitBlocking : Future[T] /** @@ -340,24 +349,6 @@ sealed trait Future[+T] { else None } - /** - * Waits for the completion of this Future, then returns the completed value. - * If the Future's timeout expires while waiting a FutureTimeoutException - * will be thrown. - * - * Equivalent to calling future.await.value. - */ - def awaitValue: Option[Either[Throwable, T]] - - /** - * Returns the result of the Future if one is available within the specified - * time, if the time left on the future is less than the specified time, the - * time left on the future will be used instead of the specified time. - * returns None if no result, Some(Right(t)) if a result, or - * Some(Left(error)) if there was an exception - */ - def valueWithin(time: Long, unit: TimeUnit): Option[Either[Throwable, T]] - /** * Returns the contained exception of this Future if it exists. */ @@ -620,39 +611,25 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com * Must be called inside _lock.lock<->_lock.unlock */ @tailrec - private def awaitUnsafe(wait: Long): Boolean = { - if (_value.isEmpty && wait > 0) { + private def awaitUnsafe(waitTimeNanos: Long): Boolean = { + if (_value.isEmpty && waitTimeNanos > 0) { val start = currentTimeInNanos - val remaining = try { - _signal.awaitNanos(wait) + val remainingNanos = try { + _signal.awaitNanos(waitTimeNanos) } catch { case e: InterruptedException => - wait - (currentTimeInNanos - start) + waitTimeNanos - (currentTimeInNanos - start) } - awaitUnsafe(remaining) + awaitUnsafe(remainingNanos) } else { _value.isDefined } } - def awaitValue: Option[Either[Throwable, T]] = { + def await(atMost: Duration) = { _lock.lock - try { - awaitUnsafe(timeLeft()) - _value - } finally { - _lock.unlock - } - } - - def valueWithin(time: Long, unit: TimeUnit): Option[Either[Throwable, T]] = { - _lock.lock - try { - awaitUnsafe(unit toNanos time min timeLeft()) - _value - } finally { - _lock.unlock - } + if (try { awaitUnsafe(atMost.toNanos min timeLeft()) } finally { _lock.unlock }) this + else throw new FutureTimeoutException("Futures timed out after [" + NANOS.toMillis(timeoutInNanos) + "] milliseconds") } def await = { @@ -741,8 +718,7 @@ sealed class AlreadyCompletedFuture[T](suppliedValue: Either[Throwable, T]) exte def complete(value: Either[Throwable, T]): CompletableFuture[T] = this def onComplete(func: Future[T] => Unit): Future[T] = { func(this); this } - def awaitValue: Option[Either[Throwable, T]] = value - def valueWithin(time: Long, unit: TimeUnit): Option[Either[Throwable, T]] = value + def await(atMost: Duration): Future[T] = this def await : Future[T] = this def awaitBlocking : Future[T] = this def isExpired: Boolean = true From 2da27123ac417a730f0042232ec99fd442e19a1b Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 27 Apr 2011 16:46:06 +0200 Subject: [PATCH 061/100] Renaming a test --- .../src/test/scala/akka/dispatch/MailboxConfigSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/MailboxConfigSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/MailboxConfigSpec.scala index 15d123867e..0da861350d 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/MailboxConfigSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/MailboxConfigSpec.scala @@ -23,7 +23,7 @@ abstract class MailboxSpec extends def factory: MailboxType => MessageQueue name should { - "create a !blockDequeue && unbounded mailbox" in { + "create an unbounded mailbox" in { val config = UnboundedMailbox() val q = factory(config) ensureInitialMailboxState(config, q) From 800840719f54e67ce25e59ff7fc691ff6055e478 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 27 Apr 2011 16:52:19 +0200 Subject: [PATCH 062/100] Making it impossible to complete a future after it`s expired, and not run onComplete callbacks if it hasn`t been completed before expiry, fixing ticket #811 --- .../src/test/scala/akka/dispatch/FutureSpec.scala | 14 ++++++++++++++ .../src/main/scala/akka/dispatch/Future.scala | 8 +++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala index bc60f7762f..7ec397025e 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -343,6 +343,20 @@ class FutureSpec extends JUnitSuite { } } + @Test def shouldNotAddOrRunCallbacksAfterFailureToBeCompletedBeforeExpiry { + val latch = new StandardLatch + val f = new DefaultCompletableFuture[Int](0) + Thread.sleep(25) + f.onComplete( _ => latch.open ) //Shouldn't throw any exception here + + assert(f.isExpired) //Should be expired + + f.complete(Right(1)) //Shouldn't complete the Future since it is expired + + assert(f.value.isEmpty) //Shouldn't be completed + assert(!latch.isOpen) //Shouldn't run the listener + } + @Test def lesslessIsMore { import akka.actor.Actor.spawn val dataflowVar, dataflowVar2 = new DefaultCompletableFuture[Int](Long.MaxValue) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index a2d5a63697..1f2c8d63e4 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -664,7 +664,7 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com def complete(value: Either[Throwable, T]): DefaultCompletableFuture[T] = { _lock.lock val notifyTheseListeners = try { - if (_value.isEmpty) { + if (_value.isEmpty && !isExpired) { //Only complete if we aren't expired _value = Some(value) val existingListeners = _listeners _listeners = Nil @@ -685,8 +685,10 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com _lock.lock val notifyNow = try { if (_value.isEmpty) { - _listeners ::= func - false + if(!isExpired) { //Only add the listener if the future isn't expired + _listeners ::= func + false + } else false //Will never run the callback since the future is expired } else true } finally { _lock.unlock From 7224abd532ea6179dc509175052bbde0659c307f Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 27 Apr 2011 17:35:50 +0200 Subject: [PATCH 063/100] Fixing the docs for the Actor Pool with regards to the factory vs instance question and closing ticket #744 --- akka-docs/pending/routing-scala.rst | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/akka-docs/pending/routing-scala.rst b/akka-docs/pending/routing-scala.rst index 4cb825219e..3885290ccb 100644 --- a/akka-docs/pending/routing-scala.rst +++ b/akka-docs/pending/routing-scala.rst @@ -219,18 +219,16 @@ Examples with SmallestMailboxSelector with BasicNoBackoffFilter { - def factory = actorOf(new Actor {def receive = {case n:Int => - Thread.sleep(n) - counter.incrementAndGet - latch.countDown()}}) - + def receive = _route def lowerBound = 2 def upperBound = 4 def rampupRate = 0.1 def partialFill = true def selectionCount = 1 - def instance = factory - def receive = _route + def instance = actorOf(new Actor {def receive = {case n:Int => + Thread.sleep(n) + counter.incrementAndGet + latch.countDown()}}) } .. code-block:: scala @@ -243,11 +241,7 @@ Examples with RunningMeanBackoff with BasicRampup { - - def factory = actorOf(new Actor {def receive = {case n:Int => - Thread.sleep(n) - latch.countDown()}}) - + def receive = _route def lowerBound = 1 def upperBound = 5 def pressureThreshold = 1 @@ -256,8 +250,9 @@ Examples def rampupRate = 0.1 def backoffRate = 0.50 def backoffThreshold = 0.50 - def instance = factory - def receive = _route + def instance = actorOf(new Actor {def receive = {case n:Int => + Thread.sleep(n) + latch.countDown()}}) } Taken from the unit test `spec `_. From 9fadbc4980398dedfa83991823c697568dda69c1 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Wed, 27 Apr 2011 20:12:23 +0200 Subject: [PATCH 064/100] Moved transactors from pending --- akka-docs/java/index.rst | 1 + akka-docs/{pending/transactors-java.rst => java/transactors.rst} | 0 akka-docs/scala/index.rst | 1 + .../{pending/transactors-scala.rst => scala/transactors.rst} | 0 4 files changed, 2 insertions(+) rename akka-docs/{pending/transactors-java.rst => java/transactors.rst} (100%) rename akka-docs/{pending/transactors-scala.rst => scala/transactors.rst} (100%) diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst index d4fed805d4..aeef671960 100644 --- a/akka-docs/java/index.rst +++ b/akka-docs/java/index.rst @@ -8,4 +8,5 @@ Java API typed-actors actor-registry stm + transactors dispatchers diff --git a/akka-docs/pending/transactors-java.rst b/akka-docs/java/transactors.rst similarity index 100% rename from akka-docs/pending/transactors-java.rst rename to akka-docs/java/transactors.rst diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index 8897cfc17b..71c399709d 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -9,6 +9,7 @@ Scala API actor-registry agents stm + transactors dispatchers fsm testing diff --git a/akka-docs/pending/transactors-scala.rst b/akka-docs/scala/transactors.rst similarity index 100% rename from akka-docs/pending/transactors-scala.rst rename to akka-docs/scala/transactors.rst From 43ebe61ab2f13a7a476890fd0e94cb9159d1b89a Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Wed, 27 Apr 2011 21:30:35 +0200 Subject: [PATCH 065/100] Reviewed and improved transactors doc --- akka-docs/java/transactors.rst | 76 +++++++++++++++++---------------- akka-docs/scala/transactors.rst | 64 ++++++++++++++------------- 2 files changed, 74 insertions(+), 66 deletions(-) diff --git a/akka-docs/java/transactors.rst b/akka-docs/java/transactors.rst index 9cc4d522f4..2d17bd2fa6 100644 --- a/akka-docs/java/transactors.rst +++ b/akka-docs/java/transactors.rst @@ -1,10 +1,14 @@ -**Transactors (Java)** -============================================================ +Transactors (Java) +================== + +.. sidebar:: Contents + + .. contents:: :local: Module stability: **SOLID** Why Transactors? -================ +---------------- Actors are excellent for solving problems where you have many independent processes that can work in isolation and only interact with other Actors through message passing. This model fits many problems. But the actor model is unfortunately a terrible model for implementing truly shared state. E.g. when you need to have consensus and a stable view of state across many components. The classic example is the bank account where clients can deposit and withdraw, in which each operation needs to be atomic. For detailed discussion on the topic see `this JavaOne presentation `_. @@ -15,21 +19,21 @@ Akka's Transactors combine Actors and STM to provide the best of the Actor model If you need Durability then you should not use one of the in-memory data structures but one of the persistent ones. Generally, the STM is not needed very often when working with Akka. Some use-cases (that we can think of) are: -# When you really need composable message flows across many actors updating their **internal local** state but need them to do that atomically in one big transaction. Might not often, but when you do need this then you are screwed without it. -# When you want to share a datastructure across actors. -# When you need to use the persistence modules. + +- When you really need composable message flows across many actors updating their **internal local** state but need them to do that atomically in one big transaction. Might not often, but when you do need this then you are screwed without it. +- When you want to share a datastructure across actors. +- When you need to use the persistence modules. Actors and STM --------------- +^^^^^^^^^^^^^^ You can combine Actors and STM in several ways. An Actor may use STM internally so that particular changes are guaranteed to be atomic. Actors may also share transactional datastructures as the STM provides safe shared state across threads. It's also possible to coordinate transactions across Actors or threads so that either the transactions in a set all commit successfully or they all fail. This is the focus of Transactors and the explicit support for coordinated transactions in this section. ----- Coordinated transactions -======================== +------------------------ Akka provides an explicit mechanism for coordinating transactions across actors. Under the hood it uses a ``CountDownCommitBarrier``, similar to a CountDownLatch. @@ -40,9 +44,11 @@ Here is an example of coordinating two simple counter UntypedActors so that they import akka.actor.ActorRef; public class Increment { - private ActorRef friend = null; + private final ActorRef friend; - public Increment() {} + public Increment() { + this.friend = null; + } public Increment(ActorRef friend) { this.friend = friend; @@ -59,9 +65,7 @@ Here is an example of coordinating two simple counter UntypedActors so that they .. code-block:: java - import akka.actor.ActorRef; import akka.actor.UntypedActor; - import static akka.actor.Actors.*; import akka.stm.Ref; import akka.transactor.Atomically; import akka.transactor.Coordinated; @@ -88,11 +92,8 @@ Here is an example of coordinating two simple counter UntypedActors so that they } }); } - } else if (incoming instanceof String) { - String message = (String) incoming; - if (message.equals("GetCount")) { - getContext().replyUnsafe(count.get()); - } + } else if (incoming.equals("GetCount")) { + getContext().replyUnsafe(count.get()); } } } @@ -104,7 +105,7 @@ Here is an example of coordinating two simple counter UntypedActors so that they counter1.sendOneWay(new Coordinated(new Increment(counter2))); -To start a new coordinated transaction set that you will also participate in, just create a ``Coordinated`` object: +To start a new coordinated transaction that you will also participate in, just create a ``Coordinated`` object: .. code-block:: java @@ -116,7 +117,7 @@ To start a coordinated transaction that you won't participate in yourself you ca actor.sendOneWay(new Coordinated(new Message())); -To include another actor in the same coordinated transaction set that you've created or received, use the ``coordinate`` method on that object. This will increment the number of parties involved by one and create a new ``Coordinated`` object to be sent. +To include another actor in the same coordinated transaction that you've created or received, use the ``coordinate`` method on that object. This will increment the number of parties involved by one and create a new ``Coordinated`` object to be sent. .. code-block:: java @@ -134,10 +135,9 @@ To enter the coordinated transaction use the atomic method of the coordinated ob The coordinated transaction will wait for the other transactions before committing. If any of the coordinated transactions fail then they all fail. ----- UntypedTransactor -================= +----------------- UntypedTransactors are untyped actors that provide a general pattern for coordinating transactions, using the explicit coordination described above. @@ -146,10 +146,12 @@ Here's an example of a simple untyped transactor that will join a coordinated tr .. code-block:: java import akka.transactor.UntypedTransactor; + import akka.stm.Ref; public class Counter extends UntypedTransactor { Ref count = new Ref(0); + @Override public void atomically(Object message) { if (message instanceof Increment) { count.set(count.get() + 1); @@ -174,7 +176,8 @@ Example of coordinating an increment, similar to the explicitly coordinated exam public class Counter extends UntypedTransactor { Ref count = new Ref(0); - @Override public Set coordinate(Object message) { + @Override + public Set coordinate(Object message) { if (message instanceof Increment) { Increment increment = (Increment) message; if (increment.hasFriend()) @@ -183,6 +186,7 @@ Example of coordinating an increment, similar to the explicitly coordinated exam return nobody(); } + @Override public void atomically(Object message) { if (message instanceof Increment) { count.set(count.get() + 1); @@ -190,14 +194,13 @@ Example of coordinating an increment, similar to the explicitly coordinated exam } } -To execute directly before or after the coordinated transaction, override the ``before`` and ``after`` methods. These methods also expect partial functions like the receive method. They do not execute within the transaction. +To execute directly before or after the coordinated transaction, override the ``before`` and ``after`` methods. They do not execute within the transaction. To completely bypass coordinated transactions override the ``normally`` method. Any message matched by ``normally`` will not be matched by the other methods, and will not be involved in coordinated transactions. In this method you can implement normal actor behavior, or use the normal STM atomic for local transactions. ----- Coordinating Typed Actors -========================= +------------------------- It's also possible to use coordinated transactions with typed actors. You can explicitly pass around ``Coordinated`` objects, or use built-in support with the ``@Coordinated`` annotation and the ``Coordination.coordinate`` method. @@ -249,17 +252,18 @@ Here's an example of using ``@Coordinated`` with a TypedActor to coordinate incr } } -``_ -Counter counter1 = (Counter) TypedActor.newInstance(Counter.class, CounterImpl.class); -Counter counter2 = (Counter) TypedActor.newInstance(Counter.class, CounterImpl.class); +.. code-block:: java -Coordination.coordinate(true, new Atomically() { + Counter counter1 = (Counter) TypedActor.newInstance(Counter.class, CounterImpl.class); + Counter counter2 = (Counter) TypedActor.newInstance(Counter.class, CounterImpl.class); + + Coordination.coordinate(true, new Atomically() { public void atomically() { - counter1.increment(); - counter2.increment(); + counter1.increment(); + counter2.increment(); } -}); + }); + + TypedActor.stop(counter1); + TypedActor.stop(counter2); -TypedActor.stop(counter1); -TypedActor.stop(counter2); -``_ diff --git a/akka-docs/scala/transactors.rst b/akka-docs/scala/transactors.rst index 6ee4126f0a..da26b4b527 100644 --- a/akka-docs/scala/transactors.rst +++ b/akka-docs/scala/transactors.rst @@ -1,10 +1,14 @@ -**Transactors (Scala)** -============================================================= +Transactors (Scala) +=================== + +.. sidebar:: Contents + + .. contents:: :local: Module stability: **SOLID** Why Transactors? -================ +---------------- Actors are excellent for solving problems where you have many independent processes that can work in isolation and only interact with other Actors through message passing. This model fits many problems. But the actor model is unfortunately a terrible model for implementing truly shared state. E.g. when you need to have consensus and a stable view of state across many components. The classic example is the bank account where clients can deposit and withdraw, in which each operation needs to be atomic. For detailed discussion on the topic see `this JavaOne presentation `_. @@ -15,21 +19,21 @@ Akka's Transactors combine Actors and STM to provide the best of the Actor model If you need Durability then you should not use one of the in-memory data structures but one of the persistent ones. Generally, the STM is not needed very often when working with Akka. Some use-cases (that we can think of) are: -# When you really need composable message flows across many actors updating their **internal local** state but need them to do that atomically in one big transaction. Might not often, but when you do need this then you are screwed without it. -# When you want to share a datastructure across actors. -# When you need to use the persistence modules. + +- When you really need composable message flows across many actors updating their **internal local** state but need them to do that atomically in one big transaction. Might not often, but when you do need this then you are screwed without it. +- When you want to share a datastructure across actors. +- When you need to use the persistence modules. Actors and STM --------------- +^^^^^^^^^^^^^^ You can combine Actors and STM in several ways. An Actor may use STM internally so that particular changes are guaranteed to be atomic. Actors may also share transactional datastructures as the STM provides safe shared state across threads. It's also possible to coordinate transactions across Actors or threads so that either the transactions in a set all commit successfully or they all fail. This is the focus of Transactors and the explicit support for coordinated transactions in this section. ----- Coordinated transactions -======================== +------------------------ Akka provides an explicit mechanism for coordinating transactions across Actors. Under the hood it uses a ``CountDownCommitBarrier``, similar to a CountDownLatch. @@ -70,7 +74,7 @@ Here is an example of coordinating two simple counter Actors so that they both i counter1.stop() counter2.stop() -To start a new coordinated transaction set that you will also participate in, just create a ``Coordinated`` object: +To start a new coordinated transaction that you will also participate in, just create a ``Coordinated`` object: .. code-block:: scala @@ -90,7 +94,7 @@ To receive a coordinated message in an actor simply match it in a case statement case coordinated @ Coordinated(Message) => ... } -To include another actor in the same coordinated transaction set that you've created or received, use the apply method on that object. This will increment the number of parties involved by one and create a new ``Coordinated`` object to be sent. +To include another actor in the same coordinated transaction that you've created or received, use the apply method on that object. This will increment the number of parties involved by one and create a new ``Coordinated`` object to be sent. .. code-block:: scala @@ -106,10 +110,9 @@ To enter the coordinated transaction use the atomic method of the coordinated ob The coordinated transaction will wait for the other transactions before committing. If any of the coordinated transactions fail then they all fail. ----- Transactor -========== +---------- Transactors are actors that provide a general pattern for coordinating transactions, using the explicit coordination described above. @@ -125,7 +128,7 @@ Here's an example of a simple transactor that will join a coordinated transactio class Counter extends Transactor { val count = Ref(0) - def atomically = { + override def atomically = { case Increment => count alter (_ + 1) } } @@ -140,6 +143,7 @@ Example of coordinating an increment: import akka.transactor.Transactor import akka.stm.Ref + import akka.actor.ActorRef case object Increment @@ -150,7 +154,7 @@ Example of coordinating an increment: case Increment => include(friend) } - def atomically = { + override def atomically = { case Increment => count alter (_ + 1) } } @@ -176,10 +180,9 @@ To execute directly before or after the coordinated transaction, override the `` To completely bypass coordinated transactions override the ``normally`` method. Any message matched by ``normally`` will not be matched by the other methods, and will not be involved in coordinated transactions. In this method you can implement normal actor behavior, or use the normal STM atomic for local transactions. ----- Coordinating Typed Actors -========================= +------------------------- It's also possible to use coordinated transactions with typed actors. You can explicitly pass around ``Coordinated`` objects, or use built-in support with the ``@Coordinated`` annotation and the ``Coordination.coordinate`` method. @@ -188,7 +191,7 @@ To specify a method should use coordinated transactions add the ``@Coordinated`` .. code-block:: scala trait Counter { - @Coordinated def increment: Unit + @Coordinated def increment() def get: Int } @@ -197,8 +200,8 @@ To coordinate transactions use a ``coordinate`` block: .. code-block:: scala coordinate { - counter1.increment - counter2.increment + counter1.increment() + counter2.increment() } Here's an example of using ``@Coordinated`` with a TypedActor to coordinate increments. @@ -211,13 +214,13 @@ Here's an example of using ``@Coordinated`` with a TypedActor to coordinate incr import akka.transactor.Coordination._ trait Counter { - @Coordinated def increment: Unit + @Coordinated def increment() def get: Int } class CounterImpl extends TypedActor with Counter { val ref = Ref(0) - def increment = ref alter (_ + 1) + def increment() { ref alter (_ + 1) } def get = ref.get } @@ -227,8 +230,8 @@ Here's an example of using ``@Coordinated`` with a TypedActor to coordinate incr val counter2 = TypedActor.newInstance(classOf[Counter], classOf[CounterImpl]) coordinate { - counter1.increment - counter2.increment + counter1.increment() + counter2.increment() } TypedActor.stop(counter1) @@ -236,9 +239,10 @@ Here's an example of using ``@Coordinated`` with a TypedActor to coordinate incr The ``coordinate`` block will wait for the transactions to complete. If you do not want to wait then you can specify this explicitly: -``_ -coordinate(wait = false) { - counter1.increment - counter2.increment -} -``_ +.. code-block:: scala + + coordinate(wait = false) { + counter1.increment() + counter2.increment() + } + From 43fc3bf463a23ef7d942de9b57c1658d49e07db2 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Wed, 27 Apr 2011 19:39:15 -0600 Subject: [PATCH 066/100] Add failing test for Ticket #812 --- .../src/test/scala/akka/dispatch/FutureSpec.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala index 7ec397025e..d848eede53 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -375,4 +375,14 @@ class FutureSpec extends JUnitSuite { assert(dataflowVar2() === 5) assert(dataflowVar.get === 5) } + + @Test def ticket812FutureDispatchCleanup { + val dispatcher = implicitly[MessageDispatcher] + assert(dispatcher.futureQueueSize === 0) + val future = Future({Thread.sleep(100);"Done"}, 10) + intercept[FutureTimeoutException] { future.await } + assert(dispatcher.futureQueueSize === 1) + Thread.sleep(200) + assert(dispatcher.futureQueueSize === 0) + } } From 485013a353d952ddeb4fb294a6929a2ab53c33f0 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Wed, 27 Apr 2011 20:45:39 -0600 Subject: [PATCH 067/100] Dispatcher executed Future will be cleaned up even after expiring --- .../test/scala/akka/dispatch/FutureSpec.scala | 14 ++-- .../ExecutorBasedEventDrivenDispatcher.scala | 2 +- .../src/main/scala/akka/dispatch/Future.scala | 7 +- .../scala/akka/dispatch/MessageHandling.scala | 71 ++++++++++--------- .../testkit/CallingThreadDispatcher.scala | 2 +- 5 files changed, 49 insertions(+), 47 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala index d848eede53..1f7dae9270 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -324,7 +324,7 @@ class FutureSpec extends JUnitSuite { assert(f3.resultOrException === Some("SUCCESS")) // make sure all futures are completed in dispatcher - assert(Dispatchers.defaultGlobalDispatcher.futureQueueSize === 0) + assert(Dispatchers.defaultGlobalDispatcher.pendingFutures === 0) } @Test def shouldBlockUntilResult { @@ -341,6 +341,10 @@ class FutureSpec extends JUnitSuite { intercept[FutureTimeoutException] { f3() } + Thread.sleep(100) + + // make sure all futures are completed in dispatcher + assert(Dispatchers.defaultGlobalDispatcher.pendingFutures === 0) } @Test def shouldNotAddOrRunCallbacksAfterFailureToBeCompletedBeforeExpiry { @@ -378,11 +382,11 @@ class FutureSpec extends JUnitSuite { @Test def ticket812FutureDispatchCleanup { val dispatcher = implicitly[MessageDispatcher] - assert(dispatcher.futureQueueSize === 0) + assert(dispatcher.pendingFutures === 0) val future = Future({Thread.sleep(100);"Done"}, 10) intercept[FutureTimeoutException] { future.await } - assert(dispatcher.futureQueueSize === 1) - Thread.sleep(200) - assert(dispatcher.futureQueueSize === 0) + assert(dispatcher.pendingFutures === 1) + Thread.sleep(100) + assert(dispatcher.pendingFutures === 0) } } diff --git a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala index 494fa85f28..dca2f2f822 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala @@ -99,7 +99,7 @@ class ExecutorBasedEventDrivenDispatcher( registerForExecution(mbox) } - private[akka] def executeFuture(invocation: FutureInvocation): Unit = if (active.isOn) { + private[akka] def executeFuture(invocation: FutureInvocation[_]): Unit = if (active.isOn) { try executorService.get() execute invocation catch { case e: RejectedExecutionException => diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 1f2c8d63e4..72cab081a2 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -232,11 +232,8 @@ object Future { * This method constructs and returns a Future that will eventually hold the result of the execution of the supplied body * The execution is performed by the specified Dispatcher. */ - def apply[T](body: => T, timeout: Long = Actor.TIMEOUT)(implicit dispatcher: MessageDispatcher): Future[T] = { - val f = new DefaultCompletableFuture[T](timeout) - dispatcher.dispatchFuture(FutureInvocation(f.asInstanceOf[CompletableFuture[Any]], () => body)) - f - } + def apply[T](body: => T, timeout: Long = Actor.TIMEOUT)(implicit dispatcher: MessageDispatcher): Future[T] = + dispatcher.dispatchFuture(() => body, timeout) /** * Construct a completable channel diff --git a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala index 9e53bb09ca..8261a0f485 100644 --- a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala +++ b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala @@ -5,6 +5,7 @@ package akka.dispatch import java.util.concurrent._ +import java.util.concurrent.atomic.AtomicLong import akka.event.EventHandler import akka.config.Configuration import akka.config.Config.TIME_UNIT @@ -29,16 +30,18 @@ final case class MessageInvocation(val receiver: ActorRef, } } -final case class FutureInvocation(future: CompletableFuture[Any], function: () => Any) extends Runnable { - val uuid = akka.actor.newUuid - - def run = future complete (try { - Right(function.apply) - } catch { - case e => - EventHandler.error(e, this, e.getMessage) - Left(e) - }) +final case class FutureInvocation[T](future: CompletableFuture[T], function: () => T, cleanup: () => Unit) extends Runnable { + def run = { + future complete (try { + Right(function()) + } catch { + case e => + EventHandler.error(e, this, e.getMessage) + Left(e) + } finally { + cleanup() + }) + } } object MessageDispatcher { @@ -56,7 +59,7 @@ trait MessageDispatcher { import MessageDispatcher._ protected val uuids = new ConcurrentSkipListSet[Uuid] - protected val futures = new ConcurrentSkipListSet[Uuid] + protected val futures = new AtomicLong(0L) protected val guard = new ReentrantGuard protected val active = new Switch(false) @@ -83,27 +86,25 @@ trait MessageDispatcher { private[akka] final def dispatchMessage(invocation: MessageInvocation): Unit = dispatch(invocation) - private[akka] final def dispatchFuture(invocation: FutureInvocation): Unit = { - guard withGuard { - futures add invocation.uuid - if (active.isOff) { active.switchOn { start } } - } - invocation.future.onComplete { f => - guard withGuard { - futures remove invocation.uuid - if (futures.isEmpty && uuids.isEmpty) { - shutdownSchedule match { - case UNSCHEDULED => - shutdownSchedule = SCHEDULED - Scheduler.scheduleOnce(shutdownAction, timeoutMs, TimeUnit.MILLISECONDS) - case SCHEDULED => - shutdownSchedule = RESCHEDULED - case RESCHEDULED => //Already marked for reschedule - } - } + private[akka] final def dispatchFuture[T](block: () => T, timeout: Long): Future[T] = { + futures.getAndIncrement() + val future = new DefaultCompletableFuture[T](timeout) + if (active.isOff) { active.switchOn { start } } + executeFuture(FutureInvocation[T](future, block, futureCleanup)) + future + } + + private val futureCleanup: () => Unit = { () => + if (futures.decrementAndGet() == 0 && uuids.isEmpty) { + shutdownSchedule match { + case UNSCHEDULED => + shutdownSchedule = SCHEDULED + Scheduler.scheduleOnce(shutdownAction, timeoutMs, TimeUnit.MILLISECONDS) + case SCHEDULED => + shutdownSchedule = RESCHEDULED + case RESCHEDULED => //Already marked for reschedule } } - executeFuture(invocation) } private[akka] def register(actorRef: ActorRef) { @@ -121,7 +122,7 @@ trait MessageDispatcher { private[akka] def unregister(actorRef: ActorRef) = { if (uuids remove actorRef.uuid) { actorRef.mailbox = null - if (uuids.isEmpty && futures.isEmpty){ + if (uuids.isEmpty && futures.get == 0){ shutdownSchedule match { case UNSCHEDULED => shutdownSchedule = SCHEDULED @@ -155,7 +156,7 @@ trait MessageDispatcher { shutdownSchedule = SCHEDULED Scheduler.scheduleOnce(this, timeoutMs, TimeUnit.MILLISECONDS) case SCHEDULED => - if (uuids.isEmpty() && futures.isEmpty) { + if (uuids.isEmpty() && futures.get == 0) { active switchOff { shutdown // shut down in the dispatcher's references is zero } @@ -187,7 +188,7 @@ trait MessageDispatcher { */ private[akka] def dispatch(invocation: MessageInvocation): Unit - private[akka] def executeFuture(invocation: FutureInvocation): Unit + private[akka] def executeFuture(invocation: FutureInvocation[_]): Unit /** * Called one time every time an actor is attached to this dispatcher and this dispatcher was previously shutdown @@ -205,9 +206,9 @@ trait MessageDispatcher { def mailboxSize(actorRef: ActorRef): Int /** - * Returns the size of the Future queue + * Returns the amount of futures queued for execution */ - def futureQueueSize: Int = futures.size + def pendingFutures: Long = futures.get } /** diff --git a/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala b/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala index 971ee2e89f..dcf20158d8 100644 --- a/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala +++ b/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala @@ -156,7 +156,7 @@ class CallingThreadDispatcher(val warnings: Boolean = true) extends MessageDispa if (execute) runQueue(mbox, queue) } - private[akka] override def executeFuture(invocation: FutureInvocation) { invocation.run } + private[akka] override def executeFuture(invocation: FutureInvocation[_]) { invocation.run } /* * This method must be called with this thread's queue, which must already From 65e553a4dfa67a7f5f508423e74d5c698fd17579 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Thu, 28 Apr 2011 08:19:04 +0200 Subject: [PATCH 068/100] Added sample to Transactional Agents --- akka-docs/scala/agents.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/akka-docs/scala/agents.rst b/akka-docs/scala/agents.rst index 1e9ea128a3..dc62000995 100644 --- a/akka-docs/scala/agents.rst +++ b/akka-docs/scala/agents.rst @@ -92,6 +92,29 @@ Transactional Agents If an Agent is used within an enclosing transaction, then it will participate in that transaction. If you send to an Agent within a transaction then the dispatch to the Agent will be held until that transaction commits, and discarded if the transaction is aborted. +.. code-block:: scala + + import akka.agent.Agent + import akka.stm._ + + def transfer(from: Agent[Int], to: Agent[Int], amount: Int): Boolean = { + atomic { + if (from.get < amount) false + else { + from send (_ - amount) + to send (_ + amount) + true + } + } + } + + val from = Agent(100) + val to = Agent(20) + val ok = transfer(from, to, 50) + + from() // -> 50 + to() // -> 70 + Monadic usage ------------- From c61f1a42dc8b2113d4be76014ea37cff104cf029 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Thu, 28 Apr 2011 07:47:51 -0600 Subject: [PATCH 069/100] make sure lock is aquired when accessing shutdownSchedule --- akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala index 8261a0f485..e63a72f366 100644 --- a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala +++ b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala @@ -95,7 +95,7 @@ trait MessageDispatcher { } private val futureCleanup: () => Unit = { () => - if (futures.decrementAndGet() == 0 && uuids.isEmpty) { + if (futures.decrementAndGet() == 0) guard withGuard { if (uuids.isEmpty) { shutdownSchedule match { case UNSCHEDULED => shutdownSchedule = SCHEDULED @@ -104,7 +104,7 @@ trait MessageDispatcher { shutdownSchedule = RESCHEDULED case RESCHEDULED => //Already marked for reschedule } - } + }} } private[akka] def register(actorRef: ActorRef) { From 4bedb4813d9950ebcedf098b2417213c804c0bbb Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Thu, 28 Apr 2011 16:01:11 +0200 Subject: [PATCH 070/100] Fixing tickets #816, #814, #817 and Dereks fixes on #812 --- .../scala/akka/dispatch/ActorModelSpec.scala | 16 +++++++ .../scala/akka/dispatch/MessageHandling.scala | 42 ++++++++++++------- .../main/scala/akka/security/Security.scala | 2 +- .../src/test/scala/config/ConfigSpec.scala | 2 +- config/akka-reference.conf | 4 +- 5 files changed, 47 insertions(+), 19 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala index 4e60ffcc96..2ee4d8a2f7 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala @@ -14,6 +14,7 @@ import java.util.concurrent.atomic.AtomicLong import java.util.concurrent. {ConcurrentHashMap, CountDownLatch, TimeUnit} import akka.actor.dispatch.ActorModelSpec.MessageDispatcherInterceptor import akka.util.{Duration, Switch} +import org.multiverse.api.latches.StandardLatch object ActorModelSpec { @@ -216,6 +217,21 @@ abstract class ActorModelSpec extends JUnitSuite { msgsProcessed = 0, restarts = 0 ) + + val futures = for(i <- 1 to 10) yield Future { i } + await(dispatcher.stops.get == 2)(withinMs = dispatcher.timeoutMs * 5) + assertDispatcher(dispatcher)(starts = 2, stops = 2) + + val a2 = newTestActor + a2.start + val futures2 = for(i <- 1 to 10) yield Future { i } + + await(dispatcher.starts.get == 3)(withinMs = dispatcher.timeoutMs * 5) + assertDispatcher(dispatcher)(starts = 3, stops = 2) + + a2.stop + await(dispatcher.stops.get == 3)(withinMs = dispatcher.timeoutMs * 5) + assertDispatcher(dispatcher)(starts = 3, stops = 3) } @Test def dispatcherShouldProcessMessagesOneAtATime { diff --git a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala index e63a72f366..d9017edc29 100644 --- a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala +++ b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala @@ -88,24 +88,36 @@ trait MessageDispatcher { private[akka] final def dispatchFuture[T](block: () => T, timeout: Long): Future[T] = { futures.getAndIncrement() - val future = new DefaultCompletableFuture[T](timeout) - if (active.isOff) { active.switchOn { start } } - executeFuture(FutureInvocation[T](future, block, futureCleanup)) - future + try { + val future = new DefaultCompletableFuture[T](timeout) + + if (active.isOff) + guard withGuard { if (active.isOff) active.switchOn { start } } + + executeFuture(FutureInvocation[T](future, block, futureCleanup)) + future + } catch { + case e => + futures.decrementAndGet + throw e + } } - private val futureCleanup: () => Unit = { () => - if (futures.decrementAndGet() == 0) guard withGuard { if (uuids.isEmpty) { - shutdownSchedule match { - case UNSCHEDULED => - shutdownSchedule = SCHEDULED - Scheduler.scheduleOnce(shutdownAction, timeoutMs, TimeUnit.MILLISECONDS) - case SCHEDULED => - shutdownSchedule = RESCHEDULED - case RESCHEDULED => //Already marked for reschedule + private val futureCleanup: () => Unit = + () => if (futures.decrementAndGet() == 0) { + guard withGuard { + if (futures.get == 0 && uuids.isEmpty) { + shutdownSchedule match { + case UNSCHEDULED => + shutdownSchedule = SCHEDULED + Scheduler.scheduleOnce(shutdownAction, timeoutMs, TimeUnit.MILLISECONDS) + case SCHEDULED => + shutdownSchedule = RESCHEDULED + case RESCHEDULED => //Already marked for reschedule + } + } } - }} - } + } private[akka] def register(actorRef: ActorRef) { if (actorRef.mailbox eq null) diff --git a/akka-http/src/main/scala/akka/security/Security.scala b/akka-http/src/main/scala/akka/security/Security.scala index dce249de46..7789164fd3 100644 --- a/akka-http/src/main/scala/akka/security/Security.scala +++ b/akka-http/src/main/scala/akka/security/Security.scala @@ -182,7 +182,7 @@ trait AuthenticationActor[C <: Credentials] extends Actor { * Responsible for the execution flow of authentication * * Credentials are extracted and verified from the request, - * and a se3curity context is created for the ContainerRequest + * and a security context is created for the ContainerRequest * this should ensure good integration with current Jersey security */ protected val authenticate: Receive = { diff --git a/akka-http/src/test/scala/config/ConfigSpec.scala b/akka-http/src/test/scala/config/ConfigSpec.scala index 3adea2fc43..2b21f3cc34 100644 --- a/akka-http/src/test/scala/config/ConfigSpec.scala +++ b/akka-http/src/test/scala/config/ConfigSpec.scala @@ -19,7 +19,7 @@ class ConfigSpec extends WordSpec with MustMatchers { getString("akka.http.authenticator") must equal(Some("N/A")) getBool("akka.http.connection-close") must equal(Some(true)) getString("akka.http.expired-header-name") must equal(Some("Async-Timeout")) - getList("akka.http.filters") must equal(List("se.scalablesolutions.akka.security.AkkaSecurityFilterFactory")) + getList("akka.http.filters") must equal(List("akka.security.AkkaSecurityFilterFactory")) getList("akka.http.resource-packages") must equal(Nil) getString("akka.http.hostname") must equal(Some("localhost")) getString("akka.http.expired-header-value") must equal(Some("expired")) diff --git a/config/akka-reference.conf b/config/akka-reference.conf index df2c2c3e0d..9a647c6ad5 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -85,7 +85,7 @@ akka { port = 9998 #If you are using akka.http.AkkaRestServlet - filters = ["se.scalablesolutions.akka.security.AkkaSecurityFilterFactory"] # List with all jersey filters to use + filters = ["akka.security.AkkaSecurityFilterFactory"] # List with all jersey filters to use # resource-packages = ["sample.rest.scala", # "sample.rest.java", # "sample.security"] # List with all resource packages for your Jersey services @@ -123,7 +123,7 @@ akka { remote { - # secure-cookie = "050E0A0D0D06010A00000900040D060F0C09060B" # generate your own with '$AKKA_HOME/scripts/generate_secure_cookie.sh' or using 'Crypt.generateSecureCookie' + # secure-cookie = "050E0A0D0D06010A00000900040D060F0C09060B" # generate your own with '$AKKA_HOME/scripts/generate_config_with_secure_cookie.sh' or using 'Crypt.generateSecureCookie' secure-cookie = "" compression-scheme = "zlib" # Options: "zlib" (lzf to come), leave out for no compression From baa12988f9174c79126c5990f099a9ff6dd8047b Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Thu, 28 Apr 2011 16:03:21 +0200 Subject: [PATCH 071/100] Fixing ticket #813 --- .../META-INF/services/javax.ws.rs.ext.MessageBodyWriter_FIXME | 1 - 1 file changed, 1 deletion(-) delete mode 100644 akka-http/src/main/resources/META-INF/services/javax.ws.rs.ext.MessageBodyWriter_FIXME diff --git a/akka-http/src/main/resources/META-INF/services/javax.ws.rs.ext.MessageBodyWriter_FIXME b/akka-http/src/main/resources/META-INF/services/javax.ws.rs.ext.MessageBodyWriter_FIXME deleted file mode 100644 index 51bc8cccd2..0000000000 --- a/akka-http/src/main/resources/META-INF/services/javax.ws.rs.ext.MessageBodyWriter_FIXME +++ /dev/null @@ -1 +0,0 @@ -se.scalablesolutions.akka.rest.ListWriter From 7d5bc131635fdbf8a761aabed010ebeff8e200ff Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Thu, 28 Apr 2011 16:23:03 +0200 Subject: [PATCH 072/100] Removing uses of awaitBlocking in the FutureSpec --- .../test/scala/akka/dispatch/FutureSpec.scala | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala index 1f7dae9270..2e5381b334 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -215,8 +215,9 @@ class FutureSpec extends JUnitSuite { def receive = { case (add: Int, wait: Int) => Thread.sleep(wait); self reply_? add } }).start() } - def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!![Int]((idx, idx * 200 )) } - assert(Futures.fold(0)(futures)(_ + _).awaitBlocking.result.get === 45) + val timeout = 10000 + def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!![Int]((idx, idx * 200 ), timeout) } + assert(Futures.fold(0, timeout)(futures)(_ + _).await.result.get === 45) } @Test def shouldFoldResultsByComposing { @@ -225,8 +226,8 @@ class FutureSpec extends JUnitSuite { def receive = { case (add: Int, wait: Int) => Thread.sleep(wait); self reply_? add } }).start() } - def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!![Int]((idx, idx * 200 )) } - assert(futures.foldLeft(Future(0))((fr, fa) => for (r <- fr; a <- fa) yield (r + a)).awaitBlocking.result.get === 45) + def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!![Int]((idx, idx * 200 ), 10000) } + assert(futures.foldLeft(Future(0))((fr, fa) => for (r <- fr; a <- fa) yield (r + a)).get === 45) } @Test def shouldFoldResultsWithException { @@ -240,12 +241,13 @@ class FutureSpec extends JUnitSuite { } }).start() } - def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!![Int]((idx, idx * 100 )) } - assert(Futures.fold(0)(futures)(_ + _).awaitBlocking.exception.get.getMessage === "shouldFoldResultsWithException: expected") + val timeout = 10000 + def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!![Int]((idx, idx * 100 ), timeout) } + assert(Futures.fold(0, timeout)(futures)(_ + _).await.exception.get.getMessage === "shouldFoldResultsWithException: expected") } @Test def shouldFoldReturnZeroOnEmptyInput { - assert(Futures.fold(0)(List[Future[Int]]())(_ + _).awaitBlocking.result.get === 0) + assert(Futures.fold(0)(List[Future[Int]]())(_ + _).get === 0) } @Test def shouldReduceResults { @@ -254,8 +256,9 @@ class FutureSpec extends JUnitSuite { def receive = { case (add: Int, wait: Int) => Thread.sleep(wait); self reply_? add } }).start() } - def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!![Int]((idx, idx * 200 )) } - assert(Futures.reduce(futures)(_ + _).awaitBlocking.result.get === 45) + val timeout = 10000 + def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!![Int]((idx, idx * 200 ), timeout) } + assert(Futures.reduce(futures, timeout)(_ + _).get === 45) } @Test def shouldReduceResultsWithException { @@ -269,8 +272,9 @@ class FutureSpec extends JUnitSuite { } }).start() } - def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!![Int]((idx, idx * 100 )) } - assert(Futures.reduce(futures)(_ + _).awaitBlocking.exception.get.getMessage === "shouldFoldResultsWithException: expected") + val timeout = 10000 + def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!![Int]((idx, idx * 100 ), timeout) } + assert(Futures.reduce(futures, timeout)(_ + _).await.exception.get.getMessage === "shouldFoldResultsWithException: expected") } @Test(expected = classOf[UnsupportedOperationException]) def shouldReduceThrowIAEOnEmptyInput { From 9a582b7c49e675340998adc10d0b76d67de97131 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Thu, 28 Apr 2011 18:05:28 +0200 Subject: [PATCH 073/100] Removing redundant isOff call --- akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala index d9017edc29..cfe69e33f3 100644 --- a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala +++ b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala @@ -92,7 +92,7 @@ trait MessageDispatcher { val future = new DefaultCompletableFuture[T](timeout) if (active.isOff) - guard withGuard { if (active.isOff) active.switchOn { start } } + guard withGuard { active.switchOn { start } } executeFuture(FutureInvocation[T](future, block, futureCleanup)) future From 241a21aaa0368f8f64638dd272bebe89c8e5a775 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Thu, 28 Apr 2011 22:42:17 +0200 Subject: [PATCH 074/100] Cross linking --- akka-docs/intro/getting-started-first-scala-eclipse.rst | 2 ++ akka-docs/java/stm.rst | 2 +- akka-docs/java/transactors.rst | 2 ++ akka-docs/scala/stm.rst | 2 +- akka-docs/scala/transactors.rst | 2 ++ 5 files changed, 8 insertions(+), 2 deletions(-) diff --git a/akka-docs/intro/getting-started-first-scala-eclipse.rst b/akka-docs/intro/getting-started-first-scala-eclipse.rst index 5aaee1439d..b4380490ef 100644 --- a/akka-docs/intro/getting-started-first-scala-eclipse.rst +++ b/akka-docs/intro/getting-started-first-scala-eclipse.rst @@ -1,3 +1,5 @@ +.. _getting-started-first-scala-eclipse: + Getting Started Tutorial (Scala with Eclipse): First Chapter ============================================================ diff --git a/akka-docs/java/stm.rst b/akka-docs/java/stm.rst index dbc0da5d4a..ed44218804 100644 --- a/akka-docs/java/stm.rst +++ b/akka-docs/java/stm.rst @@ -141,7 +141,7 @@ It can happen for the first few executions that you get a few failures of execut Coordinated transactions and Transactors ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you need coordinated transactions across actors or threads then see `Transactors `_. +If you need coordinated transactions across actors or threads then see :ref:`transactors-java`. Configuring transactions ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/akka-docs/java/transactors.rst b/akka-docs/java/transactors.rst index 2d17bd2fa6..b724ef89b6 100644 --- a/akka-docs/java/transactors.rst +++ b/akka-docs/java/transactors.rst @@ -1,3 +1,5 @@ +.. _transactors-java: + Transactors (Java) ================== diff --git a/akka-docs/scala/stm.rst b/akka-docs/scala/stm.rst index 21b8d7b522..42cd67ce2c 100644 --- a/akka-docs/scala/stm.rst +++ b/akka-docs/scala/stm.rst @@ -206,7 +206,7 @@ It can happen for the first few executions that you get a few failures of execut Coordinated transactions and Transactors ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you need coordinated transactions across actors or threads then see `Transactors `_. +If you need coordinated transactions across actors or threads then see :ref:`transactors-scala`. Configuring transactions ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/akka-docs/scala/transactors.rst b/akka-docs/scala/transactors.rst index da26b4b527..e4ee824cd3 100644 --- a/akka-docs/scala/transactors.rst +++ b/akka-docs/scala/transactors.rst @@ -1,3 +1,5 @@ +.. _transactors-scala: + Transactors (Scala) =================== From 390176b64dd56d002c4feb4cb910bae37fbfd8a1 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Thu, 28 Apr 2011 22:43:11 +0200 Subject: [PATCH 075/100] Added sbt reload before initial update --- akka-docs/intro/getting-started-first-scala.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/akka-docs/intro/getting-started-first-scala.rst b/akka-docs/intro/getting-started-first-scala.rst index 59d8fd5a82..867b6fe3f4 100644 --- a/akka-docs/intro/getting-started-first-scala.rst +++ b/akka-docs/intro/getting-started-first-scala.rst @@ -176,8 +176,11 @@ Not needed in this tutorial, but if you would like to use additional Akka module So, now we are all set. Just one final thing to do; make SBT download the dependencies it needs. That is done by invoking:: + > reload > update +The first reload command is needed because we have changed the project definition since the sbt session started. + SBT itself needs a whole bunch of dependencies but our project will only need one; ``akka-actor-1.1.jar``. SBT downloads that as well. Start writing the code From 2451d4a8d3b76896e92033fd205dcbfb5194b69e Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Thu, 28 Apr 2011 22:43:56 +0200 Subject: [PATCH 076/100] Added instructions for SBT project and IDE --- akka-docs/scala/tutorial-chat-server.rst | 47 ++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/akka-docs/scala/tutorial-chat-server.rst b/akka-docs/scala/tutorial-chat-server.rst index 4a10af9a45..73f66cc394 100644 --- a/akka-docs/scala/tutorial-chat-server.rst +++ b/akka-docs/scala/tutorial-chat-server.rst @@ -1,4 +1,4 @@ -Tutorial: write a scalable, fault-tolerant, persistent network chat server and client (Scala) +Tutorial: write a scalable, fault-tolerant, network chat server and client (Scala) ============================================================================================= .. sidebar:: Contents @@ -87,12 +87,53 @@ We will try to write a simple chat/IM system. It is client-server based and uses We will use many of the features of Akka along the way. In particular; Actors, fault-tolerance using Actor supervision, remote Actors, Software Transactional Memory (STM) and persistence. -But let's start by defining the messages that will flow in our system. +Creating an Akka SBT project +---------------------------- + +First we need to create an SBT project for our tutorial. You do that by stepping into the directory you want to create your project in and invoking the ``sbt`` command answering the questions for setting up your project:: + + $ sbt + Project does not exist, create new project? (y/N/s) y + Name: Chat + Organization: Hakkers Inc + Version [1.0]: + Scala version [2.9.0.RC1]: + sbt version [0.7.6.RC0]: + +Add the Akka SBT plugin definition to your SBT project by creating a ``Plugins.scala`` file in the ``project/plugins`` directory containing:: + + import sbt._ + + class Plugins(info: ProjectInfo) extends PluginDefinition(info) { + val akkaRepo = "Akka Repo" at "http://akka.io/repository" + val akkaPlugin = "se.scalablesolutions.akka" % "akka-sbt-plugin" % "1.1-M1" + } + +Create a project definition ``project/build/Project.scala`` file containing:: + + import sbt._ + + class ChatProject(info: ProjectInfo) extends DefaultProject(info) with AkkaProject { + val akkaRepo = "Akka Repo" at "http://akka.io/repository" + val akkaSTM = akkaModule("stm") + val akkaRemote = akkaModule("remote") + } + + +Make SBT download the dependencies it needs. That is done by invoking:: + + > reload + > update + +From the SBT project you can generate files for your IDE: + +- `SbtEclipsify `_ to generate the Eclipse project. Detailed instructions are available in :ref:`getting-started-first-scala-eclipse`. +- `sbt-idea `_ to generate the Eclipse project Creating messages ----------------- -It is very important that all messages that will be sent around in the system are immutable. The Actor model relies on the simple fact that no state is shared between Actors and the only way to guarantee that is to make sure we don't pass mutable state around as part of the messages. +Let's start by defining the messages that will flow in our system. It is very important that all messages that will be sent around in the system are immutable. The Actor model relies on the simple fact that no state is shared between Actors and the only way to guarantee that is to make sure we don't pass mutable state around as part of the messages. In Scala we have something called `case classes `_. These make excellent messages since they are both immutable and great to pattern match on. From f6e142a58351b820406c806769225db128819c65 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Thu, 28 Apr 2011 20:53:45 -0600 Subject: [PATCH 077/100] prevent chain of callbacks from overflowing the stack --- .../src/main/scala/akka/dispatch/Future.scala | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 72cab081a2..c6d270324a 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -16,6 +16,7 @@ import java.util.concurrent.TimeUnit.{NANOSECONDS => NANOS, MILLISECONDS => MILL import java.util.concurrent.atomic. {AtomicBoolean} import java.lang.{Iterable => JIterable} import java.util.{LinkedList => JLinkedList} +import scala.collection.mutable.Stack import annotation.tailrec class FutureTimeoutException(message: String) extends AkkaException(message) @@ -271,6 +272,10 @@ object Future { val fb = fn(a.asInstanceOf[A]) for (r <- fr; b <-fb) yield (r += b) }.map(_.result) + + private[akka] val callbacks = new ThreadLocal[Option[Stack[() => Unit]]]() { + override def initialValue = None + } } sealed trait Future[+T] { @@ -672,8 +677,30 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com _lock.unlock } - if (notifyTheseListeners.nonEmpty) - notifyTheseListeners.reverse foreach notify + @tailrec + def addToCallbacks(rest: List[Future[T] => Unit], callbacks: Stack[() => Unit]) { + if (rest.nonEmpty) { + callbacks.push(() => notify(rest.head)) + addToCallbacks(rest.tail, callbacks) + } + } + + if (notifyTheseListeners.nonEmpty) { + val optCallbacks = Future.callbacks.get + if (optCallbacks.isDefined) addToCallbacks(notifyTheseListeners, optCallbacks.get) + else { + try { + val callbacks = Stack[() => Unit]() + Future.callbacks.set(Some(callbacks)) + addToCallbacks(notifyTheseListeners, callbacks) + while (callbacks.nonEmpty) { + callbacks.pop().apply + } + } finally { + Future.callbacks.set(None) + } + } + } this } From ae481fc39a14423bd7a74138e0f150761c2fff61 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Thu, 28 Apr 2011 21:14:18 -0600 Subject: [PATCH 078/100] Avoid unneeded allocations --- .../src/main/scala/akka/dispatch/Future.scala | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index c6d270324a..a925978b24 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -685,6 +685,14 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com } } + def runCallbacks(rest: List[Future[T] => Unit], callbacks: Stack[() => Unit]) { + if (rest.nonEmpty) { + notify(rest.head) + while (callbacks.nonEmpty) { callbacks.pop().apply } + runCallbacks(rest.tail, callbacks) + } + } + if (notifyTheseListeners.nonEmpty) { val optCallbacks = Future.callbacks.get if (optCallbacks.isDefined) addToCallbacks(notifyTheseListeners, optCallbacks.get) @@ -692,10 +700,7 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com try { val callbacks = Stack[() => Unit]() Future.callbacks.set(Some(callbacks)) - addToCallbacks(notifyTheseListeners, callbacks) - while (callbacks.nonEmpty) { - callbacks.pop().apply - } + runCallbacks(notifyTheseListeners, callbacks) } finally { Future.callbacks.set(None) } From 2bfa5e5fc2c800b28d19b786bead5015af7d8948 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Thu, 28 Apr 2011 21:24:58 -0600 Subject: [PATCH 079/100] Add @tailrec check --- akka-actor/src/main/scala/akka/dispatch/Future.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index a925978b24..d745f8ec4a 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -685,6 +685,7 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com } } + @tailrec def runCallbacks(rest: List[Future[T] => Unit], callbacks: Stack[() => Unit]) { if (rest.nonEmpty) { notify(rest.head) From c2f810ecdb87b50c4aecd4ba4d21b69803cc94f4 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 29 Apr 2011 07:59:54 +0200 Subject: [PATCH 080/100] Fixed typo --- akka-docs/scala/tutorial-chat-server.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/akka-docs/scala/tutorial-chat-server.rst b/akka-docs/scala/tutorial-chat-server.rst index 73f66cc394..daa28d2ca3 100644 --- a/akka-docs/scala/tutorial-chat-server.rst +++ b/akka-docs/scala/tutorial-chat-server.rst @@ -127,8 +127,8 @@ Make SBT download the dependencies it needs. That is done by invoking:: From the SBT project you can generate files for your IDE: -- `SbtEclipsify `_ to generate the Eclipse project. Detailed instructions are available in :ref:`getting-started-first-scala-eclipse`. -- `sbt-idea `_ to generate the Eclipse project +- `SbtEclipsify `_ to generate Eclipse project. Detailed instructions are available in :ref:`getting-started-first-scala-eclipse`. +- `sbt-idea `_ to generate IntelliJ IDEA project. Creating messages ----------------- From 2cec337c97d1ae0b238dded451f642ba42b7527e Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 29 Apr 2011 08:57:26 +0200 Subject: [PATCH 081/100] Moved remote-actors from pending --- akka-docs/java/index.rst | 1 + .../{pending/remote-actors-java.rst => java/remote-actors.rst} | 0 akka-docs/scala/index.rst | 1 + .../{pending/remote-actors-scala.rst => scala/remote-actors.rst} | 0 4 files changed, 2 insertions(+) rename akka-docs/{pending/remote-actors-java.rst => java/remote-actors.rst} (100%) rename akka-docs/{pending/remote-actors-scala.rst => scala/remote-actors.rst} (100%) diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst index aeef671960..f0cb45d08f 100644 --- a/akka-docs/java/index.rst +++ b/akka-docs/java/index.rst @@ -9,4 +9,5 @@ Java API actor-registry stm transactors + remote-actors dispatchers diff --git a/akka-docs/pending/remote-actors-java.rst b/akka-docs/java/remote-actors.rst similarity index 100% rename from akka-docs/pending/remote-actors-java.rst rename to akka-docs/java/remote-actors.rst diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index 71c399709d..35a5c0a79b 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -10,6 +10,7 @@ Scala API agents stm transactors + remote-actors dispatchers fsm testing diff --git a/akka-docs/pending/remote-actors-scala.rst b/akka-docs/scala/remote-actors.rst similarity index 100% rename from akka-docs/pending/remote-actors-scala.rst rename to akka-docs/scala/remote-actors.rst From 52e7d078a9b0aaad5f5c08eaae12fbcdb2353f9a Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 29 Apr 2011 09:36:17 +0200 Subject: [PATCH 082/100] Cleanup --- akka-docs/java/remote-actors.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/akka-docs/java/remote-actors.rst b/akka-docs/java/remote-actors.rst index 47f27d6cef..15786c7a6a 100644 --- a/akka-docs/java/remote-actors.rst +++ b/akka-docs/java/remote-actors.rst @@ -3,9 +3,9 @@ Remote Actors (Java) Module stability: **SOLID** -Akka supports starting UntypedActors and TypedActors on remote nodes using a very efficient and scalable NIO implementation built upon `JBoss Netty `_ and `Google Protocol Buffers `_ . +Akka supports starting interacting with UntypedActors and TypedActors on remote nodes using a very efficient and scalable NIO implementation built upon `JBoss Netty `_ and `Google Protocol Buffers `_ . -The usage is completely transparent both in regards to sending messages and error handling and propagation as well as supervision, linking and restarts. You can send references to other Actors as part of the message. +The usage is completely transparent with local actors, both in regards to sending messages and error handling and propagation as well as supervision, linking and restarts. You can send references to other Actors as part of the message. **WARNING**: For security reasons, do not run an Akka node with a Remote Actor port reachable by untrusted connections unless you have supplied a classloader that restricts access to the JVM. From cdf9da112bc4f76b33f0cda276368c87fc7a7fd8 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 29 Apr 2011 09:36:27 +0200 Subject: [PATCH 083/100] Cleanup --- akka-docs/scala/remote-actors.rst | 45 ++++++++++++++++--------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/akka-docs/scala/remote-actors.rst b/akka-docs/scala/remote-actors.rst index 9389a5d284..d06e34c2de 100644 --- a/akka-docs/scala/remote-actors.rst +++ b/akka-docs/scala/remote-actors.rst @@ -3,11 +3,11 @@ Remote Actors (Scala) Module stability: **SOLID** -Akka supports starting Actors and Typed Actors on remote nodes using a very efficient and scalable NIO implementation built upon `JBoss Netty `_ and `Google Protocol Buffers `_ . +Akka supports starting and interacting with Actors and Typed Actors on remote nodes using a very efficient and scalable NIO implementation built upon `JBoss Netty `_ and `Google Protocol Buffers `_ . -The usage is completely transparent both in regards to sending messages and error handling and propagation as well as supervision, linking and restarts. You can send references to other Actors as part of the message. +The usage is completely transparent with local actors, both in regards to sending messages and error handling and propagation as well as supervision, linking and restarts. You can send references to other Actors as part of the message. -You can find a runnable sample `here `_. +You can find a runnable sample `here `__. Starting up the remote service ------------------------------ @@ -332,7 +332,7 @@ Session bound server side setup Session bound server managed remote actors work by creating and starting a new actor for every client that connects. Actors are stopped automatically when the client disconnects. The client side is the same as regular server managed remote actors. Use the function registerPerSession instead of register. Session bound actors are useful if you need to keep state per session, e.g. username. -They are also useful if you need to perform some cleanup when a client disconnects by overriding the postStop method as described `here `_ +They are also useful if you need to perform some cleanup when a client disconnects by overriding the postStop method as described `here `__ .. code-block:: scala @@ -697,26 +697,27 @@ Using the generated message builder to send the message to a remote actor: SBinary ^^^^^^^ -``_ -case class User(firstNameLastName: Tuple2[String, String], email: String, age: Int) extends Serializable.SBinary[User] { - import sbinary.DefaultProtocol._ +.. code-block:: scala - def this() = this(null, null, 0) + case class User(firstNameLastName: Tuple2[String, String], email: String, age: Int) extends Serializable.SBinary[User] { + import sbinary.DefaultProtocol._ - implicit object UserFormat extends Format[User] { - def reads(in : Input) = User( - read[Tuple2[String, String]](in), - read[String](in), - read[Int](in)) - def writes(out: Output, value: User) = { - write[Tuple2[String, String]](out, value. firstNameLastName) - write[String](out, value.email) - write[Int](out, value.age) + def this() = this(null, null, 0) + + implicit object UserFormat extends Format[User] { + def reads(in : Input) = User( + read[Tuple2[String, String]](in), + read[String](in), + read[Int](in)) + def writes(out: Output, value: User) = { + write[Tuple2[String, String]](out, value. firstNameLastName) + write[String](out, value.email) + write[Int](out, value.age) + } } + + def fromBytes(bytes: Array[Byte]) = fromByteArray[User](bytes) + + def toBytes: Array[Byte] = toByteArray(this) } - def fromBytes(bytes: Array[Byte]) = fromByteArray[User](bytes) - - def toBytes: Array[Byte] = toByteArray(this) -} -``_ From cf494781836917daaf58f6c5c4f4fb45ad45a627 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 29 Apr 2011 10:19:09 +0200 Subject: [PATCH 084/100] Scala style fixes, added parens for side effecting shutdown methods --- akka-docs/scala/remote-actors.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-docs/scala/remote-actors.rst b/akka-docs/scala/remote-actors.rst index d06e34c2de..8f01882956 100644 --- a/akka-docs/scala/remote-actors.rst +++ b/akka-docs/scala/remote-actors.rst @@ -64,7 +64,7 @@ If you invoke 'shutdown' on the server then the connection will be closed. import akka.actor.Actor._ - remote.shutdown + remote.shutdown() Connecting and shutting down a client explicitly ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 6576cd51e9f888e89d43dca24a19cfd1f713141d Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 29 Apr 2011 10:20:16 +0200 Subject: [PATCH 085/100] Scala style fixes, added parens for side effecting shutdown methods --- .../actor/supervisor/SupervisorSpec.scala | 2 +- .../akka/actor/supervisor/Ticket669Spec.scala | 2 +- .../scala/akka/dataflow/DataFlowSpec.scala | 6 ++-- .../scala/akka/dispatch/ActorModelSpec.scala | 8 +++--- .../src/main/scala/akka/actor/Scheduler.scala | 14 ++++++---- .../main/scala/akka/dataflow/DataFlow.scala | 2 +- .../scala/akka/dispatch/MessageHandling.scala | 6 ++-- .../akka/dispatch/ThreadPoolBuilder.scala | 4 +-- .../main/scala/akka/event/EventHandler.scala | 6 ++-- .../remoteinterface/RemoteInterface.scala | 6 ++-- .../src/main/scala/akka/util/AkkaLoader.scala | 18 ++++++------ .../src/main/scala/akka/util/Bootable.scala | 4 +-- .../remote/BootableRemoteActorService.scala | 14 +++++----- .../remote/netty/NettyRemoteSupport.scala | 28 +++++++++---------- .../test/scala/remote/AkkaRemoteTest.scala | 8 +++--- ...erverInitiatedRemoteSessionActorSpec.scala | 4 +-- ...InitiatedRemoteTypedSessionActorSpec.scala | 10 +++---- .../src/main/scala/ChatServer.scala | 25 +++++++++-------- .../testkit/CallingThreadDispatcher.scala | 4 +-- .../config/TypedActorGuiceConfigurator.scala | 2 +- 20 files changed, 91 insertions(+), 82 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorSpec.scala index 253570f576..668a2709cc 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorSpec.scala @@ -381,7 +381,7 @@ class SupervisorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach inits.get must be (3) - supervisor.shutdown + supervisor.shutdown() } } } diff --git a/akka-actor-tests/src/test/scala/akka/actor/supervisor/Ticket669Spec.scala b/akka-actor-tests/src/test/scala/akka/actor/supervisor/Ticket669Spec.scala index 33f7a72434..b61bd1a937 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/supervisor/Ticket669Spec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/supervisor/Ticket669Spec.scala @@ -14,7 +14,7 @@ import org.scalatest.matchers.MustMatchers class Ticket669Spec extends WordSpec with MustMatchers with BeforeAndAfterAll { import Ticket669Spec._ - override def afterAll = Actor.registry.shutdownAll() + override def afterAll() { Actor.registry.shutdownAll() } "A supervised actor with lifecycle PERMANENT" should { "be able to reply on failure during preRestart" in { diff --git a/akka-actor-tests/src/test/scala/akka/dataflow/DataFlowSpec.scala b/akka-actor-tests/src/test/scala/akka/dataflow/DataFlowSpec.scala index e0e0a09e6b..412605c02b 100644 --- a/akka-actor-tests/src/test/scala/akka/dataflow/DataFlowSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dataflow/DataFlowSpec.scala @@ -36,7 +36,7 @@ class DataFlowTest extends Spec with ShouldMatchers with BeforeAndAfterAll { latch.await(10,TimeUnit.SECONDS) should equal (true) result.get should equal (42) - List(x,y,z).foreach(_.shutdown) + List(x,y,z).foreach(_.shutdown()) } it("should be able to sum a sequence of ints") { @@ -67,7 +67,7 @@ class DataFlowTest extends Spec with ShouldMatchers with BeforeAndAfterAll { latch.await(10,TimeUnit.SECONDS) should equal (true) result.get should equal (sum(0,ints(0,1000))) - List(x,y,z).foreach(_.shutdown) + List(x,y,z).foreach(_.shutdown()) } /* it("should be able to join streams") { @@ -158,7 +158,7 @@ class DataFlowTest extends Spec with ShouldMatchers with BeforeAndAfterAll { val setV = thread { v << y } - List(x,y,z,v) foreach (_.shutdown) + List(x,y,z,v) foreach (_.shutdown()) latch.await(2,TimeUnit.SECONDS) should equal (true) }*/ } diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala index 2ee4d8a2f7..d5cea19bf5 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala @@ -111,13 +111,13 @@ object ActorModelSpec { super.dispatch(invocation) } - private[akka] abstract override def start { - super.start + private[akka] abstract override def start() { + super.start() starts.incrementAndGet() } - private[akka] abstract override def shutdown { - super.shutdown + private[akka] abstract override def shutdown() { + super.shutdown() stops.incrementAndGet() } } diff --git a/akka-actor/src/main/scala/akka/actor/Scheduler.scala b/akka-actor/src/main/scala/akka/actor/Scheduler.scala index cbda9d0af9..1c1da8e7a2 100644 --- a/akka-actor/src/main/scala/akka/actor/Scheduler.scala +++ b/akka-actor/src/main/scala/akka/actor/Scheduler.scala @@ -105,13 +105,17 @@ object Scheduler { } } - def shutdown: Unit = synchronized { - service.shutdown + def shutdown() { + synchronized { + service.shutdown() + } } - def restart: Unit = synchronized { - shutdown - service = Executors.newSingleThreadScheduledExecutor(SchedulerThreadFactory) + def restart() { + synchronized { + shutdown() + service = Executors.newSingleThreadScheduledExecutor(SchedulerThreadFactory) + } } } diff --git a/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala b/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala index 7ac900333d..446bc9652b 100644 --- a/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala +++ b/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala @@ -160,6 +160,6 @@ object DataFlow { } } - def shutdown = in ! Exit + def shutdown() { in ! Exit } } } diff --git a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala index cfe69e33f3..415ed053dc 100644 --- a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala +++ b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala @@ -168,7 +168,7 @@ trait MessageDispatcher { shutdownSchedule = SCHEDULED Scheduler.scheduleOnce(this, timeoutMs, TimeUnit.MILLISECONDS) case SCHEDULED => - if (uuids.isEmpty() && futures.get == 0) { + if (uuids.isEmpty && futures.get == 0) { active switchOff { shutdown // shut down in the dispatcher's references is zero } @@ -205,12 +205,12 @@ trait MessageDispatcher { /** * Called one time every time an actor is attached to this dispatcher and this dispatcher was previously shutdown */ - private[akka] def start: Unit + private[akka] def start(): Unit /** * Called one time every time an actor is detached from this dispatcher and this dispatcher has no actors left attached */ - private[akka] def shutdown: Unit + private[akka] def shutdown(): Unit /** * Returns the size of the mailbox for the specified actor diff --git a/akka-actor/src/main/scala/akka/dispatch/ThreadPoolBuilder.scala b/akka-actor/src/main/scala/akka/dispatch/ThreadPoolBuilder.scala index 83c30f23e0..0bcc0662e0 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ThreadPoolBuilder.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ThreadPoolBuilder.scala @@ -221,9 +221,9 @@ trait ExecutorServiceDelegate extends ExecutorService { def execute(command: Runnable) = executor.execute(command) - def shutdown = executor.shutdown + def shutdown() { executor.shutdown() } - def shutdownNow = executor.shutdownNow + def shutdownNow() = executor.shutdownNow() def isShutdown = executor.isShutdown diff --git a/akka-actor/src/main/scala/akka/event/EventHandler.scala b/akka-actor/src/main/scala/akka/event/EventHandler.scala index b29eb0ca72..1d7d81c1b6 100644 --- a/akka-actor/src/main/scala/akka/event/EventHandler.scala +++ b/akka-actor/src/main/scala/akka/event/EventHandler.scala @@ -102,9 +102,9 @@ object EventHandler extends ListenerManagement { /** * Shuts down all event handler listeners including the event handle dispatcher. */ - def shutdown() = { - foreachListener(_.stop) - EventHandlerDispatcher.shutdown + def shutdown() { + foreachListener(_.stop()) + EventHandlerDispatcher.shutdown() } def notify(event: Any) { diff --git a/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala b/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala index 081b622f39..7b61f224e8 100644 --- a/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala +++ b/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala @@ -143,11 +143,11 @@ abstract class RemoteSupport extends ListenerManagement with RemoteServerModule handler } - def shutdown { + def shutdown() { eventHandler.stop() removeListener(eventHandler) - this.shutdownClientModule - this.shutdownServerModule + this.shutdownClientModule() + this.shutdownServerModule() clear } diff --git a/akka-actor/src/main/scala/akka/util/AkkaLoader.scala b/akka-actor/src/main/scala/akka/util/AkkaLoader.scala index b7f113313d..2a2ee13db7 100644 --- a/akka-actor/src/main/scala/akka/util/AkkaLoader.scala +++ b/akka-actor/src/main/scala/akka/util/AkkaLoader.scala @@ -21,7 +21,7 @@ class AkkaLoader { * Boot initializes the specified bundles */ def boot(withBanner: Boolean, b : Bootable): Unit = hasBooted switchOn { - if (withBanner) printBanner + if (withBanner) printBanner() println("Starting Akka...") b.onLoad Thread.currentThread.setContextClassLoader(getClass.getClassLoader) @@ -32,15 +32,17 @@ class AkkaLoader { /* * Shutdown, well, shuts down the bundles used in boot */ - def shutdown: Unit = hasBooted switchOff { - println("Shutting down Akka...") - _bundles.foreach(_.onUnload) - _bundles = None - Actor.shutdownHook.run - println("Akka succesfully shut down") + def shutdown() { + hasBooted switchOff { + println("Shutting down Akka...") + _bundles.foreach(_.onUnload) + _bundles = None + Actor.shutdownHook.run + println("Akka succesfully shut down") + } } - private def printBanner = { + private def printBanner() { println("==================================================") println(" t") println(" t t t") diff --git a/akka-actor/src/main/scala/akka/util/Bootable.scala b/akka-actor/src/main/scala/akka/util/Bootable.scala index bea62e5ac7..d07643e1ac 100644 --- a/akka-actor/src/main/scala/akka/util/Bootable.scala +++ b/akka-actor/src/main/scala/akka/util/Bootable.scala @@ -5,6 +5,6 @@ package akka.util trait Bootable { - def onLoad {} - def onUnload {} + def onLoad() {} + def onUnload() {} } diff --git a/akka-remote/src/main/scala/akka/remote/BootableRemoteActorService.scala b/akka-remote/src/main/scala/akka/remote/BootableRemoteActorService.scala index 8139b35d0b..aa88be92c0 100644 --- a/akka-remote/src/main/scala/akka/remote/BootableRemoteActorService.scala +++ b/akka-remote/src/main/scala/akka/remote/BootableRemoteActorService.scala @@ -20,18 +20,18 @@ trait BootableRemoteActorService extends Bootable { def run = Actor.remote.start(self.applicationLoader.getOrElse(null)) //Use config host/port }, "Akka Remote Service") - def startRemoteService = remoteServerThread.start() + def startRemoteService() { remoteServerThread.start() } - abstract override def onLoad = { + abstract override def onLoad() { if (ReflectiveAccess.isRemotingEnabled && RemoteServerSettings.isRemotingEnabled) { - startRemoteService + startRemoteService() } - super.onLoad + super.onLoad() } - abstract override def onUnload = { - Actor.remote.shutdown + abstract override def onUnload() { + Actor.remote.shutdown() if (remoteServerThread.isAlive) remoteServerThread.join(1000) - super.onUnload + super.onUnload() } } 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 3be65cdea3..7196231c2d 100644 --- a/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala +++ b/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala @@ -107,7 +107,7 @@ trait NettyRemoteClientModule extends RemoteClientModule { self: ListenerManagem def shutdownClientConnection(address: InetSocketAddress): Boolean = lock withWriteGuard { remoteClients.remove(Address(address)) match { - case s: Some[RemoteClient] => s.get.shutdown + case s: Some[RemoteClient] => s.get.shutdown() case None => false } } @@ -132,15 +132,15 @@ trait NettyRemoteClientModule extends RemoteClientModule { self: ListenerManagem /** * Clean-up all open connections. */ - def shutdownClientModule = { - shutdownRemoteClients + def shutdownClientModule() { + shutdownRemoteClients() //TODO: Should we empty our remoteActors too? //remoteActors.clear } - def shutdownRemoteClients = lock withWriteGuard { - remoteClients.foreach({ case (addr, client) => client.shutdown }) - remoteClients.clear + def shutdownRemoteClients() = lock withWriteGuard { + remoteClients.foreach({ case (addr, client) => client.shutdown() }) + remoteClients.clear() } def registerClientManagedActor(hostname: String, port: Int, uuid: Uuid) = { @@ -187,7 +187,7 @@ abstract class RemoteClient private[akka] ( def connect(reconnectIfAlreadyConnected: Boolean = false): Boolean - def shutdown: Boolean + def shutdown(): Boolean /** * Returns an array with the current pending messages not yet delivered. @@ -403,16 +403,16 @@ 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 { + def shutdown() = runSwitch switchOff { notifyListeners(RemoteClientShutdown(module, remoteAddress)) timer.stop() timer = null openChannels.close.awaitUninterruptibly openChannels = null - bootstrap.releaseExternalResources + bootstrap.releaseExternalResources() bootstrap = null connection = null - pendingRequests.clear + pendingRequests.clear() } private[akka] def isWithinReconnectionTimeWindow: Boolean = { @@ -629,7 +629,7 @@ class NettyRemoteServer(serverModule: NettyRemoteServerModule, val host: String, openChannels.add(bootstrap.bind(address)) serverModule.notifyListeners(RemoteServerStarted(serverModule)) - def shutdown { + def shutdown() { try { val shutdownSignal = { val b = RemoteControlProtocol.newBuilder @@ -641,7 +641,7 @@ class NettyRemoteServer(serverModule: NettyRemoteServerModule, val host: String, openChannels.write(RemoteEncoder.encode(shutdownSignal)).awaitUninterruptibly openChannels.disconnect openChannels.close.awaitUninterruptibly - bootstrap.releaseExternalResources + bootstrap.releaseExternalResources() serverModule.notifyListeners(RemoteServerShutdown(serverModule)) } catch { case e: Exception => @@ -684,11 +684,11 @@ trait NettyRemoteServerModule extends RemoteServerModule { self: RemoteModule => this } - def shutdownServerModule = guard withGuard { + def shutdownServerModule() = guard withGuard { _isRunning switchOff { currentServer.getAndSet(None) foreach { instance => - instance.shutdown + instance.shutdown() } } } diff --git a/akka-remote/src/test/scala/remote/AkkaRemoteTest.scala b/akka-remote/src/test/scala/remote/AkkaRemoteTest.scala index 9b2b299d25..22c3a6e949 100644 --- a/akka-remote/src/test/scala/remote/AkkaRemoteTest.scala +++ b/akka-remote/src/test/scala/remote/AkkaRemoteTest.scala @@ -40,20 +40,20 @@ class AkkaRemoteTest extends remote.asInstanceOf[NettyRemoteSupport].optimizeLocal.set(false) //Can't run the test if we're eliminating all remote calls } - override def afterAll { + override def afterAll() { if (!OptimizeLocal) remote.asInstanceOf[NettyRemoteSupport].optimizeLocal.set(optimizeLocal_?) //Reset optimizelocal after all tests } - override def beforeEach { + override def beforeEach() { remote.start(host,port) super.beforeEach } override def afterEach() { - remote.shutdown + remote.shutdown() Actor.registry.shutdownAll() - super.afterEach + super.afterEach() } /* Utilities */ diff --git a/akka-remote/src/test/scala/remote/ServerInitiatedRemoteSessionActorSpec.scala b/akka-remote/src/test/scala/remote/ServerInitiatedRemoteSessionActorSpec.scala index 09a5f96bde..af29bb0bcb 100644 --- a/akka-remote/src/test/scala/remote/ServerInitiatedRemoteSessionActorSpec.scala +++ b/akka-remote/src/test/scala/remote/ServerInitiatedRemoteSessionActorSpec.scala @@ -48,7 +48,7 @@ class ServerInitiatedRemoteSessionActorSpec extends AkkaRemoteTest { val result1 = session1 !! GetUser() result1.as[String] must equal (Some("session[1]")) - remote.shutdownClientModule + remote.shutdownClientModule() val session2 = remote.actorFor("untyped-session-actor-service", 5000L, host, port) @@ -66,7 +66,7 @@ class ServerInitiatedRemoteSessionActorSpec extends AkkaRemoteTest { default1.as[String] must equal (Some("anonymous")) instantiatedSessionActors must have size (1) - remote.shutdownClientModule + remote.shutdownClientModule() Thread.sleep(1000) instantiatedSessionActors must have size (0) } diff --git a/akka-remote/src/test/scala/remote/ServerInitiatedRemoteTypedSessionActorSpec.scala b/akka-remote/src/test/scala/remote/ServerInitiatedRemoteTypedSessionActorSpec.scala index e357127641..e0d1a32ac3 100644 --- a/akka-remote/src/test/scala/remote/ServerInitiatedRemoteTypedSessionActorSpec.scala +++ b/akka-remote/src/test/scala/remote/ServerInitiatedRemoteTypedSessionActorSpec.scala @@ -18,8 +18,8 @@ class ServerInitiatedRemoteTypedSessionActorSpec extends AkkaRemoteTest { } // make sure the servers shutdown cleanly after the test has finished - override def afterEach = { - super.afterEach + override def afterEach() { + super.afterEach() clearMessageLogs } @@ -32,7 +32,7 @@ class ServerInitiatedRemoteTypedSessionActorSpec extends AkkaRemoteTest { session1.login("session[1]") session1.getUser() must equal ("session[1]") - remote.shutdownClientModule + remote.shutdownClientModule() val session2 = remote.typedActorFor(classOf[RemoteTypedSessionActor], "typed-session-actor-service", 5000L, host, port) @@ -46,7 +46,7 @@ class ServerInitiatedRemoteTypedSessionActorSpec extends AkkaRemoteTest { session1.getUser() must equal ("anonymous") RemoteTypedSessionActorImpl.getInstances() must have size (1) - remote.shutdownClientModule + remote.shutdownClientModule() Thread.sleep(1000) RemoteTypedSessionActorImpl.getInstances() must have size (0) @@ -57,7 +57,7 @@ class ServerInitiatedRemoteTypedSessionActorSpec extends AkkaRemoteTest { session1.doSomethingFunny() - remote.shutdownClientModule + remote.shutdownClientModule() Thread.sleep(1000) RemoteTypedSessionActorImpl.getInstances() must have size (0) } diff --git a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala index 90f6f2701e..8b0358a4e1 100644 --- a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala +++ b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala @@ -6,7 +6,7 @@ import scala.collection.mutable.HashMap - import akka.actor.{SupervisorFactory, Actor, ActorRef} + import akka.actor.{Actor, ActorRef} import akka.stm._ import akka.config.Supervision.{OneForOneStrategy,Permanent} import Actor._ @@ -108,7 +108,9 @@ self.reply(ChatLog(messageList)) } - override def postRestart(reason: Throwable) = chatLog = TransactionalVector() + override def postRestart(reason: Throwable) { + chatLog = TransactionalVector() + } } /** @@ -135,8 +137,9 @@ sessions -= username } - protected def shutdownSessions = + protected def shutdownSessions() { sessions.foreach { case (_, session) => session.stop() } + } } /** @@ -184,11 +187,11 @@ // abstract methods to be defined somewhere else protected def chatManagement: Receive protected def sessionManagement: Receive - protected def shutdownSessions(): Unit + protected def shutdownSessions() - override def postStop() = { + override def postStop() { EventHandler.info(this, "Chat server is shutting down...") - shutdownSessions + shutdownSessions() self.unlink(storage) storage.stop() } @@ -206,7 +209,7 @@ SessionManagement with ChatManagement with MemoryChatStorageFactory { - override def preStart() = { + override def preStart() { remote.start("localhost", 2552); remote.register("chat:service", self) //Register the actor with the specified service id } @@ -217,9 +220,9 @@ */ object ServerRunner { - def main(args: Array[String]): Unit = ServerRunner.run + def main(args: Array[String]) { ServerRunner.run() } - def run = { + def run() { actorOf[ChatService].start() } } @@ -229,9 +232,9 @@ */ object ClientRunner { - def main(args: Array[String]): Unit = ClientRunner.run + def main(args: Array[String]) { ClientRunner.run() } - def run = { + def run() { val client1 = new ChatClient("jonas") client1.login diff --git a/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala b/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala index dcf20158d8..319b40b6f4 100644 --- a/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala +++ b/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala @@ -108,9 +108,9 @@ class CallingThreadDispatcher(val warnings: Boolean = true) extends MessageDispa private def getMailbox(actor: ActorRef) = actor.mailbox.asInstanceOf[CallingThreadMailbox] - private[akka] override def start {} + private[akka] override def start() {} - private[akka] override def shutdown {} + private[akka] override def shutdown() {} private[akka] override def timeoutMs = 100L diff --git a/akka-typed-actor/src/main/scala/akka/config/TypedActorGuiceConfigurator.scala b/akka-typed-actor/src/main/scala/akka/config/TypedActorGuiceConfigurator.scala index ae19601351..98ce6d8b20 100644 --- a/akka-typed-actor/src/main/scala/akka/config/TypedActorGuiceConfigurator.scala +++ b/akka-typed-actor/src/main/scala/akka/config/TypedActorGuiceConfigurator.scala @@ -173,7 +173,7 @@ private[akka] class TypedActorGuiceConfigurator extends TypedActorConfiguratorBa } def stop = synchronized { - if (supervisor.isDefined) supervisor.get.shutdown + if (supervisor.isDefined) supervisor.get.shutdown() } } From 5e3f8d3e89b94459b3f42bafe3a41c54a49230ee Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 29 Apr 2011 11:02:35 +0200 Subject: [PATCH 086/100] Failing test due to timeout, decreased number of messages --- .../src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-remote/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala b/akka-remote/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala index af5aaffcc3..55f0ac3e3e 100644 --- a/akka-remote/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala +++ b/akka-remote/src/test/scala/remote/ClientInitiatedRemoteActorSpec.scala @@ -101,7 +101,7 @@ class ClientInitiatedRemoteActorSpec extends AkkaRemoteTest { } "shouldSendBangBangMessageAndReceiveReplyConcurrently" in { - val actors = (1 to 10).map(num => { remote.actorOf[RemoteActorSpecActorBidirectional](host,port).start() }).toList + val actors = (1 to 5).map(num => { remote.actorOf[RemoteActorSpecActorBidirectional](host,port).start() }).toList actors.map(_ !!! ("Hello", 10000)) foreach { future => "World" must equal (future.await.result.asInstanceOf[Option[String]].get) } From c24063496dab7061a0973fa9f81ed797ea2e901c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Fri, 29 Apr 2011 03:39:25 -0700 Subject: [PATCH 087/100] Added instructions to checkout tutorial with git --- akka-docs/intro/getting-started-first-java.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/akka-docs/intro/getting-started-first-java.rst b/akka-docs/intro/getting-started-first-java.rst index b907118f15..aafe02446f 100644 --- a/akka-docs/intro/getting-started-first-java.rst +++ b/akka-docs/intro/getting-started-first-java.rst @@ -31,6 +31,14 @@ If you want don't want to type in the code and/or set up a Maven project then yo __ https://github.com/jboner/akka/tree/master/akka-tutorials/akka-tutorial-first __ https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java +To check out the code using Git invoke the following:: + + $ git clone git://github.com/jboner/akka.git + +Then you can navigate down to the tutorial:: + + $ cd akka/akka-tutorials/akka-tutorial-first + Prerequisites ------------- From 36535d50e266c080c0668eef9762698bd5027561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Fri, 29 Apr 2011 03:40:08 -0700 Subject: [PATCH 088/100] Added instructions on how to check out the tutorial code using git --- akka-docs/intro/getting-started-first-scala.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/akka-docs/intro/getting-started-first-scala.rst b/akka-docs/intro/getting-started-first-scala.rst index 867b6fe3f4..20e592c4ea 100644 --- a/akka-docs/intro/getting-started-first-scala.rst +++ b/akka-docs/intro/getting-started-first-scala.rst @@ -31,6 +31,14 @@ If you want don't want to type in the code and/or set up an SBT project then you __ https://github.com/jboner/akka/tree/master/akka-tutorials/akka-tutorial-first __ https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala +To check out the code using Git invoke the following:: + + $ git clone git://github.com/jboner/akka.git + +Then you can navigate down to the tutorial:: + + $ cd akka/akka-tutorials/akka-tutorial-first + Prerequisites ------------- From 1fb228c06d5fdb67c22d59455357821f5a2290fb Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 29 Apr 2011 13:22:39 +0200 Subject: [PATCH 089/100] Reducing object creation overhead --- .../src/main/scala/akka/dispatch/Future.scala | 56 ++++++++----------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index d745f8ec4a..0f326410c2 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -18,6 +18,7 @@ import java.lang.{Iterable => JIterable} import java.util.{LinkedList => JLinkedList} import scala.collection.mutable.Stack import annotation.tailrec +import util.DynamicVariable class FutureTimeoutException(message: String) extends AkkaException(message) @@ -273,9 +274,7 @@ object Future { for (r <- fr; b <-fb) yield (r += b) }.map(_.result) - private[akka] val callbacks = new ThreadLocal[Option[Stack[() => Unit]]]() { - override def initialValue = None - } + private[akka] val callbacksPendingExecution = new DynamicVariable[Option[Stack[() => Unit]]](None) } sealed trait Future[+T] { @@ -677,35 +676,28 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com _lock.unlock } - @tailrec - def addToCallbacks(rest: List[Future[T] => Unit], callbacks: Stack[() => Unit]) { - if (rest.nonEmpty) { - callbacks.push(() => notify(rest.head)) - addToCallbacks(rest.tail, callbacks) - } - } - - @tailrec - def runCallbacks(rest: List[Future[T] => Unit], callbacks: Stack[() => Unit]) { - if (rest.nonEmpty) { - notify(rest.head) - while (callbacks.nonEmpty) { callbacks.pop().apply } - runCallbacks(rest.tail, callbacks) - } - } - - if (notifyTheseListeners.nonEmpty) { - val optCallbacks = Future.callbacks.get - if (optCallbacks.isDefined) addToCallbacks(notifyTheseListeners, optCallbacks.get) - else { - try { - val callbacks = Stack[() => Unit]() - Future.callbacks.set(Some(callbacks)) - runCallbacks(notifyTheseListeners, callbacks) - } finally { - Future.callbacks.set(None) + if (notifyTheseListeners.nonEmpty) { // Steps to ensure we don't run into a stack-overflow situation + @tailrec def runCallbacks(rest: List[Future[T] => Unit], callbacks: Stack[() => Unit]) { + if (rest.nonEmpty) { + notifyCompleted(rest.head) + while (callbacks.nonEmpty) { callbacks.pop().apply() } + runCallbacks(rest.tail, callbacks) } } + + val pending = Future.callbacksPendingExecution.value + if (pending.isDefined) { //Instead of nesting the calls to the callbacks (leading to stack overflow) + pending.get.push(() => { // Linearize/aggregate callbacks at top level and then execute + val doNotify = notifyCompleted _ //Hoist closure to avoid garbage + notifyTheseListeners foreach doNotify + }) + } else { + try { + val callbacks = Stack[() => Unit]() // Allocate new aggregator for pending callbacks + Future.callbacksPendingExecution.value = Some(callbacks) // Specify the callback aggregator + runCallbacks(notifyTheseListeners, callbacks) // Execute callbacks, if they trigger new callbacks, they are aggregated + } finally { Future.callbacksPendingExecution.value = None } // Ensure cleanup + } } this @@ -724,12 +716,12 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com _lock.unlock } - if (notifyNow) notify(func) + if (notifyNow) notifyCompleted(func) this } - private def notify(func: Future[T] => Unit) { + private def notifyCompleted(func: Future[T] => Unit) { try { func(this) } catch { From e4e99ef56399e892206ce4a46b9a9107da6c7770 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 29 Apr 2011 14:43:01 +0200 Subject: [PATCH 090/100] Reenabling the on-send-redistribution of messages in WorkStealer --- ...sedEventDrivenWorkStealingDispatcher.scala | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala index f2f63a3ff4..d5f1307a84 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala @@ -10,6 +10,7 @@ import akka.util.{ReflectiveAccess, Switch} import java.util.Queue import java.util.concurrent.atomic.{AtomicReference, AtomicInteger} import java.util.concurrent.{ TimeUnit, ExecutorService, RejectedExecutionException, ConcurrentLinkedQueue, LinkedBlockingQueue} +import util.DynamicVariable /** * An executor based event driven dispatcher which will try to redistribute work from busy actors to idle actors. It is assumed @@ -55,6 +56,7 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher( @volatile private var actorType: Option[Class[_]] = None @volatile private var members = Vector[ActorRef]() + private val donationInProgress = new DynamicVariable(false) private[akka] override def register(actorRef: ActorRef) = { //Verify actor type conformity @@ -78,18 +80,22 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher( override private[akka] def dispatch(invocation: MessageInvocation) = { val mbox = getMailbox(invocation.receiver) - /*if (!mbox.isEmpty && attemptDonationOf(invocation, mbox)) { + if (donationInProgress.value == false && mbox.dispatcherLock.locked && attemptDonationOf(invocation, mbox)) { //We were busy and we got to donate the message to some other lucky guy, we're done here - } else {*/ + } else { mbox enqueue invocation registerForExecution(mbox) - //} + } } override private[akka] def reRegisterForExecution(mbox: MessageQueue with ExecutableMailbox): Unit = { - while(donateFrom(mbox)) {} //When we reregister, first donate messages to another actor + try { + donationInProgress.value = true + while(donateFrom(mbox)) {} //When we reregister, first donate messages to another actor + } finally { donationInProgress.value = false } + if (!mbox.isEmpty) //If we still have messages left to process, reschedule for execution - super.reRegisterForExecution(mbox) + super.reRegisterForExecution(mbox) } /** @@ -110,13 +116,14 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher( /** * Returns true if the donation succeeded or false otherwise */ - /*protected def attemptDonationOf(message: MessageInvocation, donorMbox: MessageQueue with ExecutableMailbox): Boolean = { + protected def attemptDonationOf(message: MessageInvocation, donorMbox: MessageQueue with ExecutableMailbox): Boolean = try { + donationInProgress.value = true val actors = members // copy to prevent concurrent modifications having any impact doFindDonorRecipient(donorMbox, actors, System.identityHashCode(message) % actors.size) match { case null => false case recipient => donate(message, recipient) } - }*/ + } finally { donationInProgress.value = false } /** * Rewrites the message and adds that message to the recipients mailbox From d69baf74ae610087d16a95a3bab473b77f438ae6 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 29 Apr 2011 15:50:25 +0200 Subject: [PATCH 091/100] Reverting to ThreadLocal --- akka-actor/src/main/scala/akka/dispatch/Future.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 0f326410c2..ff0b6fdc57 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -18,7 +18,6 @@ import java.lang.{Iterable => JIterable} import java.util.{LinkedList => JLinkedList} import scala.collection.mutable.Stack import annotation.tailrec -import util.DynamicVariable class FutureTimeoutException(message: String) extends AkkaException(message) @@ -274,7 +273,9 @@ object Future { for (r <- fr; b <-fb) yield (r += b) }.map(_.result) - private[akka] val callbacksPendingExecution = new DynamicVariable[Option[Stack[() => Unit]]](None) + private[akka] val callbacksPendingExecution = new ThreadLocal[Option[Stack[() => Unit]]]() { + override def initialValue = None + } } sealed trait Future[+T] { @@ -685,7 +686,7 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com } } - val pending = Future.callbacksPendingExecution.value + val pending = Future.callbacksPendingExecution.get if (pending.isDefined) { //Instead of nesting the calls to the callbacks (leading to stack overflow) pending.get.push(() => { // Linearize/aggregate callbacks at top level and then execute val doNotify = notifyCompleted _ //Hoist closure to avoid garbage @@ -694,9 +695,9 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com } else { try { val callbacks = Stack[() => Unit]() // Allocate new aggregator for pending callbacks - Future.callbacksPendingExecution.value = Some(callbacks) // Specify the callback aggregator + Future.callbacksPendingExecution.set(Some(callbacks)) // Specify the callback aggregator runCallbacks(notifyTheseListeners, callbacks) // Execute callbacks, if they trigger new callbacks, they are aggregated - } finally { Future.callbacksPendingExecution.value = None } // Ensure cleanup + } finally { Future.callbacksPendingExecution.set(None) } // Ensure cleanup } } From d89c286fb2a89b4f3f40deeee6a1388e07a7c246 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 29 Apr 2011 15:58:13 +0200 Subject: [PATCH 092/100] Switching from DynamicVariable to ThreadLocal to avoid child threads inheriting the current value --- akka-actor/src/main/scala/akka/actor/Actor.scala | 8 +++++--- akka-actor/src/main/scala/akka/actor/ActorRef.scala | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index ff3cf26d7b..cf4c1bf042 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -125,7 +125,9 @@ object Actor extends ListenerManagement { */ type Receive = PartialFunction[Any, Unit] - private[actor] val actorRefInCreation = new scala.util.DynamicVariable[Option[ActorRef]](None) + private[actor] val actorRefInCreation = new ThreadLocal[Option[ActorRef]]{ + override def initialValue = None + } /** * Creates an ActorRef out of the Actor with type T. @@ -290,7 +292,7 @@ trait Actor { * the 'forward' function. */ @transient implicit val someSelf: Some[ActorRef] = { - val optRef = Actor.actorRefInCreation.value + val optRef = Actor.actorRefInCreation.get if (optRef.isEmpty) throw new ActorInitializationException( "ActorRef for instance of actor [" + getClass.getName + "] is not in scope." + "\n\tYou can not create an instance of an actor explicitly using 'new MyActor'." + @@ -298,7 +300,7 @@ trait Actor { "\n\tEither use:" + "\n\t\t'val actor = Actor.actorOf[MyActor]', or" + "\n\t\t'val actor = Actor.actorOf(new MyActor(..))'") - Actor.actorRefInCreation.value = None + Actor.actorRefInCreation.set(None) optRef.asInstanceOf[Some[ActorRef]].get.id = getClass.getName //FIXME: Is this needed? optRef.asInstanceOf[Some[ActorRef]] } diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index 12e2b5949a..08e99206c2 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -1015,12 +1015,12 @@ class LocalActorRef private[akka] ( private[this] def newActor: Actor = { try { - Actor.actorRefInCreation.value = Some(this) + Actor.actorRefInCreation.set(Some(this)) val a = actorFactory() if (a eq null) throw new ActorInitializationException("Actor instance passed to ActorRef can not be 'null'") a } finally { - Actor.actorRefInCreation.value = None + Actor.actorRefInCreation.set(None) } } From b5873ff2c7428828deba99011d5311a57ca610df Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 29 Apr 2011 16:14:29 +0200 Subject: [PATCH 093/100] Improving throughput for WorkStealer even more --- .../ExecutorBasedEventDrivenWorkStealingDispatcher.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala index d5f1307a84..7829e47712 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala @@ -80,7 +80,7 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher( override private[akka] def dispatch(invocation: MessageInvocation) = { val mbox = getMailbox(invocation.receiver) - if (donationInProgress.value == false && mbox.dispatcherLock.locked && attemptDonationOf(invocation, mbox)) { + if (donationInProgress.value == false && (!mbox.isEmpty || mbox.dispatcherLock.locked) && attemptDonationOf(invocation, mbox)) { //We were busy and we got to donate the message to some other lucky guy, we're done here } else { mbox enqueue invocation From 3366dd507c4f34a6f8a6130c6374d6617f1c59bf Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 29 Apr 2011 16:33:54 +0200 Subject: [PATCH 094/100] Moved serialization from pending --- akka-docs/java/index.rst | 1 + .../{pending/serialization-java.rst => java/serialization.rst} | 0 akka-docs/scala/index.rst | 1 + .../{pending/serialization-scala.rst => scala/serialization.rst} | 0 4 files changed, 2 insertions(+) rename akka-docs/{pending/serialization-java.rst => java/serialization.rst} (100%) rename akka-docs/{pending/serialization-scala.rst => scala/serialization.rst} (100%) diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst index f0cb45d08f..553f75da45 100644 --- a/akka-docs/java/index.rst +++ b/akka-docs/java/index.rst @@ -10,4 +10,5 @@ Java API stm transactors remote-actors + serialization dispatchers diff --git a/akka-docs/pending/serialization-java.rst b/akka-docs/java/serialization.rst similarity index 100% rename from akka-docs/pending/serialization-java.rst rename to akka-docs/java/serialization.rst diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index 35a5c0a79b..d5269336aa 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -11,6 +11,7 @@ Scala API stm transactors remote-actors + serialization dispatchers fsm testing diff --git a/akka-docs/pending/serialization-scala.rst b/akka-docs/scala/serialization.rst similarity index 100% rename from akka-docs/pending/serialization-scala.rst rename to akka-docs/scala/serialization.rst From 888af3479e9abdbaf8aaff8c36a75e1c19de4c5d Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 29 Apr 2011 17:07:52 +0200 Subject: [PATCH 095/100] Cleanup of serialization docs --- akka-docs/java/serialization.rst | 21 ++-- akka-docs/scala/serialization.rst | 153 ++++++++++++++++-------------- 2 files changed, 93 insertions(+), 81 deletions(-) diff --git a/akka-docs/java/serialization.rst b/akka-docs/java/serialization.rst index 1206211b8d..813db45a9a 100644 --- a/akka-docs/java/serialization.rst +++ b/akka-docs/java/serialization.rst @@ -1,10 +1,16 @@ +.. _serialization-java: + Serialization (Java) ==================== -Akka serialization module has been documented extensively under the Scala API section. In this section we will point out the different APIs that are available in Akka for Java based serialization of ActorRefs. The Scala APIs of ActorSerialization has implicit Format objects that set up the type class based serialization. In the Java API, the Format objects need to be specified explicitly. +.. sidebar:: Contents + + .. contents:: :local: + +Akka serialization module has been documented extensively under the :ref:`serialization-scala` section. In this section we will point out the different APIs that are available in Akka for Java based serialization of ActorRefs. The Scala APIs of ActorSerialization has implicit Format objects that set up the type class based serialization. In the Java API, the Format objects need to be specified explicitly. Serialization of ActorRef -========================= +------------------------- The following are the Java APIs for serialization of local ActorRefs: @@ -26,10 +32,9 @@ The following are the Java APIs for serialization of local ActorRefs: The following steps describe the procedure for serializing an Actor and ActorRef. Serialization of a Stateless Actor -================================== +---------------------------------- Step 1: Define the Actor ------------------------- .. code-block:: scala @@ -40,7 +45,6 @@ Step 1: Define the Actor } Step 2: Define the typeclass instance for the actor ---------------------------------------------------- Note how the generated Java classes are accessed using the $class based naming convention of the Scala compiler. @@ -58,7 +62,7 @@ Note how the generated Java classes are accessed using the $class based naming c } } -**Step 3: Serialize and de-serialize** +Step 3: Serialize and de-serialize The following JUnit snippet first creates an actor using the default constructor. The actor is, as we saw above a stateless one. Then it is serialized and de-serialized to get back the original actor. Being stateless, the de-serialized version behaves in the same way on a message as the original actor. @@ -91,12 +95,11 @@ The following JUnit snippet first creates an actor using the default constructor } Serialization of a Stateful Actor -================================= +--------------------------------- Let's now have a look at how to serialize an actor that carries a state with it. Here the expectation is that the serialization of the actor will also persist the state information. And after de-serialization we will get back the state with which it was serialized. Step 1: Define the Actor ------------------------- Here we consider an actor defined in Scala. We will however serialize using the Java APIs. @@ -119,7 +122,6 @@ Here we consider an actor defined in Scala. We will however serialize using the Note the actor has a state in the form of an Integer. And every message that the actor receives, it replies with an addition to the integer member. Step 2: Define the instance of the typeclass --------------------------------------------- .. code-block:: java @@ -141,7 +143,6 @@ Step 2: Define the instance of the typeclass Note the usage of Protocol Buffers to serialize the state of the actor. Step 3: Serialize and de-serialize ----------------------------------- .. code-block:: java diff --git a/akka-docs/scala/serialization.rst b/akka-docs/scala/serialization.rst index a0b0e312e6..39f9304bc8 100644 --- a/akka-docs/scala/serialization.rst +++ b/akka-docs/scala/serialization.rst @@ -1,10 +1,16 @@ +.. _serialization-scala: + Serialization (Scala) ===================== +.. sidebar:: Contents + + .. contents:: :local: + Module stability: **SOLID** Serialization of ActorRef -========================= +------------------------- An Actor can be serialized in two different ways: @@ -13,7 +19,7 @@ An Actor can be serialized in two different ways: Both of these can be sent as messages over the network and/or store them to disk, in a persistent storage backend etc. -Actor serialization in Akka is implemented through a type class 'Format[T <: Actor]' which publishes the 'fromBinary' and 'toBinary' methods for serialization. Here's the complete definition of the type class: +Actor serialization in Akka is implemented through a type class ``Format[T <: Actor]`` which publishes the ``fromBinary`` and ``toBinary`` methods for serialization. Here's the complete definition of the type class: .. code-block:: scala @@ -31,15 +37,14 @@ Actor serialization in Akka is implemented through a type class 'Format[T <: Act // client needs to implement Format[] for the respective actor trait Format[T <: Actor] extends FromBinary[T] with ToBinary[T] -**Deep serialization of an Actor and ActorRef** ------------------------------------------------ +Deep serialization of an Actor and ActorRef +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -You can serialize the whole actor deeply, e.g. both the 'ActorRef' and then instance of its 'Actor'. This can be useful if you want to move an actor from one node to another, or if you want to store away an actor, with its state, into a database. +You can serialize the whole actor deeply, e.g. both the ``ActorRef`` and then instance of its ``Actor``. This can be useful if you want to move an actor from one node to another, or if you want to store away an actor, with its state, into a database. Here is an example of how to serialize an Actor. Step 1: Define the actor -^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: scala @@ -54,7 +59,6 @@ Step 1: Define the actor } Step 2: Implement the type class for the actor -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: scala @@ -72,7 +76,6 @@ Step 2: Implement the type class for the actor } Step 3: Import the type class module definition and serialize / de-serialize -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: scala @@ -90,7 +93,8 @@ Step 3: Import the type class module definition and serialize / de-serialize (actor2 !! "hello").getOrElse("_") should equal("world 3") } -**Helper Type Class for Stateless Actors** +Helper Type Class for Stateless Actors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If your actor is stateless, then you can use the helper trait that Akka provides to serialize / de-serialize. Here's the definition: @@ -138,9 +142,10 @@ and use it for serialization: (actor2 !! "hello").getOrElse("_") should equal("world") } -**Helper Type Class for actors with external serializer** +Helper Type Class for actors with external serializer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Use the trait 'SerializerBasedActorFormat' for specifying serializers. +Use the trait ``SerializerBasedActorFormat`` for specifying serializers. .. code-block:: scala @@ -192,14 +197,14 @@ and serialize / de-serialize .. (actor2 !! "hello").getOrElse("_") should equal("world 3") } -**Serialization of a RemoteActorRef** -------------------------------------- +Serialization of a RemoteActorRef +--------------------------------- -You can serialize an 'ActorRef' to an immutable, network-aware Actor reference that can be freely shared across the network, a reference that "remembers" and stay mapped to its original Actor instance and host node, and will always work as expected. +You can serialize an ``ActorRef`` to an immutable, network-aware Actor reference that can be freely shared across the network, a reference that "remembers" and stay mapped to its original Actor instance and host node, and will always work as expected. -The 'RemoteActorRef' serialization is based upon Protobuf (Google Protocol Buffers) and you don't need to do anything to use it, it works on any 'ActorRef' (as long as the actor has **not** implemented one of the 'SerializableActor' traits, since then deep serialization will happen). +The ``RemoteActorRef`` serialization is based upon Protobuf (Google Protocol Buffers) and you don't need to do anything to use it, it works on any ``ActorRef`` (as long as the actor has **not** implemented one of the ``SerializableActor`` traits, since then deep serialization will happen). -Currently Akka will **not** autodetect an 'ActorRef' as part of your message and serialize it for you automatically, so you have to do that manually or as part of your custom serialization mechanisms. +Currently Akka will **not** autodetect an ``ActorRef`` as part of your message and serialize it for you automatically, so you have to do that manually or as part of your custom serialization mechanisms. Here is an example of how to serialize an Actor. @@ -209,14 +214,14 @@ Here is an example of how to serialize an Actor. val bytes = toBinary(actor1) -To deserialize the 'ActorRef' to a 'RemoteActorRef' you need to use the 'fromBinaryToRemoteActorRef(bytes: Array[Byte])' method on the 'ActorRef' companion object: +To deserialize the ``ActorRef`` to a ``RemoteActorRef`` you need to use the ``fromBinaryToRemoteActorRef(bytes: Array[Byte])`` method on the ``ActorRef`` companion object: .. code-block:: scala import RemoteActorSerialization._ val actor2 = fromBinaryToRemoteActorRef(bytes) -You can also pass in a class loader to load the 'ActorRef' class and dependencies from: +You can also pass in a class loader to load the ``ActorRef`` class and dependencies from: .. code-block:: scala @@ -226,14 +231,12 @@ You can also pass in a class loader to load the 'ActorRef' class and dependencie Deep serialization of a TypedActor ---------------------------------- -Serialization of typed actors works almost the same way as untyped actors. You can serialize the whole actor deeply, e.g. both the 'proxied ActorRef' and the instance of its 'TypedActor'. +Serialization of typed actors works almost the same way as untyped actors. You can serialize the whole actor deeply, e.g. both the 'proxied ActorRef' and the instance of its ``TypedActor``. Here is the example from above implemented as a TypedActor. -^ Step 1: Define the actor -^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: scala @@ -252,7 +255,6 @@ Step 1: Define the actor } Step 2: Implement the type class for the actor -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: scala @@ -266,7 +268,6 @@ Step 2: Implement the type class for the actor } Step 3: Import the type class module definition and serialize / de-serialize -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: scala @@ -278,12 +279,12 @@ Step 3: Import the type class module definition and serialize / de-serialize val typedActor2: MyTypedActor = fromBinaryJ(bytes, f) //type hint needed typedActor2.requestReply("hello") -- + Serialization of a remote typed ActorRef ---------------------------------------- -To deserialize the TypedActor to a 'RemoteTypedActorRef' (an aspectwerkz proxy to a RemoteActorRef) you need to use the 'fromBinaryToRemoteTypedActorRef(bytes: Array[Byte])' method on 'RemoteTypedActorSerialization' object: +To deserialize the TypedActor to a ``RemoteTypedActorRef`` (an aspectwerkz proxy to a RemoteActorRef) you need to use the ``fromBinaryToRemoteTypedActorRef(bytes: Array[Byte])`` method on ``RemoteTypedActorSerialization`` object: .. code-block:: scala @@ -294,7 +295,7 @@ To deserialize the TypedActor to a 'RemoteTypedActorRef' (an aspectwerkz proxy t val typedActor2 = fromBinaryToRemoteTypedActorRef(bytes, classLoader) Compression -=========== +----------- Akka has a helper class for doing compression of binary data. This can be useful for example when storing data in one of the backing storages. It currently supports LZF which is a very fast compression algorithm suited for runtime dynamic compression. @@ -309,24 +310,28 @@ Here is an example of how it can be used: val uncompressBytes = Compression.LZF.uncompress(compressBytes) Using the Serializable trait and Serializer class for custom serialization -========================================================================== +-------------------------------------------------------------------------- -If you are sending messages to a remote Actor and these messages implement one of the predefined interfaces/traits in the 'akka.serialization.Serializable.*' object, then Akka will transparently detect which serialization format it should use as wire protocol and will automatically serialize and deserialize the message according to this protocol. +If you are sending messages to a remote Actor and these messages implement one of the predefined interfaces/traits in the ``akka.serialization.Serializable.*`` object, then Akka will transparently detect which serialization format it should use as wire protocol and will automatically serialize and deserialize the message according to this protocol. Each serialization interface/trait in -* akka.serialization.Serializable.* -> has a matching serializer in -* akka.serialization.Serializer.* + +- akka.serialization.Serializable.* + +has a matching serializer in + +- akka.serialization.Serializer.* Note however that if you are using one of the Serializable interfaces then you don’t have to do anything else in regard to sending remote messages. The ones currently supported are (besides the default which is regular Java serialization): -* ScalaJSON (Scala only) -* JavaJSON (Java but some Scala structures) -* SBinary (Scala only) -* Protobuf (Scala and Java) -Apart from the above, Akka also supports Scala object serialization through `SJSON `_ that implements APIs similar to 'akka.serialization.Serializer.*'. See the section on SJSON below for details. +- ScalaJSON (Scala only) +- JavaJSON (Java but some Scala structures) +- SBinary (Scala only) +- Protobuf (Scala and Java) + +Apart from the above, Akka also supports Scala object serialization through `SJSON `_ that implements APIs similar to ``akka.serialization.Serializer.*``. See the section on SJSON below for details. Protobuf -------- @@ -481,8 +486,8 @@ You may also see this exception when trying to serialize a case class with out a @BeanInfo case class Empty() // cannot be serialized - SJSON: Scala -------------- +SJSON: Scala +------------ SJSON supports serialization of Scala objects into JSON. It implements support for built in Scala structures like List, Map or String as well as custom objects. SJSON is available as an Apache 2 licensed project on Github `here `_. @@ -535,7 +540,7 @@ What you get back from is a JsValue, an abstraction of the JSON object model. Fo Serialization of Embedded Objects ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - SJSON supports serialization of Scala objects that have other embedded objects. Suppose you have the following Scala classes .. Here Contact has an embedded Address Map .. +SJSON supports serialization of Scala objects that have other embedded objects. Suppose you have the following Scala classes .. Here Contact has an embedded Address Map .. .. code-block:: scala @@ -593,7 +598,7 @@ With SJSON, I can do the following: "Market Street" should equal( (r ># { ('addresses ? obj) andThen ('residence ? obj) andThen ('street ? str) })) -^ + Changing property names during serialization ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -619,7 +624,7 @@ When this will be serialized out, the property name will be changed. JsString("ISBN") -> JsString("012-456372") ) -^ + Serialization with ignore properties ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -653,7 +658,7 @@ The annotation @JSONProperty can be used to selectively ignore fields. When I se Similarly, we can ignore properties of an object **only** if they are null and not ignore otherwise. Just specify the annotation @JSONProperty as @JSONProperty {val ignoreIfNull = true}. -^ + Serialization with Type Hints for Generic Data Members ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -892,7 +897,8 @@ and the serialization in action in the REPL: There are other nifty ways to implement case class serialization using sjson. For more details, have a look at the `wiki `_ for sjson. -**JSON: Java** +JSON: Java +---------- Use the akka.serialization.Serialization.JavaJSON base class with its toJSONmethod. Akka’s Java JSON is based upon the Jackson library. @@ -924,7 +930,6 @@ Use the akka.serialization.SerializerFactory.getJavaJSON to do generic JSON seri String json = factory.getJavaJSON().out(foo); Foo fooCopy = factory.getJavaJSON().in(json, Foo.class); -- SBinary: Scala -------------- @@ -942,37 +947,43 @@ Here is an example of using the akka.serialization.Serializer.SBinary serializer val usersCopy = Serializer.SBinary.in(bytes, Some(classOf[List[Tuple2[String,String]]])) If you need to serialize your own user-defined objects then you have to do three things: -# Define an empty constructor -# Mix in the Serializable.SBinary[T] trait, and implement its methods: -## fromBytes(bytes: Array[Byte])[T] -## toBytes: Array[Byte] -# Create an implicit sbinary.Format[T] object for your class. Which means that you have to define its two methods: -## reads(in: Input): T; in which you read in all the fields in your object, using read[FieldType](in)and recreate it. -## writes(out: Output, value: T): Unit; in which you write out all the fields in your object, using write[FieldType](out, value.field). + +- Define an empty constructor +- Mix in the Serializable.SBinary[T] trait, and implement its methods: + + - fromBytes(bytes: Array[Byte])[T] + - toBytes: Array[Byte] + +- Create an implicit sbinary.Format[T] object for your class. Which means that you have to define its two methods: + + - reads(in: Input): T; in which you read in all the fields in your object, using read[FieldType](in)and recreate it. + - writes(out: Output, value: T): Unit; in which you write out all the fields in your object, using write[FieldType](out, value.field). Here is an example: -``_ -case class User(val usernamePassword: Tuple2[String, String], val email: String, val age: Int) - extends Serializable.SBinary[User] { - import sbinary.DefaultProtocol._ - import sbinary.Operations._ - def this() = this(null, null, 0) +.. code-block:: scala - implicit object UserFormat extends Format[User] { - def reads(in : Input) = User( - read[Tuple2[String, String]](in), - read[String](in), - read[Int](in)) - def writes(out: Output, value: User) = { - write[Tuple2[String, String]](out, value.usernamePassword) - write[String](out, value.email) - write[Int](out, value.age) + case class User(val usernamePassword: Tuple2[String, String], val email: String, val age: Int) + extends Serializable.SBinary[User] { + import sbinary.DefaultProtocol._ + import sbinary.Operations._ + + def this() = this(null, null, 0) + + implicit object UserFormat extends Format[User] { + def reads(in : Input) = User( + read[Tuple2[String, String]](in), + read[String](in), + read[Int](in)) + def writes(out: Output, value: User) = { + write[Tuple2[String, String]](out, value.usernamePassword) + write[String](out, value.email) + write[Int](out, value.age) + } } + + def fromBytes(bytes: Array[Byte]) = fromByteArray[User](bytes) + + def toBytes: Array[Byte] = toByteArray(this) } - def fromBytes(bytes: Array[Byte]) = fromByteArray[User](bytes) - - def toBytes: Array[Byte] = toByteArray(this) -} -``_ From 1c29885f3da971abc185e00b71bce17a5f67f74c Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 29 Apr 2011 17:09:22 +0200 Subject: [PATCH 096/100] Reviewed and improved remote-actors doc --- .../remoteinterface/RemoteInterface.scala | 3 +- akka-docs/java/remote-actors.rst | 351 +++++++++--------- akka-docs/scala/remote-actors.rst | 135 +++---- .../ServerInitiatedRemoteActorSample.scala | 26 +- 4 files changed, 268 insertions(+), 247 deletions(-) diff --git a/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala b/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala index 7b61f224e8..906f8b8d18 100644 --- a/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala +++ b/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala @@ -354,7 +354,8 @@ trait RemoteServerModule extends RemoteModule { def registerByUuid(actorRef: ActorRef): Unit /** - * Register Remote Actor by a specific 'id' passed as argument. + * Register Remote Actor by a specific 'id' passed as argument. The actor is registered by UUID rather than ID + * when prefixing the handle with the “uuid:” protocol. *

    * NOTE: If you use this method to register your remote actor then you must unregister the actor by this ID yourself. */ diff --git a/akka-docs/java/remote-actors.rst b/akka-docs/java/remote-actors.rst index 15786c7a6a..3894f2dacc 100644 --- a/akka-docs/java/remote-actors.rst +++ b/akka-docs/java/remote-actors.rst @@ -1,6 +1,10 @@ Remote Actors (Java) ==================== +.. sidebar:: Contents + + .. contents:: :local: + Module stability: **SOLID** Akka supports starting interacting with UntypedActors and TypedActors on remote nodes using a very efficient and scalable NIO implementation built upon `JBoss Netty `_ and `Google Protocol Buffers `_ . @@ -142,12 +146,6 @@ The default behavior is that the remote client will maintain a transaction log o If you choose a capacity higher than 0, then a bounded queue will be used and if the limit of the queue is reached then a 'RemoteClientMessageBufferException' will be thrown. -You can also get an Array with all the messages that the remote client has failed to send. Since the remote client events passes you an instance of the RemoteClient you have an easy way to act upon failure and do something with these messages (while waiting for them to be retried). - -.. code-block:: java - - Object[] pending = Actors.remote().pendingMessages(); - Running Remote Server in untrusted mode --------------------------------------- @@ -253,21 +251,13 @@ You can also generate the secure cookie by using the 'Crypt' object and its 'gen The secure cookie is a cryptographically secure randomly generated byte array turned into a SHA-1 hash. -Remote Actors -------------- - -Akka has two types of remote actors: - -* Client-initiated and managed. Here it is the client that creates the remote actor and "moves it" to the server. -* Server-initiated and managed. Here it is the server that creates the remote actor and the client can ask for a handle to this actor. - -They are good for different use-cases. The client-initiated are great when you want to monitor an actor on another node since it allows you to link to it and supervise it using the regular supervision semantics. They also make RPC completely transparent. The server-initiated, on the other hand, are great when you have a service running on the server that you want clients to connect to, and you want full control over the actor on the server side for security reasons etc. - Client-managed Remote UntypedActor -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +---------------------------------- DEPRECATED AS OF 1.1 +The client creates the remote actor and "moves it" to the server. + When you define an actors as being remote it is instantiated as on the remote host and your local actor becomes a proxy, it works as a handle to the remote actor. The real execution is always happening on the remote node. Here is an example: @@ -291,26 +281,31 @@ An UntypedActor can also start remote child Actors through one of the “spawn/l .. code-block:: java ... - getContext().spawnRemote(MyActor.class, hostname, port); + getContext().spawnRemote(MyActor.class, hostname, port, timeoutInMsForFutures); getContext().spawnLinkRemote(MyActor.class, hostname, port, timeoutInMsForFutures); ... Server-managed Remote UntypedActor -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +---------------------------------- + +Here it is the server that creates the remote actor and the client can ask for a handle to this actor. Server side setup -***************** +^^^^^^^^^^^^^^^^^ The API for server managed remote actors is really simple. 2 methods only: .. code-block:: java + import akka.actor.Actors; + import akka.actor.UntypedActor; + class MyActor extends UntypedActor { public void onReceive(Object message) throws Exception { ... } } - Actors.remote().start("localhost", 2552).register("hello-service", Actors.actorOf(HelloWorldActor.class); + Actors.remote().start("localhost", 2552).register("hello-service", Actors.actorOf(HelloWorldActor.class)); Actors created like this are automatically started. @@ -322,88 +317,6 @@ You can also register an actor by its UUID rather than ID or handle. This is don server.unregister("uuid:" + actor.uuid); -Client side usage -***************** - -.. code-block:: java - - ActorRef actor = Actors.remote().actorFor("hello-service", "localhost", 2552); - actor.sendOneWay("Hello"); - -There are many variations on the 'remote()#actorFor' method. Here are some of them: - -.. code-block:: java - - ... = actorFor(className, hostname, port); - ... = actorFor(className, timeout, hostname, port); - ... = actorFor(uuid, className, hostname, port); - ... = actorFor(uuid, className, timeout, hostname, port); - ... // etc - -All of these also have variations where you can pass in an explicit 'ClassLoader' which can be used when deserializing messages sent from the remote actor. - -Client-managed Remote TypedActor -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -DEPRECATED AS OF 1.1 - -Remote Typed Actors are created through the 'TypedActor.newRemoteInstance' factory method. - -.. code-block:: java - - MyPOJO remoteActor = (MyPOJO)TypedActor.newRemoteInstance(MyPOJO.class, MyPOJOImpl.class, , "localhost", 2552); - -And if you want to specify the timeout: - -.. code-block:: java - - MyPOJO remoteActor = (MyPOJO)TypedActor.newRemoteInstance(MyPOJO.class, MyPOJOImpl.class, timeout, "localhost", 2552); - -You can also define the Typed Actor to be a client-managed-remote service by adding the ‘RemoteAddress’ configuration element in the declarative supervisor configuration: - -.. code-block:: java - - new Component( - Foo.class, - FooImpl.class, - new LifeCycle(new Permanent(), 1000), - 1000, - new RemoteAddress("localhost", 2552)) - -Server-managed Remote TypedActor -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -WARNING: Remote TypedActors do not work with overloaded methods on your TypedActor, refrain from using overloading. - -Server side setup -***************** - -The API for server managed remote typed actors is nearly the same as for untyped actor: - -.. code-block:: java - - import static akka.actor.Actors.*; - remote().start("localhost", 2552); - - RegistrationService typedActor = TypedActor.newInstance(RegistrationService.class, RegistrationServiceImpl.class, 2000); - remote().registerTypedActor("user-service", typedActor); - -Client side usage - -.. code-block:: java - - import static akka.actor.Actors.*; - RegistrationService actor = remote().typedActorFor(RegistrationService.class, "user-service", 5000L, "localhost", 2552); - actor.registerUser(...); - -There are variations on the 'remote()#typedActorFor' method. Here are some of them: - -.. code-block:: java - - ... = typedActorFor(interfaceClazz, serviceIdOrClassName, hostname, port); - ... = typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port); - ... = typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port, classLoader); - Session bound server side setup ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -414,17 +327,19 @@ Session bound actors are useful if you need to keep state per session, e.g. user .. code-block:: java import static akka.actor.Actors.*; + import akka.japi.Creator; + class HelloWorldActor extends Actor { ... } remote().start("localhost", 2552); - remote().registerPerSession("hello-service", new Creator[ActorRef]() { + remote().registerPerSession("hello-service", new Creator() { public ActorRef create() { return actorOf(HelloWorldActor.class); } - }) + }); Note that the second argument in registerPerSession is a Creator, it means that the create method will create a new ActorRef each invocation. It will be called to create an actor every time a session is established. @@ -443,19 +358,22 @@ There are many variations on the 'remote()#actorFor' method. Here are some of th .. code-block:: java - ... = actorFor(className, hostname, port); - ... = actorFor(className, timeout, hostname, port); - ... = actorFor(uuid, className, hostname, port); - ... = actorFor(uuid, className, timeout, hostname, port); + ... = remote().actorFor(className, hostname, port); + ... = remote().actorFor(className, timeout, hostname, port); + ... = remote().actorFor(uuid, className, hostname, port); + ... = remote().actorFor(uuid, className, timeout, hostname, port); ... // etc -Automatic remote 'sender' reference management -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +All of these also have variations where you can pass in an explicit 'ClassLoader' which can be used when deserializing messages sent from the remote actor. -Akka is automatically remote-enabling the sender Actor reference for you in order to allow the receiver to respond to the message using 'getContext().getSender().sendOneWay(msg);' or 'getContext().reply(msg);'. By default it is registering the sender reference in the remote server with the 'hostname' and 'port' from the akka.conf configuration file. The default is "localhost" and 2552 and if there is no remote server with this hostname and port then it creates and starts it. +Automatic remote 'sender' reference management +---------------------------------------------- + +The sender of a remote message will be reachable with a reply through the remote server on the node that the actor is residing, automatically. +Please note that firewalled clients won't work right now. [2011-01-05] Identifying remote actors -^^^^^^^^^^^^^^^^^^^^^^^^^ +------------------------- The 'id' field in the 'Actor' class is of importance since it is used as identifier for the remote actor. If you want to create a brand new actor every time you instantiate a remote actor then you have to set the 'id' field to a unique 'String' for each instance. If you want to reuse the same remote actor instance for each new remote actor (of the same class) you create then you don't have to do anything since the 'id' field by default is equal to the name of the actor class. @@ -463,18 +381,83 @@ Here is an example of overriding the 'id' field: .. code-block:: java - import akka.util.UUID; + import akka.actor.UntypedActor; + import com.eaio.uuid.UUID; class MyActor extends UntypedActor { public MyActor() { - getContext().setId(UUID.newUuid().toString()); + getContext().setId(new UUID().toString()); } public void onReceive(Object message) throws Exception { - ... + // ... } } +Client-managed Remote Typed Actors +---------------------------------- + +DEPRECATED AS OF 1.1 + +Remote Typed Actors are created through the 'TypedActor.newRemoteInstance' factory method. + +.. code-block:: java + + MyPOJO remoteActor = (MyPOJO) TypedActor.newRemoteInstance(MyPOJO.class, MyPOJOImpl.class, "localhost", 2552); + +And if you want to specify the timeout: + +.. code-block:: java + + MyPOJO remoteActor = (MyPOJO)TypedActor.newRemoteInstance(MyPOJO.class, MyPOJOImpl.class, timeout, "localhost", 2552); + +You can also define the Typed Actor to be a client-managed-remote service by adding the ‘RemoteAddress’ configuration element in the declarative supervisor configuration: + +.. code-block:: java + + new Component( + Foo.class, + FooImpl.class, + new LifeCycle(new Permanent(), 1000), + 1000, + new RemoteAddress("localhost", 2552)) + +Server-managed Remote Typed Actors +---------------------------------- + +WARNING: Remote TypedActors do not work with overloaded methods on your TypedActor, refrain from using overloading. + +Server side setup +^^^^^^^^^^^^^^^^^ + +The API for server managed remote typed actors is nearly the same as for untyped actor: + +.. code-block:: java + + import static akka.actor.Actors.*; + remote().start("localhost", 2552); + + RegistrationService typedActor = TypedActor.newInstance(RegistrationService.class, RegistrationServiceImpl.class, 2000); + remote().registerTypedActor("user-service", typedActor); + + +Client side usage +^^^^^^^^^^^^^^^^^ + +.. code-block:: java + + import static akka.actor.Actors.*; + RegistrationService actor = remote().typedActorFor(RegistrationService.class, "user-service", 5000L, "localhost", 2552); + actor.registerUser(...); + +There are variations on the 'remote()#typedActorFor' method. Here are some of them: + +.. code-block:: java + + ... = remote().typedActorFor(interfaceClazz, serviceIdOrClassName, hostname, port); + ... = remote().typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port); + ... = remote().typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port, classLoader); + Data Compression Configuration ------------------------------ @@ -493,44 +476,55 @@ You can configure it like this: } } +Code provisioning +----------------- + +Akka does currently not support automatic code provisioning but requires you to have the remote actor class files available on both the "client" the "server" nodes. +This is something that will be addressed soon. Until then, sorry for the inconvenience. + Subscribe to Remote Client events --------------------------------- Akka has a subscription API for remote client events. You can register an Actor as a listener and this actor will have to be able to process these events: -RemoteClientError { Throwable cause; RemoteClientModule client; InetSocketAddress remoteAddress; } -RemoteClientDisconnected { RemoteClientModule client; InetSocketAddress remoteAddress; } -RemoteClientConnected { RemoteClientModule client; InetSocketAddress remoteAddress; } -RemoteClientStarted { RemoteClientModule client; InetSocketAddress remoteAddress; } -RemoteClientShutdown { RemoteClientModule client; InetSocketAddress remoteAddress; } -RemoteClientWriteFailed { Object message; Throwable cause; RemoteClientModule client; InetSocketAddress remoteAddress; } +.. code-block:: java + + class RemoteClientError { Throwable cause; RemoteClientModule client; InetSocketAddress remoteAddress; } + class RemoteClientDisconnected { RemoteClientModule client; InetSocketAddress remoteAddress; } + class RemoteClientConnected { RemoteClientModule client; InetSocketAddress remoteAddress; } + class RemoteClientStarted { RemoteClientModule client; InetSocketAddress remoteAddress; } + class RemoteClientShutdown { RemoteClientModule client; InetSocketAddress remoteAddress; } + class RemoteClientWriteFailed { Object message; Throwable cause; RemoteClientModule client; InetSocketAddress remoteAddress; } So a simple listener actor can look like this: .. code-block:: java + import akka.actor.UntypedActor; + import akka.remoteinterface.*; + class Listener extends UntypedActor { public void onReceive(Object message) throws Exception { if (message instanceof RemoteClientError) { - RemoteClientError event = (RemoteClientError)message; - Exception cause = event.getCause(); - ... + RemoteClientError event = (RemoteClientError) message; + Throwable cause = event.getCause(); + // ... } else if (message instanceof RemoteClientConnected) { - RemoteClientConnected event = (RemoteClientConnected)message; - ... + RemoteClientConnected event = (RemoteClientConnected) message; + // ... } else if (message instanceof RemoteClientDisconnected) { - RemoteClientDisconnected event = (RemoteClientDisconnected)message; - ... + RemoteClientDisconnected event = (RemoteClientDisconnected) message; + // ... } else if (message instanceof RemoteClientStarted) { - RemoteClientStarted event = (RemoteClientStarted)message; - ... + RemoteClientStarted event = (RemoteClientStarted) message; + // ... } else if (message instanceof RemoteClientShutdown) { - RemoteClientShutdown event = (RemoteClientShutdown)message; - ... + RemoteClientShutdown event = (RemoteClientShutdown) message; + // ... } else if (message instanceof RemoteClientWriteFailed) { - RemoteClientWriteFailed event = (RemoteClientWriteFailed)message; - ... + RemoteClientWriteFailed event = (RemoteClientWriteFailed) message; + // ... } } } @@ -550,43 +544,45 @@ Subscribe to Remote Server events Akka has a subscription API for the server events. You can register an Actor as a listener and this actor will have to be able to process these events: -RemoteServerStarted { RemoteServerModule server; } -RemoteServerShutdown { RemoteServerModule server; } -RemoteServerError { Throwable cause; RemoteServerModule server; } -RemoteServerClientConnected { RemoteServerModule server; Option clientAddress; } -RemoteServerClientDisconnected { RemoteServerModule server; Option clientAddress; } -RemoteServerClientClosed { RemoteServerModule server; Option clientAddress; } -RemoteServerWriteFailed { Object request; Throwable cause; RemoteServerModule server; Option clientAddress; } +.. code-block:: java + + class RemoteServerStarted { RemoteServerModule server; } + class RemoteServerShutdown { RemoteServerModule server; } + class RemoteServerError { Throwable cause; RemoteServerModule server; } + class RemoteServerClientConnected { RemoteServerModule server; Option clientAddress; } + class RemoteServerClientDisconnected { RemoteServerModule server; Option clientAddress; } + class RemoteServerClientClosed { RemoteServerModule server; Option clientAddress; } + class RemoteServerWriteFailed { Object request; Throwable cause; RemoteServerModule server; Option clientAddress; } So a simple listener actor can look like this: .. code-block:: java + import akka.actor.UntypedActor; + import akka.remoteinterface.*; + class Listener extends UntypedActor { public void onReceive(Object message) throws Exception { - if (message instanceof RemoteServerError) { - RemoteServerError event = (RemoteServerError)message; - Exception cause = event.getCause(); - ... - } else if (message instanceof RemoteServerStarted) { - RemoteServerStarted event = (RemoteServerStarted)message; - ... - } else if (message instanceof RemoteServerShutdown) { - RemoteServerShutdown event = (RemoteServerShutdown)message; - ... - } else if (message instanceof RemoteServerClientConnected) { - RemoteServerClientConnected event = (RemoteServerClientConnected)message; - ... - } else if (message instanceof RemoteServerClientDisconnected) { - RemoteServerClientDisconnected event = (RemoteServerClientDisconnected)message; - ... - } else if (message instanceof RemoteServerClientClosed) { - RemoteServerClientClosed event = (RemoteServerClientClosed)message; - ... - } else if (message instanceof RemoteServerWriteFailed) { - RemoteServerWriteFailed event = (RemoteServerWriteFailed)message; - ... + if (message instanceof RemoteClientError) { + RemoteClientError event = (RemoteClientError) message; + Throwable cause = event.getCause(); + // ... + } else if (message instanceof RemoteClientConnected) { + RemoteClientConnected event = (RemoteClientConnected) message; + // ... + } else if (message instanceof RemoteClientDisconnected) { + RemoteClientDisconnected event = (RemoteClientDisconnected) message; + // ... + } else if (message instanceof RemoteClientStarted) { + RemoteClientStarted event = (RemoteClientStarted) message; + // ... + } else if (message instanceof RemoteClientShutdown) { + RemoteClientShutdown event = (RemoteClientShutdown) message; + // ... + } else if (message instanceof RemoteClientWriteFailed) { + RemoteClientWriteFailed event = (RemoteClientWriteFailed) message; + // ... } } } @@ -608,10 +604,27 @@ Message Serialization All messages that are sent to remote actors needs to be serialized to binary format to be able to travel over the wire to the remote node. This is done by letting your messages extend one of the traits in the 'akka.serialization.Serializable' object. If the messages don't implement any specific serialization trait then the runtime will try to use standard Java serialization. -Read more about that in the `Serialization section `_. +Here is one example, but full documentation can be found in the :ref:`serialization-java`. -Code provisioning ------------------ +Protobuf +^^^^^^^^ -Akka does currently not support automatic code provisioning but requires you to have the remote actor class files available on both the "client" the "server" nodes. -This is something that will be addressed soon. Until then, sorry for the inconvenience. +Protobuf message specification needs to be compiled with 'protoc' compiler. + +:: + + message ProtobufPOJO { + required uint64 id = 1; + required string name = 2; + required bool status = 3; + } + +Using the generated message builder to send the message to a remote actor: + +.. code-block:: java + + actor.sendOneWay(ProtobufPOJO.newBuilder() + .setId(11) + .setStatus(true) + .setName("Coltrane") + .build()); diff --git a/akka-docs/scala/remote-actors.rst b/akka-docs/scala/remote-actors.rst index 8f01882956..0f7e68d095 100644 --- a/akka-docs/scala/remote-actors.rst +++ b/akka-docs/scala/remote-actors.rst @@ -1,6 +1,10 @@ Remote Actors (Scala) ===================== +.. sidebar:: Contents + + .. contents:: :local: + Module stability: **SOLID** Akka supports starting and interacting with Actors and Typed Actors on remote nodes using a very efficient and scalable NIO implementation built upon `JBoss Netty `_ and `Google Protocol Buffers `_ . @@ -74,6 +78,7 @@ Normally you should not have to start and stop the client connection explicitly .. code-block:: scala import akka.actor.Actor._ + import java.net.InetSocketAddress remote.shutdownClientConnection(new InetSocketAddress("localhost", 6666)) //Returns true if successful, false otherwise remote.restartClientConnection(new InetSocketAddress("localhost", 6666)) //Returns true if successful, false otherwise @@ -143,12 +148,6 @@ The default behavior is that the remote client will maintain a transaction log o If you choose a capacity higher than 0, then a bounded queue will be used and if the limit of the queue is reached then a 'RemoteClientMessageBufferException' will be thrown. -You can also get an Array with all the messages that the remote client has failed to send. Since the remote client events passes you an instance of the RemoteClient you have an easy way to act upon failure and do something with these messages (while waiting for them to be retried). - -.. code-block:: scala - - val pending: Array[Any] = Actor.remote.pendingMessages - Running Remote Server in untrusted mode --------------------------------------- @@ -255,24 +254,16 @@ You can also generate the secure cookie by using the 'Crypt' object and its 'gen The secure cookie is a cryptographically secure randomly generated byte array turned into a SHA-1 hash. -Remote Actors -------------- - -Akka has two types of remote actors: - -* Client-initiated and managed. Here it is the client that creates the remote actor and "moves it" to the server. -* Server-initiated and managed. Here it is the server that creates the remote actor and the client can ask for a handle to this actor. - -They are good for different use-cases. The client-initiated are great when you want to monitor an actor on another node since it allows you to link to it and supervise it using the regular supervision semantics. They also make RPC completely transparent. The server-initiated, on the other hand, are great when you have a service running on the server that you want clients to connect to, and you want full control over the actor on the server side for security reasons etc. - Client-managed Remote Actors -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +---------------------------- DEPRECATED AS OF 1.1 -When you define an actors as being remote it is instantiated as on the remote host and your local actor becomes a proxy, it works as a handle to the remote actor. The real execution is always happening on the remote node. +The client creates the remote actor and "moves it" to the server. -Actors can be made remote by calling remote().actorOf[MyActor](host, port) +When you define an actor as being remote it is instantiated as on the remote host and your local actor becomes a proxy, it works as a handle to the remote actor. The real execution is always happening on the remote node. + +Actors can be made remote by calling remote.actorOf[MyActor](host, port) Here is an example: @@ -280,29 +271,30 @@ Here is an example: import akka.actor.Actor - class MyActor extends RemoteActor() { + class MyActor extends Actor { def receive = { case "hello" => self.reply("world") } } - val remote = Actor.remote().actorOf[MyActor]("192.68.23.769", 2552) + val remoteActor = Actor.remote.actorOf[MyActor]("192.68.23.769", 2552) An Actor can also start remote child Actors through one of the 'spawn/link' methods. These will start, link and make the Actor remote atomically. .. code-block:: scala ... - spawnRemote[MyActor](hostname, port) - spawnLinkRemote[MyActor](hostname, port) + self.spawnRemote[MyActor](hostname, port, timeout) + self.spawnLinkRemote[MyActor](hostname, port, timeout) ... Server-managed Remote Actors ---------------------------- +Here it is the server that creates the remote actor and the client can ask for a handle to this actor. + Server side setup ^^^^^^^^^^^^^^^^^ - The API for server managed remote actors is really simple. 2 methods only: .. code-block:: scala @@ -358,10 +350,10 @@ There are many variations on the 'remote#actorFor' method. Here are some of them .. code-block:: scala - ... = actorFor(className, hostname, port) - ... = actorFor(className, timeout, hostname, port) - ... = actorFor(uuid, className, hostname, port) - ... = actorFor(uuid, className, timeout, hostname, port) + ... = remote.actorFor(className, hostname, port) + ... = remote.actorFor(className, timeout, hostname, port) + ... = remote.actorFor(uuid, className, hostname, port) + ... = remote.actorFor(uuid, className, timeout, hostname, port) ... // etc All of these also have variations where you can pass in an explicit 'ClassLoader' which can be used when deserializing messages sent from the remote actor. @@ -371,11 +363,16 @@ Running sample Here is a complete running sample (also available `here `_): +Paste in the code below into two sbt concole shells. Then run: + +- ServerInitiatedRemoteActorServer.run() in one shell +- ServerInitiatedRemoteActorClient.run() in the other shell + .. code-block:: scala import akka.actor.Actor - import akka.util.Logging import Actor._ + import akka.event.EventHandler class HelloWorldActor extends Actor { def receive = { @@ -385,27 +382,27 @@ Here is a complete running sample (also available `here self.reply("world") } @@ -430,11 +427,9 @@ Here is an example of overriding the 'id' field: val actor = remote.actorOf[MyActor]("192.68.23.769", 2552) -Remote Typed Actors -------------------- -Client-managed Remote Actors -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Client-managed Remote Typed Actors +---------------------------------- DEPRECATED AS OF 1.1 @@ -458,13 +453,13 @@ You can also define an Typed Actor to be remote programmatically when creating i ... // use pojo as usual -Server-managed Remote Actors -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Server-managed Remote Typed Actors +---------------------------------- WARNING: Remote TypedActors do not work with overloaded methods on your TypedActor, refrain from using overloading. Server side setup -***************** +^^^^^^^^^^^^^^^^^ The API for server managed remote typed actors is nearly the same as for untyped actor @@ -507,20 +502,20 @@ They are also useful if you need to perform some cleanup when a client disconnec Note that the second argument in registerTypedPerSessionActor is an implicit function. It will be called to create an actor every time a session is established. Client side usage -***************** +^^^^^^^^^^^^^^^^^ .. code-block:: scala val actor = remote.typedActorFor(classOf[RegistrationService], "user-service", 5000L, "localhost", 2552) actor.registerUser(…) -There are variations on the 'RemoteClient#typedActorFor' method. Here are some of them: +There are variations on the 'remote#typedActorFor' method. Here are some of them: .. code-block:: scala - ... = typedActorFor(interfaceClazz, serviceIdOrClassName, hostname, port) - ... = typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port) - ... = typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port, classLoader) + ... = remote.typedActorFor(interfaceClazz, serviceIdOrClassName, hostname, port) + ... = remote.typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port) + ... = remote.typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port, classLoader) Data Compression Configuration ------------------------------ @@ -583,15 +578,19 @@ So a simple listener actor can look like this: .. code-block:: scala + import akka.actor.Actor + import akka.actor.Actor._ + import akka.remoteinterface._ + val listener = actorOf(new Actor { def receive = { - case RemoteClientError(cause, client, address) => ... // act upon error - case RemoteClientDisconnected(client, address) => ... // act upon disconnection - case RemoteClientConnected(client, address) => ... // act upon connection - case RemoteClientStarted(client, address) => ... // act upon client shutdown - case RemoteClientShutdown(client, address) => ... // act upon client shutdown - case RemoteClientWriteFailed(request, cause, client, address) => ... // act upon write failure - case _ => //ignore other + case RemoteClientError(cause, client, address) => //... act upon error + case RemoteClientDisconnected(client, address) => //... act upon disconnection + case RemoteClientConnected(client, address) => //... act upon connection + case RemoteClientStarted(client, address) => //... act upon client shutdown + case RemoteClientShutdown(client, address) => //... act upon client shutdown + case RemoteClientWriteFailed(request, cause, client, address) => //... act upon write failure + case _ => // ignore other } }).start() @@ -637,15 +636,19 @@ So a simple listener actor can look like this: .. code-block:: scala + import akka.actor.Actor + import akka.actor.Actor._ + import akka.remoteinterface._ + val listener = actorOf(new Actor { def receive = { - case RemoteServerStarted(server) => ... // act upon server start - case RemoteServerShutdown(server) => ... // act upon server shutdown - case RemoteServerError(cause, server) => ... // act upon server error - case RemoteServerClientConnected(server, clientAddress) => ... // act upon client connection - case RemoteServerClientDisconnected(server, clientAddress) => ... // act upon client disconnection - case RemoteServerClientClosed(server, clientAddress) => ... // act upon client connection close - case RemoteServerWriteFailed(request, cause, server, clientAddress) => ... // act upon server write failure + case RemoteServerStarted(server) => //... act upon server start + case RemoteServerShutdown(server) => //... act upon server shutdown + case RemoteServerError(cause, server) => //... act upon server error + case RemoteServerClientConnected(server, clientAddress) => //... act upon client connection + case RemoteServerClientDisconnected(server, clientAddress) => //... act upon client disconnection + case RemoteServerClientClosed(server, clientAddress) => //... act upon client connection close + case RemoteServerWriteFailed(request, cause, server, clientAddress) => //... act upon server write failure } }).start() @@ -662,7 +665,7 @@ Message Serialization All messages that are sent to remote actors needs to be serialized to binary format to be able to travel over the wire to the remote node. This is done by letting your messages extend one of the traits in the 'akka.serialization.Serializable' object. If the messages don't implement any specific serialization trait then the runtime will try to use standard Java serialization. -Here are some examples, but full documentation can be found in the `Serialization section `_. +Here are some examples, but full documentation can be found in the :ref:`serialization-scala`. Scala JSON ^^^^^^^^^^ @@ -676,7 +679,7 @@ Protobuf Protobuf message specification needs to be compiled with 'protoc' compiler. -.. code-block:: scala +:: message ProtobufPOJO { required uint64 id = 1; diff --git a/akka-remote/src/test/scala/remote/ServerInitiatedRemoteActorSample.scala b/akka-remote/src/test/scala/remote/ServerInitiatedRemoteActorSample.scala index cae866e6e2..0c5a9565a6 100644 --- a/akka-remote/src/test/scala/remote/ServerInitiatedRemoteActorSample.scala +++ b/akka-remote/src/test/scala/remote/ServerInitiatedRemoteActorSample.scala @@ -1,8 +1,8 @@ package akka.actor.remote -import akka.actor.{Actor, ActorRegistry} - +import akka.actor.Actor import Actor._ +import akka.event.EventHandler /************************************* Instructions how to run the sample: @@ -19,14 +19,12 @@ Instructions how to run the sample: * Then paste in the code below into both shells. Then run: -* ServerInitiatedRemoteActorServer.run in one shell -* ServerInitiatedRemoteActorClient.run in one shell +* ServerInitiatedRemoteActorServer.run() in one shell +* ServerInitiatedRemoteActorClient.run() in the other shell Have fun. *************************************/ class HelloWorldActor extends Actor { - self.start() - def receive = { case "Hello" => self.reply("World") } @@ -34,16 +32,22 @@ class HelloWorldActor extends Actor { object ServerInitiatedRemoteActorServer { - def main(args: Array[String]) = { - Actor.remote.start("localhost", 2552) - Actor.remote.register("hello-service", actorOf[HelloWorldActor]) + def run() { + remote.start("localhost", 2552) + remote.register("hello-service", actorOf[HelloWorldActor]) } + + def main(args: Array[String]) { run() } } object ServerInitiatedRemoteActorClient { - def main(args: Array[String]) = { - val actor = Actor.remote.actorFor("hello-service", "localhost", 2552) + + def run() { + val actor = remote.actorFor("hello-service", "localhost", 2552) val result = actor !! "Hello" + EventHandler.info("Result from Remote Actor: %s", result) } + + def main(args: Array[String]) { run() } } From c2486cd52ced11a133c54981b08e39a8601cc39b Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 29 Apr 2011 17:15:00 +0200 Subject: [PATCH 097/100] Fixing ticket 808 --- .../src/main/scala/akka/AkkaException.scala | 2 +- .../src/main/scala/akka/actor/Actor.scala | 21 +++--- .../main/scala/akka/actor/Supervisor.scala | 2 +- .../src/main/scala/akka/config/Config.scala | 4 +- .../main/scala/akka/dataflow/DataFlow.scala | 2 +- .../scala/akka/dispatch/Dispatchers.scala | 16 ++--- .../src/main/scala/akka/dispatch/Future.scala | 2 +- .../scala/akka/dispatch/MailboxHandling.scala | 2 +- .../main/scala/akka/event/EventHandler.scala | 7 +- .../remoteinterface/RemoteInterface.scala | 32 +++++---- .../scala/akka/util/ReflectiveAccess.scala | 72 ++++++++++--------- .../remote/netty/NettyRemoteSupport.scala | 2 +- .../scala/akka/testkit/TestActorRef.scala | 8 ++- .../scala/akka/transactor/Coordination.scala | 2 +- 14 files changed, 95 insertions(+), 79 deletions(-) diff --git a/akka-actor/src/main/scala/akka/AkkaException.scala b/akka-actor/src/main/scala/akka/AkkaException.scala index 748df1ced0..fe0bac916e 100644 --- a/akka-actor/src/main/scala/akka/AkkaException.scala +++ b/akka-actor/src/main/scala/akka/AkkaException.scala @@ -16,7 +16,7 @@ import java.net.{InetAddress, UnknownHostException} * * @author Jonas Bonér */ -class AkkaException(message: String = "") extends RuntimeException(message) with Serializable { +class AkkaException(message: String = "", cause: Throwable = null) extends RuntimeException(message, cause) with Serializable { val uuid = "%s_%s".format(AkkaException.hostname, newUuid) override lazy val toString = { diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index cf4c1bf042..104283d853 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -67,12 +67,12 @@ case class MaximumNumberOfRestartsWithinTimeRangeReached( @BeanProperty val lastExceptionCausingRestart: Throwable) extends LifeCycleMessage // Exceptions for Actors -class ActorStartException private[akka](message: String) extends AkkaException(message) -class IllegalActorStateException private[akka](message: String) extends AkkaException(message) -class ActorKilledException private[akka](message: String) extends AkkaException(message) -class ActorInitializationException private[akka](message: String) extends AkkaException(message) -class ActorTimeoutException private[akka](message: String) extends AkkaException(message) -class InvalidMessageException private[akka](message: String) extends AkkaException(message) +class ActorStartException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause) +class IllegalActorStateException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause) +class ActorKilledException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause) +class ActorInitializationException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause) +class ActorTimeoutException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause) +class InvalidMessageException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause) /** * This message is thrown by default when an Actors behavior doesn't match a message @@ -161,12 +161,15 @@ object Actor extends ListenerManagement { */ def actorOf(clazz: Class[_ <: Actor]): ActorRef = new LocalActorRef(() => { import ReflectiveAccess.{ createInstance, noParams, noArgs } - createInstance[Actor](clazz.asInstanceOf[Class[_]], noParams, noArgs).getOrElse( - throw new ActorInitializationException( + createInstance[Actor](clazz.asInstanceOf[Class[_]], noParams, noArgs) match { + case r: Right[Exception, Actor] => r.b + case l: Left[Exception, Actor] => throw new ActorInitializationException( "Could not instantiate Actor of " + clazz + "\nMake sure Actor is NOT defined inside a class/trait," + "\nif so put it outside the class/trait, f.e. in a companion object," + - "\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.")) + "\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.", l.a) + } + }, None) /** diff --git a/akka-actor/src/main/scala/akka/actor/Supervisor.scala b/akka-actor/src/main/scala/akka/actor/Supervisor.scala index e32b515ae5..50071524dc 100644 --- a/akka-actor/src/main/scala/akka/actor/Supervisor.scala +++ b/akka-actor/src/main/scala/akka/actor/Supervisor.scala @@ -13,7 +13,7 @@ import java.util.concurrent.{CopyOnWriteArrayList, ConcurrentHashMap} import java.net.InetSocketAddress import akka.config.Supervision._ -class SupervisorException private[akka](message: String) extends AkkaException(message) +class SupervisorException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause) /** * Factory object for creating supervisors declarative. It creates instances of the 'Supervisor' class. diff --git a/akka-actor/src/main/scala/akka/config/Config.scala b/akka-actor/src/main/scala/akka/config/Config.scala index 7d50e59cd7..1b5d8f774e 100644 --- a/akka-actor/src/main/scala/akka/config/Config.scala +++ b/akka-actor/src/main/scala/akka/config/Config.scala @@ -6,8 +6,8 @@ package akka.config import akka.AkkaException -class ConfigurationException(message: String) extends AkkaException(message) -class ModuleNotAvailableException(message: String) extends AkkaException(message) +class ConfigurationException(message: String, cause: Throwable = null) extends AkkaException(message, cause) +class ModuleNotAvailableException(message: String, cause: Throwable = null) extends AkkaException(message, cause) /** * Loads up the configuration (from the akka.conf file). diff --git a/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala b/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala index 446bc9652b..258bc4fff0 100644 --- a/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala +++ b/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala @@ -23,7 +23,7 @@ object DataFlow { object Start object Exit - class DataFlowVariableException(msg: String) extends AkkaException(msg) + class DataFlowVariableException(message: String, cause: Throwable = null) extends AkkaException(message, cause) /** * Executes the supplied thunk in another thread. diff --git a/akka-actor/src/main/scala/akka/dispatch/Dispatchers.scala b/akka-actor/src/main/scala/akka/dispatch/Dispatchers.scala index 04ff6a9504..eee5d53c51 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Dispatchers.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Dispatchers.scala @@ -187,14 +187,14 @@ object Dispatchers { case "GlobalExecutorBasedEventDriven" => GlobalExecutorBasedEventDrivenDispatcherConfigurator case fqn => ReflectiveAccess.getClassFor[MessageDispatcherConfigurator](fqn) match { - case Some(clazz) => - val instance = ReflectiveAccess.createInstance[MessageDispatcherConfigurator](clazz, Array[Class[_]](), Array[AnyRef]()) - if (instance.isEmpty) - throw new IllegalArgumentException("Cannot instantiate MessageDispatcherConfigurator type [%s], make sure it has a default no-args constructor" format fqn) - else - instance.get - case None => - throw new IllegalArgumentException("Unknown MessageDispatcherConfigurator type [%s]" format fqn) + case r: Right[_, Class[MessageDispatcherConfigurator]] => + ReflectiveAccess.createInstance[MessageDispatcherConfigurator](r.b, Array[Class[_]](), Array[AnyRef]()) match { + case r: Right[Exception, MessageDispatcherConfigurator] => r.b + case l: Left[Exception, MessageDispatcherConfigurator] => + throw new IllegalArgumentException("Cannot instantiate MessageDispatcherConfigurator type [%s], make sure it has a default no-args constructor" format fqn, l.a) + } + case l: Left[Exception, _] => + throw new IllegalArgumentException("Unknown MessageDispatcherConfigurator type [%s]" format fqn, l.a) } } map { _ configure cfg diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index ff0b6fdc57..11c124e55c 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -19,7 +19,7 @@ import java.util.{LinkedList => JLinkedList} import scala.collection.mutable.Stack import annotation.tailrec -class FutureTimeoutException(message: String) extends AkkaException(message) +class FutureTimeoutException(message: String, cause: Throwable = null) extends AkkaException(message, cause) object Futures { diff --git a/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala b/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala index cacdefe95c..388d8f10b0 100644 --- a/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala +++ b/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala @@ -11,7 +11,7 @@ import java.util.{Queue, List, Comparator, PriorityQueue} import java.util.concurrent._ import akka.util._ -class MessageQueueAppendFailedException(message: String) extends AkkaException(message) +class MessageQueueAppendFailedException(message: String, cause: Throwable = null) extends AkkaException(message, cause) /** * @author Jonas Bonér diff --git a/akka-actor/src/main/scala/akka/event/EventHandler.scala b/akka-actor/src/main/scala/akka/event/EventHandler.scala index 1d7d81c1b6..15dd7eaa30 100644 --- a/akka-actor/src/main/scala/akka/event/EventHandler.scala +++ b/akka-actor/src/main/scala/akka/event/EventHandler.scala @@ -220,14 +220,15 @@ object EventHandler extends ListenerManagement { } defaultListeners foreach { listenerName => try { - ReflectiveAccess.getClassFor[Actor](listenerName) map { clazz => - addListener(Actor.actorOf(clazz).start()) + ReflectiveAccess.getClassFor[Actor](listenerName) match { + case r: Right[_, Class[Actor]] => addListener(Actor.actorOf(r.b).start()) + case l: Left[Exception,_] => throw l.a } } catch { case e: Exception => throw new ConfigurationException( "Event Handler specified in config can't be loaded [" + listenerName + - "] due to [" + e.toString + "]") + "] due to [" + e.toString + "]", e) } } } diff --git a/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala b/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala index 7b61f224e8..695885ad9a 100644 --- a/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala +++ b/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala @@ -116,7 +116,7 @@ case class RemoteServerWriteFailed( class RemoteClientException private[akka] ( message: String, @BeanProperty val client: RemoteClientModule, - val remoteAddress: InetSocketAddress) extends AkkaException(message) + val remoteAddress: InetSocketAddress, cause: Throwable = null) extends AkkaException(message, cause) /** * Thrown when the remote server actor dispatching fails for some reason. @@ -189,13 +189,14 @@ abstract class RemoteSupport extends ListenerManagement with RemoteServerModule def actorOf(clazz: Class[_ <: Actor], host: String, port: Int): ActorRef = { import ReflectiveAccess.{ createInstance, noParams, noArgs } clientManagedActorOf(() => - createInstance[Actor](clazz.asInstanceOf[Class[_]], noParams, noArgs).getOrElse( - throw new ActorInitializationException( - "Could not instantiate Actor" + - "\nMake sure Actor is NOT defined inside a class/trait," + - "\nif so put it outside the class/trait, f.e. in a companion object," + - "\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.")), - host, port) + createInstance[Actor](clazz.asInstanceOf[Class[_]], noParams, noArgs) match { + case r: Right[_, Actor] => r.b + case l: Left[Exception, _] => throw new ActorInitializationException( + "Could not instantiate Actor" + + "\nMake sure Actor is NOT defined inside a class/trait," + + "\nif so put it outside the class/trait, f.e. in a companion object," + + "\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.", l.a) + }, host, port) } /** @@ -217,13 +218,14 @@ abstract class RemoteSupport extends ListenerManagement with RemoteServerModule def actorOf[T <: Actor : Manifest](host: String, port: Int): ActorRef = { import ReflectiveAccess.{ createInstance, noParams, noArgs } clientManagedActorOf(() => - createInstance[Actor](manifest[T].erasure.asInstanceOf[Class[_]], noParams, noArgs).getOrElse( - throw new ActorInitializationException( - "Could not instantiate Actor" + - "\nMake sure Actor is NOT defined inside a class/trait," + - "\nif so put it outside the class/trait, f.e. in a companion object," + - "\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.")), - host, port) + createInstance[Actor](manifest[T].erasure.asInstanceOf[Class[_]], noParams, noArgs) match { + case r: Right[_, Actor] => r.b + case l: Left[Exception, _] => throw new ActorInitializationException( + "Could not instantiate Actor" + + "\nMake sure Actor is NOT defined inside a class/trait," + + "\nif so put it outside the class/trait, f.e. in a companion object," + + "\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.", l.a) + }, host, port) } protected override def manageLifeCycleOfListeners = false diff --git a/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala b/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala index cda46c1a95..6164be7bef 100644 --- a/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala +++ b/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala @@ -42,12 +42,16 @@ object ReflectiveAccess { lazy val isEnabled = remoteSupportClass.isDefined def ensureEnabled = if (!isEnabled) { - val e = new ModuleNotAvailableException( - "Can't load the remoting module, make sure that akka-remote.jar is on the classpath") + val e = new ModuleNotAvailableException("Can't load the remoting module, make sure that akka-remote.jar is on the classpath") EventHandler.debug(this, e.toString) throw e } - val remoteSupportClass: Option[Class[_ <: RemoteSupport]] = getClassFor(TRANSPORT) + val remoteSupportClass = getClassFor[RemoteSupport](TRANSPORT) match { + case r: Right[_, Class[RemoteSupport]] => Some(r.b) + case l: Left[Exception,_] => + EventHandler.debug(this, l.a) + None + } protected[akka] val defaultRemoteSupport: Option[() => RemoteSupport] = remoteSupportClass map { remoteClass => @@ -55,9 +59,11 @@ object ReflectiveAccess { remoteClass, Array[Class[_]](), Array[AnyRef]() - ) getOrElse { + ) match { + case r: Right[Exception, RemoteSupport] => r.b + case l: Left[Exception, RemoteSupport] => val e = new ModuleNotAvailableException( - "Can't instantiate [%s] - make sure that akka-remote.jar is on the classpath".format(remoteClass.getName)) + "Can't instantiate [%s] - make sure that akka-remote.jar is on the classpath".format(remoteClass.getName), l.a) EventHandler.debug(this, e.toString) throw e } @@ -114,7 +120,12 @@ object ReflectiveAccess { getObjectFor("akka.cloud.cluster.Cluster$") val serializerClass: Option[Class[_]] = - getClassFor("akka.serialization.Serializer") + getClassFor("akka.serialization.Serializer") match { + case r: Right[_, Class[_]] => Some(r.b) + case l: Left[Exception,_] => + EventHandler.debug(this, l.toString) + None + } def ensureEnabled = if (!isEnabled) throw new ModuleNotAvailableException( "Feature is only available in Akka Cloud") @@ -125,17 +136,15 @@ object ReflectiveAccess { def createInstance[T](clazz: Class[_], params: Array[Class[_]], - args: Array[AnyRef]): Option[T] = try { + args: Array[AnyRef]): Either[Exception,T] = try { assert(clazz ne null) assert(params ne null) assert(args ne null) val ctor = clazz.getDeclaredConstructor(params: _*) ctor.setAccessible(true) - Some(ctor.newInstance(args: _*).asInstanceOf[T]) + Right(ctor.newInstance(args: _*).asInstanceOf[T]) } catch { - case e: Exception => - EventHandler.debug(this, e.toString) - None + case e: Exception => Left(e) } def createInstance[T](fqn: String, @@ -145,11 +154,11 @@ object ReflectiveAccess { assert(params ne null) assert(args ne null) getClassFor(fqn) match { - case Some(clazz) => - val ctor = clazz.getDeclaredConstructor(params: _*) + case r: Right[Exception, Class[T]] => + val ctor = r.b.getDeclaredConstructor(params: _*) ctor.setAccessible(true) Some(ctor.newInstance(args: _*).asInstanceOf[T]) - case None => None + case _ => None } } catch { case e: Exception => @@ -159,11 +168,11 @@ object ReflectiveAccess { def getObjectFor[T](fqn: String, classloader: ClassLoader = loader): Option[T] = try {//Obtains a reference to $MODULE$ getClassFor(fqn) match { - case Some(clazz) => - val instance = clazz.getDeclaredField("MODULE$") + case r: Right[Exception, Class[T]] => + val instance = r.b.getDeclaredField("MODULE$") instance.setAccessible(true) Option(instance.get(null).asInstanceOf[T]) - case None => None + case _ => None } } catch { case e: ExceptionInInitializerError => @@ -171,45 +180,44 @@ object ReflectiveAccess { throw e } - def getClassFor[T](fqn: String, classloader: ClassLoader = loader): Option[Class[T]] = { + def getClassFor[T](fqn: String, classloader: ClassLoader = loader): Either[Exception,Class[T]] = try { assert(fqn ne null) // First, use the specified CL val first = try { - Option(classloader.loadClass(fqn).asInstanceOf[Class[T]]) + Right(classloader.loadClass(fqn).asInstanceOf[Class[T]]) } catch { - case c: ClassNotFoundException => None + case c: ClassNotFoundException => Left(c) } - if (first.isDefined) first + if (first.isRight) first else { // Second option is to use the ContextClassLoader val second = try { - Option(Thread.currentThread.getContextClassLoader.loadClass(fqn).asInstanceOf[Class[T]]) + Right(Thread.currentThread.getContextClassLoader.loadClass(fqn).asInstanceOf[Class[T]]) } catch { - case c: ClassNotFoundException => None + case c: ClassNotFoundException => Left(c) } - if (second.isDefined) second + if (second.isRight) second else { val third = try { - // Don't try to use "loader" if we got the default "classloader" parameter - if (classloader ne loader) Option(loader.loadClass(fqn).asInstanceOf[Class[T]]) - else None + if (classloader ne loader) Right(loader.loadClass(fqn).asInstanceOf[Class[T]]) else Left(null) //Horrid } catch { - case c: ClassNotFoundException => None + case c: ClassNotFoundException => Left(c) } - if (third.isDefined) third + if (third.isRight) third else { - // Last option is Class.forName try { - Option(Class.forName(fqn).asInstanceOf[Class[T]]) + Right(Class.forName(fqn).asInstanceOf[Class[T]]) // Last option is Class.forName } catch { - case c: ClassNotFoundException => None + case c: ClassNotFoundException => Left(c) } } } } + } catch { + case e: Exception => Left(e) } } 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 7196231c2d..7caea56e88 100644 --- a/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala +++ b/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala @@ -41,7 +41,7 @@ import java.util.concurrent.atomic.{AtomicReference, AtomicBoolean} import java.util.concurrent._ import akka.AkkaException -class RemoteClientMessageBufferException(message: String) extends AkkaException(message) +class RemoteClientMessageBufferException(message: String, cause: Throwable = null) extends AkkaException(message, cause) object RemoteEncoder { def encode(rmp: RemoteMessageProtocol): AkkaRemoteProtocol = { diff --git a/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala b/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala index a31582ac3a..53ceed69eb 100644 --- a/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala @@ -72,12 +72,14 @@ object TestActorRef { def apply[T <: Actor : Manifest] : TestActorRef[T] = new TestActorRef[T] ({ () => import ReflectiveAccess.{ createInstance, noParams, noArgs } - createInstance[T](manifest[T].erasure, noParams, noArgs).getOrElse( - throw new ActorInitializationException( + createInstance[T](manifest[T].erasure, noParams, noArgs) match { + case r: Right[_, T] => r.b + case l: Left[Exception, _] => throw new ActorInitializationException( "Could not instantiate Actor" + "\nMake sure Actor is NOT defined inside a class/trait," + "\nif so put it outside the class/trait, f.e. in a companion object," + - "\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.")) + "\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.", l.a) + } }) } diff --git a/akka-typed-actor/src/main/scala/akka/transactor/Coordination.scala b/akka-typed-actor/src/main/scala/akka/transactor/Coordination.scala index 33a74fea79..1f72176eed 100644 --- a/akka-typed-actor/src/main/scala/akka/transactor/Coordination.scala +++ b/akka-typed-actor/src/main/scala/akka/transactor/Coordination.scala @@ -9,7 +9,7 @@ import akka.stm.Atomic import scala.util.DynamicVariable -class CoordinateException private[akka](message: String) extends AkkaException(message) +class CoordinateException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause) /** * Coordinating transactions between typed actors. From 8d95f180a97a709ef6573785d85bfca5394f6441 Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Fri, 29 Apr 2011 21:11:20 +0200 Subject: [PATCH 098/100] also adapt createInstance(String, ...) and getObjectFor --- .../scala/akka/util/ReflectiveAccess.scala | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala b/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala index 6164be7bef..f1b76da678 100644 --- a/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala +++ b/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala @@ -91,7 +91,12 @@ object ReflectiveAccess { "Can't load the typed actor module, make sure that akka-typed-actor.jar is on the classpath") val typedActorObjectInstance: Option[TypedActorObject] = - getObjectFor("akka.actor.TypedActor$") + getObjectFor[TypedActorObject]("akka.actor.TypedActor$") match { + case r: Right[_, TypedActorObject] => Some(r.b) + case l: Left[Exception, _] => + EventHandler.debug(this, l.toString) + None + } def resolveFutureIfMessageIsJoinPoint(message: Any, future: Future[_]): Boolean = { ensureEnabled @@ -117,7 +122,12 @@ object ReflectiveAccess { lazy val isEnabled = clusterObjectInstance.isDefined val clusterObjectInstance: Option[AnyRef] = - getObjectFor("akka.cloud.cluster.Cluster$") + getObjectFor[AnyRef]("akka.cloud.cluster.Cluster$") match { + case r: Right[_, AnyRef] => Some(r.b) + case l: Left[Exception, _] => + EventHandler.debug(this, l.toString) + None + } val serializerClass: Option[Class[_]] = getClassFor("akka.serialization.Serializer") match { @@ -150,34 +160,34 @@ object ReflectiveAccess { def createInstance[T](fqn: String, params: Array[Class[_]], args: Array[AnyRef], - classloader: ClassLoader = loader): Option[T] = try { + classloader: ClassLoader = loader): Either[Exception,T] = try { assert(params ne null) assert(args ne null) getClassFor(fqn) match { - case r: Right[Exception, Class[T]] => + case r: Right[_, Class[T]] => val ctor = r.b.getDeclaredConstructor(params: _*) ctor.setAccessible(true) - Some(ctor.newInstance(args: _*).asInstanceOf[T]) - case _ => None + Right(ctor.newInstance(args: _*).asInstanceOf[T]) + case l : Left[Exception, _] => Left(l.a) } } catch { case e: Exception => - EventHandler.debug(this, e.toString) - None + Left(e) } - def getObjectFor[T](fqn: String, classloader: ClassLoader = loader): Option[T] = try {//Obtains a reference to $MODULE$ + //Obtains a reference to fqn.MODULE$ + def getObjectFor[T](fqn: String, classloader: ClassLoader = loader): Either[Exception,T] = try { getClassFor(fqn) match { - case r: Right[Exception, Class[T]] => + case r: Right[_, Class[_]] => val instance = r.b.getDeclaredField("MODULE$") instance.setAccessible(true) - Option(instance.get(null).asInstanceOf[T]) - case _ => None + val obj = instance.get(null) + if (obj eq null) Left(new NullPointerException) else Right(obj.asInstanceOf[T]) + case l : Left[Exception, _] => Left(l.a) } } catch { - case e: ExceptionInInitializerError => - EventHandler.debug(this, e.toString) - throw e + case e: Exception => + Left(e) } def getClassFor[T](fqn: String, classloader: ClassLoader = loader): Either[Exception,Class[T]] = try { From 20c5be20e61c7d5f98e441dda76e159ca1fc8379 Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Sat, 30 Apr 2011 11:22:51 +0200 Subject: [PATCH 099/100] fix exception logging --- akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala b/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala index f1b76da678..374a60928a 100644 --- a/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala +++ b/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala @@ -94,7 +94,7 @@ object ReflectiveAccess { getObjectFor[TypedActorObject]("akka.actor.TypedActor$") match { case r: Right[_, TypedActorObject] => Some(r.b) case l: Left[Exception, _] => - EventHandler.debug(this, l.toString) + EventHandler.debug(this, l.a.toString) None } @@ -125,7 +125,7 @@ object ReflectiveAccess { getObjectFor[AnyRef]("akka.cloud.cluster.Cluster$") match { case r: Right[_, AnyRef] => Some(r.b) case l: Left[Exception, _] => - EventHandler.debug(this, l.toString) + EventHandler.debug(this, l.a.toString) None } @@ -133,7 +133,7 @@ object ReflectiveAccess { getClassFor("akka.serialization.Serializer") match { case r: Right[_, Class[_]] => Some(r.b) case l: Left[Exception,_] => - EventHandler.debug(this, l.toString) + EventHandler.debug(this, l.a.toString) None } From 08049c5c9d6d1463df036bf7949ba0d3f96553ae Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sat, 30 Apr 2011 13:18:04 +0200 Subject: [PATCH 100/100] Rewriting matches to use case-class extractors --- .../scala/akka/util/ReflectiveAccess.scala | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala b/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala index 374a60928a..14b46ceae5 100644 --- a/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala +++ b/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala @@ -47,9 +47,9 @@ object ReflectiveAccess { throw e } val remoteSupportClass = getClassFor[RemoteSupport](TRANSPORT) match { - case r: Right[_, Class[RemoteSupport]] => Some(r.b) - case l: Left[Exception,_] => - EventHandler.debug(this, l.a) + case Right(value) => Some(value) + case Left(exception) => + EventHandler.debug(this, exception.toString) None } @@ -60,10 +60,10 @@ object ReflectiveAccess { Array[Class[_]](), Array[AnyRef]() ) match { - case r: Right[Exception, RemoteSupport] => r.b - case l: Left[Exception, RemoteSupport] => + case Right(value) => value + case Left(exception) => val e = new ModuleNotAvailableException( - "Can't instantiate [%s] - make sure that akka-remote.jar is on the classpath".format(remoteClass.getName), l.a) + "Can't instantiate [%s] - make sure that akka-remote.jar is on the classpath".format(remoteClass.getName), exception) EventHandler.debug(this, e.toString) throw e } @@ -92,9 +92,9 @@ object ReflectiveAccess { val typedActorObjectInstance: Option[TypedActorObject] = getObjectFor[TypedActorObject]("akka.actor.TypedActor$") match { - case r: Right[_, TypedActorObject] => Some(r.b) - case l: Left[Exception, _] => - EventHandler.debug(this, l.a.toString) + case Right(value) => Some(value) + case Left(exception)=> + EventHandler.debug(this, exception.toString) None } @@ -123,17 +123,17 @@ object ReflectiveAccess { val clusterObjectInstance: Option[AnyRef] = getObjectFor[AnyRef]("akka.cloud.cluster.Cluster$") match { - case r: Right[_, AnyRef] => Some(r.b) - case l: Left[Exception, _] => - EventHandler.debug(this, l.a.toString) + case Right(value) => Some(value) + case Left(exception) => + EventHandler.debug(this, exception.toString) None } val serializerClass: Option[Class[_]] = getClassFor("akka.serialization.Serializer") match { - case r: Right[_, Class[_]] => Some(r.b) - case l: Left[Exception,_] => - EventHandler.debug(this, l.a.toString) + case Right(value) => Some(value) + case Left(exception) => + EventHandler.debug(this, exception.toString) None } @@ -164,11 +164,11 @@ object ReflectiveAccess { assert(params ne null) assert(args ne null) getClassFor(fqn) match { - case r: Right[_, Class[T]] => - val ctor = r.b.getDeclaredConstructor(params: _*) + case Right(value) => + val ctor = value.getDeclaredConstructor(params: _*) ctor.setAccessible(true) Right(ctor.newInstance(args: _*).asInstanceOf[T]) - case l : Left[Exception, _] => Left(l.a) + case Left(exception) => Left(exception) //We could just cast this to Either[Exception, T] but it's ugly } } catch { case e: Exception => @@ -178,12 +178,12 @@ object ReflectiveAccess { //Obtains a reference to fqn.MODULE$ def getObjectFor[T](fqn: String, classloader: ClassLoader = loader): Either[Exception,T] = try { getClassFor(fqn) match { - case r: Right[_, Class[_]] => - val instance = r.b.getDeclaredField("MODULE$") + case Right(value) => + val instance = value.getDeclaredField("MODULE$") instance.setAccessible(true) val obj = instance.get(null) if (obj eq null) Left(new NullPointerException) else Right(obj.asInstanceOf[T]) - case l : Left[Exception, _] => Left(l.a) + case Left(exception) => Left(exception) //We could just cast this to Either[Exception, T] but it's ugly } } catch { case e: Exception =>