From 120f12d739d727bbb3cba3c4f9375cd62ba6803c Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Thu, 21 Apr 2011 15:12:47 -0600 Subject: [PATCH 001/233] Adding delimited continuations to Future --- .../test/scala/akka/dispatch/FutureSpec.scala | 95 +++++++++++++++---- .../src/main/scala/akka/dispatch/Future.scala | 34 ++++--- project/build/AkkaProject.scala | 10 +- 3 files changed, 106 insertions(+), 33 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..34edbb653f 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -351,34 +351,93 @@ class FutureSpec extends JUnitSuite { val latch = new StandardLatch val f = Future({ latch.await; 5}) - val f2 = Future({ f() + 5 }) + val f2 = Future({ f.get + 5 }) assert(f2.resultOrException === None) latch.open - assert(f2() === 10) + assert(f2.get === 10) val f3 = Future({ Thread.sleep(100); 5}, 10) intercept[FutureTimeoutException] { - f3() + f3.get } } - @Test def lesslessIsMore { - import akka.actor.Actor.spawn - val dataflowVar, dataflowVar2 = new DefaultCompletableFuture[Int](Long.MaxValue) - val begin, end = new StandardLatch - spawn { - begin.await - dataflowVar2 << dataflowVar - end.open + @Test def futureComposingWithContinuations { + import Future.flow + + val actor = actorOf[TestActor].start + + val x = Future("Hello") + val y = x flatMap (actor !!! _) + + val r = flow(x() + " " + y[String]() + "!") + + assert(r.get === "Hello World!") + + actor.stop + } + + @Test def futureComposingWithContinuationsFailureDivideZero { + import Future.flow + + val x = Future("Hello") + val y = x map (_.length) + + val r = flow(x() + " " + y.map(_ / 0).map(_.toString)(), 100) + + intercept[java.lang.ArithmeticException](r.get) + } + + @Test def futureComposingWithContinuationsFailureCastInt { + import Future.flow + + val actor = actorOf[TestActor].start + + val x = Future(3) + val y = actor !!! "Hello" + + val r = flow(x() + y[Int](), 100) + + intercept[ClassCastException](r.get) + } + + @Test def futureComposingWithContinuationsFailureCastNothing { + import Future.flow + + val actor = actorOf[TestActor].start + + val x = Future("Hello") + val y = actor !!! "Hello" + + val r = flow(x() + y()) + + intercept[ClassCastException](r.get) + } + + @Test def futureCompletingWithContinuations { + import Future.flow + + val x, y, z = new DefaultCompletableFuture[Int](Actor.TIMEOUT) + val ly, lz = new StandardLatch + + val result = flow { + y completeWith x + ly.open // not within continuation + + z << x + lz.open // within continuation, will wait for 'z' to complete + z() + y() } - spawn { - dataflowVar << 5 - } - begin.open - end.await - assert(dataflowVar2() === 5) - assert(dataflowVar.get === 5) + assert(ly.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS)) + assert(!lz.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS)) + + x << 5 + + assert(y.get === 5) + assert(z.get === 5) + assert(lz.isOpen) + assert(result.get === 10) } } diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index c69ca82bad..f66d1ab25b 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -10,6 +10,8 @@ import akka.actor.Actor import akka.routing.Dispatcher import akka.japi.{ Procedure, Function => JFunc } +import scala.util.continuations._ + import java.util.concurrent.locks.ReentrantLock import java.util.concurrent. {ConcurrentLinkedQueue, TimeUnit, Callable} import java.util.concurrent.TimeUnit.{NANOSECONDS => NANOS, MILLISECONDS => MILLIS} @@ -261,22 +263,30 @@ object Future { val fb = fn(a.asInstanceOf[A]) for (r <- fr; b <-fb) yield (r += b) }.map(_.result) + + def flow[A](body: => A @cpsParam[Future[Any],Future[Any]], timeout: Long = Actor.TIMEOUT): Future[A] = { + + val future = new DefaultCompletableFuture[A](timeout) + + reset(future completeWithResult body) onComplete { f => + val ex = f.exception + if (ex.isDefined) future.completeWithException(ex.get) + } + + future + } } sealed trait Future[+T] { - /** - * Returns the result of this future after waiting for it to complete, - * this method will throw any throwable that this Future was completed with - * and will throw a java.util.concurrent.TimeoutException if there is no result - * within the Futures timeout - */ - def apply(): T = this.await.resultOrException.get + def apply[A >: T](): A @cpsParam[Future[Any],Future[Any]] = shift { f: (A => Future[Any]) => + (new DefaultCompletableFuture[Any](timeoutInNanos, NANOS)) completeWith (this flatMap f) + } /** * Java API for apply() */ - def get: T = apply() + def get: T = this.await.resultOrException.get /** * Blocks the current thread until the Future has been completed or the @@ -581,10 +591,10 @@ trait CompletableFuture[T] extends Future[T] { */ final def << (value: T): Future[T] = complete(Right(value)) - /** - * Alias for completeWith(other). - */ - final def << (other : Future[T]): Future[T] = completeWith(other) + final def << (other: Future[T]): T @cpsParam[Future[Any],Future[Any]] = shift { k: (T => Future[Any]) => + this completeWith other flatMap k + } + } /** diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 8073d8831b..db1f97ae80 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -10,7 +10,7 @@ import sbt._ import sbt.CompileOrder._ import spde._ -class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { +class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) with AutoCompilerPlugins { // ------------------------------------------------------------------------------------------------------------------- // Compile settings @@ -273,8 +273,10 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // akka-actor subproject // ------------------------------------------------------------------------------------------------------------------- - class AkkaActorProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with OsgiProject { + class AkkaActorProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with OsgiProject with AutoCompilerPlugins { override def bndExportPackage = super.bndExportPackage ++ Seq("com.eaio.*;version=3.2") + val cont = compilerPlugin("org.scala-lang.plugins" % "continuations" % "2.9.0.RC1") + override def compileOptions = super.compileOptions ++ compileOptions("-P:continuations:enable") } // ------------------------------------------------------------------------------------------------------------------- @@ -436,11 +438,13 @@ 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, distPath) with AutoCompilerPlugins { // testing val junit = Dependencies.junit val scalatest = Dependencies.scalatest val multiverse_test = Dependencies.multiverse_test // StandardLatch + val cont = compilerPlugin("org.scala-lang.plugins" % "continuations" % "2.9.0.RC1") + override def compileOptions = super.compileOptions ++ compileOptions("-P:continuations:enable") } // ------------------------------------------------------------------------------------------------------------------- From eecfea5c5ef5d026a74bb41e51e31c73c689af1b Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Sat, 23 Apr 2011 07:42:30 -0600 Subject: [PATCH 002/233] Add additional test to make sure Future.flow does not block on long running Futures --- .../test/scala/akka/dispatch/FutureSpec.scala | 20 +++++++++++++++++++ 1 file changed, 20 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 34edbb653f..a7c0590161 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -440,4 +440,24 @@ class FutureSpec extends JUnitSuite { assert(lz.isOpen) assert(result.get === 10) } + + @Test def futureContinuationsShouldNotBlock { + import Future.flow + + val latch = new StandardLatch + val future = Future { + latch.await + "Hello" + } + + val result = flow { + Some(future()).filter(_ == "Hello") + } + + assert(!result.isCompleted) + + latch.open + + assert(result.get === Some("Hello")) + } } From 62c3419f31e8b11c787a474dee2a2527bd882fbd Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Sat, 23 Apr 2011 07:43:44 -0600 Subject: [PATCH 003/233] Remove redundant Future --- akka-actor/src/main/scala/akka/dispatch/Future.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index f66d1ab25b..ebf643cc01 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -279,9 +279,7 @@ object Future { sealed trait Future[+T] { - def apply[A >: T](): A @cpsParam[Future[Any],Future[Any]] = shift { f: (A => Future[Any]) => - (new DefaultCompletableFuture[Any](timeoutInNanos, NANOS)) completeWith (this flatMap f) - } + def apply[A >: T](): A @cpsParam[Future[Any],Future[Any]] = shift(this.flatMap(_)) /** * Java API for apply() From b692a8c5dd33e049895c858c0bb2667f5686e9ae Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Sat, 23 Apr 2011 07:49:06 -0600 Subject: [PATCH 004/233] Use simpler annotation --- akka-actor/src/main/scala/akka/dispatch/Future.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index ebf643cc01..35ff398d54 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -264,7 +264,7 @@ object Future { for (r <- fr; b <-fb) yield (r += b) }.map(_.result) - def flow[A](body: => A @cpsParam[Future[Any],Future[Any]], timeout: Long = Actor.TIMEOUT): Future[A] = { + def flow[A](body: => A @cps[Future[Any]], timeout: Long = Actor.TIMEOUT): Future[A] = { val future = new DefaultCompletableFuture[A](timeout) @@ -279,7 +279,7 @@ object Future { sealed trait Future[+T] { - def apply[A >: T](): A @cpsParam[Future[Any],Future[Any]] = shift(this.flatMap(_)) + def apply[A >: T](): A @cps[Future[Any]] = shift(this.flatMap(_)) /** * Java API for apply() @@ -589,7 +589,7 @@ trait CompletableFuture[T] extends Future[T] { */ final def << (value: T): Future[T] = complete(Right(value)) - final def << (other: Future[T]): T @cpsParam[Future[Any],Future[Any]] = shift { k: (T => Future[Any]) => + final def << (other: Future[T]): T @cps[Future[Any]] = shift { k: (T => Future[Any]) => this completeWith other flatMap k } From 530be7b95dbe5d33b939c4bbea32d865cae093fc Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Sat, 23 Apr 2011 09:14:20 -0600 Subject: [PATCH 005/233] Fix CompletableFuture.<<(other: Future) to return a Future instead of the result --- .../test/scala/akka/dispatch/FutureSpec.scala | 41 +++++++++++++++++++ .../src/main/scala/akka/dispatch/Future.scala | 16 ++++++-- 2 files changed, 54 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 a7c0590161..bcb1a8f8ab 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -439,6 +439,47 @@ class FutureSpec extends JUnitSuite { assert(z.get === 5) assert(lz.isOpen) assert(result.get === 10) + + val a, b, c = new DefaultCompletableFuture[Int](Actor.TIMEOUT) + + val result2 = flow { + a << (c() - 2) + val n = c() + 10 + val bb = b << a + a() + n * bb() + } + + c completeWith Future(5) + + assert(a.get === 3) + assert(b.get === 3) + assert(result2.get === 48) + } + + @Test def futureCompletingWithContinuationsFailure { + import Future.flow + + val x, y, z = new DefaultCompletableFuture[Int](Actor.TIMEOUT) + val ly, lz = new StandardLatch + + val result = flow { + y << x + ly.open + val oops = 1 / 0 + z << x + lz.open + z() + y() + oops + } + + assert(!ly.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS)) + assert(!lz.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS)) + + x << 5 + + assert(y.get === 5) + intercept[java.lang.ArithmeticException](result.get) + assert(z.value === None) + assert(!lz.isOpen) } @Test def futureContinuationsShouldNotBlock { diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 35ff398d54..f93696f925 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -279,7 +279,7 @@ object Future { sealed trait Future[+T] { - def apply[A >: T](): A @cps[Future[Any]] = shift(this.flatMap(_)) + def apply[A >: T](): A @cps[Future[Any]] = shift(this flatMap _) /** * Java API for apply() @@ -589,8 +589,18 @@ trait CompletableFuture[T] extends Future[T] { */ final def << (value: T): Future[T] = complete(Right(value)) - final def << (other: Future[T]): T @cps[Future[Any]] = shift { k: (T => Future[Any]) => - this completeWith other flatMap k + final def << (other: Future[T]): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] => Future[Any]) => + val fr = new DefaultCompletableFuture[Any](Actor.TIMEOUT) + this completeWith other onComplete { f => + try { + fr completeWith cont(f) + } catch { + case e: Exception => + EventHandler.error(e, this, e.getMessage) + fr completeWithException e + } + } + fr } } From 5dfc416607c26ce24f78560fcd885ccf0bc5f9d3 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Sat, 23 Apr 2011 09:21:04 -0600 Subject: [PATCH 006/233] make test more aggressive --- .../src/test/scala/akka/dispatch/FutureSpec.scala | 11 +++++------ 1 file changed, 5 insertions(+), 6 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 bcb1a8f8ab..e88799161e 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -443,17 +443,16 @@ class FutureSpec extends JUnitSuite { val a, b, c = new DefaultCompletableFuture[Int](Actor.TIMEOUT) val result2 = flow { - a << (c() - 2) - val n = c() + 10 - val bb = b << a - a() + n * bb() + val n = (a << c).result.get + 10 + b << (c() - 2) + a() + n * b() } c completeWith Future(5) - assert(a.get === 3) + assert(a.get === 5) assert(b.get === 3) - assert(result2.get === 48) + assert(result2.get === 50) } @Test def futureCompletingWithContinuationsFailure { From 2c9a813eb61c7b6fcb331c8b54233bd10450bbd7 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Sat, 23 Apr 2011 11:53:51 -0600 Subject: [PATCH 007/233] Refactor Future.flow --- .../src/main/scala/akka/dispatch/Future.scala | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index f93696f925..fc8e8ecf3f 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -264,17 +264,8 @@ object Future { for (r <- fr; b <-fb) yield (r += b) }.map(_.result) - def flow[A](body: => A @cps[Future[Any]], timeout: Long = Actor.TIMEOUT): Future[A] = { - - val future = new DefaultCompletableFuture[A](timeout) - - reset(future completeWithResult body) onComplete { f => - val ex = f.exception - if (ex.isDefined) future.completeWithException(ex.get) - } - - future - } + def flow[A](body: => A @cps[Future[A]], timeout: Long = Actor.TIMEOUT): Future[A] = + reset(new DefaultCompletableFuture[A](timeout).completeWithResult(body)) } sealed trait Future[+T] { From e74aa8f09fbda2597167f1d4e639775016ac4782 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Sat, 23 Apr 2011 12:57:28 -0600 Subject: [PATCH 008/233] Add documentation to Future.flow and Future.apply --- .../src/main/scala/akka/dispatch/Future.scala | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index fc8e8ecf3f..ac2ee9bbe0 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -264,12 +264,38 @@ object Future { for (r <- fr; b <-fb) yield (r += b) }.map(_.result) + /** + * Captures a block that will be transformed into 'Continuation Passing Style' using Scala's Delimited + * Continuations plugin. + * + * Within the block, the result of a Future may be accessed by calling Future.apply. At that point + * execution is suspended with the rest of the block being stored in a continuation until the result + * of the Future is available. If an Exception is thrown while processing, it will be contained + * within the resulting Future. + * + * This allows working with Futures in an imperative style without blocking for each result. + * + * Completing a Future using 'CompletableFuture << Future' will also suspend execution until the + * value of the other Future is available. + * + * The Delimited Continuations compiler plugin must be enabled in order to use this method. + */ def flow[A](body: => A @cps[Future[A]], timeout: Long = Actor.TIMEOUT): Future[A] = reset(new DefaultCompletableFuture[A](timeout).completeWithResult(body)) } sealed trait Future[+T] { + /** + * For use only within a Future.flow block or another compatible Delimited Continuations reset block. + * + * Returns the result of this Future without blocking, by suspending execution and storing it as a + * continuation until the result is available. + * + * If this Future is untyped (a Future[Nothing]), a type parameter must be explicitly provided or + * execution will fail. The normal result of getting a Future from an ActorRef using !!! will return + * an untyped Future. + */ def apply[A >: T](): A @cps[Future[Any]] = shift(this flatMap _) /** From 7613d8e2c4eb3c83400275ab5f9330d9cee9d7c5 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Sun, 24 Apr 2011 14:07:04 -0600 Subject: [PATCH 009/233] Fix continuation dependency. Building from clean project was causing errors --- project/build/AkkaProject.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index db1f97ae80..597d01f490 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -443,7 +443,6 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) with Aut val junit = Dependencies.junit val scalatest = Dependencies.scalatest val multiverse_test = Dependencies.multiverse_test // StandardLatch - val cont = compilerPlugin("org.scala-lang.plugins" % "continuations" % "2.9.0.RC1") override def compileOptions = super.compileOptions ++ compileOptions("-P:continuations:enable") } From 997151e49c7cef9ea74fb49937cf5f508d47f628 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Sun, 24 Apr 2011 16:05:28 -0600 Subject: [PATCH 010/233] Improve pattern matching within for comprehensions with Future --- .../test/scala/akka/dispatch/FutureSpec.scala | 30 +++++++++---------- .../src/main/scala/akka/dispatch/Future.scala | 4 +-- 2 files changed, 17 insertions(+), 17 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 e88799161e..336a650177 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -62,9 +62,9 @@ class FutureSpec extends JUnitSuite { val future1 = actor1 !!! "Hello" flatMap ((s: String) => actor2 !!! s) val future2 = actor1 !!! "Hello" flatMap (actor2 !!! (_: String)) val future3 = actor1 !!! "Hello" flatMap (actor2 !!! (_: Int)) - assert(Some(Right("WORLD")) === future1.await.value) - assert(Some(Right("WORLD")) === future2.await.value) - intercept[ClassCastException] { future3.await.resultOrException } + assert(future1.get === "WORLD") + assert(future2.get === "WORLD") + intercept[ClassCastException] { future3.get } actor1.stop() actor2.stop() } @@ -74,8 +74,8 @@ class FutureSpec extends JUnitSuite { val actor2 = actorOf(new Actor { def receive = { case s: String => self reply s.toUpperCase } } ).start() val future1 = actor1 !!! "Hello" collect { case (s: String) => s } flatMap (actor2 !!! _) val future2 = actor1 !!! "Hello" collect { case (n: Int) => n } flatMap (actor2 !!! _) - assert(Some(Right("WORLD")) === future1.await.value) - intercept[MatchError] { future2.await.resultOrException } + assert(future1.get === "WORLD") + intercept[MatchError] { future2.get } actor1.stop() actor2.stop() } @@ -102,8 +102,8 @@ class FutureSpec extends JUnitSuite { c: String <- actor !!! 7 } yield b + "-" + c - assert(Some(Right("10-14")) === future1.await.value) - intercept[ClassCastException] { future2.await.resultOrException } + assert(future1.get === "10-14") + intercept[MatchError] { future2.get } actor.stop() } @@ -118,19 +118,19 @@ class FutureSpec extends JUnitSuite { }).start() val future1 = for { - a <- actor !!! Req("Hello") collect { case Res(x: Int) => x } - b <- actor !!! Req(a) collect { case Res(x: String) => x } - c <- actor !!! Req(7) collect { case Res(x: String) => x } + Res(a: Int) <- actor !!! Req("Hello") + Res(b: String) <- actor !!! Req(a) + Res(c: String) <- actor !!! Req(7) } yield b + "-" + c val future2 = for { - a <- actor !!! Req("Hello") collect { case Res(x: Int) => x } - b <- actor !!! Req(a) collect { case Res(x: Int) => x } - c <- actor !!! Req(7) collect { case Res(x: String) => x } + Res(a: Int) <- actor !!! Req("Hello") + Res(b: Int) <- actor !!! Req(a) + Res(c: Int) <- actor !!! Req(7) } yield b + "-" + c - assert(Some(Right("10-14")) === future1.await.value) - intercept[MatchError] { future2.await.resultOrException } + assert(future1.get === "10-14") + intercept[MatchError] { future2.get } actor.stop() } diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index ac2ee9bbe0..354832e31a 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -520,7 +520,7 @@ sealed trait Future[+T] { f(optr.get) } - final def filter(p: T => Boolean): Future[T] = { + final def filter(p: Any => Boolean): Future[Any] = { val f = new DefaultCompletableFuture[T](timeoutInNanos, NANOS) onComplete { ft => val optv = ft.value @@ -565,7 +565,7 @@ sealed trait Future[+T] { final def foreach[A >: T](proc: Procedure[A]): Unit = foreach(proc(_)) - final def filter[A >: T](p: JFunc[A,Boolean]): Future[T] = filter(p(_)) + final def filter(p: JFunc[Any,Boolean]): Future[Any] = filter(p(_)) } From da8e5064ef2ed7de03a7dfb86eae31725c8ad9a9 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Mon, 25 Apr 2011 14:55:49 -0600 Subject: [PATCH 011/233] Fix failing tests --- .../src/test/scala/akka/dispatch/FutureSpec.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 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 336a650177..37900a7b2e 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -62,8 +62,8 @@ class FutureSpec extends JUnitSuite { val future1 = actor1 !!! "Hello" flatMap ((s: String) => actor2 !!! s) val future2 = actor1 !!! "Hello" flatMap (actor2 !!! (_: String)) val future3 = actor1 !!! "Hello" flatMap (actor2 !!! (_: Int)) - assert(future1.get === "WORLD") - assert(future2.get === "WORLD") + assert((future1.get: Any) === "WORLD") + assert((future2.get: Any) === "WORLD") intercept[ClassCastException] { future3.get } actor1.stop() actor2.stop() @@ -74,7 +74,7 @@ class FutureSpec extends JUnitSuite { val actor2 = actorOf(new Actor { def receive = { case s: String => self reply s.toUpperCase } } ).start() val future1 = actor1 !!! "Hello" collect { case (s: String) => s } flatMap (actor2 !!! _) val future2 = actor1 !!! "Hello" collect { case (n: Int) => n } flatMap (actor2 !!! _) - assert(future1.get === "WORLD") + assert((future1.get: Any) === "WORLD") intercept[MatchError] { future2.get } actor1.stop() actor2.stop() @@ -103,7 +103,7 @@ class FutureSpec extends JUnitSuite { } yield b + "-" + c assert(future1.get === "10-14") - intercept[MatchError] { future2.get } + intercept[ClassCastException] { future2.get } actor.stop() } From 74fcef3891a70774178434178ed381748a4ef891 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Mon, 25 Apr 2011 16:14:07 -0600 Subject: [PATCH 012/233] Add Future.failure --- .../test/scala/akka/dispatch/FutureSpec.scala | 45 +++++++++++++ .../src/main/scala/akka/dispatch/Future.scala | 65 +++++++++++++------ 2 files changed, 91 insertions(+), 19 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 37900a7b2e..3ce5021441 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -134,6 +134,51 @@ class FutureSpec extends JUnitSuite { actor.stop() } + @Test def shouldMapMatchedExceptionsToResult { + val future1 = Future(5) + val future2 = future1 map (_ / 0) + val future3 = future2 map (_.toString) + + val future4 = future1 failure { + case e: ArithmeticException => 0 + } map (_.toString) + + val future5 = future2 failure { + case e: ArithmeticException => 0 + } map (_.toString) + + val future6 = future2 failure { + case e: MatchError => 0 + } map (_.toString) + + val future7 = future3 failure { case e: ArithmeticException => "You got ERROR" } + + val actor = actorOf[TestActor].start() + + val future8 = actor !!! "Failure" + val future9 = actor !!! "Failure" failure { + case e: RuntimeException => "FAIL!" + } + val future10 = actor !!! "Hello" failure { + case e: RuntimeException => "FAIL!" + } + val future11 = actor !!! "Failure" failure { case _ => "Oops!" } + + assert(future1.get === 5) + intercept[ArithmeticException] { future2.get } + intercept[ArithmeticException] { future3.get } + assert(future4.get === "5") + assert(future5.get === "0") + intercept[ArithmeticException] { future6.get } + assert(future7.get === "You got ERROR") + intercept[RuntimeException] { future8.get } + assert(future9.get === "FAIL!") + assert(future10.get === "World") + assert(future11.get === "Oops!") + + actor.stop() + } + @Test def shouldFutureAwaitEitherLeft = { val actor1 = actorOf[TestActor].start() val actor2 = 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 354832e31a..2b6f107a34 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -201,7 +201,7 @@ object Futures { // ===================================== // Deprecations // ===================================== - + /** * (Blocking!) */ @@ -421,21 +421,18 @@ sealed trait Future[+T] { final def collect[A](pf: PartialFunction[Any, A]): Future[A] = { val fa = new DefaultCompletableFuture[A](timeoutInNanos, NANOS) onComplete { ft => - val optv = ft.value - if (optv.isDefined) { - val v = optv.get - fa complete { - if (v.isLeft) v.asInstanceOf[Either[Throwable, A]] - else { - try { - val r = v.right.get - if (pf isDefinedAt r) Right(pf(r)) - else Left(new MatchError(r)) - } catch { - case e: Exception => - EventHandler.error(e, this, e.getMessage) - Left(e) - } + val v = ft.value.get + fa complete { + if (v.isLeft) v.asInstanceOf[Either[Throwable, A]] + else { + try { + val r = v.right.get + if (pf isDefinedAt r) Right(pf(r)) + else Left(new MatchError(r)) + } catch { + case e: Exception => + EventHandler.error(e, this, e.getMessage) + Left(e) } } } @@ -443,6 +440,36 @@ sealed trait Future[+T] { fa } + /** + * Creates a new Future that will handle any matching Throwable that this + * Future might contain. If there is no match, or if this Future contains + * a valid result then the new Future will contain the same. + * Example: + *
+   * Future(6 / 0) failure { case e: ArithmeticException => 0 } // result: 0
+   * Future(6 / 0) failure { case e: NotFoundException   => 0 } // result: exception
+   * Future(6 / 2) failure { case e: ArithmeticException => 0 } // result: 3
+   * 
+ */ + final def failure[A >: T](pf: PartialFunction[Throwable, A]): Future[A] = { + val fa = new DefaultCompletableFuture[A](timeoutInNanos, NANOS) + onComplete { ft => + val opte = ft.exception + fa complete { + if (opte.isDefined) { + val e = opte.get + try { + if (pf isDefinedAt e) Right(pf(e)) + else Left(e) + } catch { + case x: Exception => Left(x) + } + } else ft.value.get + } + } + fa + } + /** * Creates a new Future by applying a function to the successful result of * this Future. If this Future is completed with an exception then the new @@ -468,7 +495,7 @@ sealed trait Future[+T] { fa complete (try { Right(f(v.right.get)) } catch { - case e: Exception => + case e: Exception => EventHandler.error(e, this, e.getMessage) Left(e) }) @@ -504,7 +531,7 @@ sealed trait Future[+T] { try { fa.completeWith(f(v.right.get)) } catch { - case e: Exception => + case e: Exception => EventHandler.error(e, this, e.getMessage) fa completeWithException e } @@ -534,7 +561,7 @@ sealed trait Future[+T] { if (p(r)) Right(r) else Left(new MatchError(r)) } catch { - case e: Exception => + case e: Exception => EventHandler.error(e, this, e.getMessage) Left(e) }) From 2432101d6fc314b9fe511b78c96b8508343efac2 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 26 Apr 2011 10:49:52 +0200 Subject: [PATCH 013/233] Fixing docs for Future.get --- akka-actor/src/main/scala/akka/dispatch/Future.scala | 7 ++++++- 1 file changed, 6 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 2b6f107a34..762beb8ba3 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -299,7 +299,12 @@ sealed trait Future[+T] { def apply[A >: T](): A @cps[Future[Any]] = shift(this flatMap _) /** - * Java API for apply() + * Blocks awaiting completion of this Future, then returns the resulting value, + * or throws the completed exception + * + * Scala & Java API + * + * throws FutureTimeoutException if this Future times out when waiting for completion */ def get: T = this.await.resultOrException.get From 25f2824b638bc595b6525833212360b9cc3b5d19 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 26 Apr 2011 11:17:29 +0200 Subject: [PATCH 014/233] Adding a test for the emulation of blocking --- .../test/scala/akka/dispatch/FutureSpec.scala | 33 +++++++++++++++++++ 1 file changed, 33 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 3ce5021441..9140cd236a 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -500,6 +500,39 @@ class FutureSpec extends JUnitSuite { assert(result2.get === 50) } + @Test def futureDataFlowShouldEmulateBlocking { + import Future.flow + val x1, x2, y1, y2 = new DefaultCompletableFuture[Int](1000 * 60) + val lx, ly, lz = new StandardLatch + val result = flow { + lx.open() + x1 << y1 + ly.open() + x2 << y2 + lz.open() + x1() + x2() + } + assert(lx.isOpen) + assert(!ly.isOpen) + assert(!lz.isOpen) + assert(List(x1,x2,y1,y2).forall(_.isCompleted == false)) + + y1 << 1 // When this is set, it should cascade down the line + + assert(ly.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS)) + assert(x1.await.result.get === 1) + assert(!lz.isOpen) + + y2 << 9 // When this is set, it should cascade down the line + + assert(lz.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS)) + assert(x2.await.result.get === 9) + + assert(List(x1,x2,y1,y2).forall(_.isCompleted == true)) + + assert(result.await.get === 10) + } + @Test def futureCompletingWithContinuationsFailure { import Future.flow From 0b5ab2112889104cd682a7af8b2feab113bef99c Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 26 Apr 2011 11:41:26 +0200 Subject: [PATCH 015/233] Adding yet another CPS test --- .../test/scala/akka/dispatch/FutureSpec.scala | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) 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 9140cd236a..3749cc4bbd 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -500,7 +500,29 @@ class FutureSpec extends JUnitSuite { assert(result2.get === 50) } - @Test def futureDataFlowShouldEmulateBlocking { + @Test def futureDataFlowShouldEmulateBlocking1 { + import Future.flow + + val one, two = new DefaultCompletableFuture[Int](1000 * 60) + val simpleResult = flow { + one() + two() + } + + assert(List(one, two, simpleResult).forall(_.isCompleted == false)) + + one << 1 + + assert(one.isCompleted) + assert(List(two, simpleResult).forall(_.isCompleted == false)) + + two << 9 + + assert(List(one, two).forall(_.isCompleted == true)) + assert(simpleResult.await.result.get === 10) + + } + + @Test def futureDataFlowShouldEmulateBlocking2 { import Future.flow val x1, x2, y1, y2 = new DefaultCompletableFuture[Int](1000 * 60) val lx, ly, lz = new StandardLatch From 69ee799dd9e9a8855c60066c54579580a51ba364 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 26 Apr 2011 11:52:17 +0200 Subject: [PATCH 016/233] 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 017/233] 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 018/233] 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 019/233] 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 020/233] 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 d567a088499acfa0369b64509bc2919f585eaf8f Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 26 Apr 2011 13:37:06 +0200 Subject: [PATCH 021/233] =?UTF-8?q?Added=20a=20test=20to=20validate=20the?= =?UTF-8?q?=20API,=20it=C2=B4s=20gorgeous?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/scala/akka/dispatch/FutureSpec.scala | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 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 3749cc4bbd..03232aac13 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -518,7 +518,7 @@ class FutureSpec extends JUnitSuite { two << 9 assert(List(one, two).forall(_.isCompleted == true)) - assert(simpleResult.await.result.get === 10) + assert(simpleResult.get === 10) } @@ -542,17 +542,30 @@ class FutureSpec extends JUnitSuite { y1 << 1 // When this is set, it should cascade down the line assert(ly.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS)) - assert(x1.await.result.get === 1) + assert(x1.get === 1) assert(!lz.isOpen) y2 << 9 // When this is set, it should cascade down the line assert(lz.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS)) - assert(x2.await.result.get === 9) + assert(x2.get === 9) assert(List(x1,x2,y1,y2).forall(_.isCompleted == true)) - assert(result.await.get === 10) + assert(result.get === 10) + } + + @Test def dataFlowAPIshouldbeSlick { + import Future.flow + + def callService1 = Future { 1 } + def callService2 = Future { 9 } + + val result = flow { + callService1() + callService2() + } + + assert(result.get === 10) } @Test def futureCompletingWithContinuationsFailure { From 0fbf8d3c652fee1b949b44d612453f8ea7bce033 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 26 Apr 2011 13:37:06 +0200 Subject: [PATCH 022/233] =?UTF-8?q?Added=20a=20test=20to=20validate=20the?= =?UTF-8?q?=20API,=20it=C2=B4s=20gorgeous?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/scala/akka/dispatch/FutureSpec.scala | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 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 3749cc4bbd..bbbb187dbb 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -518,7 +518,7 @@ class FutureSpec extends JUnitSuite { two << 9 assert(List(one, two).forall(_.isCompleted == true)) - assert(simpleResult.await.result.get === 10) + assert(simpleResult.get === 10) } @@ -542,17 +542,36 @@ class FutureSpec extends JUnitSuite { y1 << 1 // When this is set, it should cascade down the line assert(ly.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS)) - assert(x1.await.result.get === 1) + assert(x1.get === 1) assert(!lz.isOpen) y2 << 9 // When this is set, it should cascade down the line assert(lz.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS)) - assert(x2.await.result.get === 9) + assert(x2.get === 9) assert(List(x1,x2,y1,y2).forall(_.isCompleted == true)) - assert(result.await.get === 10) + assert(result.get === 10) + } + + @Test def dataFlowAPIshouldbeSlick { + import Future.flow + val s1, s2 = new StandardLatch + + def callService1 = Future { s1.awaitUninterruptible; 1 } + def callService2 = Future { s2.awaitUninterruptible; 9 } + + val result = flow { + callService1() + callService2() + } + + assert(!s1.isOpen) + assert(!s2.isOpen) + assert(!result.isCompleted) + s1.open + s2.open + assert(result.get === 10) } @Test def futureCompletingWithContinuationsFailure { From d9fbefd4521b27e5b96b78d4bddd91abada4d1bf Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 26 Apr 2011 14:16:12 +0200 Subject: [PATCH 023/233] 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 024/233] 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 025/233] 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 026/233] 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 027/233] 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 bd54c9b937200e98073e42a585c3e976a175392f Mon Sep 17 00:00:00 2001 From: Eugene Vigdorchik Date: Tue, 26 Apr 2011 20:32:50 +0400 Subject: [PATCH 028/233] A typo and small clarification to actor-registry-java documentation. --- akka-docs/pending/actor-registry-java.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-docs/pending/actor-registry-java.rst b/akka-docs/pending/actor-registry-java.rst index 67be08b2a8..b495226598 100644 --- a/akka-docs/pending/actor-registry-java.rst +++ b/akka-docs/pending/actor-registry-java.rst @@ -31,7 +31,7 @@ You can shut down all Actors in the system by invoking: registry().shutdownAll(); -If you want to know when a new Actor is added or to or removed from the registry, you can use the subscription API. You can register an Actor that should be notified when an event happens in the ActorRegistry: +If you want to know when a new Actor is added to or removed from the registry, you can use the subscription API on the registry. You can register an Actor that should be notified when an event happens in the ActorRegistry: .. code-block:: java From d0447c76cb4568eccc5762cd9ebc517bdae66596 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 26 Apr 2011 20:16:31 +0200 Subject: [PATCH 029/233] 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 030/233] 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 031/233] 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 032/233] 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 033/233] 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 034/233] 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 035/233] 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 036/233] 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 037/233] 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 038/233] 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 039/233] 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 040/233] 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 041/233] 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 042/233] 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 043/233] 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 044/233] 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 045/233] 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 046/233] 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 047/233] 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 048/233] 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 049/233] 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 050/233] 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 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

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 051/233] 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 052/233] 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 053/233] 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 054/233] 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 055/233] 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 056/233] 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 057/233] 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 058/233] 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 059/233] 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 060/233] 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 061/233] 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 062/233] 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 063/233] 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 064/233] 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 065/233] 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 066/233] 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 067/233] 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 068/233] 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 069/233] 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 070/233] 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 071/233] 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 072/233] 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 073/233] 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 074/233] 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 075/233] 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 076/233] 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 077/233] 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 078/233] 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 079/233] 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 080/233] 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 081/233] 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 082/233] 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 083/233] 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 084/233] 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 085/233] 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 086/233] 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 087/233] 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 088/233] 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 089/233] 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 090/233] 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 091/233] 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 092/233] 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 093/233] 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 094/233] 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 095/233] 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 => From e4e53af508d9df96e16c58f757302b4749642f23 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Sat, 30 Apr 2011 09:09:30 -0600 Subject: [PATCH 096/233] Fix Scaladoc generation failure --- 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 9e657e15fa..1ca3db193a 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -311,7 +311,7 @@ sealed trait Future[+T] { * execution will fail. The normal result of getting a Future from an ActorRef using !!! will return * an untyped Future. */ - def apply[A >: T](): A @cps[Future[Any]] = shift(this flatMap _) + def apply[A >: T](): A @cps[Future[Any]] = shift(this flatMap (_: A => Future[Any])) /** * Blocks awaiting completion of this Future, then returns the resulting value, From c7444193e6e25148678aca93a585c4c50026e3c3 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Sun, 1 May 2011 17:35:05 +0200 Subject: [PATCH 097/233] Ticket 739. Beefing up config documentation. --- akka-docs/general/configuration.rst | 92 +++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 10 deletions(-) diff --git a/akka-docs/general/configuration.rst b/akka-docs/general/configuration.rst index fd19b71db4..bd293383d8 100644 --- a/akka-docs/general/configuration.rst +++ b/akka-docs/general/configuration.rst @@ -1,25 +1,30 @@ Configuration ============= +.. sidebar:: Contents + + .. contents:: :local: + Specifying the configuration file --------------------------------- -If you don't specify a configuration file then Akka uses default values. If -you want to override these then you should edit the ``akka.conf`` file in the -``AKKA_HOME/config`` directory. This config inherits from the -``akka-reference.conf`` file that you see below. Use your ``akka.conf`` to override -any property in the reference config. +If you don't specify a configuration file then Akka uses default values, corresponding to the ``akka-reference.conf`` +that you see below. You can specify your own configuration file to override any property in the reference config. +You only have to define the properties that differ from the default configuration. -The config can be specified in various ways: +The location of the config file to use can be specified in various ways: -* Define the ``-Dakka.config=...`` system property option +* Define the ``-Dakka.config=...`` system property parameter with a file path to configuration file. -* Put an ``akka.conf`` file on the classpath +* Put an ``akka.conf`` file in the root of the classpath. * Define the ``AKKA_HOME`` environment variable pointing to the root of the Akka - distribution. The config is taken from the ``AKKA_HOME/config`` directory. You + distribution. The config is taken from the ``AKKA_HOME/config/akka.conf``. You can also point to the AKKA_HOME by specifying the ``-Dakka.home=...`` system - property option. + property parameter. + +If several of these ways to specify the config file are used at the same time the precedence is the order as given above, +i.e. you can always redefine the location with the ``-Dakka.config=...`` system property. Defining the configuration file @@ -29,3 +34,70 @@ Here is the reference configuration file: .. literalinclude:: ../../config/akka-reference.conf :language: none + +A custom ``akka.conf`` might look like this: + +:: + + # In this file you can override any option defined in the 'akka-reference.conf' file. + # Copy in all or parts of the 'akka-reference.conf' file and modify as you please. + + akka { + event-handlers = ["akka.event.slf4j.Slf4jEventHandler"] + + # Comma separated list of the enabled modules. + enabled-modules = ["camel", "remote"] + + # These boot classes are loaded (and created) automatically when the Akka Microkernel boots up + # Can be used to bootstrap your application(s) + # Should be the FQN (Fully Qualified Name) of the boot class which needs to have a default constructor + boot = ["sample.camel.Boot", + "sample.myservice.Boot"] + + actor { + throughput = 10 # Throughput for ExecutorBasedEventDrivenDispatcher, set to 1 for complete fairness + } + + remote { + server { + port = 2562 # The port clients should connect to. Default is 2552 (AKKA) + } + } + } + +Specifying files for different modes +------------------------------------ + +You can use different configuration files for different purposes by specifying a mode option, either as +``-Dakka.mode=...`` system property or as ``AKKA_MODE=...`` environment variable. For example using DEBUG log level +when in development mode. Run with ``-Dakka.mode=dev`` and place the following ``akka.dev.conf`` in the root of +the classpath. + +akka.dev.conf: + +:: + + akka { + event-handler-level = "DEBUG" + } + +The mode option works in the same way when using configuration files in ``AKKA_HOME/config/`` directory. + +The mode option is not used when specifying the configuration file with ``-Dakka.config=...`` system property. + +Including files +--------------- + +Sometimes it can be useful to include another configuration file, for example if you have one ``akka.conf`` with all +environment independent settings and then override some settings for specific modes. + +akka.dev.conf: + +:: + + include "akka.conf" + + akka { + event-handler-level = "DEBUG" + } + From 56acccf82ecec2c2b7220593c047b61d1b866a68 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Sun, 1 May 2011 21:33:16 +0200 Subject: [PATCH 098/233] Added installation instructions for Sphinx etc --- akka-docs/dev/documentation.rst | 84 +++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/akka-docs/dev/documentation.rst b/akka-docs/dev/documentation.rst index 9e280220e6..04b5a31e1c 100644 --- a/akka-docs/dev/documentation.rst +++ b/akka-docs/dev/documentation.rst @@ -7,6 +7,10 @@ Documentation ############### +.. sidebar:: Contents + + .. contents:: :local: + The Akka documentation uses `reStructuredText`_ as its markup language and is built using `Sphinx`_. @@ -67,3 +71,83 @@ For example:: Here is a reference to "akka section": :ref:`akka-section` which will have the name "Akka Section". +Build the documentation +======================= + +First install `Sphinx`_. See below. + +Building +-------- + +:: + + cd akka-docs + + make html + open _build/html/index.html + + make pdf + open _build/latex/Akka.pdf + + +Installing Sphinx on OS X +------------------------- + +Install `Homebrew `_ + +Install Python and pip: + +:: + + brew install python + /usr/local/share/python/easy_install pip + +Add the Homebrew Python path to your $PATH: + +:: + + /usr/local/Cellar/python/2.7.1/bin + + +More information in case of trouble: +https://github.com/mxcl/homebrew/wiki/Homebrew-and-Python + +Install sphinx: + +:: + + pip install sphinx + +Add sphinx_build to your $PATH: + +:: + + /usr/local/share/python + +Install BasicTeX package from: +http://www.tug.org/mactex/morepackages.html + +Add texlive bin to $PATH: + +:: + + /usr/local/texlive/2010basic/bin/universal-darwin + +Add missing tex packages: + +:: + + sudo tlmgr update --self + sudo tlmgr install titlesec + sudo tlmgr install framed + sudo tlmgr install threeparttable + sudo tlmgr install wrapfig + sudo tlmgr install helvetic + sudo tlmgr install courier + +Link the akka pygments style: + +:: + + cd /usr/local/Cellar/python/2.7.1/lib/python2.7/site-packages/pygments/styles + ln -s /path/to/akka-cloud/akka-cloud-docs/themes/akka/pygments/akka.py akka.py From 3b3f8d307a15f0731934fc61308c8f59e8c6af61 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Sun, 1 May 2011 21:47:50 +0200 Subject: [PATCH 099/233] fixed typo --- akka-docs/dev/documentation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-docs/dev/documentation.rst b/akka-docs/dev/documentation.rst index 04b5a31e1c..b0da0bd698 100644 --- a/akka-docs/dev/documentation.rst +++ b/akka-docs/dev/documentation.rst @@ -150,4 +150,4 @@ Link the akka pygments style: :: cd /usr/local/Cellar/python/2.7.1/lib/python2.7/site-packages/pygments/styles - ln -s /path/to/akka-cloud/akka-cloud-docs/themes/akka/pygments/akka.py akka.py + ln -s /path/to/akka/akka-docs/themes/akka/pygments/akka.py akka.py From de7741a4644e4cbb63db661be90cc72ba0525a70 Mon Sep 17 00:00:00 2001 From: alarmnummer Date: Mon, 2 May 2011 11:40:51 +0200 Subject: [PATCH 100/233] added jmm documentation for actors and stm --- akka-docs/general/jmm.rst | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 akka-docs/general/jmm.rst diff --git a/akka-docs/general/jmm.rst b/akka-docs/general/jmm.rst new file mode 100644 index 0000000000..ed7bffb748 --- /dev/null +++ b/akka-docs/general/jmm.rst @@ -0,0 +1,33 @@ +Akka and the Java Memory Model +================================ + +Prior to Java 5, the Java Memory Model (JMM) was broken. It was possible to get all kinds of strange results like unpredictable merged writes made by concurrent executing threads, unexpected reordering of instructions, and even final fields were not guaranteed to be final. With Java 5 and JSR-133, the Java Memory Model is clearly specified. This specification makes it possible to write code that performs, but doesn't cause concurrency problems. The Java Memory Model is specified in 'happens before'-rules, e.g.: +* **monitor lock rule**: a release of a lock happens before every subsequent acquire of the same lock. +* **volatile variable rule**: a write of a volatile variable happens before every subsequent read of the same volatile variable + +The 'happens before'-rules clearly specify which visibility guarantees are provided on memory and which re-orderings are allowed. Without these rules it would not be possible to write concurrent and performant code in Java. + +Actors and the Java Memory Model +-------------------------------- + +With the Actors implementation in Akka, there are 2 ways multiple threads can execute actions on shared memory over time: +* if a message is send to an actor (e.g. by another actor). In most cases messages are immutable, but if that message is not a properly constructed immutable object, without happens before rules, the system still could be subject to instruction re-orderings and visibility problems (so a possible source of concurrency errors). +* if an actor makes changes to its internal state in one 'receive' method and access that state while processing another message. With the actors model you don't get any guarantee that the same thread will be executing the same actor for different messages. Without a happens before relation between these actions, there could be another source of concurrency errors. + +To solve the 2 problems above, Akka adds the following 2 'happens before'-rules to the JMM: +* **the actor send rule**: where the send of the message to an actor happens before the receive of the **same** actor. +* **the actor subsequent processing rule**: where processing of one message happens before processing of the next message by the **same** actor. + +Both rules only apply for the same actor instance and are not valid if different actors are used. + +STM and the Java Memory Model +----------------------------- + +The Akka STM also provides a happens before rule called: + +* **the transaction rule**: a commit on a transaction happens before every subsequent start of a transaction where there is at least 1 shared reference. + +How these rules are realized in Akka, is an implementation detail and can change over time (the exact details could even depend on the used configuration) but they will lift on the other JMM rules like the monitor lock rule or the volatile variable rule. Essentially this means that you, the Akka user, do not need to worry about adding synchronization to provide such a happens before relation, because it is the responsibility of Akka. So you have your hands free to deal with your problems and not that of the framework. + + + From 39519af3c20f815bd3982e06cafe7cf6607afcc4 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 2 May 2011 11:50:56 +0200 Subject: [PATCH 101/233] Removing the CONFIG val in BootableActorLoaderService to fix #825 --- .../src/main/scala/akka/actor/BootableActorLoaderService.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/actor/BootableActorLoaderService.scala b/akka-actor/src/main/scala/akka/actor/BootableActorLoaderService.scala index 48c1127f84..f5112470bb 100644 --- a/akka-actor/src/main/scala/akka/actor/BootableActorLoaderService.scala +++ b/akka-actor/src/main/scala/akka/actor/BootableActorLoaderService.scala @@ -21,7 +21,6 @@ trait BootableActorLoaderService extends Bootable { protected def createApplicationClassLoader : Option[ClassLoader] = Some({ if (HOME.isDefined) { - val CONFIG = HOME.get + "/config" val DEPLOY = HOME.get + "/deploy" val DEPLOY_DIR = new File(DEPLOY) if (!DEPLOY_DIR.exists) { From 2a26a707efefd4cec1b66a4beb44d27773d35313 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Mon, 2 May 2011 12:45:54 +0200 Subject: [PATCH 102/233] Reviewed and improved serialization docs, still error with in/out, waiting for answer from Debasish --- akka-docs/java/serialization.rst | 98 ++++++++++++++------------- akka-docs/scala/serialization.rst | 108 ++++++++++-------------------- 2 files changed, 87 insertions(+), 119 deletions(-) diff --git a/akka-docs/java/serialization.rst b/akka-docs/java/serialization.rst index 813db45a9a..0a41941ba5 100644 --- a/akka-docs/java/serialization.rst +++ b/akka-docs/java/serialization.rst @@ -9,28 +9,6 @@ Serialization (Java) 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: - -.. code-block:: scala - - /** - * Module for local actor serialization. - */ - object ActorSerialization { - // wrapper for implicits to be used by Java - def fromBinaryJ[T <: Actor](bytes: Array[Byte], format: Format[T]): ActorRef = - fromBinary(bytes)(format) - - // wrapper for implicits to be used by Java - def toBinaryJ[T <: Actor](a: ActorRef, format: Format[T], srlMailBox: Boolean = true): Array[Byte] = - toBinary(a, srlMailBox)(format) - } - -The following steps describe the procedure for serializing an Actor and ActorRef. - Serialization of a Stateless Actor ---------------------------------- @@ -38,6 +16,8 @@ Step 1: Define the Actor .. code-block:: scala + import akka.actor.UntypedActor; + public class SerializationTestActor extends UntypedActor { public void onReceive(Object msg) { getContext().replySafe("got it!"); @@ -50,6 +30,8 @@ Note how the generated Java classes are accessed using the $class based naming c .. code-block:: scala + import akka.serialization.StatelessActorFormat; + class SerializationTestActorFormat implements StatelessActorFormat { @Override public SerializationTestActor fromBinary(byte[] bytes, SerializationTestActor act) { @@ -68,6 +50,14 @@ The following JUnit snippet first creates an actor using the default constructor .. code-block:: java + import akka.actor.ActorRef; + import akka.actor.ActorTimeoutException; + import akka.actor.Actors; + import akka.actor.UntypedActor; + import akka.serialization.Format; + import akka.serialization.StatelessActorFormat; + import static akka.serialization.ActorSerialization.*; + @Test public void mustBeAbleToSerializeAfterCreateActorRefFromClass() { ActorRef ref = Actors.actorOf(SerializationTestActor.class); assertNotNull(ref); @@ -101,21 +91,23 @@ Let's now have a look at how to serialize an actor that carries a state with it. Step 1: Define the Actor -Here we consider an actor defined in Scala. We will however serialize using the Java APIs. - .. code-block:: scala - class MyUntypedActor extends UntypedActor { - var count = 0 - def onReceive(message: Any): Unit = message match { - case m: String if m == "hello" => - count = count + 1 - getContext.replyUnsafe("world " + count) - case m: String => - count = count + 1 - getContext.replyUnsafe("hello " + m + " " + count) - case _ => - throw new Exception("invalid message type") + import akka.actor.UntypedActor; + + public class MyUntypedActor extends UntypedActor { + int count = 0; + + public void onReceive(Object msg) { + if (msg.equals("hello")) { + count = count + 1; + getContext().replyUnsafe("world " + count); + } else if (msg instanceof String) { + count = count + 1; + getContext().replyUnsafe("hello " + msg + " " + count); + } else { + throw new IllegalArgumentException("invalid message type"); + } } } @@ -125,27 +117,37 @@ Step 2: Define the instance of the typeclass .. code-block:: java - class MyUntypedActorFormat implements Format { - @Override - public MyUntypedActor fromBinary(byte[] bytes, MyUntypedActor act) { - ProtobufProtocol.Counter p = - (ProtobufProtocol.Counter) new SerializerFactory().getProtobuf().fromBinary(bytes, ProtobufProtocol.Counter.class); - act.count_$eq(p.getCount()); - return act; - } + import akka.actor.UntypedActor; + import akka.serialization.Format; + import akka.serialization.SerializerFactory; - @Override - public byte[] toBinary(MyUntypedActor ac) { - return ProtobufProtocol.Counter.newBuilder().setCount(ac.count()).build().toByteArray(); - } + class MyUntypedActorFormat implements Format { + @Override + public MyUntypedActor fromBinary(byte[] bytes, MyUntypedActor act) { + ProtobufProtocol.Counter p = + (ProtobufProtocol.Counter) new SerializerFactory().getProtobuf().fromBinary(bytes, ProtobufProtocol.Counter.class); + act.count = p.getCount(); + return act; } -Note the usage of Protocol Buffers to serialize the state of the actor. + @Override + public byte[] toBinary(MyUntypedActor ac) { + return ProtobufProtocol.Counter.newBuilder().setCount(ac.count()).build().toByteArray(); + } + } + +Note the usage of Protocol Buffers to serialize the state of the actor. ProtobufProtocol.Counter is something +you need to define yourself Step 3: Serialize and de-serialize .. code-block:: java + import akka.actor.ActorRef; + import akka.actor.ActorTimeoutException; + import akka.actor.Actors; + import static akka.serialization.ActorSerialization.*; + @Test public void mustBeAbleToSerializeAStatefulActor() { ActorRef ref = Actors.actorOf(MyUntypedActor.class); assertNotNull(ref); diff --git a/akka-docs/scala/serialization.rst b/akka-docs/scala/serialization.rst index 39f9304bc8..b229b57942 100644 --- a/akka-docs/scala/serialization.rst +++ b/akka-docs/scala/serialization.rst @@ -58,10 +58,15 @@ Step 1: Define the actor } } -Step 2: Implement the type class for the actor +Step 2: Implement the type class for the actor. ProtobufProtocol.Counter is something you need to define yourself, as +explained in the Protobuf section. .. code-block:: scala + import akka.serialization.{Serializer, Format} + import akka.actor.Actor + import akka.actor.Actor._ + object BinaryFormatMyActor { implicit object MyActorFormat extends Format[MyActor] { def fromBinary(bytes: Array[Byte], act: MyActor) = { @@ -70,8 +75,7 @@ Step 2: Implement the type class for the actor act } def toBinary(ac: MyActor) = - ProtobufProtocol.Counter.newBuilder.setCount(ac.count).build.toByteArray - } + ProtobufProtocol.Counter.newBuilder.setCount(ac.count).build.toByteArray } } @@ -159,7 +163,7 @@ For a Java serializable actor: .. code-block:: scala - @serializable class MyJavaSerializableActor extends Actor { + class MyJavaSerializableActor extends Actor with scala.Serializable { var count = 0 def receive = { @@ -173,6 +177,8 @@ Create a module for the type class .. .. code-block:: scala + import akka.serialization.{SerializerBasedActorFormat, Serializer} + object BinaryFormatMyJavaSerializableActor { implicit object MyJavaSerializableActorFormat extends SerializerBasedActorFormat[MyJavaSerializableActor] { val serializer = Serializer.Java @@ -184,6 +190,7 @@ and serialize / de-serialize .. .. code-block:: scala it("should be able to serialize and de-serialize a stateful actor with a given serializer") { + import akka.actor.Actor._ import akka.serialization.ActorSerialization._ import BinaryFormatMyJavaSerializableActor._ @@ -202,7 +209,7 @@ 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. -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``. 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. @@ -218,14 +225,14 @@ To deserialize the ``ActorRef`` to a ``RemoteActorRef`` you need to use the ``fr .. code-block:: scala - import RemoteActorSerialization._ + import akka.serialization.RemoteActorSerialization._ val actor2 = fromBinaryToRemoteActorRef(bytes) You can also pass in a class loader to load the ``ActorRef`` class and dependencies from: .. code-block:: scala - import RemoteActorSerialization._ + import akka.serialization.RemoteActorSerialization._ val actor2 = fromBinaryToRemoteActorRef(bytes, classLoader) Deep serialization of a TypedActor @@ -240,6 +247,8 @@ Step 1: Define the actor .. code-block:: scala + import akka.actor.TypedActor + trait MyTypedActor { def requestReply(s: String) : String def oneWay() : Unit @@ -252,12 +261,18 @@ Step 1: Define the actor count = count + 1 "world " + count } + + override def oneWay() { + count = count + 1 + } } Step 2: Implement the type class for the actor .. code-block:: scala + import akka.serialization.{Serializer, Format} + class MyTypedActorFormat extends Format[MyTypedActorImpl] { def fromBinary(bytes: Array[Byte], act: MyTypedActorImpl) = { val p = Serializer.Protobuf.fromBinary(bytes, Some(classOf[ProtobufProtocol.Counter])).asInstanceOf[ProtobufProtocol.Counter] @@ -271,6 +286,8 @@ Step 3: Import the type class module definition and serialize / de-serialize .. code-block:: scala + import akka.serialization.TypedActorSerialization._ + val typedActor1 = TypedActor.newInstance(classOf[MyTypedActor], classOf[MyTypedActorImpl], 1000) val f = new MyTypedActorFormat @@ -288,7 +305,7 @@ To deserialize the TypedActor to a ``RemoteTypedActorRef`` (an aspectwerkz proxy .. code-block:: scala - import RemoteTypedActorSerialization._ + import akka.serialization.RemoteTypedActorSerialization._ val typedActor = fromBinaryToRemoteTypedActorRef(bytes) // you can also pass in a class loader @@ -328,7 +345,6 @@ The ones currently supported are (besides the default which is regular Java seri - 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. @@ -377,15 +393,16 @@ The remote Actor can then receive the Protobuf message typed as-is: JSON: Scala ----------- -Use the akka.serialization.Serialization.ScalaJSON base class with its toJSON method. Akka’s Scala JSON is based upon the SJSON library. +Use the ``akka.serialization.Serializable.ScalaJSON`` base class with its toJSON method. Akka’s Scala JSON is based upon the SJSON library. For your POJOs to be able to serialize themselves you have to extend the ScalaJSON[] trait as follows. JSON serialization is based on a type class protocol which you need to define for your own abstraction. The instance of the type class is defined as an implicit object which is used for serialization and de-serialization. You also need to implement the methods in terms of the APIs which sjson publishes. .. code-block:: scala - import akka.serialization.Serializer + import akka.serialization._ import akka.serialization.Serializable.ScalaJSON - import scala.reflect.BeanInfo + import akka.serialization.JsonSerialization._ + import akka.serialization.DefaultProtocol._ case class MyMessage(val id: String, val value: Tuple2[String, Int]) extends ScalaJSON[MyMessage] { // type class instance @@ -427,7 +444,7 @@ Here are the steps that you need to follow: .. code-block:: scala - import DefaultProtocol._ + import akka.serialization.DefaultProtocol._ implicit val MyMessageFormat: sjson.json.Format[MyMessage] = asProduct2("id", "value")(MyMessage)(MyMessage.unapply(_).get) @@ -436,6 +453,7 @@ Here are the steps that you need to follow: .. code-block:: scala import akka.serialization.Serializer.ScalaJSON + import akka.serialization.JsonSerialization._ val o = MyMessage("dg", ("akka", 100)) fromjson[MyMessage](tojson(o)) should equal(o) @@ -480,7 +498,7 @@ So if you see something like that: it means, that you haven't got a @BeanInfo annotation on your class. -You may also see this exception when trying to serialize a case class with out an attribute like this: +You may also see this exception when trying to serialize a case class without any attributes, like this: .. code-block:: scala @@ -900,12 +918,15 @@ There are other nifty ways to implement case class serialization using sjson. Fo JSON: Java ---------- -Use the akka.serialization.Serialization.JavaJSON base class with its toJSONmethod. Akka’s Java JSON is based upon the Jackson library. +Use the ``akka.serialization.Serializable.JavaJSON`` base class with its toJSONmethod. Akka’s Java JSON is based upon the Jackson library. -For your POJOs to be able to serialize themselves you have to extend the JavaJSON trait. +For your POJOs to be able to serialize themselves you have to extend the JavaJSON base class. .. code-block:: java + import akka.serialization.Serializable.JavaJSON; + import akka.serialization.SerializerFactory; + class MyMessage extends JavaJSON { private String name = null; public MyMessage(String name) { @@ -931,59 +952,4 @@ Use the akka.serialization.SerializerFactory.getJavaJSON to do generic JSON seri Foo fooCopy = factory.getJavaJSON().in(json, Foo.class); -SBinary: Scala --------------- - -To serialize Scala structures you can use SBinary serializer. SBinary can serialize all primitives and most default Scala datastructures; such as List, Tuple, Map, Set, BigInt etc. - -Here is an example of using the akka.serialization.Serializer.SBinary serializer to serialize standard Scala library objects. - -.. code-block:: scala - - import akka.serialization.Serializer - import sbinary.DefaultProtocol._ // you always need to import these implicits - val users = List(("user1", "passwd1"), ("user2", "passwd2"), ("user3", "passwd3")) - val bytes = Serializer.SBinary.out(users) - 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). - -Here is an example: - -.. code-block:: scala - - 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) - } From 2d2bdeec7ac018dcbe6f88936542542b3fbad68a Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Mon, 2 May 2011 09:09:14 -0600 Subject: [PATCH 103/233] Will always infer type as Any, so should explicitly state it. --- akka-actor/src/main/scala/akka/dispatch/Future.scala | 4 ++-- 1 file changed, 2 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 1ca3db193a..34e9c6da9b 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -291,8 +291,8 @@ object Future { * * The Delimited Continuations compiler plugin must be enabled in order to use this method. */ - def flow[A](body: => A @cps[Future[A]], timeout: Long = Actor.TIMEOUT): Future[A] = - reset(new DefaultCompletableFuture[A](timeout).completeWithResult(body)) + def flow(body: => Any @cps[Future[Any]], timeout: Long = Actor.TIMEOUT): Future[Any] = + reset(new DefaultCompletableFuture[Any](timeout).completeWithResult(body)) private[akka] val callbacksPendingExecution = new ThreadLocal[Option[Stack[() => Unit]]]() { override def initialValue = None From 769078e7101e49335afe9afc532716e9b50fb538 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 2 May 2011 17:13:56 +0200 Subject: [PATCH 104/233] Added alter and alterOff to Agent, #758 --- .../src/main/scala/akka/agent/Agent.scala | 61 +++++++++++++++++-- akka-stm/src/test/scala/agent/AgentSpec.scala | 17 ++++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/akka-stm/src/main/scala/akka/agent/Agent.scala b/akka-stm/src/main/scala/akka/agent/Agent.scala index 80db8bff21..d64dd22715 100644 --- a/akka-stm/src/main/scala/akka/agent/Agent.scala +++ b/akka-stm/src/main/scala/akka/agent/Agent.scala @@ -7,7 +7,7 @@ package akka.agent import akka.stm._ import akka.actor.Actor import akka.japi.{Function => JFunc, Procedure => JProc} -import akka.dispatch.{Dispatchers, Future} +import akka.dispatch.{DefaultCompletableFuture, Dispatchers, Future} /** * Used internally to send functions. @@ -115,6 +115,23 @@ class Agent[T](initialValue: T) { else dispatch } + /** + * Dispatch a function to update the internal state, and return a Future where that new state can be obtained + * within the given timeout + */ + def alter(f: T => T)(timeout: Long): Future[T] = { + def dispatch = updater.!!!(Update(f),timeout) + if (Stm.activeTransaction) { + val result = new DefaultCompletableFuture[T](timeout) + get //Join xa + deferred { + result completeWith dispatch + } //Attach deferred-block to current transaction + result + } + else dispatch + } + /** * Dispatch a new value for the internal state. Behaves the same * as sending a fuction (x => newValue). @@ -140,6 +157,24 @@ class Agent[T](initialValue: T) { value }) + /** + * Dispatch a function to update the internal state but on its own thread, + * and return a Future where that new state can be obtained within the given timeout. + * This does not use the reactive thread pool and can be used for long-running + * or blocking operations. Dispatches using either `alterOff` or `alter` will + * still be executed in order. + */ + def alterOff(f: T => T)(timeout: Long): Future[T] = { + val result = new DefaultCompletableFuture[T](timeout) + send((value: T) => { + suspend + val threadBased = Actor.actorOf(new ThreadBasedAgentUpdater(this)).start() + result completeWith threadBased.!!!(Update(f), timeout) + value + }) + result + } + /** * A future to the current value that will be completed after any currently * queued updates. @@ -194,6 +229,13 @@ class Agent[T](initialValue: T) { */ def send(f: JFunc[T, T]): Unit = send(x => f(x)) + /** + * Java API + * Dispatch a function to update the internal state, and return a Future where that new state can be obtained + * within the given timeout + */ + def alter(f: JFunc[T, T], timeout: Long): Future[T] = alter(x => f(x))(timeout) + /** * Java API: * Dispatch a function to update the internal state but on its own thread. @@ -203,6 +245,16 @@ class Agent[T](initialValue: T) { */ def sendOff(f: JFunc[T, T]): Unit = sendOff(x => f(x)) + /** + * Java API: + * Dispatch a function to update the internal state but on its own thread, + * and return a Future where that new state can be obtained within the given timeout. + * This does not use the reactive thread pool and can be used for long-running + * or blocking operations. Dispatches using either `alterOff` or `alter` will + * still be executed in order. + */ + def alterOff(f: JFunc[T, T], timeout: Long): Unit = alterOff(x => f(x))(timeout) + /** * Java API: * Map this agent to a new agent, applying the function to the internal state. @@ -232,7 +284,7 @@ class AgentUpdater[T](agent: Agent[T]) extends Actor { def receive = { case update: Update[T] => - atomic(txFactory) { agent.ref alter update.function } + self.reply_?(atomic(txFactory) { agent.ref alter update.function }) case Get => self reply agent.get case _ => () } @@ -247,8 +299,9 @@ class ThreadBasedAgentUpdater[T](agent: Agent[T]) extends Actor { val txFactory = TransactionFactory(familyName = "ThreadBasedAgentUpdater", readonly = false) def receive = { - case update: Update[T] => { - atomic(txFactory) { agent.ref alter update.function } + case update: Update[T] => try { + self.reply_?(atomic(txFactory) { agent.ref alter update.function }) + } finally { agent.resume self.stop() } diff --git a/akka-stm/src/test/scala/agent/AgentSpec.scala b/akka-stm/src/test/scala/agent/AgentSpec.scala index ed07dea6bd..18233917c3 100644 --- a/akka-stm/src/test/scala/agent/AgentSpec.scala +++ b/akka-stm/src/test/scala/agent/AgentSpec.scala @@ -49,6 +49,23 @@ class AgentSpec extends WordSpec with MustMatchers { agent.close } + "maintain order between alter and alterOff" in { + + val agent = Agent("a") + + val r1 = agent.alter(_ + "b")(5000) + val r2 = agent.alterOff((s: String) => { Thread.sleep(2000); s + "c" })(5000) + val r3 = agent.alter(_ + "d")(5000) + + r1.await.resultOrException.get must be === "ab" + r2.await.resultOrException.get must be === "abc" + r3.await.resultOrException.get must be === "abcd" + + agent() must be ("abcd") + + agent.close + } + "be immediately readable" in { val countDown = new CountDownFunction[Int] val readLatch = new CountDownLatch(1) From 9bb184021114936de6661d932a21ff50e4ff97ff Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 2 May 2011 17:34:17 +0200 Subject: [PATCH 105/233] Fixing ticket #824 --- .../src/main/scala/akka/remote/BootableRemoteActorService.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/akka-remote/src/main/scala/akka/remote/BootableRemoteActorService.scala b/akka-remote/src/main/scala/akka/remote/BootableRemoteActorService.scala index aa88be92c0..5293f3a0a0 100644 --- a/akka-remote/src/main/scala/akka/remote/BootableRemoteActorService.scala +++ b/akka-remote/src/main/scala/akka/remote/BootableRemoteActorService.scala @@ -4,7 +4,6 @@ package akka.remote -import akka.config.Config.config import akka.actor. {Actor, BootableActorLoaderService} import akka.util. {ReflectiveAccess, Bootable} From 03943cd536a187317a6a49ef536fb093e1a1ec32 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 2 May 2011 18:02:56 +0200 Subject: [PATCH 106/233] Fixing typos in scaladoc --- akka-stm/src/main/scala/akka/agent/Agent.scala | 6 +++--- akka-stm/src/main/scala/akka/stm/package.scala | 2 +- .../src/main/scala/akka/actor/TypedActor.scala | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/akka-stm/src/main/scala/akka/agent/Agent.scala b/akka-stm/src/main/scala/akka/agent/Agent.scala index d64dd22715..378ee2995e 100644 --- a/akka-stm/src/main/scala/akka/agent/Agent.scala +++ b/akka-stm/src/main/scala/akka/agent/Agent.scala @@ -134,13 +134,13 @@ class Agent[T](initialValue: T) { /** * Dispatch a new value for the internal state. Behaves the same - * as sending a fuction (x => newValue). + * as sending a function (x => newValue). */ def send(newValue: T): Unit = send(x => newValue) /** * Dispatch a new value for the internal state. Behaves the same - * as sending a fuction (x => newValue). + * as sending a function (x => newValue). */ def update(newValue: T) = send(newValue) @@ -214,7 +214,7 @@ class Agent[T](initialValue: T) { def resume() = updater.dispatcher.resume(updater) /** - * Closes the agents and makes it eligable for garbage collection. + * Closes the agents and makes it eligible for garbage collection. * A closed agent cannot accept any `send` actions. */ def close() = updater.stop() diff --git a/akka-stm/src/main/scala/akka/stm/package.scala b/akka-stm/src/main/scala/akka/stm/package.scala index 055b1d3adf..c7587ac24a 100644 --- a/akka-stm/src/main/scala/akka/stm/package.scala +++ b/akka-stm/src/main/scala/akka/stm/package.scala @@ -5,7 +5,7 @@ package akka /** - * For easily importing everthing needed for STM. + * For easily importing everything needed for STM. */ package object stm extends akka.stm.Stm with akka.stm.StmUtil { 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 591613a203..4cc1892e65 100644 --- a/akka-typed-actor/src/main/scala/akka/actor/TypedActor.scala +++ b/akka-typed-actor/src/main/scala/akka/actor/TypedActor.scala @@ -515,7 +515,7 @@ object TypedActor { * Factory method for remote typed actor. * @param intfClass interface the typed actor implements * @param targetClass implementation class of the typed actor - * @param host hostanme of the remote server + * @param host hostname of the remote server * @param port port of the remote server */ @deprecated("Will be removed after 1.1") @@ -527,7 +527,7 @@ object TypedActor { * Factory method for remote typed actor. * @param intfClass interface the typed actor implements * @param factory factory method that constructs the typed actor - * @param host hostanme of the remote server + * @param host hostname of the remote server * @param port port of the remote server */ @deprecated("Will be removed after 1.1") @@ -560,7 +560,7 @@ object TypedActor { * @param intfClass interface the typed actor implements * @param targetClass implementation class of the typed actor * @paramm timeout timeout for future - * @param host hostanme of the remote server + * @param host hostname of the remote server * @param port port of the remote server */ @deprecated("Will be removed after 1.1") @@ -573,7 +573,7 @@ object TypedActor { * @param intfClass interface the typed actor implements * @param factory factory method that constructs the typed actor * @paramm timeout timeout for future - * @param host hostanme of the remote server + * @param host hostname of the remote server * @param port port of the remote server */ @deprecated("Will be removed after 1.1") @@ -585,7 +585,7 @@ object TypedActor { * Factory method for typed actor. * @param intfClass interface the typed actor implements * @param factory factory method that constructs the typed actor - * @paramm config configuration object fo the typed actor + * @paramm config configuration object forthe typed actor */ def newInstance[T](intfClass: Class[T], factory: => AnyRef, config: TypedActorConfiguration): T = newInstance(intfClass, createActorRef(newTypedActor(factory),config), config) @@ -607,7 +607,7 @@ object TypedActor { * Factory method for typed actor. * @param intfClass interface the typed actor implements * @param targetClass implementation class of the typed actor - * @paramm config configuration object fo the typed actor + * @paramm config configuration object forthe typed actor */ def newInstance[T](intfClass: Class[T], targetClass: Class[_], config: TypedActorConfiguration): T = newInstance(intfClass, createActorRef(newTypedActor(targetClass),config), config) From 41273567868175106f669ac3753027e027b5c37b Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 2 May 2011 18:09:46 +0200 Subject: [PATCH 107/233] Removing web.rst because of being severely outdated --- akka-docs/pending/web.rst | 99 --------------------------------------- 1 file changed, 99 deletions(-) delete mode 100644 akka-docs/pending/web.rst diff --git a/akka-docs/pending/web.rst b/akka-docs/pending/web.rst deleted file mode 100644 index 7d09ede65c..0000000000 --- a/akka-docs/pending/web.rst +++ /dev/null @@ -1,99 +0,0 @@ -Web Framework Integrations -========================== - -Play Framework -============== - -Home page: ``_ -Akka Play plugin: ``_ -Read more here: ``_ - -Lift Web Framework -================== - -Home page: ``_ - -In order to use Akka with Lift you basically just have to do one thing, add the 'AkkaServlet' to your 'web.xml'. - -web.xml -------- - -.. code-block:: xml - - - - - AkkaServlet - akka.comet.AkkaServlet - - - AkkaServlet - /* - - - - - LiftFilter - Lift Filter - The Filter that intercepts lift calls - net.liftweb.http.LiftFilter - - - LiftFilter - /* - - - -Boot class ----------- - -Lift bootstrap happens in the Lift 'Boot' class. Here is a good place to add Akka specific initialization. For example add declarative supervisor configuration to wire up the initial Actors. -Here is a full example taken from the Akka sample code, found here ``_. - -If a request is processed by Liftweb filter, Akka will not process the request. To disable processing of a request by the Lift filter : -* append partial function to LiftRules.liftRequest and return *false* value to disable processing of matching request -* use LiftRules.passNotFoundToChain to chain the request to the Akka filter - -Example of Boot class source code : -``_ -class Boot { - def boot { - // where to search snippet - LiftRules.addToPackages("sample.lift") - - LiftRules.httpAuthProtectedResource.prepend { - case (ParsePath("liftpage" :: Nil, _, _, _)) => Full(AuthRole("admin")) - } - - LiftRules.authentication = HttpBasicAuthentication("lift") { - case ("someuser", "1234", req) => { - Log.info("You are now authenticated !") - userRoles(AuthRole("admin")) - true - } - } - - LiftRules.liftRequest.append { - case Req("liftcount" :: _, _, _) => false - case Req("persistentliftcount" :: _, _, _) => false - } - LiftRules.passNotFoundToChain = true - - // Akka supervisor configuration wiring up initial Actor services - val supervisor = Supervisor( - SupervisorConfig( - RestartStrategy(OneForOne, 3, 100, List(classOf[Exception])), - Supervise( - actorOf[SimpleService], - LifeCycle(Permanent)) :: - Supervise( - actorOf[PersistentSimpleService], - LifeCycle(Permanent)) :: - Nil)) - - // Build SiteMap - // val entries = Menu(Loc("Home", List("index"), "Home")) :: Nil - // LiftRules.setSiteMap(SiteMap(entries:_*)) - } -} -``_ From 15c2e1070df61780d3aeabd81b164cea4a297337 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 2 May 2011 18:22:13 +0200 Subject: [PATCH 108/233] Adding a Common section to the docs and fixing the Scheduler docs --- akka-docs/common/index.rst | 7 +++++++ akka-docs/common/scheduler.rst | 23 +++++++++++++++++++++++ akka-docs/general/index.rst | 1 + akka-docs/index.rst | 1 + akka-docs/pending/scheduler.rst | 16 ---------------- 5 files changed, 32 insertions(+), 16 deletions(-) create mode 100644 akka-docs/common/index.rst create mode 100644 akka-docs/common/scheduler.rst delete mode 100644 akka-docs/pending/scheduler.rst diff --git a/akka-docs/common/index.rst b/akka-docs/common/index.rst new file mode 100644 index 0000000000..6ed8cb1593 --- /dev/null +++ b/akka-docs/common/index.rst @@ -0,0 +1,7 @@ +Common utilities +========================== + +.. toctree:: + :maxdepth: 2 + + scheduler diff --git a/akka-docs/common/scheduler.rst b/akka-docs/common/scheduler.rst new file mode 100644 index 0000000000..bf2b813d2e --- /dev/null +++ b/akka-docs/common/scheduler.rst @@ -0,0 +1,23 @@ +Scheduler +========= + +Module stability: **SOLID** + +``Akka`` has a little scheduler written using actors. +This can be convenient if you want to schedule some periodic task for maintenance or similar. + +It allows you to register a message that you want to be sent to a specific actor at a periodic interval. + +Here is an example: +------------------- + +.. code-block:: scala + + import akka.actor.Scheduler + + //Sends messageToBeSent to receiverActor after initialDelayBeforeSending and then after each delayBetweenMessages + Scheduler.schedule(receiverActor, messageToBeSent, initialDelayBeforeSending, delayBetweenMessages, timeUnit) + + //Sends messageToBeSent to receiverActor after delayUntilSend + Scheduler.scheduleOnce(receiverActor, messageToBeSent, delayUntilSend, timeUnit) + diff --git a/akka-docs/general/index.rst b/akka-docs/general/index.rst index 367e45b9d5..6eae15cf67 100644 --- a/akka-docs/general/index.rst +++ b/akka-docs/general/index.rst @@ -4,6 +4,7 @@ General .. toctree:: :maxdepth: 2 + jmm migration-guides building-akka configuration diff --git a/akka-docs/index.rst b/akka-docs/index.rst index 11bfef862a..692664ce81 100644 --- a/akka-docs/index.rst +++ b/akka-docs/index.rst @@ -6,6 +6,7 @@ Contents intro/index general/index + common/index scala/index java/index dev/index diff --git a/akka-docs/pending/scheduler.rst b/akka-docs/pending/scheduler.rst deleted file mode 100644 index ac0c7a3a50..0000000000 --- a/akka-docs/pending/scheduler.rst +++ /dev/null @@ -1,16 +0,0 @@ -Scheduler -========= - -Module stability: **SOLID** - -Akka has a little scheduler written using actors. Can be convenient if you want to schedule some periodic task for maintenance or similar. - -It allows you to register a message that you want to be sent to a specific actor at a periodic interval. Here is an example: - -``_ -//Sends messageToBeSent to receiverActor after initialDelayBeforeSending and then after each delayBetweenMessages -Scheduler.schedule(receiverActor, messageToBeSent, initialDelayBeforeSending, delayBetweenMessages, timeUnit) - -//Sends messageToBeSent to receiverActor after delayUntilSend -Scheduler.scheduleOnce(receiverActor, messageToBeSent, delayUntilSend, timeUnit) -``_ From c978ba16199fd5abc5b62c4c9d75baebafe467cf Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 2 May 2011 18:30:35 +0200 Subject: [PATCH 109/233] Scrapping parts of Home.rst and add a what-is-akka.rst under intro --- akka-docs/intro/index.rst | 1 + akka-docs/intro/what-is-akka.rst | 33 ++++++++++++++++++ akka-docs/pending/Home.rst | 60 -------------------------------- 3 files changed, 34 insertions(+), 60 deletions(-) create mode 100644 akka-docs/intro/what-is-akka.rst delete mode 100644 akka-docs/pending/Home.rst diff --git a/akka-docs/intro/index.rst b/akka-docs/intro/index.rst index 6550f1bd79..2de18010c4 100644 --- a/akka-docs/intro/index.rst +++ b/akka-docs/intro/index.rst @@ -4,6 +4,7 @@ Introduction .. toctree:: :maxdepth: 2 + what-is-akka why-akka getting-started-first-scala getting-started-first-scala-eclipse diff --git a/akka-docs/intro/what-is-akka.rst b/akka-docs/intro/what-is-akka.rst new file mode 100644 index 0000000000..c9f4e6bd18 --- /dev/null +++ b/akka-docs/intro/what-is-akka.rst @@ -0,0 +1,33 @@ +What is Akka? +==== + +**Akka** +^^^^^^ + +**Simpler Scalability, Fault-Tolerance, Concurrency & Remoting through Actors** + +We believe that writing correct concurrent, fault-tolerant and scalable applications is too hard. Most of the time it's because we are using the wrong tools and the wrong level of abstraction. Akka is here to change that. Using the Actor Model together with ``Software Transactional Memory`` we raise the abstraction level and provide a better platform to build correct concurrent and scalable applications. For fault-tolerance we adopt the ``Let it crash`` / ``Embrace failure`` model which have been used with great success in the telecom industry to build applications that self-heals, systems that never stop. Actors also provides the abstraction for transparent distribution and the basis for truly scalable and fault-tolerant applications. Akka is Open Source and available under the ``Apache 2 License``. + + +Download from ``_ + +**Akka implements a unique hybrid of:** +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* `Actors `_, which gives you: + * Simple and high-level abstractions for concurrency and parallelism. + * Asynchronous, non-blocking and highly performant event-driven programming model. + * Very lightweight event-driven processes (create ~6.5 million actors on 4GB RAM). +* `Failure management `_ through supervisor hierarchies with `let-it-crash `_ semantics. Excellent for writing highly fault-tolerant systems that never stop, systems that self-heal. +* `Software Transactional Memory `_ (STM). (Distributed transactions coming soon). +* `Transactors `_: combine actors and STM into transactional actors. Allows you to compose atomic message flows with automatic retry and rollback. +* `Remote actors `_: highly performant distributed actors with remote supervision and error management. +* Java and Scala API. + +**Akka can be used in two different ways:** +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* As a library: used by a web app, to be put into ‘WEB-INF/lib’ or as a regular JAR on your classpath. +* As a microkernel: stand-alone kernel, embedding a servlet container and all the other modules. + +See the `Use-case and Deployment Scenarios `_ for details. diff --git a/akka-docs/pending/Home.rst b/akka-docs/pending/Home.rst deleted file mode 100644 index 73c9f31172..0000000000 --- a/akka-docs/pending/Home.rst +++ /dev/null @@ -1,60 +0,0 @@ -Akka -==== - -**Simpler Scalability, Fault-Tolerance, Concurrency & Remoting through Actors** - -We believe that writing correct concurrent, fault-tolerant and scalable applications is too hard. Most of the time it's because we are using the wrong tools and the wrong level of abstraction. Akka is here to change that. Using the Actor Model together with Software Transactional Memory we raise the abstraction level and provide a better platform to build correct concurrent and scalable applications. For fault-tolerance we adopt the "Let it crash" / "Embrace failure" model which have been used with great success in the telecom industry to build applications that self-heals, systems that never stop. Actors also provides the abstraction for transparent distribution and the basis for truly scalable and fault-tolerant applications. Akka is Open Source and available under the Apache 2 License. - -Akka is split up into two different parts: -* Akka - Reflects all the sections under 'Scala API' and 'Java API' in the navigation bar. -* Akka Modules - Reflects all the sections under 'Add-on modules' in the navigation bar. - -Download from ``_ - -News: Akka 1.0 final is released --------------------------------- - -1.0 documentation ------------------ - -This documentation covers the latest release ready code in 'master' branch in the repository. -If you want the documentation for the 1.0 release you can find it `here `_. - -You can watch the recording of the `Akka talk at JFokus in Feb 2011 `_. - -``_ - -**Akka implements a unique hybrid of:** -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -* `Actors `_, which gives you: - * Simple and high-level abstractions for concurrency and parallelism. - * Asynchronous, non-blocking and highly performant event-driven programming model. - * Very lightweight event-driven processes (create ~6.5 million actors on 4 G RAM). -* `Failure management `_ through supervisor hierarchies with `let-it-crash `_ semantics. Excellent for writing highly fault-tolerant systems that never stop, systems that self-heal. -* `Software Transactional Memory `_ (STM). (Distributed transactions coming soon). -* `Transactors `_: combine actors and STM into transactional actors. Allows you to compose atomic message flows with automatic retry and rollback. -* `Remote actors `_: highly performant distributed actors with remote supervision and error management. -* Java and Scala API. - -**Akka also has a set of add-on modules:** -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -* `Camel `_: Expose actors as Apache Camel endpoints. -* `Spring `_: Wire up typed actors in the Spring config using Akka's namespace. -* `REST `_ (JAX-RS): Expose actors as REST services. -* `OSGi `_: Akka and all its dependency is OSGi enabled. -* `Mist `_: Expose actors as asynchronous HTTP services. -* `Security `_: Basic, Digest and Kerberos based security. -* `Microkernel `_: Run Akka as a stand-alone self-hosted kernel. -* `FSM `_: Finite State Machine support. -* `JTA `_: Let the STM interoperate with other transactional resources. -* `Pub/Sub `_: Publish-Subscribe across remote nodes. - -**Akka can be used in two different ways:** -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -* As a library: used by a web app, to be put into ‘WEB-INF/lib’ or as a regular JAR on your classpath. -* As a microkernel: stand-alone kernel, embedding a servlet container and all the other modules. - -See the `Use-case and Deployment Scenarios `_ for details. From a97bdda6e82cd142dd3532bcaee2e0d3dedaba4b Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 2 May 2011 18:38:21 +0200 Subject: [PATCH 110/233] Converting the Akka Developer Guidelines and add then to the General section --- .../{pending => general}/developer-guidelines.rst | 12 ++++++------ akka-docs/general/index.rst | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) rename akka-docs/{pending => general}/developer-guidelines.rst (64%) diff --git a/akka-docs/pending/developer-guidelines.rst b/akka-docs/general/developer-guidelines.rst similarity index 64% rename from akka-docs/pending/developer-guidelines.rst rename to akka-docs/general/developer-guidelines.rst index bf2e9dad26..5965a2fe41 100644 --- a/akka-docs/pending/developer-guidelines.rst +++ b/akka-docs/general/developer-guidelines.rst @@ -1,4 +1,4 @@ -Developer Guidelines +Developer Guidelines (committers) ==================== Code Style @@ -6,25 +6,25 @@ Code Style The Akka code style follows `this document `_ . -Here is a code style settings file for IntelliJ IDEA. -``_ +Here is a code style settings file for ``IntelliJ IDEA``: +`Download <@http://scalablesolutions.se/akka/docs/akka-0.10/files/akka-intellij-code-style.jar>`_ Please follow the code style. Look at the code around you and mimic. Testing ------- -All code that is checked in should have tests. All testing is done with ScalaTest and ScalaCheck. +All code that is checked in **should** have tests. All testing is done with ``ScalaTest`` and ``ScalaCheck``. * Name tests as *Test.scala if they do not depend on any external stuff. That keeps surefire happy. * Name tests as *Spec.scala if they have external dependencies. -There is a testing standard that should be followed: `Ticket001Spec <@https://github.com/jboner/akka/blob/master/akka-actor/src/test/scala/akka/ticket/Ticket001Spec.scala>`_ +There is a testing standard that should be followed: `Ticket001Spec <@https://github.com/jboner/akka/blob/master/akka-actor-tests/src/test/scala/akka/ticket/Ticket001Spec.scala>`_ Actor TestKit ^^^^^^^^^^^^^ -There is a useful test kit for testing actors: `akka.util.TestKit <@https://github.com/jboner/akka/tree/master/akka-actor/src/main/scala/akka/util/TestKit.scala>`_. It enables assertions concerning replies received and their timing, there is more documentation in the ``_ module. +There is a useful test kit for testing actors: `akka.util.TestKit <@https://github.com/jboner/akka/tree/master/akka-testkit/src/main/scala/akka/testkit/TestKit.scala>`_. It enables assertions concerning replies received and their timing, there is more documentation in the ``_ module. NetworkFailureTest ^^^^^^^^^^^^^^^^^^ diff --git a/akka-docs/general/index.rst b/akka-docs/general/index.rst index 6eae15cf67..f836c00d1c 100644 --- a/akka-docs/general/index.rst +++ b/akka-docs/general/index.rst @@ -10,3 +10,4 @@ General configuration event-handler util + developer-guidelines From 25a56ef0539f83c24964233bbb10a845d9aa2399 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 2 May 2011 18:46:45 +0200 Subject: [PATCH 111/233] Converting the issue-tracking doc and putting it under General --- akka-docs/general/index.rst | 1 + akka-docs/general/issue-tracking.rst | 56 ++++++++++++++++++++++++++++ akka-docs/pending/issue-tracking.rst | 51 ------------------------- 3 files changed, 57 insertions(+), 51 deletions(-) create mode 100644 akka-docs/general/issue-tracking.rst delete mode 100644 akka-docs/pending/issue-tracking.rst diff --git a/akka-docs/general/index.rst b/akka-docs/general/index.rst index f836c00d1c..eef8e30d65 100644 --- a/akka-docs/general/index.rst +++ b/akka-docs/general/index.rst @@ -11,3 +11,4 @@ General event-handler util developer-guidelines + issue-tracking diff --git a/akka-docs/general/issue-tracking.rst b/akka-docs/general/issue-tracking.rst new file mode 100644 index 0000000000..b0137c3ecf --- /dev/null +++ b/akka-docs/general/issue-tracking.rst @@ -0,0 +1,56 @@ +Issue Tracking +============== + +Akka is using ``Assembla`` as issue tracking system. + +Browsing +-------- + +Tickets +^^^^^^^ + +`You can find the Akka tickets here `_ + +`You can find the Akka Modules tickets here `_ + +Roadmaps +^^^^^^^^ + +`The roadmap for each Akka milestone is here `_ + +`The roadmap for each Akka Modules milestone is here `_ + +Creating tickets +---------------- + +In order to create tickets you need to do the following: + +`Register here `_ then log in + +For Akka tickets: +`Link to create new ticket `_ + + +For Akka Modules tickets: +`Link to create new ticket `_ + +Thanks a lot for reporting bugs and suggesting features. + +Failing test +------------ + +Please submit a failing test on the following format: + +.. code-block:: scala + + import org.scalatest.WordSpec + import org.scalatest.matchers.MustMatchers + + class Ticket001Spec extends WordSpec with MustMatchers { + + "An XXX" should { + "do YYY" in { + 1 must be (1) + } + } + } diff --git a/akka-docs/pending/issue-tracking.rst b/akka-docs/pending/issue-tracking.rst deleted file mode 100644 index fa81a4d254..0000000000 --- a/akka-docs/pending/issue-tracking.rst +++ /dev/null @@ -1,51 +0,0 @@ -Issue Tracking -============== - -Akka is using Assembla as issue tracking system. - -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 ----------------- - -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 ------------- - -Please submit a failing test on the following format: - -``_ - -import org.scalatest.WordSpec -import org.scalatest.matchers.MustMatchers - -class Ticket001Spec extends WordSpec with MustMatchers { - - "An XXX" should { - "do YYY" in { - 1 must be (1) - } - } -} -``_ From 05db33ee97768758e07aa9816fac5642f35187cd Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 2 May 2011 18:50:56 +0200 Subject: [PATCH 112/233] Moving Developer guidelines to the Dev section --- akka-docs/{general => dev}/developer-guidelines.rst | 2 +- akka-docs/dev/index.rst | 1 + akka-docs/general/index.rst | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) rename akka-docs/{general => dev}/developer-guidelines.rst (98%) diff --git a/akka-docs/general/developer-guidelines.rst b/akka-docs/dev/developer-guidelines.rst similarity index 98% rename from akka-docs/general/developer-guidelines.rst rename to akka-docs/dev/developer-guidelines.rst index 5965a2fe41..3834dec120 100644 --- a/akka-docs/general/developer-guidelines.rst +++ b/akka-docs/dev/developer-guidelines.rst @@ -1,4 +1,4 @@ -Developer Guidelines (committers) +Developer Guidelines ==================== Code Style diff --git a/akka-docs/dev/index.rst b/akka-docs/dev/index.rst index 05ab53742d..b50702ccdb 100644 --- a/akka-docs/dev/index.rst +++ b/akka-docs/dev/index.rst @@ -5,3 +5,4 @@ Information for Developers :maxdepth: 2 documentation + developer-guidelines diff --git a/akka-docs/general/index.rst b/akka-docs/general/index.rst index eef8e30d65..6807aeff83 100644 --- a/akka-docs/general/index.rst +++ b/akka-docs/general/index.rst @@ -10,5 +10,4 @@ General configuration event-handler util - developer-guidelines issue-tracking From 0d476c24d621724c0e6debd3f820b4ce9c0071d2 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 2 May 2011 19:28:00 +0200 Subject: [PATCH 113/233] Converting team.rst, sponsors.rst and fixing some details in dev-guides --- akka-docs/dev/developer-guidelines.rst | 10 +- akka-docs/dev/index.rst | 2 + akka-docs/{pending => dev}/sponsors.rst | 10 +- akka-docs/dev/team.rst | 25 + .../pending/migration-guide-0.10.x-1.0.x.rst | 432 ------------------ akka-docs/pending/team.rst | 22 - 6 files changed, 34 insertions(+), 467 deletions(-) rename akka-docs/{pending => dev}/sponsors.rst (58%) create mode 100644 akka-docs/dev/team.rst delete mode 100644 akka-docs/pending/migration-guide-0.10.x-1.0.x.rst delete mode 100644 akka-docs/pending/team.rst diff --git a/akka-docs/dev/developer-guidelines.rst b/akka-docs/dev/developer-guidelines.rst index 3834dec120..ab19c370a2 100644 --- a/akka-docs/dev/developer-guidelines.rst +++ b/akka-docs/dev/developer-guidelines.rst @@ -7,7 +7,7 @@ Code Style The Akka code style follows `this document `_ . Here is a code style settings file for ``IntelliJ IDEA``: -`Download <@http://scalablesolutions.se/akka/docs/akka-0.10/files/akka-intellij-code-style.jar>`_ +`Download `_ Please follow the code style. Look at the code around you and mimic. @@ -16,15 +16,15 @@ Testing All code that is checked in **should** have tests. All testing is done with ``ScalaTest`` and ``ScalaCheck``. -* Name tests as *Test.scala if they do not depend on any external stuff. That keeps surefire happy. -* Name tests as *Spec.scala if they have external dependencies. +* Name tests as **Test.scala** if they do not depend on any external stuff. That keeps surefire happy. +* Name tests as **Spec.scala** if they have external dependencies. -There is a testing standard that should be followed: `Ticket001Spec <@https://github.com/jboner/akka/blob/master/akka-actor-tests/src/test/scala/akka/ticket/Ticket001Spec.scala>`_ +There is a testing standard that should be followed: `Ticket001Spec `_ Actor TestKit ^^^^^^^^^^^^^ -There is a useful test kit for testing actors: `akka.util.TestKit <@https://github.com/jboner/akka/tree/master/akka-testkit/src/main/scala/akka/testkit/TestKit.scala>`_. It enables assertions concerning replies received and their timing, there is more documentation in the ``_ module. +There is a useful test kit for testing actors: `akka.util.TestKit `_. It enables assertions concerning replies received and their timing, there is more documentation in the ``_ module. NetworkFailureTest ^^^^^^^^^^^^^^^^^^ diff --git a/akka-docs/dev/index.rst b/akka-docs/dev/index.rst index b50702ccdb..a4f1cee593 100644 --- a/akka-docs/dev/index.rst +++ b/akka-docs/dev/index.rst @@ -6,3 +6,5 @@ Information for Developers documentation developer-guidelines + sponsors + team diff --git a/akka-docs/pending/sponsors.rst b/akka-docs/dev/sponsors.rst similarity index 58% rename from akka-docs/pending/sponsors.rst rename to akka-docs/dev/sponsors.rst index 88d35f1f0a..544a35825a 100644 --- a/akka-docs/pending/sponsors.rst +++ b/akka-docs/dev/sponsors.rst @@ -1,11 +1,5 @@ -****Sponsors **** -======================================================= - -Scalable Solutions -================== - -Scalable Solutions AB is the commercial entity behind Akka, providing support, consulting and training around Akka. -``_ +**Sponsors** +============ YourKit ======= diff --git a/akka-docs/dev/team.rst b/akka-docs/dev/team.rst new file mode 100644 index 0000000000..4038fa9347 --- /dev/null +++ b/akka-docs/dev/team.rst @@ -0,0 +1,25 @@ +Team +===== + +=================== ========================== ================================= +Name Role Email +=================== ========================== ================================= +Jonas Bonér Founder, Despot, Committer jonas AT jonasboner DOT com +Viktor Klang Bad cop, Committer viktor DOT klang AT gmail DOT com +Debasish Ghosh Committer dghosh AT acm DOT org +Ross McDonald Alumni rossajmcd AT gmail DOT com +Eckhart Hertzler Alumni +Mikael Högqvist Alumni +Tim Perrett Alumni +Jeanfrancois Arcand Alumni jfarcand AT apache DOT org +Martin Krasser Committer krasserm AT googlemail DOT com +Jan Van Besien Alumni +Michael Kober Alumni +Peter Vlugter Committer +Peter Veentjer Committer +Irmo Manie Committer +Heiko Seeberger Committer +Hiram Chirino Committer +Scott Clasen Committer +Roland Kuhn Committer +=================== ========================== ================================= \ No newline at end of file diff --git a/akka-docs/pending/migration-guide-0.10.x-1.0.x.rst b/akka-docs/pending/migration-guide-0.10.x-1.0.x.rst deleted file mode 100644 index 300100941f..0000000000 --- a/akka-docs/pending/migration-guide-0.10.x-1.0.x.rst +++ /dev/null @@ -1,432 +0,0 @@ -Migration guide from 0.10.x to 1.0.x -==================================== - ----- - -Akka & Akka Modules separated into two different repositories and distributions -------------------------------------------------------------------------------- - -Akka is split up into two different parts: -* Akka - Reflects all the sections under 'Scala API' and 'Java API' in the navigation bar. -* Akka Modules - Reflects all the sections under 'Add-on modules' in the navigation bar. - -Download the release you need (Akka core or Akka Modules) from ``_ and unzip it. - ----- - -Changed Akka URI ----------------- - -http:*akkasource.org changed to http:*akka.io - -Reflects XSDs, Maven repositories, ScalaDoc etc. - ----- - -Removed 'se.scalablesolutions' prefix -------------------------------------- - -We have removed some boilerplate by shortening the Akka package from -**se.scalablesolutions.akka** to just **akka** so just do a search-replace in your project, -we apologize for the inconvenience, but we did it for our users. - ----- - -Akka-core is no more --------------------- - -Akka-core has been split into akka-actor, akka-stm, akka-typed-actor & akka-remote this means that you need to update any deps you have on akka-core. - ----- - -Config ------- - -Turning on/off modules -^^^^^^^^^^^^^^^^^^^^^^ - -All the 'service = on' elements for turning modules on and off have been replaced by a top-level list of the enabled services. - -Services available for turning on/off are: -* "remote" -* "http" -* "camel" - -**All** services are **OFF** by default. Enable the ones you are using. - -.. code-block:: ruby - - akka { - enabled-modules = [] # Comma separated list of the enabled modules. Options: ["remote", "camel", "http"] - } - -Renames -^^^^^^^ - -* 'rest' section - has been renamed to 'http' to align with the module name 'akka-http'. -* 'storage' section - has been renamed to 'persistence' to align with the module name 'akka-persistence'. - -.. code-block:: ruby - - akka { - http { - .. - } - - persistence { - .. - } - } - ----- - -Important changes from RC2-RC3 ------------------------------- - -**akka.config.SupervisionSupervise** -def apply(actorRef: ActorRef, lifeCycle: LifeCycle, registerAsRemoteService: Boolean = false) -- boolean instead of remoteAddress, registers that actor with it's id as service name on the local server - -**akka.actor.Actors now is the API for Java to interact with Actors, Remoting and ActorRegistry:** - -import static akka.actor.Actors.*; -*actorOf()..* -remote().actorOf()... -*registry().actorsFor("foo")...* - -***akka.actor.Actor now is the API for Scala to interact with Actors, Remoting and ActorRegistry:*** - -*import akka.actor.Actor._* -actorOf()... -*remote.actorOf()...* -registry.actorsFor("foo") - -**object UntypedActor has been deleted and replaced with akka.actor.Actors/akka.actor.Actor (Java/Scala)** -UntypedActor.actorOf -> Actors.actorOf (Java) or Actor.actorOf (Scala) - -**object ActorRegistry has been deleted and replaced with akka.actor.Actors.registry()/akka.actor.Actor.registry (Java/Scala)** -ActorRegistry. -> Actors.registry(). (Java) or Actor.registry. (Scala) - -**object RemoteClient has been deleted and replaced with akka.actor.Actors.remote()/akka.actor.Actor.remote (Java/Scala)** -RemoteClient -> Actors.remote() (Java) or Actor.remote (Scala) - -**object RemoteServer has been deleted and replaced with akka.actor.Actors.remote()/akka.actor.Actor.remote (Java/Scala)** -RemoteServer - deleted -> Actors.remote() (Java) or Actor.remote (Scala) - -**classes RemoteActor, RemoteUntypedActor and RemoteUntypedConsumerActors has been deleted and replaced** -**with akka.actor.Actors.remote().actorOf(x, host port)/akka.actor.Actor.remote.actorOf(x, host, port)** -RemoteActor, RemoteUntypedActor - deleted, use: remote().actorOf(YourActor.class, host, port) (Java) or remote.actorOf[YourActor](host, port) - -**Remoted spring-actors now default to spring id as service-name, use "service-name" attribute on "remote"-tag to override** - -**Listeners for RemoteServer and RemoteClient** are now registered on Actors.remote().addListener (Java) or Actor.remote.addListener (Scala), -this means that all listeners get all remote events, both remote server evens and remote client events, **so adjust your code accordingly.** - -**ActorRef.startLinkRemote has been removed since one specified on creation wether the actor is client-managed or not.** - -Important change from RC3 to RC4 --------------------------------- - -The Akka-Spring namespace has changed from akkasource.org and scalablesolutions.se to http:*akka.io/schema and http:*akka.io/akka-.xsd - ----- - -Module akka-actor ------------------ - -The Actor.init callback has been renamed to "preStart" to align with the general callback naming and is more clear about when it's called. - -The Actor.shutdown callback has been renamed to "postStop" to align with the general callback naming and is more clear about when it's called. - -The Actor.initTransactionalState callback has been removed, logic should be moved to preStart and be wrapped in an atomic block - -**se.scalablesolutions.akka.config.ScalaConfig** and **se.scalablesolutions.akka.config.JavaConfig** have been merged into **akka.config.Supervision** - -**RemoteAddress** has moved from **se.scalablesolutions.akka.config.ScalaConfig** to **akka.config** - -The ActorRef.lifeCycle has changed signature from Option[LifeCycle] to LifeCycle, this means you need to change code that looks like this: -**self.lifeCycle = Some(LifeCycle(Permanent))** to **self.lifeCycle = Permanent** - -The equivalent to **self.lifeCycle = None** is **self.lifeCycle = UndefinedLifeCycle** -**LifeCycle(Permanent)** becomes **Permanent** -**new LifeCycle(permanent())** becomes **permanent()** (need to do: import static se.scalablesolutions.akka.config.Supervision.*; first) - -**JavaConfig.Component** and **ScalaConfig.Component** have been consolidated and renamed as **Supervision.SuperviseTypedActor** - -**self.trapExit** has been moved into the FaultHandlingStrategy, and **ActorRef.faultHandler** has switched type from Option[FaultHandlingStrategy] -to FaultHandlingStrategy: - -|| **Scala** || -|| -``_ -import akka.config.Supervision._ - -self.faultHandler = OneForOneStrategy(List(classOf[Exception]), 3, 5000) - -``_ || -|| **Java** || -|| -``_ -import static akka.Supervision.*; - -getContext().setFaultHandler(new OneForOneStrategy(new Class[] { Exception.class },50,1000)) - -``_ || - -**RestartStrategy, AllForOne, OneForOne** have been replaced with **AllForOneStrategy** and **OneForOneStrategy** in **se.scalablesolutions.akka.config.Supervision** - -|| **Scala** || -|| -``_ -import akka.config.Supervision._ -SupervisorConfig( - OneForOneStrategy(List(classOf[Exception]), 3, 5000), - Supervise(pingpong1,Permanent) :: Nil -) - -``_ || -|| **Java** || -|| -``_ -import static akka.Supervision.*; - -new SupervisorConfig( - new OneForOneStrategy(new Class[] { Exception.class },50,1000), - new Server[] { new Supervise(pingpong1, permanent()) } -) - -``_ || - -We have removed the following factory methods: - -**Actor.actor { case foo => bar }** -**Actor.transactor { case foo => bar }** -**Actor.temporaryActor { case foo => bar }** -**Actor.init {} receive { case foo => bar }** - -They started the actor and no config was possible, it was inconsistent and irreparable. - -replace with your own factories, or: - -**actorOf( new Actor { def receive = { case foo => bar } } ).start** -**actorOf( new Actor { self.lifeCycle = Temporary; def receive = { case foo => bar } } ).start** - -ReceiveTimeout is now rescheduled after every message, before there was only an initial timeout. -To stop rescheduling of ReceiveTimeout, set **receiveTimeout = None** - -HotSwap -------- - -HotSwap does no longer use behavior stacking by default, but that is an option to both "become" and HotSwap. - -HotSwap now takes for Scala a Function from ActorRef to a Receive, the ActorRef passed in is the reference to self, so you can do self.reply() etc. - ----- - -Module akka-stm ---------------- - -The STM stuff is now in its own module. This means that there is no support for transactions or transactors in akka-actor. - -Local and global -^^^^^^^^^^^^^^^^ - -The **local/global** distinction has been dropped. This means that if the following general import was being used: - -.. code-block:: scala - - import akka.stm.local._ - -this is now just: - -.. code-block:: scala - - import akka.stm._ - -Coordinated is the new global -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -There is a new explicit mechanism for coordinated transactions. See the `Scala Transactors `_ and `Java Transactors `_ documentation for more information. Coordinated transactions and transactors are found in the ``akka.transactor`` package now. The usage of transactors has changed. - -Agents -^^^^^^ - -Agent is now in the akka-stm module and has moved to the ``akka.agent`` package. The implementation has been reworked and is now closer to Clojure agents. There is not much difference in general usage, the main changes involve interaction with the STM. - -While updates to Agents are asynchronous, the state of an Agent is always immediately available for reading by any thread. Agents are integrated with the STM - any dispatches made in a transaction are held until that transaction commits, and are discarded if it is retried or aborted. There is a new ``sendOff`` method for long-running or blocking update functions. - ----- - -Module akka-camel ------------------ - -Access to the CamelService managed by CamelServiceManager has changed: - -* Method service renamed to mandatoryService (Scala) -* Method service now returns Option[CamelService] (Scala) -* Introduced method getMandatoryService() (Java) -* Introduced method getService() (Java) - -|| **Scala** || -|| -``_ -import se.scalablesolutions.akka.camel.CamelServiceManager._ -import se.scalablesolutions.akka.camel.CamelService - -val o: Option[CamelService] = service -val s: CamelService = mandatoryService - -``_ || -|| **Java** || -|| -``_ -import se.scalablesolutions.akka.camel.CamelService; -import se.scalablesolutions.akka.japi.Option; -import static se.scalablesolutions.akka.camel.CamelServiceManager.*; - -Option o = getService(); -CamelService s = getMandatoryService(); - -``_ || - -Access to the CamelContext and ProducerTemplate managed by CamelContextManager has changed: - -* Method context renamed to mandatoryContext (Scala) -* Method template renamed to mandatoryTemplate (Scala) -* Method service now returns Option[CamelContext] (Scala) -* Method template now returns Option[ProducerTemplate] (Scala) -* Introduced method getMandatoryContext() (Java) -* Introduced method getContext() (Java) -* Introduced method getMandatoryTemplate() (Java) -* Introduced method getTemplate() (Java) - -|| **Scala** || -|| -``_ -import org.apache.camel.CamelContext -import org.apache.camel.ProducerTemplate - -import se.scalablesolutions.akka.camel.CamelContextManager._ - -val co: Option[CamelContext] = context -val to: Option[ProducerTemplate] = template - -val c: CamelContext = mandatoryContext -val t: ProducerTemplate = mandatoryTemplate - -``_ || -|| **Java** || -|| -``_ -import org.apache.camel.CamelContext; -import org.apache.camel.ProducerTemplate; - -import se.scalablesolutions.akka.japi.Option; -import static se.scalablesolutions.akka.camel.CamelContextManager.*; - -Option co = getContext(); -Option to = getTemplate(); - -CamelContext c = getMandatoryContext(); -ProducerTemplate t = getMandatoryTemplate(); - -``_ || - -The following methods have been renamed on class se.scalablesolutions.akka.camel.Message: - -* bodyAs(Class) has been renamed to getBodyAs(Class) -* headerAs(String, Class) has been renamed to getHeaderAs(String, Class) - -The API for waiting for consumer endpoint activation and de-activation has been changed - -* CamelService.expectEndpointActivationCount has been removed and replaced by CamelService.awaitEndpointActivation -* CamelService.expectEndpointDeactivationCount has been removed and replaced by CamelService.awaitEndpointDeactivation - -|| **Scala** || -|| -``_ -import se.scalablesolutions.akka.actor.Actor -import se.scalablesolutions.akka.camel.CamelServiceManager._ - -val s = startCamelService -val actor = Actor.actorOf[SampleConsumer] - -// wait for 1 consumer being activated -s.awaitEndpointActivation(1) { - actor.start -} - -// wait for 1 consumer being de-activated -s.awaitEndpointDeactivation(1) { - actor.stop -} - -s.stop - -``_ || -|| **Java** || -|| -``_ -import java.util.concurrent.TimeUnit; -import se.scalablesolutions.akka.actor.ActorRef; -import se.scalablesolutions.akka.actor.Actors; -import se.scalablesolutions.akka.camel.CamelService; -import se.scalablesolutions.akka.japi.SideEffect; -import static se.scalablesolutions.akka.camel.CamelServiceManager.*; - -CamelService s = startCamelService(); -final ActorRef actor = Actors.actorOf(SampleUntypedConsumer.class); - -// wait for 1 consumer being activated -s.awaitEndpointActivation(1, new SideEffect() { - public void apply() { - actor.start(); - } -}); - -// wait for 1 consumer being de-activated -s.awaitEndpointDeactivation(1, new SideEffect() { - public void apply() { - actor.stop(); - } -}); - -s.stop(); - -``_ || - -- - -Module Akka-Http ----------------- - -Atmosphere support has been removed. If you were using akka.comet.AkkaServlet for Jersey support only, -you can switch that to: akka.http.AkkaRestServlet and it should work just like before. - -Atmosphere has been removed because we have a new async http support in the form of Akka Mist, a very thin bridge -between Servlet3.0/JettyContinuations and Actors, enabling Http-as-messages, read more about it here: -http://doc.akka.io/http#Mist%20-%20Lightweight%20Asynchronous%20HTTP - -If you really need Atmosphere support, you can add it yourself by following the steps listed at the start of: -http://doc.akka.io/comet - -Module akka-spring ------------------- - -The Akka XML schema URI has changed to http://akka.io/schema/akka - -``_ - - - - - - -``_ diff --git a/akka-docs/pending/team.rst b/akka-docs/pending/team.rst deleted file mode 100644 index cdc97244bd..0000000000 --- a/akka-docs/pending/team.rst +++ /dev/null @@ -1,22 +0,0 @@ -Team -===== - -|| **Name** || **Role** || **Email** || -|| Jonas Bonér || Founder, Despot, Committer || jonas AT jonasboner DOT com || -|| Viktor Klang || Bad cop, Committer || viktor DOT klang AT gmail DOT com || -|| Debasish Ghosh || Committer || dghosh AT acm DOT org || -|| Ross McDonald || Alumni || rossajmcd AT gmail DOT com || -|| Eckhart Hertzler || Alumni || || -|| Mikael Högqvist || Alumni || || -|| Tim Perrett || Alumni || || -|| Jeanfrancois Arcand || Alumni || jfarcand AT apache DOT org || -|| Martin Krasser || Committer || krasserm AT googlemail DOT com || -|| Jan Van Besien || Alumni || || -|| Michael Kober || Committer || || -|| Peter Vlugter || Committer || || -|| Peter Veentjer || Committer || || -|| Irmo Manie || Committer || || -|| Heiko Seeberger || Committer || || -|| Hiram Chirino || Committer || || -|| Scott Clasen || Committer || || -|| Roland Kuhn || Committer || || From 4eddce0fe2729e50424b99890c14055bf666749e Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 2 May 2011 19:38:10 +0200 Subject: [PATCH 114/233] Porting licenses.rst and removing boldness in sponsors title --- akka-docs/dev/sponsors.rst | 2 +- akka-docs/general/index.rst | 3 ++- akka-docs/{pending => general}/licenses.rst | 0 3 files changed, 3 insertions(+), 2 deletions(-) rename akka-docs/{pending => general}/licenses.rst (100%) diff --git a/akka-docs/dev/sponsors.rst b/akka-docs/dev/sponsors.rst index 544a35825a..127ab17ee1 100644 --- a/akka-docs/dev/sponsors.rst +++ b/akka-docs/dev/sponsors.rst @@ -1,4 +1,4 @@ -**Sponsors** +Sponsors ============ YourKit diff --git a/akka-docs/general/index.rst b/akka-docs/general/index.rst index 6807aeff83..d5288e2af3 100644 --- a/akka-docs/general/index.rst +++ b/akka-docs/general/index.rst @@ -9,5 +9,6 @@ General building-akka configuration event-handler - util issue-tracking + util + licenses diff --git a/akka-docs/pending/licenses.rst b/akka-docs/general/licenses.rst similarity index 100% rename from akka-docs/pending/licenses.rst rename to akka-docs/general/licenses.rst From dadc572d61f6df0005031674e502e6b9a1b0718f Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Mon, 2 May 2011 21:16:04 +0200 Subject: [PATCH 115/233] no need to install pygments on every single run --- akka-docs/Makefile | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/akka-docs/Makefile b/akka-docs/Makefile index 49f649367f..d7b391e802 100644 --- a/akka-docs/Makefile +++ b/akka-docs/Makefile @@ -16,7 +16,11 @@ PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # Set python path to include local packages for pygments styles. -PYTHONPATH += $(LOCALPACKAGES) +ifneq (,$(PYTHONPATH)) + PYTHONPATH := $(PYTHONPATH):$(LOCALPACKAGES) +else + PYTHONPATH := $(LOCALPACKAGES) +endif export PYTHONPATH .PHONY: help clean pygments html singlehtml latex pdf @@ -40,8 +44,11 @@ pygments: @echo "Custom pygments styles have been installed." @echo -html: pygments - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html +$(LOCALPACKAGES)/akkastyles-0.1-py2.6.egg: + $(MAKE) pygments + +html: $(LOCALPACKAGES)/akkastyles-0.1-py2.6.egg + $(SPHINXBUILD) -a -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." From ece765775bd09c9c7bcc6272506cc147f35887dd Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Mon, 2 May 2011 21:18:14 +0200 Subject: [PATCH 116/233] move FSM._ import into class, fixes #831 --- akka-docs/scala/fsm.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/akka-docs/scala/fsm.rst b/akka-docs/scala/fsm.rst index 3b5fdd4394..89dedb52ec 100644 --- a/akka-docs/scala/fsm.rst +++ b/akka-docs/scala/fsm.rst @@ -49,13 +49,14 @@ Now lets create an object representing the FSM and defining the behavior. import akka.actor.{Actor, FSM} import akka.event.EventHandler - import FSM._ import akka.util.duration._ case object Move class ABC extends Actor with FSM[ExampleState, Unit] { + import FSM._ + startWith(A, Unit) when(A) { From 859b61d582c4118c1310d7bec14b4b047b1fa6ff Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Mon, 2 May 2011 21:40:35 +0200 Subject: [PATCH 117/233] move Duration docs below common/ next to Scheduler --- akka-docs/common/index.rst | 1 + akka-docs/general/index.rst | 1 - akka-docs/general/util.rst | 49 ------------------------------------- 3 files changed, 1 insertion(+), 50 deletions(-) delete mode 100644 akka-docs/general/util.rst diff --git a/akka-docs/common/index.rst b/akka-docs/common/index.rst index 6ed8cb1593..f3ed26aa73 100644 --- a/akka-docs/common/index.rst +++ b/akka-docs/common/index.rst @@ -5,3 +5,4 @@ Common utilities :maxdepth: 2 scheduler + duration diff --git a/akka-docs/general/index.rst b/akka-docs/general/index.rst index d5288e2af3..4cd57b37b7 100644 --- a/akka-docs/general/index.rst +++ b/akka-docs/general/index.rst @@ -10,5 +10,4 @@ General configuration event-handler issue-tracking - util licenses diff --git a/akka-docs/general/util.rst b/akka-docs/general/util.rst deleted file mode 100644 index bb0f61e778..0000000000 --- a/akka-docs/general/util.rst +++ /dev/null @@ -1,49 +0,0 @@ -######### -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())); - - From 67f1e2fbca2a4c3f1104d4321518a19baa9b196b Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Mon, 2 May 2011 14:44:40 -0600 Subject: [PATCH 118/233] Fix Future.flow compile time type safety --- .../test/scala/akka/dispatch/FutureSpec.scala | 26 +++++++++++++++++++ .../src/main/scala/akka/dispatch/Future.scala | 10 +++++-- 2 files changed, 34 insertions(+), 2 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 b74526118e..e8d20919a9 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -623,6 +623,32 @@ class FutureSpec extends JUnitSuite { assert(result.get === Some("Hello")) } + @Test def futureFlowShouldBeTypeSafe { + import Future.flow + + def checkType[A: Manifest, B](in: Future[A], refmanifest: Manifest[B]): Boolean = manifest[A] == refmanifest + + val rString = flow { + val x = Future(5) + x().toString + } + + val rInt = flow { + val x = rString.apply + val y = Future(5) + x.length + y() + } + + assert(checkType(rString, manifest[String])) + assert(checkType(rInt, manifest[Int])) + assert(!checkType(rInt, manifest[String])) + assert(!checkType(rInt, manifest[Nothing])) + assert(!checkType(rInt, manifest[Any])) + + rString.await + rInt.await + } + @Test def ticket812FutureDispatchCleanup { val dispatcher = implicitly[MessageDispatcher] assert(dispatcher.pendingFutures === 0) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 34e9c6da9b..632ccdac3e 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -291,8 +291,14 @@ object Future { * * The Delimited Continuations compiler plugin must be enabled in order to use this method. */ - def flow(body: => Any @cps[Future[Any]], timeout: Long = Actor.TIMEOUT): Future[Any] = - reset(new DefaultCompletableFuture[Any](timeout).completeWithResult(body)) + def flow[A](body: => A @cps[Future[Any]], timeout: Long = Actor.TIMEOUT): Future[A] = { + val f = new DefaultCompletableFuture[A](timeout) + reset(f.asInstanceOf[CompletableFuture[Any]].completeWithResult(body)).onComplete{ f2 => + val e = f2.exception + e foreach (f.completeWithException(_)) + } + f + } private[akka] val callbacksPendingExecution = new ThreadLocal[Option[Stack[() => Unit]]]() { override def initialValue = None From d175ef413979b8c642b07f7c3ab0d8a9f685856b Mon Sep 17 00:00:00 2001 From: Roland Date: Mon, 2 May 2011 23:45:40 +0200 Subject: [PATCH 119/233] fix pygments dependency and add forgotten common/duration.rst --- akka-docs/Makefile | 4 +-- akka-docs/common/duration.rst | 51 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 akka-docs/common/duration.rst diff --git a/akka-docs/Makefile b/akka-docs/Makefile index d7b391e802..9811732058 100644 --- a/akka-docs/Makefile +++ b/akka-docs/Makefile @@ -44,10 +44,10 @@ pygments: @echo "Custom pygments styles have been installed." @echo -$(LOCALPACKAGES)/akkastyles-0.1-py2.6.egg: +$(LOCALPACKAGES): $(MAKE) pygments -html: $(LOCALPACKAGES)/akkastyles-0.1-py2.6.egg +html: $(LOCALPACKAGES) $(SPHINXBUILD) -a -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." diff --git a/akka-docs/common/duration.rst b/akka-docs/common/duration.rst new file mode 100644 index 0000000000..523c8a2283 --- /dev/null +++ b/akka-docs/common/duration.rst @@ -0,0 +1,51 @@ +.. _Duration: + +######## +Duration +######## + +Module stability: **SOLID** + +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. + +Scala +===== + +In Scala durations are constructable using a mini-DSL and support all expected operations: + +.. 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) + val fourmillis = threemillis * 4 / 3 // though you cannot write it the other way around + val n = threemillis / (1 millisecond) + +.. 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 +==== + +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 6c0d5c74a889868f2d60a69ffe5640d15dd327c5 Mon Sep 17 00:00:00 2001 From: Roland Date: Tue, 3 May 2011 00:52:34 +0200 Subject: [PATCH 120/233] add import statements to testing.rst --- akka-docs/scala/testing.rst | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/akka-docs/scala/testing.rst b/akka-docs/scala/testing.rst index 9238cfd198..c0e36ada78 100644 --- a/akka-docs/scala/testing.rst +++ b/akka-docs/scala/testing.rst @@ -40,6 +40,10 @@ 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. +.. note:: + + Be sure to add the module :mod:`akka-testkit` to your dependencies. + Unit Testing with :class:`TestActorRef` ======================================= @@ -68,6 +72,8 @@ reference is done like this: .. code-block:: scala + import akka.testkit.TestActorRef + val actorRef = TestActorRef[MyActor] val actor = actorRef.underlyingActor @@ -169,6 +175,10 @@ common task easy: .. code-block:: scala + import akka.testkit.TestKit + import org.scalatest.WordSpec + import org.scalatest.matchers.MustMatchers + class MySpec extends WordSpec with MustMatchers with TestKit { "An Echo actor" must { @@ -252,6 +262,31 @@ 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 to use it +------------- + +Just set the dispatcher as you normally would, either from within the actor + +.. code-block:: scala + + import akka.testkit.CallingThreadDispatcher + + class MyActor extends Actor { + self.dispatcher = CallingThreadDispatcher.global + ... + } + +or from the client code + +.. code-block:: scala + + val ref = Actor.actorOf[MyActor] + ref.dispatcher = CallingThreadDispatcher.global + ref.start() + +As the :class:`CallingThreadDispatcher` does not have any configurable state, +you may always use the (lazily) preallocated one as shown in the examples. + How it works ------------ From cb2d60739345a71df2335a7b6295e9ffb55f57ce Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Mon, 2 May 2011 16:56:42 -0600 Subject: [PATCH 121/233] remove extra allocations and fix scaladoc type inference problem --- akka-actor/src/main/scala/akka/dispatch/Future.scala | 10 +++++----- 1 file changed, 5 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 632ccdac3e..0f09a7535a 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -292,12 +292,12 @@ object Future { * The Delimited Continuations compiler plugin must be enabled in order to use this method. */ def flow[A](body: => A @cps[Future[Any]], timeout: Long = Actor.TIMEOUT): Future[A] = { - val f = new DefaultCompletableFuture[A](timeout) - reset(f.asInstanceOf[CompletableFuture[Any]].completeWithResult(body)).onComplete{ f2 => - val e = f2.exception - e foreach (f.completeWithException(_)) + val future = new DefaultCompletableFuture[A](timeout) + (reset(future.asInstanceOf[CompletableFuture[Any]].completeWithResult(body)): Future[Any]) onComplete { f => + val opte = f.exception + if (opte.isDefined) future completeWithException (opte.get) } - f + future } private[akka] val callbacksPendingExecution = new ThreadLocal[Option[Stack[() => Unit]]]() { From d97e0c1672eb5191a2f1b3e0de78eff2ecbf2285 Mon Sep 17 00:00:00 2001 From: Roland Date: Tue, 3 May 2011 04:43:05 +0200 Subject: [PATCH 122/233] fix import in testkit example; fixes #833 --- akka-docs/scala/testkit-example.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/akka-docs/scala/testkit-example.rst b/akka-docs/scala/testkit-example.rst index f53543e474..a0ec001902 100644 --- a/akka-docs/scala/testkit-example.rst +++ b/akka-docs/scala/testkit-example.rst @@ -4,7 +4,7 @@ TestKit Example ############### -Ray Roestenburg's example code from `his blog `_. +Ray Roestenburg's example code from `his blog `_ adapted to work with Akka 1.1. .. code-block:: scala @@ -14,7 +14,7 @@ Ray Roestenburg's example code from `his blog Date: Tue, 3 May 2011 15:20:00 +0400 Subject: [PATCH 123/233] Typos and clarification for futures. --- akka-docs/pending/futures-scala.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/akka-docs/pending/futures-scala.rst b/akka-docs/pending/futures-scala.rst index f03990a1cf..4bea4fa389 100644 --- a/akka-docs/pending/futures-scala.rst +++ b/akka-docs/pending/futures-scala.rst @@ -17,10 +17,10 @@ Using an ``Actor``\'s ``!!!`` method to send a message will return a Future. To val future = actor !!! msg val result: Any = future.apply - // or more simply + // or simply val result: Any = future() -This will cause the current thread to block and wait for the ``Actor`` to 'complete' the ``Future`` with it's reply. Due to the dynamic nature of Akka's ``Actor``\s this result will be untyped and will default to ``Nothing``. The safest way to deal with this is to cast the result to an ``Any`` as is shown in the above example. You can also use the expected result type instead of ``Any``, but if an unexpected type were to be returned you will get a ``ClassCastException``. For more elegant ways to deal with this and to use the result without blocking refer to `Functional Futures`_. +This will cause the current thread to block and wait for the ``Actor`` to 'complete' the ``Future`` with its reply. Due to the dynamic nature of Akka's ``Actor``\s this result will be untyped and will default to ``Nothing``. The safest way to deal with this is to cast the result to an ``Any`` as is shown in the above example. You can also use the expected result type instead of ``Any``, but if an unexpected type were to be returned you will get a ``ClassCastException``. For more elegant ways to deal with this and to use the result without blocking, refer to `Functional Futures`_. Use Directly ------------ @@ -60,7 +60,7 @@ The first method for working with ``Future`` functionally is ``map``. This metho val result = f2() -In this example we are joining two strings together within a Future. Instead of waiting for this to complete, we apply our Function that calculates the length of the string using the 'map' method. Now we have a second Future that will contain an Int. When our original Future completes, it will also apply our Function and complete the second Future with that result. When we finally await the result, it will contain the number 10. Our original Future still contains the string "HelloWorld" and is unaffected by the 'map'. +In this example we are joining two strings together within a Future. Instead of waiting for this to complete, we apply our Function that calculates the length of the string using the 'map' method. Now we have a second Future that will contain an Int. When our original Future completes, it will also apply our Function and complete the second Future with its result. When we finally await the result, it will contain the number 10. Our original Future still contains the string "HelloWorld" and is unaffected by the 'map'. Something to note when using these methods: if the Future is still being processed when one of these methods are called, it will be the completing thread that actually does the work. If the Future is already complete though, it will be run in our current thread. For example: @@ -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 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': +Normally this works quite well 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 @@ -148,9 +148,9 @@ The example for comprehension above is an example of composing Futures. A common val result = f3() -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'. +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 (note that we don't block to get these results!), 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 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: +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, for a subclass T of Traversable, T[Future[A]] into a Future[T[A]]. For example: .. code-block:: scala @@ -165,7 +165,7 @@ This is fine when dealing with a known amount of Actors, but can grow unwieldy i To better explain what happened in the example, Future.sequence is taking the List[Future[Int]] and turning it into a Future[List[Int]]. We can then use 'map' to work with the List[Int] directly, and we find the sum of the List. -The 'traverse' method is similar to 'sequence', but it takes a Traversable[A] and a Function T => Future[B] to return a Future[Traversable[B]]. For example, to use 'traverse' to sum the first 100 odd numbers: +The 'traverse' method is similar to 'sequence', but it takes a T[A] and a Function T => Future[B] to return a Future[T[B]], where T is again a subclass of Traversable. For example, to use 'traverse' to sum the first 100 odd numbers: .. code-block:: scala @@ -179,7 +179,7 @@ This is the same result as this example: But it may be faster to use 'traverse' as it doesn't have to create an intermediate List[Future[Int]]. -This is just a sample of what can be done, but to use more advanced techniques it is easier to take advantage of Scalaz, which Akka has support for in it's akka-scalaz module. +This is just a sample of what can be done, but to use more advanced techniques it is easier to take advantage of Scalaz, which Akka has support for in its akka-scalaz module. Scalaz ^^^^^^ From c81748c95c5cc95e3d39a8d800b5779c91b0437d Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 3 May 2011 16:26:30 +0200 Subject: [PATCH 124/233] Removing the intermediate InvocationTargetException and harmonizing creation of Actor instances --- .../src/main/scala/akka/AkkaException.scala | 7 +-- .../src/main/scala/akka/actor/Actor.scala | 19 +++++--- .../remoteinterface/RemoteInterface.scala | 44 +++++++++---------- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/akka-actor/src/main/scala/akka/AkkaException.scala b/akka-actor/src/main/scala/akka/AkkaException.scala index fe0bac916e..a9edf69a0b 100644 --- a/akka-actor/src/main/scala/akka/AkkaException.scala +++ b/akka-actor/src/main/scala/akka/AkkaException.scala @@ -19,11 +19,8 @@ import java.net.{InetAddress, UnknownHostException} 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 = { - val name = getClass.getName - val trace = stackTraceToString - "%s: %s\n[%s]\n%s".format(name, message, uuid, trace) - } + override lazy val toString = + "%s: %s\n[%s]\n%s".format(getClass.getName, message, uuid, stackTraceToString) def stackTraceToString = { val trace = getStackTrace diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 104283d853..7b61f174c0 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -14,6 +14,7 @@ import scala.reflect.BeanProperty import akka.util. {ReflectiveAccess, Duration} import akka.remoteinterface.RemoteSupport import akka.japi. {Creator, Procedure} +import java.lang.reflect.InvocationTargetException /** * Life-cycle messages for the Actors @@ -162,12 +163,18 @@ object Actor extends ListenerManagement { def actorOf(clazz: Class[_ <: Actor]): ActorRef = new LocalActorRef(() => { import ReflectiveAccess.{ createInstance, noParams, noArgs } 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)'.", l.a) + case Right(actor) => actor + case Left(exception) => + val cause = exception match { + case i: InvocationTargetException => i.getTargetException + case _ => exception + } + + 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)'.", cause) } }, None) diff --git a/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala b/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala index 0409ae9d6c..9c631576ef 100644 --- a/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala +++ b/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala @@ -15,6 +15,7 @@ import scala.reflect.BeanProperty import java.net.InetSocketAddress import java.util.concurrent.ConcurrentHashMap import java.io.{PrintWriter, PrintStream} +import java.lang.reflect.InvocationTargetException trait RemoteModule { val UUID_PREFIX = "uuid:".intern @@ -186,18 +187,8 @@ abstract class RemoteSupport extends ListenerManagement with RemoteServerModule * */ @deprecated("Will be removed after 1.1") - def actorOf(clazz: Class[_ <: Actor], host: String, port: Int): ActorRef = { - import ReflectiveAccess.{ createInstance, noParams, noArgs } - clientManagedActorOf(() => - 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) - } + def actorOf(clazz: Class[_ <: Actor], host: String, port: Int): ActorRef = + clientManagedActorOf(() => createActorFromClass(clazz), host, port) /** * Creates a Client-managed ActorRef out of the Actor of the specified Class. @@ -215,17 +206,26 @@ abstract class RemoteSupport extends ListenerManagement with RemoteServerModule * */ @deprecated("Will be removed after 1.1") - def actorOf[T <: Actor : Manifest](host: String, port: Int): ActorRef = { + def actorOf[T <: Actor : Manifest](host: String, port: Int): ActorRef = + clientManagedActorOf(() => createActorFromClass(manifest.erasure), host, port) + + + protected def createActorFromClass(clazz: Class[_]): Actor = { import ReflectiveAccess.{ createInstance, noParams, noArgs } - clientManagedActorOf(() => - 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) + createInstance[Actor](clazz, noParams, noArgs) match { + case Right(actor) => actor + case Left(exception) => + val cause = exception match { + case i: InvocationTargetException => i.getTargetException + case _ => exception + } + + 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)'.", cause) + } } protected override def manageLifeCycleOfListeners = false From 417acee9db737f77048a15e9043b26f2b7736ad1 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 3 May 2011 16:32:23 +0200 Subject: [PATCH 125/233] Adding implicit conversion for: (actor !!! msg).as[X] --- akka-actor/src/main/scala/akka/actor/Actor.scala | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 7b61f174c0..972a87d5cf 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -233,11 +233,25 @@ object Actor extends ListenerManagement { } }).start() ! Spawn } + /** * Implicitly converts the given Option[Any] to a AnyOptionAsTypedOption which offers the method as[T] * to convert an Option[Any] to an Option[T]. */ implicit def toAnyOptionAsTypedOption(anyOption: Option[Any]) = new AnyOptionAsTypedOption(anyOption) + + /** + * Implicitly converts the given Future[_] to a AnyOptionAsTypedOption which offers the method as[T] + * to convert an Option[Any] to an Option[T]. + * This means that the following code is equivalent: + * (actor !! "foo").as[Int] (Deprecated) + * and + * (actor !!! "foo").as[Int] (Recommended) + */ + implicit def futureToAnyOptionAsTypedOption(anyFuture: Future[_]) = new AnyOptionAsTypedOption({ + try { anyFuture.await } catch { case t: FutureTimeoutException => } + anyFuture.resultOrException + }) } /** From cf17b775f6659dfda9332bf62e290cc39ec27515 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 3 May 2011 15:57:22 +0200 Subject: [PATCH 126/233] Moved fault-tolerance from pending --- .../fault-tolerance-java.rst => java/fault-tolerance.rst} | 0 akka-docs/java/index.rst | 1 + .../fault-tolerance-scala.rst => scala/fault-tolerance.rst} | 0 akka-docs/scala/index.rst | 1 + 4 files changed, 2 insertions(+) rename akka-docs/{pending/fault-tolerance-java.rst => java/fault-tolerance.rst} (100%) rename akka-docs/{pending/fault-tolerance-scala.rst => scala/fault-tolerance.rst} (100%) diff --git a/akka-docs/pending/fault-tolerance-java.rst b/akka-docs/java/fault-tolerance.rst similarity index 100% rename from akka-docs/pending/fault-tolerance-java.rst rename to akka-docs/java/fault-tolerance.rst diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst index 553f75da45..2fdac42c34 100644 --- a/akka-docs/java/index.rst +++ b/akka-docs/java/index.rst @@ -11,4 +11,5 @@ Java API transactors remote-actors serialization + fault-tolerance dispatchers diff --git a/akka-docs/pending/fault-tolerance-scala.rst b/akka-docs/scala/fault-tolerance.rst similarity index 100% rename from akka-docs/pending/fault-tolerance-scala.rst rename to akka-docs/scala/fault-tolerance.rst diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index d5269336aa..af182cfd6c 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -12,6 +12,7 @@ Scala API transactors remote-actors serialization + fault-tolerance dispatchers fsm testing From 65eb70c69c8523177bbd23c05cfb2b28b8c6d2e8 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 3 May 2011 16:11:43 +0200 Subject: [PATCH 127/233] Cleanup of fault-tolerance --- akka-docs/java/fault-tolerance.rst | 50 +++++++++++++++++------------ akka-docs/scala/fault-tolerance.rst | 28 ++++++++++------ 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/akka-docs/java/fault-tolerance.rst b/akka-docs/java/fault-tolerance.rst index 96190c7b8e..9aacecad44 100644 --- a/akka-docs/java/fault-tolerance.rst +++ b/akka-docs/java/fault-tolerance.rst @@ -1,6 +1,10 @@ Fault Tolerance Through Supervisor Hierarchies (Java) ===================================================== +.. sidebar:: Contents + + .. contents:: :local: + Module stability: **SOLID** The "let it crash" approach to fault/error handling, implemented by linking actors, is very different to what Java and most non-concurrency oriented languages/frameworks have adopted. It’s a way of dealing with failure that is designed for concurrent and distributed systems. @@ -10,14 +14,14 @@ Concurrency Throwing an exception in concurrent code (let’s assume we are using non-linked actors), will just simply blow up the thread that currently executes the actor. -# There is no way to find out that things went wrong (apart from inspecting the stack trace). -# There is nothing you can do about it. +- There is no way to find out that things went wrong (apart from inspecting the stack trace). +- There is nothing you can do about it. Here actors provide a clean way of getting notification of the error and do something about it. Linking actors also allow you to create sets of actors where you can be sure that either: -# All are dead -# None are dead +- All are dead +- None are dead This is very useful when you have thousands of concurrent actors. Some actors might have implicit dependencies and together implement a service, computation, user session etc. @@ -56,8 +60,8 @@ Restart callbacks There are two different callbacks that an UntypedActor or TypedActor can hook in to: -* Pre restart -* Post restart +- Pre restart +- Post restart These are called prior to and after the restart upon failure and can be used to clean up and reset/reinitialize state upon restart. This is important in order to reset the component failure and leave the component in a fresh and stable state before consuming further messages. @@ -66,8 +70,8 @@ Defining a supervisor's restart strategy Both the Typed Actor supervisor configuration and the Actor supervisor configuration take a ‘FaultHandlingStrategy’ instance which defines the fault management. The different strategies are: -* AllForOne -* OneForOne +- AllForOne +- OneForOne These have the semantics outlined in the section above. @@ -86,8 +90,8 @@ Defining actor life-cycle The other common configuration element is the ‘LifeCycle’ which defines the life-cycle. The supervised actor can define one of two different life-cycle configurations: -* 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. +- 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. Here is an example of how to define the life-cycle: @@ -126,7 +130,7 @@ The Actor’s supervision can be declaratively defined by creating a ‘Supervis Supervisors created like this are implicitly instantiated and started. 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 +a Procedure2 when creating the SupervisorConfig. This handler will be called with the ActorRef of the supervisor and the MaximumNumberOfRestartsWithinTimeRangeReached message. .. code-block:: java @@ -254,10 +258,13 @@ The supervising Actor also needs to define a fault handler that defines the rest The different options are: -* AllForOneStrategy(trapExit, maxNrOfRetries, withinTimeRange) - * trapExit is an Array of classes inheriting from Throwable, they signal which types of exceptions this actor will handle -* OneForOneStrategy(trapExit, maxNrOfRetries, withinTimeRange) - * trapExit is an Array of classes inheriting from Throwable, they signal which types of exceptions this actor will handle +- AllForOneStrategy(trapExit, maxNrOfRetries, withinTimeRange) + + - trapExit is an Array of classes inheriting from Throwable, they signal which types of exceptions this actor will handle + +- OneForOneStrategy(trapExit, maxNrOfRetries, withinTimeRange) + + - trapExit is an Array of classes inheriting from Throwable, they signal which types of exceptions this actor will handle Here is an example: @@ -346,8 +353,8 @@ Supervised actors have the option to reply to the initial sender within preResta } } -* A reply within preRestart or postRestart must be a safe reply via getContext().replySafe() because a getContext().replyUnsafe() will throw an exception when the actor is restarted without having failed. This can be the case in context of AllForOne restart strategies. -* A reply within postStop must be a safe reply via getContext().replySafe() because a getContext().replyUnsafe() will throw an exception when the actor has been stopped by the application (and not by a supervisor) after successful execution of receive (or no execution at all). +- A reply within preRestart or postRestart must be a safe reply via getContext().replySafe() because a getContext().replyUnsafe() will throw an exception when the actor is restarted without having failed. This can be the case in context of AllForOne restart strategies. +- A reply within postStop must be a safe reply via getContext().replySafe() because a getContext().replyUnsafe() will throw an exception when the actor has been stopped by the application (and not by a supervisor) after successful execution of receive (or no execution at all). Handling too many actor restarts within a specific time limit ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -366,10 +373,10 @@ Now, what happens if this limit is reached? What will happen is that the failing actor will send a system message to its supervisor called 'MaximumNumberOfRestartsWithinTimeRangeReached' with the following these properties: -* victim: ActorRef -* maxNrOfRetries: int -* withinTimeRange: int -* lastExceptionCausingRestart: Throwable +- victim: ActorRef +- maxNrOfRetries: int +- withinTimeRange: int +- lastExceptionCausingRestart: Throwable If you want to be able to take action upon this event (highly recommended) then you have to create a message handle for it in the supervisor. @@ -479,6 +486,7 @@ If the parent TypedActor (supervisor) wants to be able to do handle failing chil For convenience there is an overloaded link that takes trapExit and faultHandler for the supervisor as arguments. Here is an example: .. code-block:: java + import static akka.actor.TypedActor.*; import static akka.config.Supervision.*; diff --git a/akka-docs/scala/fault-tolerance.rst b/akka-docs/scala/fault-tolerance.rst index 84cc48d549..7276c5cdaf 100644 --- a/akka-docs/scala/fault-tolerance.rst +++ b/akka-docs/scala/fault-tolerance.rst @@ -1,6 +1,10 @@ Fault Tolerance Through Supervisor Hierarchies (Scala) ====================================================== +.. sidebar:: Contents + + .. contents:: :local: + Module stability: **SOLID** The "let it crash" approach to fault/error handling, implemented by linking actors, is very different to what Java and most non-concurrency oriented languages/frameworks have adopted. It's a way of dealing with failure that is designed for concurrent and distributed systems. @@ -10,15 +14,15 @@ Concurrency Throwing an exception in concurrent code (let's assume we are using non-linked actors), will just simply blow up the thread that currently executes the actor. -# There is no way to find out that things went wrong (apart from inspecting the stack trace). -# There is nothing you can do about it. +- There is no way to find out that things went wrong (apart from inspecting the stack trace). +- There is nothing you can do about it. Here actors provide a clean way of getting notification of the error and do something about it. Linking actors also allow you to create sets of actors where you can be sure that either: -# All are dead -# None are dead +- All are dead +- None are dead This is very useful when you have thousands of concurrent actors. Some actors might have implicit dependencies and together implement a service, computation, user session etc. @@ -241,10 +245,13 @@ The supervising Actor also needs to define a fault handler that defines the rest The different options are: -* AllForOneStrategy(trapExit, maxNrOfRetries, withinTimeRange) - * trapExit is a List or Array of classes inheriting from Throwable, they signal which types of exceptions this actor will handle -* OneForOneStrategy(trapExit, maxNrOfRetries, withinTimeRange) - * trapExit is a List or Array of classes inheriting from Throwable, they signal which types of exceptions this actor will handle +- AllForOneStrategy(trapExit, maxNrOfRetries, withinTimeRange) + + - trapExit is a List or Array of classes inheriting from Throwable, they signal which types of exceptions this actor will handle + +- OneForOneStrategy(trapExit, maxNrOfRetries, withinTimeRange) + + - trapExit is a List or Array of classes inheriting from Throwable, they signal which types of exceptions this actor will handle Here is an example: @@ -321,8 +328,8 @@ Supervised actors have the option to reply to the initial sender within preResta } } -* A reply within preRestart or postRestart must be a safe reply via self.reply_? because an unsafe self.reply will throw an exception when the actor is restarted without having failed. This can be the case in context of AllForOne restart strategies. -* A reply within postStop must be a safe reply via self.reply_? because an unsafe self.reply will throw an exception when the actor has been stopped by the application (and not by a supervisor) after successful execution of receive (or no execution at all). +- A reply within preRestart or postRestart must be a safe reply via `self.reply_?` because an unsafe self.reply will throw an exception when the actor is restarted without having failed. This can be the case in context of AllForOne restart strategies. +- A reply within postStop must be a safe reply via `self.reply_?` because an unsafe self.reply will throw an exception when the actor has been stopped by the application (and not by a supervisor) after successful execution of receive (or no execution at all). Handling too many actor restarts within a specific time limit ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -431,6 +438,7 @@ If the parent TypedActor (supervisor) wants to be able to do handle failing chil For convenience there is an overloaded link that takes trapExit and faultHandler for the supervisor as arguments. Here is an example: .. code-block:: scala + import akka.actor.TypedActor._ val foo = newInstance(classOf[Foo], 1000) From 854614dd5536dbf38bb269e35a770bd417813c17 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 3 May 2011 16:38:07 +0200 Subject: [PATCH 128/233] Fixed what-is-akka --- akka-docs/intro/what-is-akka.rst | 38 ++++++++++++++++-------------- akka-docs/java/fault-tolerance.rst | 2 ++ akka-docs/java/remote-actors.rst | 2 ++ akka-docs/java/stm.rst | 2 ++ akka-docs/java/untyped-actors.rst | 2 ++ akka-docs/scala/stm.rst | 2 ++ 6 files changed, 30 insertions(+), 18 deletions(-) diff --git a/akka-docs/intro/what-is-akka.rst b/akka-docs/intro/what-is-akka.rst index c9f4e6bd18..17e20bade7 100644 --- a/akka-docs/intro/what-is-akka.rst +++ b/akka-docs/intro/what-is-akka.rst @@ -1,8 +1,8 @@ What is Akka? -==== +============= **Akka** -^^^^^^ +-------- **Simpler Scalability, Fault-Tolerance, Concurrency & Remoting through Actors** @@ -11,23 +11,25 @@ We believe that writing correct concurrent, fault-tolerant and scalable applicat Download from ``_ -**Akka implements a unique hybrid of:** -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Akka implements a unique hybrid of: +----------------------------------- -* `Actors `_, which gives you: - * Simple and high-level abstractions for concurrency and parallelism. - * Asynchronous, non-blocking and highly performant event-driven programming model. - * Very lightweight event-driven processes (create ~6.5 million actors on 4GB RAM). -* `Failure management `_ through supervisor hierarchies with `let-it-crash `_ semantics. Excellent for writing highly fault-tolerant systems that never stop, systems that self-heal. -* `Software Transactional Memory `_ (STM). (Distributed transactions coming soon). -* `Transactors `_: combine actors and STM into transactional actors. Allows you to compose atomic message flows with automatic retry and rollback. -* `Remote actors `_: highly performant distributed actors with remote supervision and error management. -* Java and Scala API. +- :ref:`untyped-actors-java`, which gives you: -**Akka can be used in two different ways:** -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + - Simple and high-level abstractions for concurrency and parallelism. + - Asynchronous, non-blocking and highly performant event-driven programming model. + - Very lightweight event-driven processes (create ~6.5 million actors on 4GB RAM). -* As a library: used by a web app, to be put into ‘WEB-INF/lib’ or as a regular JAR on your classpath. -* As a microkernel: stand-alone kernel, embedding a servlet container and all the other modules. +- :ref:`fault-tolerance-java` through supervisor hierarchies with `let-it-crash `_ semantics. Excellent for writing highly fault-tolerant systems that never stop, systems that self-heal. +- :ref:`stm-java` (STM). (Distributed transactions coming soon). +- :ref:`transactors-java`: combine actors and STM into transactional actors. Allows you to compose atomic message flows with automatic retry and rollback. +- :ref:`remote-actors-java`: highly performant distributed actors with remote supervision and error management. +- Java and Scala API. -See the `Use-case and Deployment Scenarios `_ for details. +Akka can be used in two different ways: +--------------------------------------- + +- As a library: used by a web app, to be put into ‘WEB-INF/lib’ or as a regular JAR on your classpath. +- As a microkernel: stand-alone kernel, embedding a servlet container and all the other modules. + +See the ref:`deployment-scenarios` for details. diff --git a/akka-docs/java/fault-tolerance.rst b/akka-docs/java/fault-tolerance.rst index 9aacecad44..b89b3978b4 100644 --- a/akka-docs/java/fault-tolerance.rst +++ b/akka-docs/java/fault-tolerance.rst @@ -1,3 +1,5 @@ +.. _fault-tolerance-java: + Fault Tolerance Through Supervisor Hierarchies (Java) ===================================================== diff --git a/akka-docs/java/remote-actors.rst b/akka-docs/java/remote-actors.rst index 3894f2dacc..8eb5573e16 100644 --- a/akka-docs/java/remote-actors.rst +++ b/akka-docs/java/remote-actors.rst @@ -1,3 +1,5 @@ +.. _remote-actors-java: + Remote Actors (Java) ==================== diff --git a/akka-docs/java/stm.rst b/akka-docs/java/stm.rst index ed44218804..df6761b52a 100644 --- a/akka-docs/java/stm.rst +++ b/akka-docs/java/stm.rst @@ -1,3 +1,5 @@ +.. _stm-java: + Software Transactional Memory (Java) ==================================== diff --git a/akka-docs/java/untyped-actors.rst b/akka-docs/java/untyped-actors.rst index 5a29d13ae5..1b73988aca 100644 --- a/akka-docs/java/untyped-actors.rst +++ b/akka-docs/java/untyped-actors.rst @@ -1,3 +1,5 @@ +.. _untyped-actors-java: + Actors (Java) ============= diff --git a/akka-docs/scala/stm.rst b/akka-docs/scala/stm.rst index 42cd67ce2c..c5bb99bcb4 100644 --- a/akka-docs/scala/stm.rst +++ b/akka-docs/scala/stm.rst @@ -1,3 +1,5 @@ +.. _stm-scala: + Software Transactional Memory (Scala) ===================================== From 625f844663c2ef205cb5df8c3bb1935332ada962 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 3 May 2011 16:40:33 +0200 Subject: [PATCH 129/233] Moved futures from pending --- akka-docs/{pending/futures-scala.rst => scala/futures.rst} | 0 akka-docs/scala/index.rst | 1 + 2 files changed, 1 insertion(+) rename akka-docs/{pending/futures-scala.rst => scala/futures.rst} (100%) diff --git a/akka-docs/pending/futures-scala.rst b/akka-docs/scala/futures.rst similarity index 100% rename from akka-docs/pending/futures-scala.rst rename to akka-docs/scala/futures.rst diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index af182cfd6c..780e7b7c25 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -7,6 +7,7 @@ Scala API actors typed-actors actor-registry + futures agents stm transactors From ea23b0fad3fecc6f88b1607ca7753c6533242829 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 3 May 2011 16:51:37 +0200 Subject: [PATCH 130/233] Moved deployment-scenarios from pending --- akka-docs/{pending => intro}/deployment-scenarios.rst | 0 akka-docs/intro/index.rst | 1 + 2 files changed, 1 insertion(+) rename akka-docs/{pending => intro}/deployment-scenarios.rst (100%) diff --git a/akka-docs/pending/deployment-scenarios.rst b/akka-docs/intro/deployment-scenarios.rst similarity index 100% rename from akka-docs/pending/deployment-scenarios.rst rename to akka-docs/intro/deployment-scenarios.rst diff --git a/akka-docs/intro/index.rst b/akka-docs/intro/index.rst index 2de18010c4..c3b167fbd7 100644 --- a/akka-docs/intro/index.rst +++ b/akka-docs/intro/index.rst @@ -9,3 +9,4 @@ Introduction getting-started-first-scala getting-started-first-scala-eclipse getting-started-first-java + From 0e8e4ef2150b687278c356ab68934c008d80ef22 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 3 May 2011 17:02:43 +0200 Subject: [PATCH 131/233] Fixed deployment-scenarios --- akka-docs/images/akka-as-kernel.png | Bin 0 -> 39638 bytes akka-docs/images/akka-as-library-1.png | Bin 0 -> 29399 bytes akka-docs/images/akka-as-library-2.png | Bin 0 -> 36522 bytes akka-docs/images/akka-as-library-3.png | Bin 0 -> 48196 bytes akka-docs/images/akka-as-library-4.png | Bin 0 -> 39979 bytes akka-docs/images/akka-as-library-5.png | Bin 0 -> 44526 bytes akka-docs/intro/deployment-scenarios.rst | 38 +++++++++++------------ akka-docs/intro/index.rst | 1 + 8 files changed, 19 insertions(+), 20 deletions(-) create mode 100644 akka-docs/images/akka-as-kernel.png create mode 100644 akka-docs/images/akka-as-library-1.png create mode 100644 akka-docs/images/akka-as-library-2.png create mode 100644 akka-docs/images/akka-as-library-3.png create mode 100644 akka-docs/images/akka-as-library-4.png create mode 100644 akka-docs/images/akka-as-library-5.png diff --git a/akka-docs/images/akka-as-kernel.png b/akka-docs/images/akka-as-kernel.png new file mode 100644 index 0000000000000000000000000000000000000000..fc3ca9ab5acd4298fe94666b2196ea0844cce0c8 GIT binary patch literal 39638 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 literal 0 HcmV?d00001 diff --git a/akka-docs/images/akka-as-library-1.png b/akka-docs/images/akka-as-library-1.png new file mode 100644 index 0000000000000000000000000000000000000000..6a9575a475622b013fdb4ef740f6e023ff98c28f GIT binary patch literal 29399 zcmeAS@N?(olHy`uVBq!ia0y~yV6tIgVD#Z&V_;x7e>CkF0|UQ%rn7T^r?ay{Kv8~L zW=<*tLj~j78I=}S?%qFsK8Gz?GP^99GxhAP6({!T8&2zZ<8w?R`E-!W$)If^8aF2g zXedtcY8B#Aow9a~sOl-R^W{5szkmJx&${1L|0>Uwp8dY(-19y5^NaUApYz`Gx+H@| z!8;KyPCf<>h4z5lo|3}8_PGTw92=MxHL!9t$-bL+P((I6yV&ECer6@Zffu3qPhM{P z=lUf-%}ncx0)v62$AP!I&$7RKv+*p8ReO7YlR~fjT-(2Q_A2zXMf7=XxY+!I?MTkP z+jkt&`*ZV_7tdI(fA_mA1J9fkeI-eTFE&S_;_e-s7ht+KR)2lMd4`V5j18~ZSXi7s zZmgXUdu*~(Yt6>j0^XO7vz}U5V#35w8mn>ajQutTafWvZ3<5nrj;k?eR_Jez$x7N^YFX_OCR3kh;y;L#PXB->X8X8-%_`v z8l|cwoX~l>rZlHHLZw}6xqi6fcaHTh_=MsYGq_!dT@mUMVZ!!3@{YrzO7*x^VQz)t zGj;s*ycyR@EHhb{voL7wv1NW&DjJ?&`EnzC>;L6qan>&%Y*;rZZbe-;eSj?e&qDk z^^NMKr_0{-#+=n=4B!0u)wVhvt{(4dsf3wE&uxch1UocT4UPw$^>w!uWol#O< zjP}CAdlJ`vi2WecqO^!xvh|R$g53tMRWg@xi*r{U0dy! zimHpAm;3+w%Z-_REw?uB>@4(jJlUza_<3k>^5wX+g0!eui@BlYg)c5enT7YIth*%I zyLwjTkL;|UUaVQI(_9sg{#4Cf`Z@K6boD9SJR$9KA#p2L?fd%T+v`^wXSLqy-TlJq zTy@gdSG>=9=Y3y1%V<`2^eed%yPEsIeqOxvxYz7T@U!$c7o;A%dhjBpma*FK_om&e zpM|=a?>xRDe9QOeulvkdYZd>hG4E)cy8Z9_RqU7V1-GqfTiRCNUs8ACr-#f{nZrH2 z{o%fI=4|<;@-Oq}=kAqbfPsyU$Tf!cgL5CA<I$A%KD_|Jm1;0b8aP1#nbXPPi~*=7Zwl} z5qc#4OdwIvwe#OG-HzVQ?;cw`roHu<&NaDg+QnO{n`ZlpsqNM<*ZLo{FwQVuGm+o( zW|V8-)X>tXw;QY0v6ZeDjAPlq;%7?ywYvAruc9>8GQNGy?p(@Kva?kEOV-z9-ZQ*E zd6)ORlQ=$O#*Cs_-)5-JlsEaG8<_67HE7GPwAgBmJ8qZT(th_8-0Mrw&-s6H;VHu} z9~jlQT$X-*NI6B^H+)L=`-|Cm|K4TE9eBJnecSZ*?R&oK&p*EJ`5&3iMMpZ5um7#} z-0k_9acatbht30uUTG8ZE|_QeZ3z32;gPc<;l*A9CyVHc{~sT5WGzXYl5oZ8%V%L% z_2lH3r0;WuY_^J-i`=iVdVl64%kj;-I^8^5Qoc`lP+a&tIC%Zit4r)pzBoT;ufy&W z#h&jr)osi6tH}E`_0#I!%Cpy_KFN!05!ohUDtdjo$rQ7=eBJ80$E81?oSo{`r_~xg zZM*ZT;;g9b^mj$qGw&;3e{TNeetM=+PG5HA2Z1fG?7iyl+&%XF%F~-)pWnACvG$HX zw3}nS*Y4?6Yvb>~zJ0g;_u9V;jtHFZ_;8$%w*YjL{UjO^e;`y9BuNYE} zGdwV0VPLfSe<^a|;&&PhHhByY&OQg2Z@iLd@ZG~|Fp=%U^FIMB4;M(TIr#0+iVa>j zRAMCj8q^O*Kh)+>ZLxo3l_aCtRpJ=u$hVLs&~JtI3PH9&HvTrDw#+Ryv%2ye@{Od7 zml=IC2sO|(%r;(bu>Y_}@*%^wI!!u_I{uNZ5r-qHZ;IRux+!)e_onoQMK@gD z2(??g>n|5eXBD>(H@dE7KDBz<@!9E9uJ?X_%y(Y4xF+#W!9O2H&BioG>w~TfLlRjZ z#!U>w(=cN~M0>@OmZl?1o=ly~7c?ix_R>tFZ@XE_YVXuKn|b&}&x@^#eYpJ5 zqDyDy6+RW5%6L^TBlO<&6*sGy=dYi+S3mdvP3FQV!&{x~>tBSt*!F7g<#@9vMnOhu zGhAkg%_^Poe~ZZGO`G~`L$W{L`evwj@Z6!g_P|D`y7-=zDUvAFia-X%(vn^kVf50<6D@+|tTryQbV|9#x(@p8q17wqbh z3%*{JI4&q(^DErRNhv%ik?UWK|Ely`S#HNzPp)4*HeUdJEOT2r^ znfUy@ffdFZjb2O(-dec!RP02nsJ||c7cX1Q-y$!$J<>k>)sJtVioUPAcgk*N;~~e5 z3m@^TC2LK775pP|-OuX#6=J4!A9PH0wb#x05&Cs^j%oSXvbm*ykF|X{_Vw)C+5H)L z(ZAaTcW~T0{3Ee!LrDbtmhy~4D?iNs;K{=GuxUqAMZbuPoZ~Y^_rOUS@gb~P6CIl$ zvK$sRVe@r(zHqrjsD!`KHseBrod#2lb&aJB;#2!ulopWs7MjsFR+ml;-xA|*YeOC=t)-|j(34ZGocI}et^3(mr zPm0&aK0ReU|9oJ<>J#f&0&hi##jOiI7-w0zdY{$iYwy>+v;FpKmPH~~wOLvX-8s#tfz4X2puNS{(--$XKqq6LvXT@iGKE_-R?`@rY|MGOE z>4n-y_nU=_Ui-E_J7x0V=11HgmT{E0#DdjJU2uQh- z-L{wK`UQ;=g(`tx=du^B|D$u6KTZD`V-M@HUf0=A8CQ#COV4J#U0qhMb0DpFMIleY zq@5RHRPV6eF}9hxCu&c8naC&0&!0c?bBb`XamWg!3IEgi6tP7>T))ic&xDp~OYZmy zYU*Y7@LbkAcK6Jt2usDvO4jP5E=${NgSGywa?3jSN>!@1Vv1Uwd}jU0#{OG}*yomP zDv5txENvkzCUJJg1K-JK+jK&7g4ZnLpLyesLcwiSs|7O2a~g_Qb?+94cIFymIO5g)!Nm7T;7qmh0+oK2P|Tw$(xIL#IEj z>&@$)w|Z}J{)gI=f0^@_-go6I-C?nTd{7-AEQ54|3<$qf4b^G)}>cBX8npf zb?e2hTfaKL?tWb?{Ybjq+{o;fdB$w1*;CGbI$JnPq1VCMzuIm7|F^$|{{CS&Q`NY{ zLF-(MfBoto|G#r6v^tecSp~7CH(nF#s-_8ScF1|>Y`UdU;!~@oT(SAqwi&!ho6K*X6HC&t)0yRT zYuDpTmQ#fmd0+qR81q0j=Y#uYftT(DKTdC-ER~hazENdK<#YKAx9gX0M}7Zv?f>fh z0ym?Rmt;0MzdiJ`Ls9L^zhdV#3_Ok23z*^`@IGm@yukdc-nMXpEc;aEg;DteJTKL@ zm(+)Ri%xy?|I;LPyXcRHw$=POn0`Y2freop*U?|+U(5@565!BOvan%Q$U>9FTmEft zyQh`8`l)?f+W*v>ecSFH&eavHa9m|yR`o~MdhgeUsQDY-9N_S+wqGI=B)PKfimRMM zmf}Z-_9w1f43$f?iu8lnSN7gvzxpu#?Dg~i?@oXF@#Bx*wrBr+k$h$OGyiq{R<4c< z|0SZomuD_?klY`Bheb)#byrLOU|?Y2EbxddW?=J`#huNu8KN+w9n^&uJb39c;|yHa0b9<14?EI#U(kZNtImWreEHSsHYJ@K?_bE_OX{`TPd^tS{+r%UdtTs#VCeo}Fo&?sM?5#IuTfqN2(35JtK?aLehI z$<)@adt9>Y>s-AGnbtGk@7MDST0hFl&i1{ioY+?=bKZJ$(wT0szZ+OQ4%l+Mm?y29 zazwmP`QOgx^RnliPCssNkv)o`)J@mQ_k#tOxNgaj>&LIh*Z1b$-j?|O-d+!H@7FxH zXR^bc$u4B^e$QuvoErvzUbx#km6Vtq>zB{(k`}bzw{KtC@eVQTD3#JJv*s#QY`HP< zp3L3m;$L4fHC zR!jZA&+~V@+x7a4oZQ{p%E3ui&R@1@O`o!$GG=4#BFlZ3RxhqD+j3*&%9RJ7&#&*} z6jn=ke5`lbgG@c!!=8}z)WG8L;P3bQ$9c`~H0=NPb^VU7*P4$_vFQi z4`Je!HE2{7@@*?AGp=(6rsa zQYG0aoxewL|Bs{kAFc%Z&unsc=@+wp^tw+he8Uc%ijsv%4J-5o?Uwt^J+%FPUH6$Y zJ}PQzyOQ`XLo*cv=a(d=)6?~j@2mYiVgCI5bDY;?wH8^IsCR2uSgBMjNw9SJ&~am; zoSbB{v~e2GudlD4ciH+lGpRgaVvKf`Jay_+!s;@`LUYxX>o#v(bLg6{>Lv*x1x9}r zh0i4^8!u1OjrPf17+(8&o?Q6rCz~EjWZL>bQ2B_$%c@^bCi^QqE&vq}cO4FdHB2?M z<9H|Dwqb(-3;Qnxy-aN@@1Qvv9)Xit9tT`lG+~u%;XyqS5%b`zTthRRUYCTqy@`LD z9tuxxY3jPg$B?zuXKwLVIhPgs=eJgCm2lJwDKPRY7$hI#d2?^??mD^t44#)UFFo}8 zPdB;qXLd41)q3jtXJ%@$@yWbkw8&KAtgvd#%85!j*mcS!!u!3H)rdSY z6Ddu4y6;fAZr0?eoUJ|$PnVvbGbOU^Ni|NOawYn_{9m@2)PR35xzNy+?mu$ld=oZLmRs(*doT${IC->Fiu z<OGyH=iedU{&cn!_u4L7{e+Kv3dQwOX+5$n z3mO<&_~!Ps=tg!NaFj8$Zq?dgTG@ElZR+=?lbGjq(`!w71yc?_+ z2N8mQq&C6H(lY7X?S)$5#voq4Icgwm7n#NW8He~ea6?)yVS=Z<0$0_S9nRp|| z%=xUe!4xjzD?&z(_kO=;ylBy)o-0?bxPAQD$l|d;no)FR za%bU!B4t&l*zG!oMQg&8wGFTJ%&uH(b$zzAC0$on+?k(zL`zy=ZpM@8H;ON~Jz3uy z@N~VY%-T!Wx6D^BOfWS1#MOJ~j>+bpj;%l20%zvCc<+1V5mL&l%`*H2q1EnoLzVc~-VjE_%DR6e#b`8dy3sXGCWmfu~v`cG7u z*74)BpJ_F@e|;ixykFj&H()l;EVj2dHXaU`%^LW@g!R~Yl?$OPSzc2GJPa1;mL)Fu zXTF-fe$B+%6{l8cZRu`5^Eb3~>2wLsSGzQWd=4fX7QLF{k@_!c>bayRr=EX`IlSmm z>WUKHrsa1cR-ZCx^@z>;sd=$9Y1t_e4?(X#CZ*Rz1dTh-a?jIRt?4IJkxouiZ{F7fsZz4IkC!`naM-KKSFnp65%JPtHEn4Z$AF|0k9eB;l=##N_^ zM4qK3>{FEu{QQqAy4UvAn#9IZXN!jsijK=)=`FC=TT{K$=faKK8(6|p>Q{tSzpZke ztNP&CLPp-y(E8T9VF||{6@ESJ_E;>9@$cWzIZ|`7rsO*t{a~Lhe{L4j5;dcIpSb=e zBU|ZR4|UBSCaW`E^AA`h6uRol^p@*q_kZMdpAwYxO~m)KO=I};R_)t=uCTldj98c> zTjY3oVd=(Cb}`aP+t2$sPQ8$H_l+5kbi%YW|EPy1bd1*#dLTxEJKPF?%(N{wv3s7zQE$3qmQRH9n;>ymml4~v-|o(m2;-9XIY~vSKd&S-@p3IKB>eg z=Bp+rRD&ZdZ>7$={lO{6#1vqxGoDMN8ejnc$+n$@{H2>8{ z%MHTyF>9;h+G1KRJe(}B_E**<^S5rf0RlPm_wk&bqLE{LNkO3`zCXhH-F|UL-H%Q+ z(^&cMnr#YX_500tDED*Ym8sDIF`^9%W;sXLW-%?g#D6e+nV@{=|Gw$-w#wKgJjsn% zedM^4_m)`(wGMBEQw}%IeJv=r)obPQsS@Qs1*{{hXBPUOM$AwmTtcW)392KK!_2U*LB?gLA?9?`MCs z)}H3ODIua@kFBvc6W4V=^YiQDMYvxd((w6o{a0v6L|ZY$vw zJ1@TD-I6a@Cqm zL05jUDXOTP5Ye%ck92yqdfHs`@TNCxxvN(-#xD?-p4y{n9kVWtnN`V0cVoUDf9$kx zvZtraos+wD9|z~uFI!(#uk^aQb7Qa7+~WN|cUktI4-islT&JR7t+)Trr_(R)$DcLd z^TCOQeYJdi*3PPggax6WFY$NiO)5zu|zm zqt}})=S}0ce|viPhKd8zTvvzhZ%W+i^v*tf+0%q*r897rh%<7{d#7}^2+=a^VPN&| zUzmOn-i63jd|G`Ai{l%UiEX0;^UrBtcIj0cOu3ccsGGrl<4SoIN<@al!_wC7qxnYMXqrANQm$$dY|Ni=V*w_4S3#j`MyxgyK z{rYrow)5NK?i#i8$sUR=zuTI8yzk*bcKIX8{kClvE(FY}|MxSm*7>3Z)Y5`*CUw6# z4v~?P_bMLq{`vFV{y3|6OvCi}I?0C*AHK^fT3uH8WkOhCq2ao{-=faUHs_yT_e-fL)hSL1@>2GKR`{pTfeY-8kC|C{yq$UOfizuHWEl}vs{G=2?QcRonObb9J! zk+XGc^c?1FGGF?D#qW)0P^H?!MK@3A#;$<$1#X}5sGg+r!Sh76Cgb{v44hk<56G6^ zVXWkd@bXD0o~-k`dF&0Eg7N7t-97_Rjv#Fv+;?Tmxp zIUW|v?(10*y4#}E4t%vb8XqM#_=(%WED-!Ef%Y1@fBk<*V^`V%Eq ztXbpo|KH!c9Lm0mjq}tEYLccHJt`HMxoff5|GH990YSz$6`a*)*Gp6M zxi>g)izv6!((+^M6S*e^1|+^;^pQWz^O|a6c~W z-~DRe2Yqey%}PoZy0*T@`1AM9n69};ub$eMmE#-Lu+;NpMBMcHJEwdNe9JB*Y<2(s zaBKPZl{X$~ZE#&GXuY&N>u1~CZy#5&Yc9?oO-#ww?+dvD>Xd!S~0{Oyn@QU52+RQx}oL0otOFK4dVU*l87 zTCWP5-1mLi_y1q@c|{*@&jqtNSa|1mWJG+N_j7@sTbkB{ZDqPbq4xgYj(_@K*OGJ5 zdV62vdZ~A_thBi1yQUn9n$gWUU$-swm2ufa#cxrUFPf{d|Fq0p5o0R;aP_wNvE8?r zEA;Bvr=8s||M{ISOVD-2{qL+dJbMzjWZALghx3RS1$iq4u|-<-BId#p1bGozB4Z(*-kS^S_>&|V0!q>U?M*g z`}v^xq4F0lYCTwSN%=aziT&yM-Rpl&{Iy$jzIoM;>q*bMCADn0A2VGI2!3Eux-9LJ z&GxMe!;ct=J*=9c^}>lK@cPvSlYW{fdCYiudVXEA`>OrEpV#kR^X&7K6_>paeaV@} zaHb;1_MN2kDkuL3S1TW8ah&2m_}4A5RaBy=aX#1j#uK`xk{%-$UUbj#y8Mm2 z&{fON-8}Ve<>r@sPL8!(o+h7_(w>(3Y|(|R)~it~+GbB%xk>zyt?k3(RWa#_zsi0l zggfx=T)l?Rdwsy@Z((j)$>Ns7uC3qUblO>ks6xon-W75eZRg6ndRkv zJonLTtz``NE!L=6Yh`uyv3M*97g+G%rRdo+Uf;x+7!My?)u>V^<`NUX{cH7w&t?*<|MS_>w82#V`%#}`9;P{FRbM=M`hTvBEIT#3RjZH3^JwzaSte^#c(r=R;GH)x^b^!UXqmL&hZ|9!?^pZ4-) zNejI;Yb5R1_3F-5(Z_$3oIM*@Y`PlW6@1fJoTR4`e{_NP)veHp*xA$rNqaIUL{A;&pOcXSY&=f8d^NpOBp&QO`{N>KP z`wgLPmaV^XT>sR(sd}IsvtL8(T5I5pC-eJDF2+4_QvT=_#?iXJ#6+f4^V9KY5a$?ewWpn{4KSQcN<-D(zDN>#SUwoSX!oXvBqV+q#Ww z%Bs?bi3=5 znjih|>I#kE$sJ5i0*?|_+{j=3)wLrtd2N(dbgRSFRMpUx>(;g9I~<7IHv4PP$3INJ z6|dxlRIfU=L&brq%c0z+eQD2`Gd{)AvnEUsU}0x>zV}FO!N2&^io5N$Kd+7R-S_B5 zpNa$1-cE-GAuFTh$7g=#>Qp4{LgKCz~(JL=z-MBCCYIWQ5D?ei9PS$rxKNqKeoY5+Mql6oi%7a`Mjfy>M zT_fd%3YeK1r{8b!*&5)(A7wg2q~mtc<^tar9c>EpCNgkNa4-kuFt!sqO)OQ?jQ1~W zoMCx4a&4_)$tND3(z4D(P>cCOE6WbA)!Yw_vY~B81;+0R4hJ90y>-xrcMWz;U|{tZ zKa_e6qys!b^UkrM>A3B!#xQ6bm4UNFk&$bjUd!n<@b1B#W`^)g)60Cz{pTNhy?%dR zPY;iXh{%J_=k42bc6dCGWl4FwN_*W7r@c~%CeO~z?Y^D2o45AYmzSTsrcIlc^!eG@ zimzA0Ki*8Azi`Kn8Q;UF2fzQvq_^vZ5|6YQ&*kO*$Jaz|F1Q*RUb`8 z-@jk4*X`PSrsn({%Vt^YvIq75p4S(gHNC##dh(B%yC0oex7B7(d*dI2$=`aryPf^~ z_?Fy#8KrhWZk}2VsMY+Lamuu5Z7YM9o9#T+f6vWfo9guVyG2Ki9RrW;+kU^3{Oy(P z*DJwuqwePK|0`yGzh?8z*(&^py7l+%2>zU&_Rd|w#6+a%PQhVb4<8?sqwg*(bl#YM z-!7(X=Fevv{7kEEZ_AzV-eBkYr(f;7r|X%%-(Gy)c6;#Y#57xf2bR98S*~$$a&nbV z1V10I2nw3?Q}*)bYde>-irDbIoiu4u)AhJ&-sz;eUvokYyd^{#CC?>`>Q~Ag0_`j>ZJ7z3BHs|nShLh1#Q{#@Vi`{+3eB-u5J=3ch z87sBl&;S1?{qw#FzZWa}?V8iw)%Bsj{?B6Nn+9ve1!owi^VNNtJU`)Rm#FNzsb?5> ze?Dh@_91WAT)xL&Uht&+2oZBQQ9ivV?1HJ9!@0%L=#I?7I1p>`-FvweS3d@eeZ;kP|%U^vSV^0yB_yD4#)StznkU%FjRkg zQ@FFq=zYMq?+cY1m$4`*D}P*H|95p}!TNtb2N%zZH=F;u?`~=J*SXIYPM!YS>0N)H zMIOgB!K!<6!_i3<#Z-D}t@x^spB80IIdj(WLINu$J$reMYF#uw*C_*R(7R+(|nQlcP>nbj}hY1dh;_>{zl6VR?izd>UQqkenOpX zcGnwQqp82`4)pz>vN+?hkO?E>kKEYFvAg`_-f0367u2s{Os+GO8Rss+d`8 zlr-h;$^XB&tpER%;*(wL>iu-aPt}{ByL+NLzj}IayrgqHuln$PsmV73=d{-ytStzM zeIYM%qhMxwtXo6V^3xZLl0k|3g8|d4#Q!mW_;hC1+dsQIn`6m3=Y4XFs{%_m{y5B^ z>ai=;Kx|px!BeW2AKv%b@xHNFLUgZ0vQbfCM!nrT?xgyH(D^PQH@nOdK1`{Um^Z2U@9)nBn^okd)vYdwh+S=2De`jV#~Hta z6pB6-O_Z418o7DCIjhgpgC#cqej2v)@4tJ-JGE=3@ZpWxKlcl6wcwYmY}Gj9>-_3b zW{2NB<`CKAzhA!Wh~FXMEOc2wFs%1%_O%01kyn!}cTWnb`&j95`t0$=*XvtbKW1-u z{J}FdHdwsHx_&RuwI7@uJhR&rIdA^>sBnMT#2-7kIRZYEFovXgf(8@kc`Q)+pSnaz z%6L7$%cH}XFG^N~ebwQWwUX=K zJBOP)?7>;3wI_5=)c!Pd>6_ov9=1#)di!JJ&q4J)Zt^U=b2~CmwREh|=y_Ytv0CiC zgVQ^+JGONyZhrgNe;X%le60We(6e;?BF}4zYwEtp_3V2RW9)Es-#JUo%9%Fjb4zE0%x*P(8MJWmiiz{)R+X3SSifF= zU)Ujy2wv`=tltissB&DmFMsa0QO*nLI@@Vlj*~u~W1RA~jy3E~`^RSe723a9zU6Jz z+Gw;(vGzf$#G+IBwwi~dF0q%$ujAb)onpTB(!YiO*4JuqF28cM>ap}1PI+t3&L#c< zt2RxZ;@tLSwg1h6OEs2@*6ML)&v;&ZGoZU9#I$hL_7~!hH*j&@h>vU9lA*omaB{)_ z6&g0*&P`M+{JC*6XrS2aqTku|)#2V(X4h4$w)B(RSyWvc;H|z@lzl_?dB*3-e|1%# zY+a{(!ADw5#a$_A*W1}Uo*kO)lKpM*+JxG_F&xE5d#g@#?pMg1EXDA$>UjnKyc?63 zobp>GE%ox(roGEpqV2A&*_afWrpL85EMjfziPJI5zMOa3lYVaR#P+{J5#DQGac_7H=(7IRn{4YcS_w?xU(t4?ozng!$S&Ws;jlilkiiKxAWkTj3`eoKJt*v|Q{LU3S9~^4i0-E(|d$Z1M24B{B zmzTG!tpC2+)gE&1zU*cz!$oZEp&$MnX_b&NV)(u%VnsxnV(ifvtH)aEA0@BM1g%|~ zoTmQ#>fP0xPn|KDwS0|-^TEjd0VO)@|5r~pOuTbi#D>qK>erT?W>ez#xzF8_ zS^V(rTK8GA_&4qkINKTE9(OHj$+7c?{=VkEHQV?1W9vK3jLvBa>gjA!b=lkPci)@w z=z#tF;Q#NRKi)Uvq=aRaiCe#1=SM^PP(_DRvs=qlrYTCuSxKEPzWQgLX<)?i=~>pC z>kYU1*7NZ2yS{xG`@?t2ES|gfJ(st)t*r`U^O>BuHe^lUT*vSCj6z(ueZR`UC{a9H z$VXf5h*+`FCf?|UwpiD11s;roU609{McwBvid*& zR?+GY$0J{EIJ4*6(%;LT)y$38;=8dbaF4BI@Rb!;CoshBWV4Sb^BP-(Cvc9VBFgmvHd|Q8i zROrUz8`qpmykT9Oxsq49<3`BsqrIIjm%cq}b11#0vFDqC*}9(9@Aq~_-wf*aH96KR z*||{p>Ku;Jp7|dIjIKC8UBIK*v0Cp~+fu`CdyXAROujpZ_4ldiDO(Svt*p%LiK^XP zdtFSYcg<_nsQsJce5`kGUp4zqvnXUBcIINvqVq@B`nXM+^*wQ&NSF7qMyrGB#hPKq zZXOm_$#?j4Y3Yqc`nPo7xz^|2*y;E6mdcT&74IHSxCI#-*!1t4UCI5fEom>FuuoVx z<-()F`Xkdzr_5^meZNFs>B@t%3HI_VZT=4TQ?(BE9o@0glYjC2GmK6fq|$=oI;5_3 zh1MO3@{vn89}(NQf4)B3*@#A-rIm}^zXs<@_PhxFJ25l1(eYcxs&cpa8TJyF9$vce zT&U@__|ZeVl(;5O;(N63OU#ip-D3%+HP5f-%UoM=^2~Z0qe#_n&O1^Tp4wtqml?ysNS+3}@PV$CDg_TIzpr;R2| zp1Hv4(y_$7ViS6WKL(cz8t^6qpB>8nmcGKmU|Px(sjRIn8_mL&cKQXsJYP2Tn#g9I?9kqc zw(&>1&#vg7K3!try2zd$o7EGi&rEo>Q}4a7a`xMrQn~Dur*F-*xGtah@Ke*WEy^=d zWyUvE3Ds2^*Jc{g&ph5ZwEbK7!bVv@+TH|Cp!-c&xfHhz_3yxVli;_vS6m$z@< zq`Nio-z^ShrKJ~EXq_~=t)!%QJm>ziLu<2jwc@s9t#?{%pnthzTD6gT)aH$wr)L@# z@r6o-PMzwOn!2-4eV|w1w5`ci2t{LO`xjTo#Km~k#2E*vlf1tF1XPw zKGrftXL6BJ$jqNR-J0%nzxsTu#`Eglb;{q}-7l4S>t<{9UYZoNc+cp8dCfoAw=hggp*nX^$nY|}}-_K(fb$@mg zzPh6M`1kw$*8^`q0WW%)eth;bmxGt5cs#GyGk5jdlE~Gts^+uf;xOJBtT*KaE*=vS z)Qd=4k?z?2(DZ3*Ta<2MN8GBFTINMAJ0h;l;%BM$`WxOkM^)?m4WG~U^_lBza&+1* z#k2*cn##MJ-t_hGI5i42x zwak@{ZgD{IN{-3~i^TNe1Z?*TdnuSQ- zbWPv#^Fg?`*Q?trcYc7Zhsj;n(z&buN`LKF)p_O9Jijh--8ku&nT%jt=+?~8xe+Jt z#FuZ@;+(VKk?Zn^*DU=@<1TdnShc@BYt6I0`c6xhR-gVTw{5?XHG zeevh3e^c5gh-Ez6t1EuxzoDSBhz(QoIZ>u(<;!=q3tckmIr8(9v*Mu>nlEqaZ&~-} z-OE#ZOzSll8?*AaZxIRnUTnK1YSFDO9@8^@Um`yQ3;P{jwkAM+jmP=G*^Mhhc~xHp zAJ%Gt4TX1Uz3d8Gt&(-6A`#4 zl<03tdFghpP${qa{O+w%x?2}!cAP2SkpCiWrPbSnsN#p}A71S#Fl~9z{-AF01}EPg zGDVJu6&jt{UWM@1icDLyXN$#J|wR&i~8d*B&=$nII+ISjIAWMvmS74L`&STo1+ zj_PFFXGt!1LwHW!6|%fkr~TsOf{e7yQU^`;le&A)rKoGDqnf+m7LKO`hQLEqMX{GyDUz;7uU;bYv!!j zlVG%I$^Zct4I6&-e0K;mZppyAJ1X<@MLRxl2X5{Znv4tZ;97 z*|oE?i=^@nb8ozOVz(~0>eJ9w??OF-?E|K}P>T>Ki zqqV!ws-Ae(ervr#w$R)&hHE$X%W-aTI$-W{HhMREzwK9%c~v=#$M_!Hy~4FIZ@u*R8^S<#RYEo3#hBu05B($|nt{4`X3dwyG3MPX8(TW96&^imOq^+92rUp4=kgO$PqCTVIto>MV~~dg!|1j@GFnU*|ffaXN1_ z$)35>$g$1h&lR!PGv>EhpIsX*J@Wv6uIhy>X|Y+dC4XPnRh)~MxZ1@-tM2FZrnE!* z4=q`mHGM+m<0}Ho<=b@c_O_@B-Vn5x-<@-=XVdzw!aYJF>)5u&ojUe#!A8*9piN@_ zjth9EpZYs_Mss}6`rDf45}$fM{=FpcXwR$vnVD1j9@|DJvh{3>zG$J}&UfmBuTc87y(zqR%~jh4FCoVslLTci7~PlY$f@s@q(d%i!o_Q_heb#-ysd_H3g8cut+|9{6JcJ3_ywY;fIuX4N)?^5`BVD;9U0n!pJ4Q2d>;fojf)Yh`LD5$JZ zEX*x6&8n>pt@~yWeKT>&YF)Kot^5tnTQ;u>$<_`&tg8O-`gVD?mTSI`EVRSUvz2`L z_afuhytR8@U0A8ny!Ki+Z)mH=&A`>)_*ObCc(pV$<8N6+MDF2-pR%Uz`}p^ApUj0j zd#`?x%Q5ud>QZ-FTR!p3Tw?6!IqTHh;#b}$7gxM4YH?o~_gL-W+Y`4QFS;hCa?ro?yVm2t?GkctPyJr3{rMWN ze(DO((=YDtw|8=K5|EMU`F!49o{^F9*~8WI%%9J>{H<>Gw&PdCE`0Z4$`ZePfw63( zvgn6U{x#paWj!Sm&Rr}Ju}-TiV!qZX`%^~5ilLcpt4`UO632yF_Y}5^S|=svmoq9F zskuPkG&udS4wS(>~0V_BP!b>F(3wsX&HpL{%_vS`vR@l*T0 zU%JG*`_=cjte3s7kKVqnoYr&ewD;TBQ>N?@;uceJ?!BWG-fb%#p43y7aB5zn_jU6+ z;a^^wr}v08^WH8`I$ZSoUqEuwq=5ff>%_kITKzuz#`k;kE8V?OeZFtrJr}Wgch6}{ zKa0nL&zviGzy6Df>Nsx1R=T3pzPDn8;M@qOaU1_WORVu{#dXYhStFLZXfOX;X)I-~n#KQ8!xlYztS5B*%nQo(|`b^0> z)$ZDv*$pM8JJ)P$HD7m|>&orR%XM8YEOL3WeqP&;quLW@?UHq0)xbQvuC1^*{DIxq zPnZ78agA6k=5pj@-~G~jr}&K)%z-<1dPE0xR$g9~aPOkW3flz+D%x_JFMnHMwc`4( zKi3n^+_q!My04LPHuN=zdQ0-;GtJr0&b|qK6`SCFMf(2Lc_|S_={8Jzcb&D}T=d8C z@yxewt34Z7tUMGHN>tMH0^Hd@Cl+mG(yw4y^ggzUvwv@AX?cH3?((Xi%Too_;#a+mEfRm#@|wxE0`{@8kN^b7@T5 zjr%*~7T%jBw?b|D?EIMX8}=9eP572&`d(i+lY80hz5P!sr@k;x+&rs4tnqsIvQ=ki zeg1f%XXB}ck0<<39bxH^1=B-mYUE|rn;;~@6K)|f@BTuZ)iq74ATI<8V;#W`S ze>7LzvtGI3+4qv5lZBoYcV+DE7r1B|s_f=8`G2fbx-327>TWyfZxM%HDt|k)YX70U z)q9R6#oV3eZzr+c;B-y!ZkGw0^ep_<@>cT67njbrTKQS}Tc(lz_6_+VhvodfEt$0X zoc}kK#l<&>Eyx2e83`EN_s%*T(;g^LY$Z_1xCv;C+0HyF${@?$Wc|39n3YcJd0R@=F6s&!9FU)=h6y|v`ML(%J^JA{)X@T|M*n(GYX>w1Ts>;}!y?k}dc|C8Jp!>UcXTSQMn{|9&U|I0% zduQHlP@lO|e#$z5+U*JlYAZsFOODqu*YscUrRz;(b%fI#zz;nH>AT`oWpn#ZpB%-hP|p zMda7yDqj6tXtCt#-!qX)pgD&L4(yyQJ+&gL*JNB`Bf9RoHl8?P+w!%%|6Rg5pLb_j zG+g%XER8e=kM|}qF}t}ZHCny!SfI;tw9WpkE1`S;`5?EGEV zES|ml$Wl|g^Wom(=1STR1X(mHLX25gPQN&H`t)j-=8GrZsVaPLy(7PF*Yvn5PC-Gz zb!zEW`sxJ_4lwdaTsSBmQ)Q_fcD|J9`JBVj&NKcbdZEYp>qEJ?m?6qeEEOw95J&6R!9aUG_D9c-H*>k(I&AW6n0c+%w}q z{J&4)XJ#57e=5}{Vc2AUEoRyCyPQwm|ENxnd31MbPWoGo3Xu)g?{+Xto97+*e!u>{ zUro=uvn=Q4ST^_c@O=JPuyvkzY>D8y-S4dSd^)AQF704RoY#U48w9Gqy>Tol*>c)+ zj#a5vT>anD>6eWceiy2E+-q)-dP?N|+6COEiys7D$lLSLtyre^@2{_V3MoSQsvFYI zO2t&YTsooM%l*!&soEQJZW@JzhL);GFZ*i51e&iF5)!g9aes5%{-2?dv2mhA+w_Zx zrB+@K-q-)Pe)4ZYwt~aK#StftteY}L{L!`O{G%@~FX!AoU3kL06nS}h9vKUP@AvEL+cQou`-Ta6`K!osW-z^c`SMwZ ztB+3)-=g�Uxg1WeblfWGyN#Ry03x+28)`?8V~374LRF|8RN!KaovMZtuF!73}~2 z_kH0(R`CP(@5{&6ehp1eIcZ}v;lRVg?S^@GEI|7RLDQ2NT=V-voLeq$K5y5p>OJkj z-SYcyJ;HomtFqjyes6o^=+T*=^i#siqER6g@!*v9dY8gN!xYVpN0SWe@78}jD*of$ z?)NfDAFtfz@c8Xz?VaDZ<@c`A*9xoCt-RlG=>L3F`F88|54Z3CyQbq@)t_Rt`D|M9 z`u^>A1NgqKT)Vb)&9utbYqx*+aG3w`tn77$^&xxz|NAW~X1!}h_WHeI(c5wwzo?vw zaoKgx+u_+;7dJPjclv_YN_sg9j;}sub0+T@zugap+uL%D>l;u0;g0;K`B~q>?;guE z@fs0DAqk0&2@?c-W}ESLc6Lsf8d_IZ7aO$u$B&9T<@akR{B-oU`>Ddk#pUCudsxQE zbJn@t9A92uW`91nTyEXIUs=WL5&{DQ+qYR}^qo7>Dg5!I`urKf3sNiorQ83WSt-xX z&R%|V@0~fiJn!w8OifLfEtvLv>YoJbk{1F?+Ml!A|8V>+`Or}{@PkFBPUQ_(SJ#>C zlTY&~HS!5gm>R8A_QGRfHwTFm`) zLT_NI>9ju+CW#)uQ@#A>w}lTf=kcxw6+sQ|f)nnY+FAU3R=c~TU$W-{X^sLue{+X4 zL3lA@>)p__JpJe95>Rmm+SS{br{Hk#GVg>)7Fglqa2_<@5&2JNE@-|3v@m6XG-za( zIYrD7UaDw7ac~fdGns0zXWPNfcIza z0?qXfpmX|#ZQ2qa+$lca`UAAL5fUhOy&de^cW=MaH~m>*)A8fS*%K2JEoy!g9M3*4 z1M)oB<+ofPyk5WGEh8gi_oW@qDgDAWOt}vZG+GoqXt4gUm=|J_%7xV&Cq9SIJ3Q~1 zP0$AycXwrDNhGRotNbT}=P#CU>rRr}g*C6hAw2P{01CcR#aVGc-UC1ao#=m}y&W*1l!eX2$uI z&m?O;om7w5k`Y)gc6t-k?F+(%COpse^WzI!ANO|R*Y96FR!_b7@87?Q=X1*iBqTb{ z+x_N|l$3n-O4ola|69nabOpw86@|kQFJHbqasK@AlatjiF87x&H`yFI+os6F`?{8b z0t0B%YKNe*LDm(GKYyOvyXWQUd3ky5@b7PPnGa1iw;Ub5-JJM*g^mByom%I^BWJ44 zf4cMa%5%xp6($9Nv(DT7USm1mUF5~$e!DLHeLoJ(x$x)j_xrCizCgX#z;esM!R-6g zzwg+1rCKD7(-Pj_+dEL?N{F}m#fam4_YI|pJOu1hbhjXq3Jlc)WlE)Mt>!TgO5Ge zx<-2*sOAXxP~jiFNlgV@LE9{vnW4g2AwHZ!pL zzt2d&Hi_j<8v|?qZ4*{r#l|?-hNk7=rsmq-Ot$_FP0OF(ZV;Q`kPiwK>FBwoYK?Lr zX7h(g6VU!+5u3d`K1P-bz2IaLv9WcWn>vYsGvLDqIZ&1lV~SaNb?uHw3C;-)vW#4E z_qbFXnC^Nt*xKCfn%z2)f%A&sf(H-Rg4%X#Su`qkiJEgOGYYLxez57m$B!Qy?NszF zmxH6^8b`;2hqo_9FFx{G#evD`Kwx^wln(Ev<(7q)V=CNxQ>ug%7~d-tJUb)#&*D?x zvJ_p91<{-qT=KQ|;?6#t^x);Ss7(e!3XJO&4{)B2yF6tL2WN#$qo1(N+t4UE_bxWj z_RJX!&)=6wC>kp;3KdLrSk5UIyKnw{`Bke{eQ^gRAe8`6P{^_Rdo-{_DFg%tHXbyVd`>6tWXlGv()-k?Q?QyEzO^K&b&&1Sjf4oaI61_=&( z_f5F6?%dsrs=IY6W{It7-?DDUBd$F^pUt+a$Fyd`ym@_ri``~6xtFiWxO>rQlbHTa zm5P#wR_T8ut18y*dZktK@8|O$uU4=Bk_y(H0E+Q@94Zb>YXvQ8f0>x&-|OiVR-YBP z_1FY{SwUMpPPyDQA~o-JKA*+qQ)9`*#AHzVDnuu8lZ*ReaEnxp@!N+FZVHS-8NCOT z`)!&Y9&T5*vAJ_+v-h{Rm!h0Y+AZF0pEmt*>2%iqu*gTJT~c4(o#@Qd-P3dA++6F4 zbLPB>5IgVLz*1$&$Tcsnj(Ni`l?z;r;qkSi(q=ga-fq92m&NVAQCZ5FTmIy-y^EGR zH@S=Jv33jo`}gm~<>mdBg+J$2zdQKoX!plM-1;X{tw2Q{11o<%laNB=Gmey$ln3Xm z-ydn_x9jNE-*@AR@FLA6!LBB4KOVVMZwY@hwvZ~rXj7K*?oY1gcaKVF*+r(09Jr+D>NjZ1!eEq-TU~Qd< z4Ghy`iaKvLDg~#w1ZT`uoaKC^&tk3ft>CKms){2Vg^#)eFCA8Hm(X0KZek+x{eHdu zz1r`wEv>DJW@ciyx8)w*n0)+Jvhw3e44f65p!H0d9%0QaRvr!|2cq-$w(i(r5iGyw z@3-4KzTc}pyVH8p|2~bXrYi=PjXwi`%!jyy;kYX|eJu zUo}}z@vGD0>pJ`G|J@L=-L!B*%wlCmp%-&mVt$rhv-aG0P36H{7MCMmUSH>DX6G|8 zZTbOfl{7Uq75w}8eD++mMXDZPFmP7LHh%h4^yJBtgllUeT%1(u8>eV zvm9%5 zD_rbfEYzOKrmW;%^YW=+e7x}81@4oNOt*D3 zgYeV|atgX#vyPQ76#^f6Aj#r4&nB}@zNMw5qV3?8Nh}@2c0iI_UL~Oiqd_5 zzeRsOy|(`Uzk896*Sc6d7DO^FVwc(4_=2q#RLHG6+9&2=RlAvWOVL8-CihQbkB@Q` zCQYdkQ%_uUPsOLDPs8Hn#X=G5su$ih?(QESdo({+d0@+u;pmJ;WMP(&-X;rqYi^|8kA6&Gx zuHx%ovh;u;SBc7lzbq+1i)zFJK2+34Z*Eh$Fr{Jf2f?!otleDHPwecFwfwU4$6VF2 zBh&b}h8HGOh zF@0)w@4s4|&Ks#Mq`+9Ndf{W{`FByqw_KT29+a`9JZzeG{(0H4iwdC1P0iupoamk;wEc;vajTyVjIm%eMy?wW*T+=Hv2_3pbWBQ1prvYA9|^6uK0 z^s(&eW?=0v->RPN%rwuXq3QVKsP1i(8rp>)EJ@|hcbMhRBNS>nbA=9)w|j_D|^?hS+jmk=i0uMC%inR+%z^iCrL&~ zf$_YG!$HWH>w>c^vxJp>56`Zf#w2rC=6tmFo}D|CDqMO_T$pBjU2fqciR&L@p5HcE z^YZz#M=uLN)#5$ZhNk1YZ%y9%vXx~{C--rK+Iv^7uQ)fyGI`#s*H$q#wPy`|&Z(~3 z{NvmqnalI+YH#_Jzq+D%{K4jPvxU_^Z7WSb7rEoy;+Lkf_pVH@DV>oldG3(R`p%xjJ>9WTI_q-^s^*w$y^K0qPJ6~q;T`nw@J6n70 z;iK@-(3$dE-&gp~Hy4%Yn=gA_bze;xPqB!3-}MrizRPoZt>)Nq zPH6ZowBSME{cQ_VRTAuSOxbjGwn!BIc~kMOBKFYjqPIUjKDH=(BXR2VY2WyIyTV5- zPe46upZ`xzPF|J)+B+em6tm>ITUAbq(qHpOgY? zUu<8|t>51A{pDr$fB*h*aBw&zBqZ#;zdb*GPWin`i~oNWkVn{|Bq{W|X2j*0>i zZY3$u7K7lBjtK&dO8aD@YC0GMA1byl_QEDw?)23W?gZXU$}7LgvXP-^$cID{5#OdeD-`_k{e54 zaNrXOwdapyE(b8o^!U-va%Wn@-*j*8XDjv39>4ljJ*wl7H)!j&g9A&K#YfrJ?m5^x zzvj=!<3FBE_CF)n3)*q9*sb?aXn1VuuTK~DJ?hdvBPY3@`5LRJ&^o0W7nQDxE63~q zeU=a2!*M-(-A=a0$9fO**Z(+NC8%N>9`(bbOVKdUF5CU#V$Tcejq{Wp4qh&P6KTXb z$24T=^8*C=Vv|NqUfD%Da_Ru&W#OgyJA^l`Ck|F^kcf0Zm;d1e-i#{yO+ z5gXkD4^@R;crootU!LjT$8-3^$4lP&5A*AP&$j%oW@RN+;*>S?jy4lxmt2gmLI8Oqgsa4eSa;g)}!eerPIylFc>X>pdAGIGsZr*`I#=K@Kl zD!o1qxx1T{CzqyHJiQj3ZyN5nHg2!f_WO0!f9Fg{+muwmFZAP4xBiC2!)$9JH?x)B zDP*tvef$2Q^XJdoELraU{o9L5JKGGcZnefZP!Sq#Zm)emm}Se2i_YOnB}>BH)h_*e zqHgaP6C*QCKmK0uvyCz094DT9to!?GCezpd|K9)iIjB7OV5xsUhy1rsI~b;UdMudC zVR3)kmdQrxW-1O$y21hv7a#3=llu8i@%e=-R&eb9^He|D@5JgncZX@8?7v)au6Vok z`k8x{f*YzT_i{vAF4+q@QK||yKd^UB!@GxtGUq1o{&{w0X7Z%Ue?Oo9EcmjZ+T~Vo z+tx{wC;P5;-Q!i=_2Odj7L78S6^@y!KD(zl!!$KDe;nkmYxw&5y7RNo9g};7T)(`Cle@dy`Ql~i z_d?fh#)4+uy}d8=#ko4j)fAm*4rsG|a$%9X!=BIQtU-P758rO*&z{YCujsVykH_-= zIU)sBGGA1+HEfx=<>2P?cG-S9(`8p|&%e)i)ArZh^82khaTHp&}@&Lqui?eJs;WreQ39zVayxLp;5NS=c#@VV)~>oHU8fxgDLYED&42-`Rv)! zyXQdgv?bYrPk1Kok$>IHZ)aeBX!6+)cE4UIe~yjZ#JF$|mt$GXw7Jtg& zZCmc`Gvd=vdHX(PUHpKVj1J^tSf)+3h^LJ>)*=>ApSf zZSwT&qfM7iTs}JS!xYs=N^<*uKAY|K{?erBOHZ8+N&TK;>0-mhEZAOj;`PQyv83F)o6-IL&7D3x(>xFS>V0Vxqp~-#=!Ep;rxPrmK7IO3 zalY%Glk+T;zR1K+($Z(0YmynX_s3*Qi&(=eDqkl!cSmx~T~@AYKiT0sXgsIxna)HO zAEsZ5E9ASPzLc2B2`XP&y;%Lqwki8>l>GVe@t2&Cd!V9|Q`@~S0&_Z41r>eOUs!dC z-OYX~q2_F-R3+m&LCmKwu}E+AH6|veS4XFE=n5JgZsRq6el9~%Oh$%BFuBP0&%ApN z{9moT9r|0h@VP1IL=ts}gO|VdL^5$&s46^ik{5D3_w?b7$$l+OU;0isKDT1LyY5l; z^NMwcUw{TEi4H$i0KV+p^IdA<(Ym*>TATbsUJeeHKXCJ~!^ z7yH(2)=;<9>xTyY+k%a`enb?dqe0ekb zSF8vRtXU!vUmx4@q_A0Vb6x+gu-5Wt?uuE5{%WkrJ1}c^)s(LP3U=#*lG4MCe)U!f z{ad^&;na)-8}meiV=8ZRI?jO4d&y8{{JVHn??uU87LNtjg zHqhRD2F@*B4Nc2KU3wgtR2WPjJn8q^v%6G1%jp-xa_tZ2-isgJq$B;UQG3BIx6+dt zl0rs8ov$W;EtX#Nr#Uh9hqsP>ooK*6)v&dm?oQ_>r=1C&Dt0D5&GA_A`v>BW(-&#g zb8kBz-`z5Kw_A4LzBaf*#Be_ zb31o<<>Igy*{xDDCiNUjPha$AC(oIlTF&YJ>$cA{$oOKrLUVr4ojG%FO#5o?6KTUg ztwpBhOGL`!M?X42)<=P6hq)Bu)EI>Xn6_GkO7>ncH%qR$8xizq+ER}C9PK*u&+n{U z_~O;K`ENXzbx^*&Wx=z%UJ*)*4Qz|HNap|gqP_8|x|r*>%5NnL7aLZeblkI{Qn+s! z-@)edZIAzY&EA!Ec-m9#23G&%V`mn2%&FuTns@n2Ed8kwtC%|cd$hP@W|Z7?;HyWYld>148P z5ZmlsX`4e6&M$kufmzr<;eyYqwnI$@AMgF%kabOG$+`XFi);VBD*Af3)Bn0c)n93^ zUpHc3PIP{iV)4r=WA!d?ki8xYT$%pd$`It&pTxjv;=aJ`?5oC7ey=M$N%f{tTa#`j ztoSFfyF0GidG@<)=ih(iXAkpr-e4&ispqbIc~;xPSGV0;ChcgrxLqOar*hhVkH1$} zX)%;9f0|gnpOY`MaqCW1iIe$y2i-t5A0Lz5+M}jCj|)YgcV2t+(Ta0|!)=Z^mc?va zw`O&`xS~17rcwxW8c}MhYM-oi+hX_rq-n2S2K;}2Yg**`j?mRry;@h4%HqFp^Ip8X z=>c6N3r&YS@qK~wm!Rb0u!LgM-Sdb?lW-g4L1*Z=tS>(QLz8#~`^i7d$AG zlog%?o}2@X^>HzB$@yn$-U9FK0;zjo#KVr#^umfs#3uImG|R~jt65~K8GbnAh)zH5lCe&=w7S~I zHA925LJ%|-oaGaCbTfxVwZ^<+zW?4;VcM6XKxeKh9>@;|h4o8QmRbK!U7vsS{`P!% z(5i~{E3@-gdxVBgjZ76>=i7hpSkqTk#&S>vo2TnkT_ws`snK=eBLDG*28No?XU%Wy zF3(^8=mGE4wJTN}=#ex|*ced&8t!k;i(ff^i?J-{1c&pWksZr#IylbMEbtjEs!b(9t=vB5?7BjEhPL9r*L__xr%$ z;O5}vevZ#-=U5anna$=s-Y?(I(<@_L#xr&5)U+F*E^p1Zo9P;wng>DW=^7>IWzzp0j>$6U_4S_wU(C9xVc(;6*ej1EQm|<(B);H)HJ=vsJ42qR8aY zGC`&&w#{3`NA7^pdG~X!44!g_+jtLO4Uf<5Qhb>Unymm$2z_q!zOz;sG?G*B;eq3c z4^zI}sQGfy{l}Nf{-C2nLB+|!g$p^jxQuEAt(4MS4tV7EaY*UrxO~a@sQmG1R6@c6 z$@Tv8?b6QofBA7?A_HfMJ>$PCFTl;*Gl_h*OH}v&cshZ7k&TQik0<|xFD#xil427T zg?8%kM^r92wPE^^iR~XxPF64c@*?naqOP;9QlATt=Zqur76l7#N-OT;SO^;BNK8!p z+P;Qs@}9jHJ0*1f{(n2$F;CiBz@?Vuu=f0e-|gG_AIvQ`d|>> zlpIRSOD7-JluBrv{O8BV4TMB-=a2KhXXeD_V#=B?2$2W z>6$NlSNr?vs#bo(xixjSUM*h}{fGOtb*wP6sLk~)(Xp?64HwLK$nZ4b$*~7>Zan-f zzTM^?U*u`W(}}%5X3m|>eS}}u@XN1XzrJer?mY4N0R>pp5cU&KlLR0;_Ab#8jg1kHqZaPgZJ@gdD+JAKYv#KP~kXd z{kiYXmK|}f9NFo2?=*dk6li?Bf9t_r=4<9!+;6vh!{PNj&Cc4wMzdnh?`8iQejBAe zI3=;-!Ol&*uZ!~A8TlOZ3OC5~bcpg*Y`+qtl_EAh^s*1oQNRT6vm35S*o!37VLO}2FPiAZaYV-R>`?^Pdk{=E{bqEb8P^dmpw4rEm^Nx#JB?4w;`7z5vzpROzm&td3##@Hv zo&NVFyf^=okgb>WzASg~@_HxN>Qi&_QV$sQ=g+Z=xwPTYC+{`;(?rdmEWe>B=6vFY zs9jWRUsAi_&pn5keh0IiI^YTlYL2(H8Il%qXF6E;qNLhW_f|E9u_~mJax{Yw6peoR{ykOISN-6XCy> zbo5E!nT-WAB}%^>37+#JdAa^FUqP0%IiEL|JpxDDX;8HNe191fZK*6R>>JxvC0@;r z+snYfECjkEK&kaM^U*G`kBbE^s&7-aS#wmp+~-)1<0CE!gW3BmKfLU=RQsHi9VT~1 z`0^#apPDKk=DOdvx#o9vQh&n9Ii?R@O0SR1ifzC4eU&bA>VqPlkPk6^zwLJN)EFL6 z>~z>rI77*3hv5OGxD$`h?`!eziSRkSd5-t}U0a^?e9Y|GeD=9qGtjcV6Q3kK(i6&q>{@HEU(rk*ucn?#GsE zNe6q=eUDvJeO6Jt=D5vHPzZfH>A-s4?)%c!yNcOtEJW=uG_GF|(BiXuiumPohFp_o zKA(B*(d9LBzs<1yYjvITr?vk>gVj%yqh1{lx?ZmLIa4xSZub;#oxd{O{_EEAOlkaU z9B^>+f7@4F@8>ky1Ozj9c0W>bJ@F;|OvHvx>GONe&#HVq?NicbwZcC&_Qk@=emb?! zJilLj(R87XjU)C)QuO?{Ze? z-3^)f|AOvSo=II}|6Cw`^XI*5tlQ7W9Q#qH?lAjYa`%W+@Ar(w&X0L*Zf@{MocBzU|bHMG{hE7PXO{ zbaUk?2bi*2qkw~(&)2?Og%IMET=C%Mgfo7jY%sAmlN-6_)xPr%M+oJda9};(mQuFL zC;+M~VLmAN6vyvE5^`qKsxVX896S}|M6hYUQy!>0#+?4^Z7KGYfq{X+)78&qol`;+ E08pR@WdHyG literal 0 HcmV?d00001 diff --git a/akka-docs/images/akka-as-library-2.png b/akka-docs/images/akka-as-library-2.png new file mode 100644 index 0000000000000000000000000000000000000000..ea1785e61d0448ac6438c221eab3cf489bcfafb5 GIT binary patch literal 36522 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 r@URw8Fw_o}TUP9_{bP0l+XkKb)|%M literal 0 HcmV?d00001 diff --git a/akka-docs/images/akka-as-library-3.png b/akka-docs/images/akka-as-library-3.png new file mode 100644 index 0000000000000000000000000000000000000000..a86468b98796abca66025369e5e22ce3c16379d3 GIT binary patch literal 48196 zcmeAS@N?(olHy`uVBq!ia0y~yV0^&9z<7a!je&uoa@|e~1_pljOlRi+PiJR^fTH}g z%$!sPh6={DGb$~v+`WJNd=6W(WOi9HXX@EoD^BdwH=Ne-#^;zs^64O#lR?`;G;U4~ z&`_M>)hfiLI%Vw|QPops=gW8Oe*gOWpLM^h{#BkUJ^Ovnx#xTA=NIpLKIgsVbx8(` zf_EZZoO}!%3he>8Jtc*G?Q;uWI5sdXYGCDPl6^PtponaCcCp7N{me>+1201JpS;}o z&-F`wnwi!W1qK64j{|RapJjjfX5(2FtM>K)Cxu@5xwe1r>{aM%i|F&(aIyIZ+mW1o zx9>Ql_vhv@+v9LIO z+*msy_Sj^n)|!p41-vgEXFavB#Ds~VG*;u-8T)Mx;tcN+7zBEL99Lt`tlZ78qV7}w z+xD~9A`VvUZmtcPvi;D(hy@WV_)Tu{Yp%$j$&izBAtT$>ec=SK?UfuKkHUbx(Tu`6EoYBN_fm68k=izw=mOi}65$9rgiRCBv)gu#HzNKzS zHA+=WIHB`$O=(VZgi5>Ca{X|{?;Pu2@Cn5)W^lU@yCT#j!i4R6o^?K-do0)dO~sq1vR`E8^08YUwT!n5dUxr?t->#BZ!h`H-|wsM zmaiv2=Y5`i>7r|f%=M{4<}ckH?0?oJFsdqcIF<5l=5brsn_Sh?)v2|){K)C6 z>l@WePnW&vjXA5$7{2-Qt97MM_G?RM%IHez)dgRQ{PmSyns6u+}{^*4hrdtU3-`c`gU&wuy&_4)P=RjYsY{k+?L z@YeTE=XZx69nMu=rmSx#^UL7J#2|Wnz9#FUV1KkefhQXOEmYbTz>hV zv5v9p=2x5F7N0AQe?EEtiMp5nXMHMQIVYWa}Yg1<7L<@Aml5yfyQFQd*?hjfxxJ6V_U;m{S>}zvFs^ zX<5sMtDpXhnFyT{nI{l>T($G8#|=+2Z%@zplYj2J?YGTuUbtew#@hY~oA!IneYI-W zwqqyve*f70yX@_E`+&7u3j!REhksvnPrG*3!aWmv3p-A-UcQ~?f0kYMna=e!hV4Ic zRB}Z*q{^(m9pf|W>uZmlvuAFnyN90QxFQIuc|zLfLgH4g+V}Ovx7V*W&T75YyZeRJ zx$2~^uXvyJ&ilT2meH*4=vQ(jb~X2Z{k(YTaj)5x;AiP?E=WCi_25NHEn~Ie?@hZ` zKMQp;-+6pR_?GX_U-y}_)++v0W8Tp?b^G7iO>*%v zNxg--5;G-k&v-PG*T82+-n5u7rQ3F8df)VKl=Vr^dA_r0=iEx3il^mop4>j!FDxJ| zBJ@c7nLwhTYv;dXx*ffp-#xZ?Ond7yoojO0w2QY?H_i4HQ`@a!uJu1?VVq&SW+K1m z%_!HvsiCD&Z#P!0V=G-R7{{`I#m|)ZYjy9LUqxxGWqkXZ-MN&fWM`@Rm#nYJyk~fS z@-FXrCvkknj2T64xI-Qz0xM+T`|35zB$Xb#(CE<$Gm(Rkk z>dDD5N#ExR*=!Xv7r9?!_5RF9mgAdub-H=BqZjGcm1nOPpdV0 z+IHtv#aU6=>F3-*_d_;Jb&_U?SUx=YIlN9xjkvbMV`t6&t*6 zsKiM4HK-qseyGi%+G796DoIAOtHd$Rk#8YOpx+AZ6@qMmZ2WCPZJAqaW_9H`O{?hGc)f_03T6;JHI}?SH2@Hz%J;=!|A1q6QD2FWA*1 z7ks@caa>To=2y6rlTvt4BGj_x}VkeE5uCeKIoY0YOkB~BlPR;9Mkf%Wphja9&7t@?CaUNv->mh zqJOsw?%=q0_(x*dhLQ;OE#(=9R(_cM!IOpWVbhMLihdCnImc&;?tzmu;zL-oCOS4h zWH~Hs!shGneBpA5PzisdZN`NLI}N59>l#ZN#Hal0IhY(|D6BI}CsHRq@@z!@row?c(>x&!EZa&VV9Nt{}g*rv13d$Z?+VQeOw>#W>f=a5&^71f`iAzG4n5WEp>vj9BmbSL{ z$+iN7^2%yhk?loiZ(?A4N2VlS?~ z{4Q}u+EBuK_Kev0HGLcV~pFe-(=M>>&p9w9~mfZ0Z z)YQxD;km4L?CzOO5tfRTm8{iAU6!`l25bFU<(75um8w*2#T2zV`ONy0js3R{vCl2p zR1*KXSlU8bOycZ}2fmZfw&{fE1g}}fKl8>Ng@W6vRtsd3=QI?r>fZ4>d3yjqul}0v ztKLf$9{nHGV6n4nkw}SpMZSmg46lmZmZX%>Eq^*!NgTZ~&EoKnW5yd;x;I;PCZFWe z-1vM~Fzcm`m*0&qipy*|rME7|?A*RN#-*(FUt{h?>?=DsS83;?O_%0g_s%;pB|Kas z{M!AXow32n;j_!9FV=f?&!sstIaIjVc;(X93uCfBExxIKEZ5cFe4g+vZL5RahfaT5 z*PGWpZ}r~d{13G!|1#$2_;yLU>hgrumXa^H1-qZ+F}I}5Us+)A#My?at~ zar*9f>HSxAVxoQiy!+++@%Z+om*)4`*XuuDw_@FvKSqD9{*8WJ{&dxWtV^$M%=#5| z>eh>0w|;eg-Tk^)`jK?Ixsll|^NiV2v!|T>bhdDoLa&3hf3@5E|8IW_{r$smrmAs? zgVwni|N7NG{(t9CXmu*zwDt(6ZR82LX<-3=&ojL`Oc_sAF1(*rFk>y_l;nl41+KMc zWPB2Tyywo>vkGEOZ@ebfRZSDv?2z-$*>p>z#HUtExnlFJZ8Lb2Hksc%CzhmPr!&jx z)~?5wET;-B^1lArG3J46&Ik9)0x#VQew^MuSt=`;eWS{f%IESKZr3m0j{5%T+W*!0 z1#U(sFUf3jetYO=hoah-f5pyg7=yQS&#vIl$ptZNEe$NOEP{6<0Zj zEX9uu?N3~}7%G=&73l}Duk5|Ue)VDe+3V;3-<|&U%?uV1YXN*2`X?|E9Fmg3K9 zIcL)(A-O;N4vUhe>#mmm!N9=4S>O>_%)r1c48n{Iv*t)JFeos1x;TbZFuvK#?+_At z@Bja0Z7ks#2NQUWOs-r=moz)M<*tdNcI(ppi}c#RNXH$=|rXR_6U2;!W(Mm7=>Ur^n1LR`3G6nPS|aUWN~BqC zFy3xB6bMm&fW>VVgY|=nYG*#p1RL4Fc|+j<-wigAz9guT2A5cFF!q}yPCL&s1FYl# z8)Ml4vjqhrP$L^BsvY2~Si-rv2Iep`j)<~Dn+i{v25vdK)YtLbqg=IT9xzE|p9AOp z@6Fx0d&<7~yZv{(NN|xUh<{%7?v7<-tl<6cbJkjjU+p~#^ZEwQ1GjcY_36G>I_xSQ ztMXghd(P9l%Qh&w?YhDkZ5{rwMEBg)M-4}Je=UrkyY};U+vD?L{@Bsgu+@314EJ*F zzN4pq-4IHzET77!S9suB-{nUsu5+G7?D_xUF#jfJvE$Q<7YR+<^XP8Tn^mVa%D~)N z%lV~r$)THF3iovNCq8@Bt^e*{&S}N(Q(wP%o@kxWeR+#TK=)?R_il>=>uSGV4X=2) zboz$!_i-lX=8Ly(y&7q2X$uPuNO+fJPD(23ZaT9^#Y=zs@91QJ4Cr$ z%hyLN5p}e9`LHF?G9ml9w#>StFT3uoE7-T(fBv!g|No@Fxw)CW{9dK{r_Y}SWo3I$ z>+i3*(Z#>~(wyngSZ*+7e0aW3*1D~&jcw15N8KJiK1XJmW>1(ltxf*#3-?X+n=KM2 zo)`6tD7zo>fo;)5?GFSyxo*~Iv_BxQGfp*AxB5Y9Zx2CKbg1Tg+#ry0z=YMjs*Sxx- z8B=;SbjQ10uQ$~GE-TyToGW|oI3(4Gw=}#k+4uEY^obKE4&-jX%XV?4P4zdOmVV*I zdmg1q{*+zSyG0Y@FDG_BELVFF%rZ?cwrl(Sy4}@sH;lw} zY8G-F610|Xd(xmQcmEQzSXV^Vfh5W8JFa#x3xAMdGAY^|)cJK+Ovdcn3$MQp2%pzK zX-%ug-MS;KSAVcAXa6$Q?M>*prlRNKW~XwT51tjCuuWAc%U8K%%e6&Y_X*u}-@IVX zZqZz`A`3CofSF-JOlptbKNa1PnlvF)Mc#<7hX8+_jT zKMMWq5j3?nQ|halQdd_P5xgb-U8elaiM^taYQk);y?yoehjmumfmh59k+p`Cmboix zC@>^m+&po!doPR3(ZZdV?=V>7qijNZIg>QU5! z_?gGV-rSgROINw{vEJlj^Rh<|q}MO{*dkao*Z9+h#Tj!gV~xsWH(t3=cGt&hiPHKN z3@4XFGk#x^>ixiep0mT7_>1@M-Mh=Y?T6h*y+4lv=59E#r!kI?Rm(-`y#B?k^v*o< zNqzG6dKY_7Uuu`H)42Fy^NVI{m3Co6#_x&?rk}c`czd0evAdS``YEqw-(Gtqdpei! zi#fes zwC_ztpV>z3_ZevZ>J$Tf87l+{W%m(0F!Bkt~+(i3NNr)^ow)_v>GITo9? zh97(v4Ys^_-*R90-}@&gCvUPBsk?a7;giRr$$T@~W=U-p*`xJsDu-)gGg~F)rlOnxprQKRW(Q`d8C3eJ8ao8viGR z1&8l~!7eedO-^~Nt`JJz?zq(nPjf2ZwD1FeXE7xKAgilx2P z&~VC~omv`xes1f;%lQmj<}a9P^it#d4v)`&)+z~czdUtd%HsT)_aFYhq~vK{A2q>D z^tI>p&gs9RHI?{Ur+t5zuDC2im{lwBz^<7kGb{|ROzHe@#K^Cz@Mliyzi=LVZg~&C z@H)%rrHYmbfgMX70(U%Bo$~Mii%p-y^Tep1GF`H~YTv~KM4D#YJiVcem-o&*i*CK< zf_SeGlh=t0?iL(T(#=h^ytZ2H_KGZ%Y0uOWf(s41xVJlm?vY5mwo+uV;Vir5)307+ zU43uMj8*Hnp4RUaWY*1}WurZ_DD~mgTlch=MBmdo^yzAJA)EMVzi&aOr!p+f`}ZR@ z=(oK<-p7+|a^H2WtFre#dF;1m!Xl@uN~W`JfBhnRy}9ke!p(Dc#OGNAnQP1b;(j2Z z_TV2&%jc~>Y#y@f@>OWevVPm(+NcyEv%!_|Va-iO{-vB2=ULSSO!#gtIr8cL8uwCf z_1L>X{W~5i%)8-Zd%1JEV$LVtfWY`|3D*@RGVO1i^N(*UI_I>fbkFgHk-rWt()+l5 zJ&W!_w`ozGq1{PUq3^3*MZ}ge-!^;aIN6TDvCS=Ko)tH@x70#`(~JBr{10CHH?N+n zBeJEYs7-r{RuscEhwqx@iykHjDv9SQx_*gxOb8h_K0K-`;va{c;I4IohyH>yw0JbgNgU|cEz_0EAOYq?vA_q^6No^gu7L$Px*ZgBy)1KXW#w2 zHN@qJwY1LlV}4IJTio9l&gdL?U}ck~p|FG?Q((mXh(=-0&j#9)WM-+ZC|aN!#}*~h zyL>g5pj|c}Q}}GZRYlq#4xABqk$v=eLeDm-rJ*kOIbN|SYa4MJSC!T`e^kD;Pw%Cn z?xy^Jq@aRno->TtOMh}dc`T_NydZX?fO*fQ2On;|T=x0XeShF6qE+6Bb#f*GApfQ<6%f zZ2nxWEkZGhrwziVv~8$vPq;NLf5EPqB@I*g_J01XuJ%BZrDgu+ z5}UQP4+6G)7oO>HgRQDMLWW<=rEw9rp4nZ2-*)xi@0Nd3yShclCvK7Myatsij}Nfe z^fw5~>YlwI`Nz=8LL^Z!ta$q(qbsj-GT%EX@CLTCe6`rT@ap7KS=p_Df-Ms!DBNs} zdeZTmN3>-2#JxgC@?z&!G5=xCec_R}b?wr*X_J`+{kDhPoSdk6`cq!)vKyJJFRyU$ zm~kTCC4Kq4S9hY_z3pzl|Moh~+V;2+<9P*zdH=P`s%5OFU90``<2VN&-?15n$pR7* z8t?gazqOS;lRVg0XaDxn#xi8+XVa|I8MGY0665a&$3x5wf@8G|`>kPxB`->Z$>Ud_)J@m%q_WId#d1TJA z*i1W6@MqrT;sf6_c-=XxjV$NETCLWC8-7@w{#{T~Jd;_jPYBVRT`!ox{w7=Z+}Bh{ zV{N;Ffew4l%ckgicNe*>VS%@+RT4f{PW!%4%v6P4himJeHQaLRjIM3^F#~Ra(2Liv zb^dM0|C6wbzgqN50_%=ndv>tC>NUUT;OWW9CvVrYYnN5H_`y%mp3Pa#j_5iy-&rj? zc33P~vgEO%uC(jlgWPT&JQ(~GE zKEL2}6r{0zphG}FcKwYT5kE`c@BRMh>-G5Kr>1IuJSralfWQ97;f^UUvlhxdY?xpB zEi&i+K3VztKOY6mSPM*zra{}-@7x?p&SYh0KTUiotnT+9zy9~^-79bS%Wp3~U|s(1 zi0$_~%vH~ntnb&IJuV1oOD8A_xI{0j`*c!$$HO-1g0rUA6HZQ2ofe$;({4%Vh3Bi) z?$`gf{rBUz{i02qg6@@nu9)VX zODBn^MY`_1yD-}N!K_(QcPbwD=CSvReu`t_-F@xC<0Ce>1J zH8RiNK4)$1RuT3Wha0~h4Lf(gs7&Mi{RbwN=d>ryb9q`HYbwGx<+;YA=SLo|C^*0< z{PFuIt~*8-L;I6u7J1F-<+Si{_`&zk`Htm|t;}NloZIXCR$4cw+}+dMp!7;AB`EZi zecm+fE8jla74>j_@$U||&zSUh*6WtlJ_paMbEl`cobBuFSbT5c{CJsbB5&K5UY7Q; zjK6)WSTFy)pk@7kd*6ygSI$e^+@4vSW&87AW|t@C$FW^|BAPJwMQgC+O6l^MRgYrb zHy9?%mc7xMP<^iM%5A+Hx1;~5hD><(nZMw{=CYk0$rBE z53MmenYkd#G~w~tk45`_%*+nBeX+ASe^1vj;X~5QzUNpODD|>x-xy~X|ws@wr%UDjjIB*J*#@BINfJ;P@Ni@x#Uw#@Gd9cYWovb zYfns9$o$H-GG@x$q)ED-Rx1NkQyfm|te*60m6xijtahQAlBHCwmEtBR+rnY2RFQ%}Huz9k1^(vp?CwU+e2lWf@KE|J*v3 ztH9nI>S=6fYsf71X~wIy>@Ro}eqA_Ps{g2O=H-nESCchV-tYXnc5&^iqwWQk|0Z&if|B z|8tf9nJn1teIatU_!2X%d2!t@FK*nS9^d_DO28b$U&;$(c+WmAS2oiUGW=Nh?2lu| z-1!X~zr1goovx&EK*YA|p55KeGuLT*eN_2cw0Lp)qlMG87apm4W9?M?fAf+8FUhjC zzK6~nss0eLPDtHmfpF3~ll_K`pS>DnKU5w)+gi!gcp`h{{kH2L>YmyaMRD$!*fvXa zavh)OwucT+t#5dzi=Uh0z&&~2K}FrOx7YQV^S-_$Q22*ymVuo1ohclg0e%M6dMt^@ ziat%Kb)GJ^G|6dais2v4`X6(?esS!c#3jXM*517QzGBxp8 zbIe%(>V(zHSVM2uHH*H^{&4vF+oE5q)!dalF1FcPW|rJ!nDb6$Y3ln*2KRj%bhv~) znSGqUysmGzbw6TJss1g;Yx(J^J}O6M=vljHfku3J{1#M;WPVsOr-}0iU*nCRryG5v zbmrWBY_K@(!UUtc4O=n-=TvrtHZD1yq4$+dRgE=$<^zr5fa&`i`m%lIZ`Brfx!=jo z@8tR;r(TBnt$Hhvu}h-$r?F4w+4|-w=BKqc&cFFva?(=^(`UXL?wp^(dG@wv$Ue_H zr(&r?e~)f3!@n`?>P2tt{aeegAdQ>5BYr>IDc@mY{|-I-fOGV$Ikuy+}yT@wlE1%M-I+~KOfl{ zS>3wzq&RWfRG->&xBuVhTy!MiP0XUB`+ln}n40&Mlb_r0v`_$fnl%O9U#0>AiJ+1ZoN%rNBO;W@G*aPb3w@fG5+ zPktOQs?WW1EF?nkyy^l6j~Rb?lbsIfXwRRs{$b=RMfr#VHm;&~g4dE>DE(mgYwdgZ z2%}=+lR8-*Mn{g<(|7d$KXj;g+-3kt|4ylR@|c3)7nh+l5mj+#$o>AFP<(H0)jH zxzh>KKzzW-a)YtjNqb`-lwS(s$4)M4g|zPu2y<*;{?-zfBnxfGy$11jsXgT~gftV? z1rpfbaIQUK1+lT=I;d%1>U&BQI=$cznr#q{>MTuxH1*djAK<(3de#)}BajC5JckC^ z4eDEjUY~&Q4DWz6@@*O!tP}h*RIg2h@b~mFSSPH{4q7Jw=_OP!F_s;e zcfo6oGL$dN*n0iy306_7;%7W%@9*`B$JYp!mX>DZ8DIN#>Yv?Dt~s1J_4D`d zUS!eVSiu{8HP3$=O)3is3u^=QMV2o=eeU(O==`G> z7rWa&+U`+sb9cFZ-QU;okDpGDcPl9|;o{}z_M@7_s^e;XRbMTN|-N9;^)J|?H`W_ z`#;$AdY!Zm_x$fhO#lBpx8GRvvnZzi@7IcNH`5j0oWFUX?1_%yd++yJ)>RK0**_eU z&VO*xUH<4I*KQlRDPgOR&Mm*!={Z^LO;Y;1z?XzY{?fHJM`b~}Pnzu1a*X?)#>P$WD-cbF1 zZ@EX(&Azslv#X7E9_o9y?|0swpU-Ch_;8qi@1dVvY++hc1^jlbUF$11>Dwwd`J~hv zjKVwq|D6B-;5^&vl;_&(5-fh`F<$(awD0YSiOQQ|Eu5R)o{)^XH@D29?8}qM{!Tuf ze_qG`pHv-s&*jylZhg1ZRMn{H=!w&(Klcrl4vMY0#|Y}wS3H^MUhv|AA`1%(52sT4 zCq1S^d|cezsk;TfJ@iPhE^3*Szf^LSZ0?;MfpWc{uiaPPnDKVy@_CcmuldildK&F^ zarrj8_YL!_-&vlSZSEiU;zB!b{g*_hq=(%bxDCbMK#X|5G|_s~-Die|h8|vCPKAXV z-?G>Vz5dJ>v8E|>Wr*FSJ2AfL938hOojQG5_SB)WCjx5oeV(k5?fkM&poLj%^^#i4 zAA6Yp{dnA8_;%~{Uz>Of?!FZeC<}~^&VHWrK4aVBdp19U`5Ct@mJ_??em$m`*Y4*N z;ny1Tt-n>W-??#ZCW;@jli|#9?+f^J)+EP$+ihDVC8p#3^4n`K zxhK`$W9+V;Rrz#tf5h&|S4FyAW$rTezCGc&!*0r)wve}DhK#(2uJspra^Lizxn~b8;@sC&^`dk;qyE|Iewee>oX9(oPUGthU4an;m}D^ zn~9(tpJRG$CM3t(%ww=l=+Adr2cDd4;H*#r<@g&}YmA{e-iER4fZY9-7;u5aApF4v zROD^E8j%hy@+v_5eVjWX&QW{N2`b`}R&P89E%H8s_%*^stgs@Fa|83YBVoyNp+(+f z5dVkT6E0}aSY0TA{Y~fEV^+ps8yk)bC$PWy=zBsGYXDfdF5s5Um3=0Yt8pr#`IX;t zmV!ru3J3TqSQc#Z0J*RsTJVKU&(4PnKY07J%#XJS%du|u{(INUd45Qn?1tNB)$Co9 z{=PW9iOs#o)~oEA*a7`@47K`kZ;yU|Qu1L%s=my>nF0)~37vIyz zF6$Hu=H)I~Ew(a6x~?{^aHVwNhNY)PMWtNYAahMdk@ddwgz79qo1c6wFAtbft?_2`PyC+1&gPdC*5ms)n9d2wlF*XMj?wx5Ue zH?QIP`n7b$>c!KSF?DZwZJ7RWTb+2T-g<+`n{iKdG|KA~yZ0%*^bGlTJ#?M^F>aNt z`SHDLp0P|x)jg1PHDt-UlLxLmy}jU!Uh}q966~8U6<#n>ddvFt>lLjH%w?CSU4Cjwhun z@-|+N&ppJg?KL4TqC;d#!uQ|T`t~zde=<6=cfQ4%+jRo!{cjGPJ*A_i$h|*iv-3a3 zJgXghm#?{EbERy?DXUG{vPPe_T@KwTKZ!f*!quSO-jjuCl_zh6CdW=IP5p60u8})Y z*(xaQ(bd9^zCK4Ep1sS|lYH~kVnynj(tb9b?U*GiEvWu|(&G)wGU~S{Ps>e67W`!7 z^LF2|1paUDPZ=-wJ=PaB+1+5<)i0ZR-(J-^=R_lb;=iN|1t8_T;r3F zKCy1n#&rt(D+`y&{hIam>fQS@m;H71=v!>`IA~({20=+l){ITxMJITFYO=o~wrcmZ zhkdEv7hAAu+}zZ=?|P`kpA)hjb1SVsz0~^Gy!cJy-9<}PJ#RVaw;$rS|D#Y^TI#vn zZ|QlZ8ioI5D@zM^R`t*d+_OM!|m7g!av~Udyzv?Yp zdbFsfDv|Z3>(nXAucs@U1_$19np(rBX*ntC^VE=4LRBJOrDwlRvO2C3o$S5xU)H?q z67FvgUo6oJ{^xX~BTjkc3B6)1Y1QU9^_L}9EB9{o(=xd|#o*#EqeT7*pRR^ZTIO^^ zSS55}*t%^;*)DOPUfLBjnV0jn=IRwwxQtKAP5BwL`-(>GV)7n(?_n{nQhl;Q^`cwbu_8~;`Ja#SA7|xTw`%>~tQ}GN zCaC|3admBd^S$N2b={vGZ}?l|MgCo!>F~)z$vPo@MsLNfvYRhgsoYeYS^vf8YH!g_ zHT9K~jE?rpZBqT!D`{}=LY(@_m&XJCJ$iY2Dc3`fr}De@T~%1FUB?o9N`-lD)>@JH zgrzRm873{ce#*Y)=F>w{yB4S~pQ$IB9i{3l8!O-tBj6Dfx^7aPjQ7(&MVr2P{p^nO zv7dL#Ny~g;z_sQ&!=+BDQ>VshT-_ez=e&D~>hXJeTxFXRepT_To3i*N+w><9UEE2^ zDczfxm(1r0jDH=TH`QQDndbakri$^GV>x?wd@#B_+iZT0LTs*8`h{6@cb;%p3rOGf z!}>>KMR=5@$7G+>brV)j?|QjN@8FvYrqR#5HY(lwBl~ijzw=xj>-fuhnJvD{X6Hmr zxg4kZ#?yTSJYa}^1G6{;X+inYu_TTMcJY!FXR;Mx|=&~g8%dn;&wYW?c0|1 zIP0g=JZ)J6en;WJ?_6#&H5;dd1S#81XuHhx_+>|0)BKyaZKtT8%heQ&n%{6x;hyfo z`aMn$`;*x>3HmrU{cBt}V|porgf$nJx7g|G>0(PwUiD5o`eFH&hsD<}v+H_kNhxq; zF7tnUsQJuF<_TW{1mDe+W7`|^FS6t2!TxVq6E_%ab1wr82^4LaXL!gV`N=6(BikZPCtsTsN=GJyo?xCN}o|1A(>978|VgRWA(N6LG@fn_82B=+pWbMft-S4HD<} zeQ{srW`1*`g{oTcwB)X%R|GmZCh=DuoN&ZSOokd1ZgE7zSa`!jL+ zfBQ8zj3lS_u3f;VHf!bVZegX|_ZL2W)0_1*)N!p~>ZR3vGDfO*w|z7%IWJOrf13l} z0uBD^zLNQhiT`%g82$aRZQdKf9E;|~Uo>k<;?LjWyt{PsLbc=f?!~lsU9#SgdDLc4 z$nEmn^8OXvl`i4elBQ%FxL$enJ=4vO6^q}P999+-5^CG9!NA4k{E@lZ!Y`uU-oK#j zmTa=rQ-Ag1Y%|-ED%%y}ZJ+kv$=LT{(d55k-y~aa%v1P%EvhD4wALx+ucZ)=?Mxpr zxrP-kL2pC)4~PB9n4>&tk5R9Zic}}}qzwsWIsVlZslK+$gWfdUU9(o|UIiQbgudUC z(v{WB#5Qg;oH4UT{OART_xr3SZ#&gFLq@E-bn4D4rg0r>P1D>nl;ghGtmJOeRz94t zq}9JZ(&O%(jkWdc)*rL(sYzXoYhChkkx$jpBag0KsrmIas<-)V_(Q!}f1>{e7)n|7 zW~|mcI7c)x!(>A8)cV_fi?;?h?r?pw@%sOBZ)R9;Ob@pDy3lx{z~#Gh4PW+$J@K8} za-}{)P)M-Z()=&eqREF38ZwC=b6ZpTy0Ap)T1v9Xszn`#l#H(AN`GJADc_*rtZe2# zx3yKZ*VZ;5+t94<@A@B_H=Q`U;#N)6*NNuKJIp1uC32=ic)8LumcRB_vrMH@oIT2? zFa_5;a;^10zV4P;$=1-v2Y*~GF}snn?e5DT4V*C&Om7~YPMgi&vzOS!x?V0{ZK!!e{^7^WfTt@a zib=El_g2<8s@8SC>%o$*Me8F?r{?|m&fS)EIbzC1U9G?=i*|qZIqqO`ZN=u5!TD0F zzVOd?zq>5yj@u^-9=8WfH#^o$v)`#FxTbgVGPOFbssq{u4b#NbZQT}|Z_%m?GVqkx7OcWqPC!}?04xWMNLr$1FxYN-^R%c}hPu69#yOTw{J zA{p29kMLHShMA>KGCk!rE%SnO$F9eQQ=V&WjF9X*@FDVt)4ofy+fFmBEq$1*R*=Z? zqcbV9A{TjRMs-%VWwluPxj6@4US96FTCotR*Vz7wZSvz2m4?^V+Wxs2&FMFC3#xBV zk+!~g?N)2U1p}6gF|#&q7Cv@BfhFSqH%k?doOE~4KnDlstCKlzUNr?SRH$6Gl} zE%Obs+}-47Q=Un;4T+WGc@Rn&b0 zKC8^Dm9lN6SL|ebuk$8vV7|Cc_V2O3Ph+@t)>dtn^_jE!{O&#bYDG%6)+qLiB`TVj zOv$lkIe74(gmD@V6Em}6))kE(zkfeo)GhP=`nNw_-vZ~@R|zuyTGZM(RpQwK4yjxH ziy}CMg|7YQzWx2Ufkuza`lTBf?}<7lb`+^RWH9a1wS$}-_|sfh+plumf6Zu7B)#}dLDjM@CSpYx_P)^<8!suvZtKy6bSkWl81! z?4+^-a@SMV7=Zd^2Mn41*>f$e{vGe^>nOBrw)>aev-mFa_9Rbqw7sbu+TL*Fsjc&; zU*|IepX=-rc5V?mGX0CD`^?@A_q4Tst32pGC-6WqBR!)q3fx6v7v$LePw$D=c1638 zH_Y?9LT=UD7BKNTrl%#{5qF(2E39cw^s;YPI_AyVGGA=pwrsA6^VYvCeZTvl!}mk- zbuG`kt220icT^uZ>~?|)+{?@JYIt`xt~#RqXLbD1-M8{HVUZrOo(SEANvgFTzc^iN{_ zoNntU)eC%Q-j2R?Vxn8}vbU|z9NbNVoYOBVl>84^IoaTL__T@N*YERAzQMR#M4dYs z)FIs=*r;fAN%@ufiJxhURu#%FW0^dA!Ox_{PXC;%8FmEzwTTfeJ0N$jVGVeJ?*X~S zh1Nm03e=5xjTirz$ouWaE~R<08?-ex@qoiAT1aA+a&P1oCXdOpq~++-?hm``O!-w8Ob;FERRT{A|$H*fz;}mG_CC%bYLo zV|;(E*Co7yDQb-JyKQcHfvtkPH7VCuY zi7A#~L(G*I*x%F0-jexIC+F4T+YZGC95|+lN`C0>@!GTeFW3FYLNSu|;+!9o+9Tqkp|XkNZ>t76z?pD{1 zkV|3?j_KNwn_ZZ_#1lK!Dulo4I$qwB6+IOJm^^N%lS~vHL ziKw_GeQmsFwX#TiWA@ve-O*lkf%`-QraJAfo_V+O(LJG`mjgI{?D_ulV}E^5{r}(h zofD5&?wNC-Q&_#=<P}R+YNrED5bwSakXLAHyf{W=CYw{t2`#T=rc>L?pk>hiMHuIoA9#GM_wdH#<8zvhb=EdJaNG98+-O&B ze0xIBYv0UclGa70im{IqH9lWYNSl%RbQ4pc@pPs6TvIComEFV`36 zn3$OFr_bv%n{H86Wxp=);iuw@R&Mb}puyXmoHf}Un?%+temJGQ-T}?@g%<@UFN}?8 zxpl=|P&(p4qWZc=h|Cnf4_tt&?>v)cRy>t3WR%xx@ zyuV@}1Vy&rud@yc4*s~@{x9dhe>stMm)O0c@+w06{H^5)cR%`T`l?+^Q~7FnSU%SC!1MM6 z>F4Hjt_)tDbb6ZZx+kYL*~~dm|NncvvYHy(A=s$lUXydTCZ6Ve**oL^sqhP7Qxtzb z4>oxE{QJT+PTl9P9a(TXWB(RU^W_YxS|V@PySlr(ufNW4^9}bCV>8YlHT)54{B*Rm z4wYVyZJ+<=iTaM$>vrcfJ@UA(2wGqM=c#_ZV{x&uOzhrMWlyZXJW$IS`5ey?Eps9L*3YqR5a_S?LUmoL3w z`qEc-xv1Ip$ia0Ec8QZ4&htA)ujrAtmpk4sf4utr-uBh&_eu5J|Ff|B|EKsxiIuld z-FGJ@(5TAo$SvD5$~P*!U)*n3bn9o-g$=Ti9zU+_Ui~)X#+RnFB>TH%SJr8|RP8%f z!#!ySuV>RihxhIB2OFZT*jWOPtBCAczrW=fGq2wSr}P)g3pTV{@I>d!3m>peSj{Hx zeBt-^_s4&~-`_85U3Nk{;r6XtAMTdlKiQT&bqfF9hKBj`<#UgVx1847&69kr=U{Tb zZCiBy-qIV3<;AS4zFrOAn0T0NXZYE&C#-DA%ek(Vm3%&HzIewD3H$#)&$s8gmvt3u z+`4hI(T_zj<=5RGw}1F;xoDGjzJITv+hQigF9vP%cI->aWbRM7b-+2d>%4w=@^>du z+eI6ge#?2uD8$EG{;5=8`u3B5%IWU&oeQ7e+?;+|Uw_@QyQ@0og$t*?$#(RyzEgg` z_QaVpN1FNVIu0CIXkX;J{`%wh_5ZES^6%-Ku70twy@pr4=4#)a#S5)JRv6gs)d1HU zEq<}N!D}0z72&NoepDVXn`tF|()q$Up~qk1JRK}Nd)=D-?S8UMR`)+veBSo?m)4FQ z1%li4d}kW5Zr!?dlTCcdzPL;UxzLpgckbWnx%PXxQo^JerXir_FD^|H~!s8#{~DTWt7z9ykebaCI*b zJ!f*jlRsIYc&Eun@Bh_t%`Z)!K8s2Cn|5W()>)IKE?)g&we6Dbk;nS7zg}EOc>3#B zrCigutzyrA@p&a&TNC+dRmDS(_xpb5RZ8!3m#+io?vz|;Mo-A)B{(e&A1+E>}J`) zo+HjX|3fL${$E$uKM}o@j*Gsx!0Ml(CTnDck}7vJKjqoa-^%d4q1~zX z^ADNEjmgK8-rdedVD|b87aV@5K7YtzzpCKIwgY?>H#j02whIgJeQ%S_n^1m2X!YWwZMv$e zsy|-G|JUO84G#~mwq{y4zg_6XpIKqPfhVF?xKw0lIIWG7=lKwE@W2BO%XquD(s$Qg zcD{a(qg3xMyAQwSnpM}8zGsL{%UM0~@`w2gK^wtJ%H~`?%~|vHYIwx99LW%^sWtrt zzP`LdLPAfXy6>%zsF*r&qM|*gCy%?iscEI9;JSIs1vzGw*+*}t zAGbX#-esEk;PwPi=U}r&5(Ek0{{;?$`} zZ@u?k(z4kS#5lJ?@u}7yrA*Ct`~TP7slOj|rgwhRR8@Dj7@w#Am3Nh{d}4Z~B3#70EEEUl1^PD`NhxTgk^) z+wq)kaO~nG{N9P|Zw~jK4}Gtw@QtCrx7Rf?Qu5o|+wJM|Dv!NnH|mi#=llPA|Nn0D z`!&Mf2st+Xu`Sw4N>M%JZzsaJ*8vuJ^m>@$HH?GlC7jhet(q z-Q8X8EYrR8`=atE+KxPHe>n;Ku4`{?J=wod#5V3?_ulXKs@r2rmj-ECCw{cK5&!?! z^-smiZuYhHUd?5VxE)wjWHe1bUhno4YYiK#B%>|e-2t4?D2TSR^`kbNcy5jqGw0whM}MXC1j* zxM=a>r}HMPw@}rViL2N6mSWQw#(Y~-pC&RVF)->$+U%uktt-fh`v0C{T z4wjjO%z8Kf{H?wt?=mkh_n)r+tS#Gp-HOVRPgYW1^B7WJh^edy65!PJy7DA6*lS_m zR{Q4LA1~$=?Yp_+%9(@rAB!vP^yz%j$=07;F|5X9k z^rTl?Ie+*^-L;uedi}{!Mco8-zV)+Onk?!QmNTwSvf6M?Uncm~*B$Cr3-0u7e{ z_T@FZJWyCz`03yIKA}YGqA63}FDbs(7jrrJ zwZpvmFCTKMUGmbH8kJFf_W7!SrMG)$esf!>TYNyn-GDmJ^%me)e%uScC`eh4VjnKI$m;YT=DFb_Ieo+?%Nj^bkrC% zZZ3Nw<>5FZeinCVp4gEReW584J3n#i%rIvT?0ff6Mx@)F@Ab!azA7E&rzB-A<`(EISH5J~d*1qiZS^+?9ht`mSbhl!^ec;OUt}qCqfchxli8VbHY#5i3R39V z*1Jdfv7pQ{(f_-ySxwRXA(qLVu4wdS`U<%WdC!kBiTfQ|+})HmUDuXrXFK)$7FWey zllk&ZV!yw4MY~uSH11<*iS77ZF3Sm>!#{W9;t7FQPQh;#Wi;wpXK%hzB*^*lq=b0J z{w?#KbF4o+C80=oOTy_D6aHVgcj4RTkCM+he(Yfu+s@SWmy4%PcUk9%Zy_63MX^~oH&{X5R(-7DRa@A|&1%bz@0=%xGY>;iTjo+@#{ z(&F|{GKtqi52&mB+`}wp9n(2AS7wGoJkudP@WO!=`nLaa6d&}+Mt3O&8X3Vzym9V&Y>P!aV4|z=Kv#Vc~CwPnT<-GivW4Y>z=ZxHRi<`eJ4@FMn zdG@{W(^t<^^6ZKS_$mY(YG#4QKO$M03huwI(otUO+J8OFK%&9H`ds)Db6admAI1}g8$d~i~ zu(HJWcK3}6O|k9TJcpm>I~TFKOgji#Tqk9bq~=DB>|7TxF% zwqKhpceF8BCtNQ`(FFJE*DGH5Y_g~GfUp8lG| zQkk0JasJ2@mjxf@TCM4l1{@;&3J>lC6tS0`o7FyV?)oVKT3au4^)A`X%jwupK30OpGM*>BG=;4NXjm?^!F)>1->)<7obO~ytjp93*%=vd+CM)a>FFu; z4GH&nTzLO|Ve?llas2u$^n$pEz4$qh<0UMaWUsPDZ2rsLGD9}w$`{rjM}L%Qte@5I zEL(dzG~w5#tv70KG+cAjJvFW8__d?O28t#Eb)Awo_HCEam960piqAUy^9)~w#TN^G zaJ2Mtvc#21Zd;jSi@m()O=lx*V zvUKLAPcL8XpZvLZ%H!Pk%~xBeDPO%2xPh&(u5reKInB?roIeIE6??_*kq01>}3K?hVtQ{pHrOSuMGr779(>b+~pO`<>3WD-U{T8!q)d zai;axwx2VE3RnJ#IGit?{jv9;yzsJP3LUoX-cqv+cd;mQLwv^FsIX+3`Dy;Z7k^Fe z%l+lKU)%2edumxfuDt&qR-|Nh3x3}Lye$LtR>rzx*y6H>r%1PZR zme-d5xMA1WCYzlykJU76|EzDfZp7`W{aAahB*{H1X8$(dGa3!6F>4 zw4bzgL~n54%x7qP_>7grg3T*?_g2jA%ylVCyzZrX?RC+@+<&}k?OGRplo>A5i|IJ` z!OEuo_~i>~a~l6=)Yts!pBk}8lW%INz0R4V=hNLc=des#!2smNK5NNuDF?T-Djh;qs&#Ay8mC?W-+OF@*&KTUvGXe zPd}(@cKrRGr`O&rk$*5H=E-vA$n$%Ww}%I8u9bZxbp61t#VS*_^u5oy)n9t@nVesY z5!bg%woQMV7}YcEJZA0@*eIx2;wsZJgLQSl%-MaJ$uHWCm83Rq+}P3Cckt??KDJbz zv?E7mJW@Jwcb+M?%?8o;TO(S^AHEH`d{$tZM4kK`hwBrsPgokdaD#>PjUbM-bAKDG zy?rBURq3AO-279vWp}P#*>|x+@U4|GXZEL;jc-DAKiM5xR_!&}ckh!OA#YZgnQz+2 zSh;fL32m(j?p9oUwT-rwl6UvMG`-F5bS6c@zoswz{@)n8cUdc!O%(I)x0c+Ub?ZWD zig;dFm#^UF52sD7pIn^Or(8X8Vun)J=DllGOwt^M z(3fX+ewET}w9Dq5v_#i$;!p9gtzMJ%R(PsEovgD1Rmch{pc_de~KddWNW<&?fl ztBi`5Z2IK2a^KFqzt?lxKw+{foan%-Ge)z4ZN#OJDAae#+TDMM!tqt_KHAziy4^ z1$6^j=C59{V#42fAL@_y%TNEi@I&_@dn;iNInNaKH$0m^&)XJTwoA)%@$yA4kB9!@ zNM9xGTXkao)F8igFWHxFIQ2&5?a|Bp^R~t4W=46=aMU-=?taOg_4LBp?DUMOr?`t% zLf7jTcZJ;35P8YDP1fM=hm~DRwM;E5KKCbXu8GmOdfI-*yvGek!;ef1=NA;0lYF&A zM9Y4+(+;WTC1T>PM?(L--2xALojFPStJFPGql#vh9!h)BzT#dI``IAA+G(;gV#@k! zubmWcO)0PGtPxEOh z#$Uc0Af-6}&6RJeY2}*~qgj92q^_(Zmz6a3AE>D0D?M$z>}Y_~cZH0NcBf0q@2`AcJ#Ed6 zZu`r15}6OT>|b54pfq)hu58oO-+x-x%)8LKjgM6<#m1%ju-Apxw*tc>yGl|y#g^H= zzEL21_Osv2TImI`z0SF^75``I2Be;KfAV5pkj=x6eRm#RFFtVHxca9x?n^2!#y@B|y6Mj`;YSCnlJ-5e`_mI5yfB)3 zG2^3kvkD(h`@{rUv~;c2IqJcIR~4K8)pdw->m2Kw^tt-5{AsO>)2GreS8MR7OMQ4H z963W^ZV&%kpVSKHzqU*H-Zb3ZyVT0`#wOS9H=De!Tr57g<(5^*)Qd3-s#wpioi%6e z4>{3n>C8Ga#>iqhhqXV;6r(>LTUI+`YmM!+>DMp5WqT6xMM~v@k>2P0EdJhehqrvP z6tR}_No8SaRY?10v?kX1?|b3&x91$cb1YEy5|=PyK5A0>q#HzNMUthTB3tvu@A;13BE~zgp%{v@JXZDAAspqv` z+-|;T(H^04E!}fl)pPeN*L(BCo)LSR=-iZ-2Q%ztSuz<6>}5 z4o{leeOE&hi|ywicqYsREIwp+}kN?w{U z<>ot!zFx85r1=Hc1~Jy%do9o_51!|g+Q*d|>J3vRO1-gKcNPf6^ec*gBxwi%Z$=bFP$SlFAkwKUva_}hCe zCoSD5?zsz&Ji0nNC5+Qj)WD;CV`Zq&As_FTxf33*`0UltcHQj4>4gWnH^>}%vdG0m zTwUz&laOzd!zV6Y;_BBLwA<$SHRJZ%5%YYDQzv-YSI&Rdm3!oRz2?*Cv+q@xq0(-r1^Kh^({Vdt$4|`r`?aQX?;GVo)w;$ zzv>Rxtj9|in4jJK?6Q-(AkSIRyWN{ytTwJ#)pSkZMDc-*NqiBLUR_a}Z8}%I+FyT~ zc=*fLzMuD$a4P9v(>gEu`M|PIN2)ivo$2m&joAD4q=|Y?lyPFl+DYODQJ+>8U0L?C zcy8FKZQF8F`$E!ZUYfRa*SCI+`x75z>}pJn)IGGQb%LnLe?2Ez-Dx4uzMZM&*LnPB zPN{G0+t;taYb~EQ{PkF{p@;X&w+$9@I&-Yc``U7OEULfhoSS3We0jP5?0YbDta%)g1;q)s9_)V^ zE%C?wvASTQ*?UE^vTX}@#@-2+^E!Ob_VB4=>-07SvogIYAVI+XWL0)W0de zQ>tFB__kqRRa6Aa#m_~}Zd<;CmpJ7;m?r4_wtm6AeJ}5Qobsk-N$0`_qg_+qToRIr z6SXe*_POGk>DR66H>`fjUHPOzP)f?nOt|U6gM=PQW46Y|MhVj_kw1U`7G8Ai*&^aX}ia&EFv5WckX?+U_qkA?r9F0${GLGTN}z(m@%f`s+%zD(%y%HkLJC1bY@V> zkgHs3<3U6Rix?1*S9);uCsXLiFw^)fge9OyzJ;6?AO;N?Sg-n}`&761T+h*CX zIKX||Ea~2rtIM>OPFcJwQRdO&!&QY}-~6gt_pNVt*7>!{+2CI92V15tfm!AInfq<( zZ*0j}Ya*|eE0*TVwOsV2q{X4?-SGvp6WD7G&TfEghv?C1Y?z>-q8IJDT}0|aRDA27 zHyIlbYqWFw9H=UJ&b0c_QOGWe11E$66m+W8!fcI-Q@Rsh@(DhW+u%IILJ-t72JH;F z4%&4QHBYA(G<FMl=TD0$s+k1M8liyfg#4QN+TfL3ZdH;jC8h z@u;}-q$P^azi;VTv~QnG%{|87vkv~=w9z`c{G;=W7yK-@CvGmORo`n|CL$)*mVCVL zC(E4t{eQ(4E?j6A7+yW~OC`IEoLt|uX=2qi7WdjYJLH`{Hot&uMao}Rc&u;To=;vi zKc7ywV_dazC1-d{VXOa@t8uEe_ZYdG^D{Cw9B+SIJELI(ccMIWY&1^7eAx)Af$Z|Nn9PV8q8tc_jsz_mfn;W#pRDtUoC}E?Bhc+^vZMds9z~ z$yk+e@Y{T75LT`9yY%(-_2lR0=4xnZeUe$~?h!n{{6N#|$NlzwplM<4@O3hZXLPPj z=HReiv0}x6XJ=>64VwM-gl0_K{E8K2A`%iE&FuWAzD=JprRCeVvh@k7ZF9M~xITP3 zt?#`2a^ja47vlogKMZM-HMl5knk537>9zTKCHT_@4t{t3<4bJJ*JqSJbTO6U1(ko^ zdOMG-JhegAvh{SfF#q*F7Kw**cJ12rB=kq{GM|anhu`o2zptiylKNfsnm<1ZL&C$8 z-`?6P@KwOJ`lF?W#)<6P_h)u(WH!6Z#ldml=H~QN<(XEcT6O<_-(S3GlhE0-XYERp z_ZT00{4(<>|ICc|i{}>Yb=}=9y86-P4ebkAwk=*5@cE66a-w@v$BqTRkK6xKv|c-5 zg20-%y;7iUWq%Dr) zM8@0&+uM(ROn7)_XR(0<&!){nds{9q_dk7@6RBXIZmE-)BK|1r{p#wLz54H4t9jRr)@(Ee z6+F(}pd%C?n6b>O`;}R_%0p+tlLIU$Ti1GIt;NpUeC7!X4t`py(bK|NAqCpFb}eZQ zXt?h#idzyr+c`{kWuY{0OQ8Y)*I(z@8oOA{XO2uS-}M|`%=&vb>Rf2GR~OtyQQDP zHhsTS+<(`kripWhG-KI;ch{8G@Czp}IV*htEf|uOmA$d6^!0>K#cFB=`#3eul{d!7 zflAv16^@Rz{6{yJq@SJD3OYRT_NhJ>OP$G_JGvOYH^jP50v!=kaFDa(tmVI7*Y}?~ zUuY)PD;^^s*1*Z*av)cB!wu%BHyf)IzctBmNX$=W@;R`Vv*T#QBe}$f9hN`z7`yok z)0y&KHyo9lm$dlLv}t1H&yMc`d8&wU&XjA{=ivmEP{ZeO%W=}zVIxt})AtrcqO50A2m3W1(j z6RI`!&kOZ(wi93r=CS;Hz5gHg=JfN^A4q;9xy#J(EKX*!XIoG%<^{r%%{|KrL2 zb`#5YO0Ls6cb;WN!+XIQ#_4^3jx4$V=b8Dw@JjhLA5Try4h#%zJUiQbckSD|yV!MJ z^h%j>J*u4VZ~IjwZ`VsTxxTXM$w{m;8-5E4Y*)E@{krp-zGn~SvK-m4`Nf7z6RFCF zcU7myJo+YDTFtJ*!N$V!;LXj=pU#;DFZVn8zW#r8NLbjSojYf~7cQ$7PG+5XpjBO= zuIFggBzE~4f!^L;@i@h62{%}d)b2aP&JnLr)AWHi>FMSlIyG(TEd4jTf@&J))qL`- zwA-;{2}}0%bw|VFYgNB@)c<(M?w@cLv?Kfh8;hFXoEL0S8@LXuDwt&nRlB;lx+-dF za?00!5j?U>(Vt81af5CE>w`CMa(GMLEOcjhs7XHM}rA zJJ(vgTTFM7{-apk`A-cQ(>W^UFqbVWnscp~qa)GLu;FG1gyo@MsMe{|9$g(j_N}TwFgE_ zN#!aVoyu-*O8s<8XgX(_qt5|PrZ)|9S&p5I^;b|=XMa?8_RJZd>)h#`J_pzsH=aDd zCG+x=ZaeAamR+24#F-9DHHjUzREoHFzWZ2AN4Q|c_q*j!ZL9g^?aq`v3Dtf5Z>nIm z>;Esy?T@bAeoyN>_wN12)-2pDu;GWz>E8igrGoe)M6xO)#cg6z*Q9>j_+8kd^3xI< zo`{&39?;^rd+~|u7Dfwl@bXT5&+X^ydvs;+@;hGUVaN7ze)(D|R)531ZbKB$Nk)FP zDd&wD-8q&lU+$bUV}HRzC-+IkQfd#1n9T3j7$0$tuls%b{;rsB|L@=1ejq$@&P)4} zW|gc(oK90aoC39@nwtJ`#$9m9N_;C6m7BXWLPIp>`qN8Qnp>h;*XdjdTcGD%V7kR& zwbRrKQEQwwEXi8I<{cokb8*uN)rl+~3M-%6pP#cQ@zc{eD(crK7vKB&_t~D}{WhQX zeXcW3-}iHigX;oCmJmksmI=qUb1|K_`>oT})n!or?oOCyJa;IIRQCb>x{ur+b|2IZ zTa%F^=DVC@3kQ?#s)nC^cg6R8VBI@+m1~vPVm8zuzgI+8JHcqO@kssZ(CRrwZLW zdpdYUmPliPL&JMk|9Li&kB{|UW_7XP@sQZ|aI(MM#$UZveQp|e7qP$OJ8I&|xVzrs zvM#Tp?fX5S-_*`oXySC)ji1T>=SlxJrG?+`6#IWEiK@P(DDa^Ag-y>9_y4qt;2Y&hTg+V%8#wtIEI-&TA+YwqY4wwU9C{Z?jnKG0e;t~R9$iUJSr zt8|O&yT!%DwP&n1+w@gnfn4?9uh$EY%a-48RTH1JWl_!T8rC!Gx*nw}uc%lW7;@m_ z$B$t@CpG5p`Iy3tM!jlsdcf4FS+aUMW77bU0UAxYHuUR>N#qt&FS8U(3dGp6+`9BBh zblTo`2X8-_6kHNtJY~b(EoP$s&p7DWjbuJcKX0%-N@h(-L%;U}v4)fcy>@PX;-}l6vERn{(E{5~^Yhrg_ zo0t3W&$4BYl^^6ucIz#kClq^Zs^0NgUeiu}d$alcv6<=fBE28(x_!adb;(f?gQ@zW zTH(h+Hf%bSx;&+NR`jHhbBh+N%yH#3-^x5o|Bt|f`m~)pcYd4nJMZqU)_CTvu3Zis z90gZDzg#~5(c10zHp%mIDy~-JEoO;%s=cdgSI;!B8C)|?U61`}H0NW;wU`DY`O}$Q znUk2Nxdk_FVqB8LGj-+anwfr6^rg>SeZMkzdDfII8VP~dd|x&^RgNMT?!{%3fkCpPHE}AMW3Fo3eWIfTi01V zS#*O&eBh283*N(-HzWhk#3RP)b$a|rWd^kyKE?UV{7*Gzw3M4zm>k;ncTN< zo0SFAp6Bz@rKah{X5IhrKxX-}WaR^fQ$h{CiiU5!lDep4I+Wvf=#yI{yEyy1-|r}yNaT+%@ntr%;}oKE3T>|yKp}Hx*Lq^s-8B8O*IhJ`FxQh*GbsKdxpY> zgo8{mwO_CPcxJv|Qcd?#ng-L_<^ykTZhrgwt#dnHt6k1EzGJz8t2F#yX-jE^>`GPj z(K;2OQ95ghVya}2U+}FvI;>~7m^Bm4e56jY1wX9XJ~#ENws!17-dQxTf6lu6SuOU6;0mX}1&bm)_lAY-+x3gLCE+;FxFB>SY|aa@TYz}<+b z`|Uw9|22cciUYSoAfGQ|z7Z^_cqZ^$pFYD;DyZLUdQN{8-3ax2)Iyzj$0l;%#$>>=2ei z9F04RpEo_14_gz#Shpj#SLs3oOVqRZPmWz%_EL$dvHt(}`tARJ_emHkDg9bJH-KeU zGlPD{gYWC!?|yIh+j94_DN8FCb2Ek>xM+Is>^t3j#z)%j`~N)EpB+0*YypQ07t@}3 zlcF!)|9$WKs~9a2&DtwLU=j^?ylVrV0Hkf5r!-4+# zKgqviOYU(jcN5IuVaoj`Uyv-dIP;J=W89KYbKln-k8`-NK7Rj)+!fn=1T)lGDl!j$ zdU_gkF3YX*$G^Y5z3r!aG0U2ZX&2Kw4dtun-aa{Nzo+M){r}JQw|A;W?Nt)k$5Hdr zJO0od%i=e-7x%P<_bLU*Gp%ZP?d<+ohpGPW>-djvHlKfFzW-;h(jpEGCSHXPpju;} z?1vYN`^_c_l=}g4$I{c%ZuL*Dd7B-7xb%8#`0^XaK2;=N-q@6o z+1z?Pa|+uQOQux~w*`vUKe+ZheUACP+*Q)gcg;NZy8N@L+CAU%%YWX^-!E%^zh?8_ zO;e@a%adE9FL9Qh&eZ7n*m!BjSN87m*Ma3JiV8J)S8iLk?Jiswed(IGn1iW;2jEx+q?d&2$bdB39B8qIcd zu1=jLy>ICbCyl&?Ii~M@ze$<~x51%O#phLkloiu1CO$^v^$K0{U3WC;<%Mrh^;socpM2S1{uT8~5C1t9iSe}> z`ugl|Pfk`33=B-vyt`$&-`q{}o1ZKc_^D@nH-~xU6nFc(OHxx)LG2{aGWo~i`+u~G z25`x+PMtD;=988Fb?TGD?(jT6zB;qgkx}=3_p;vZ?#wYzN9JxcSix8!m)3sA^3^=j{V z#HaE^=vu@pE{)cmJ9bF8_sKB+&t5tkRDk?=v-$j*3qgWiwsn7YlMkuWAD{fv{`;NcgKupX#_lfLS{L*zeYfk2R40z9Y+B8=;0n}+#l*qO?64}(0Cm4Q_xar zY0kv>jJ=yw9!?S5Hof+9&_vb1S8w?5ZfQ*Ee0ei%qWZRpZzu0OFjci~e?&?rKZ~cQ zXIPs?_!Q+|GvjaFi>Z86u}McNeA|*3>4o-UjG+gv_Bb>y5N!MwBq${)vn+Lnb)bh7 z7u){H+TSMo+Z|j!zpl%P%rdu{FP+M;C}c1+83J>XJUnzHQK zkw<<_pFSMqoII`EZ}O)_)qbmzel9rFId!t)#zjxHgO>cU)3)a;1T_XHuQ++~WZBdQ zetL1{$0sEkWwLZlH(8P;<>v6DNz+yD)nwU;=J`=}2Rdcm-rp||@)TDm*U_n}4>`3I z!uwu`MJ`I}5{X=7w9KaP-^WvvPE0xD`00T@|KBO92fbIE5PI467k0oAQl+brTmwF$TZLY~tMS!*(xJ;cv|(JuR(6-`?KdSp7Y(=EuYKYwzyv zzHT?Qi%H>fT**b(7dJPn?<#+vC(0r@d%n?Qmj%mSas+S|8;N+id9>E7Pz$CteIk51qJC)JwmU5nDG>DCNJnI}?|>$XLx%~`hH?o(0M zrP8OKDIJOHWDH{-{aEnzAiMmPiK?eWMA_Br?K}e8Ec&*5+Ta?wXz8cW%Oxxwn@>!% zd2nKmo8=>C0r|QePj!oqd=!X$v{bp~g5b`NrzVECsl-j$aADc^1%JO}M;38~M@7GNn}x^MZk-!q;aFfavD5j^91WEo z+a=bwcvD;5Qrbd9^zN>U37l3j-6GZVSi@@5^*Vul87ph9w(v8BoSUK=KJQ-jds`7P zv0MB5Qe=XIg5G3D$T+WC=bF~C>ywJn(Kxr+N_|?aZ$kIHYjJe-w$}{b^7`7^&FvHJ zRXpb1{kG_8X!uR-XBB5<)U)kc-K96ToHw~(q^oHEL394(>oHl@M)zC2u8nC)d73fr9S6Ol4Eo`$)`D z$gqt3plPG6%?)bBy}kDI`u=~VOO`HGw6(pPS6f>1@q(O*7^9zxrKM%W{yN)#UzXe7 z{J-x6zp$FmhUjZHCCZc8ry8pBm4C{}v6Z!yP3)pktjtd|bx({NVuUng@>_NCe?kqy?7 z9gm)79{&0H`R(b?-1}rQ96t=Oc+Zh{Ky1E&4(yeuAHBlu_C zkN&zZi=`rkTy?x6LJKS+VgS!|L^s0TMpjN`ts7oiEYo+T?hZ`SH5WJ zdfKTzZ$;%??|F|>wbxEv#XmdgE_-C)o|*}e!KmsMs-q=$|e>5+==U27ALX#JoP?$y3<*WXWur5)BlSqJ=)bZuWB&yDjsmw zut_=o-|Xi*mHYpvTs2VV)q3bL&*X{YwAHbvL`3y<y4FJQtHkc?eAV|-Rngf6*xX@RkwWT*Ls-U zqH@9G!?KmSmwGi4f^Jrcv7Xt-FJW+C-+I}X78(C%wKK#&Uh-+_!|bLh%2AOTuFX0d z-wUp9xalc%(`>S5^JI1PKq)z)hu0qY*L@QHowuI%ioELrDaL*8eeWssf5?lMvfDGU zJw;VRnEj{DBwls5sPBo>EKVGLbGQ6{Zv5%$2aW6pt_NSyVA5l%Vf_&Jd8XUDX+Co- zF1mHDJU=_TT{n8$jqCnxe6n5ZYo2PC9Rl4Y zl^d~ych|S&bu2%mgpSu8>yex$enOZ>xqm`<#*Ytfc8it@O=Gt?=KAXL?l0cIv{s9S zPm`-oq+M3AZ{7fagtVQgMpV;DG{(0$NpA~Pv`(c~( z8$QnSoF69ln!L*ss`Pr6eaXXI@W1>9o3+IWPd#JTIXrnSWb;u+n45j}yFGhstP?-H z_+$Nk4|C-v7|}S^Z$DYSQu7{5$7*L_S*Wwob=Tym0BK zr`oYK*Jp`e|6rRiQB^DB-nu2r9xESc4?HFG+~<^#X1U|9@9*Q4y=(U0o65V(Jm*G$ zviF`>ll~~QZ>m^#$oKxoz*TpfR|Fi`^!5yA^KIEZ`co&f-T%Ay>$T|DK2mxvXAUnn zP1M>|`B!EAj3=s0F}lUsL3!^UUhA6kp!jrP`ESJobNT1(nj&5EBX8B6dxx{XRp_ez z-@PPg!K0%^eI?$Y;7YMWPP+A-~Mk9fD?Y3zj}b`wo?u0D3n(i4{lkTn5%P{LNN<3ulq~RYIIqCNTP4AlImQ5e;HqW_VmK%4(=L?$qvlnFt&*Ms6sv2LgGQafMwxx@A>^yzo>XaI* zzS}dCcJ6v49`oe2?mO{`D`GT16e-&KYn)Cwo2&14!zlA}<}Ov<OFIca0GW}ajq>!c3Qk|1!%cIlX z%h@NZ+O6o)32ck0Tr^u=ci}>wzHmm%=koCQbDp47N-nBo8JM5~!Eo;+Nr8GWF! zAoH}rOrOZ*J~FwH_0`v1?+L^uMt8VZOMVjlufL4 zrXJ8t*%b6fBe!o$8Q1@UPaR^)wPIR--<^;MP%J84k#3Y?a(mvS`A2qbt^RqYw_Bt< zWDon9gO3tUxhOJgn*^~1|Gv15gXNL>rx2CTQ*WDZU!PZTyRYe*7$*nE{|+Blr}V4m&0C;6-?{I<$6QlgShOB17VB+Pva`*6F=`Vota_C%sp+* zv3r{mIGbC;7iyUE|6a{>JbbFi!>MzWDi-tSojg3PS+voZm1FIV{jBTP)Uif?W6w+Q z4Ad*yE>l#rDc15vkw?}td-30^SL@u|DY*X0wcP0<0YOumSvRL899zR0vA1wN%XVek z-p>bhciu>`3M$RuHoNw!*xF2=X;m1j3}f?(*Z>wQJ|@+Og6#4?fA;E07?p4c*;R?{ z6}mL>^{b6+*1>I8oz)g;bm-~c-^+A3zjA9I)4i{kT>kuP{>0|Jos;irLck}99BsV= zGoEa`A+liI-PP?0CsxJ!RX$x7yFKCRx~$gNZ({FcJ}GG>&s@TzUt=tKxcyG&cCR@) zSIH>dwQcdBh;^1fu9cyJm6?}}(2?)Evyc7AF0y%8GK z`YCqxgkQfGiLFzw>JDa=*_*uHHk{>_T;rzP8JpxP-ZXkjOta&8EEm_x+ugQs$rP{E zEUVLdzt-Q__djCKrpqm%Z;wyPR9;H2a4Cz2X;3&qaLDJ{ns6zp0f~*s$1#QEdIqmrp)g zZ{KUhI>S7 zXVDpaWI?g`lB07|U!R?6E%wql!tZd!EuLS&p!8m^|PuS!84+_p_K|C3$jTs_<4;!A}WYMvF|-_^Jy z{e;)%X*RiB^LFxEDvK|^6yB+R`PsvLsnOX=?`z(;ioB0(Kl`2Mpz)QstIqb*yA zY~1p=(4R3ue2<&v-hCSE<-I!&rD;^uDOTyvZ+vMV*^;{We}bH8)@*IJG{*iVQm!-l zB)O+4e0Pwyv8a?-_v2UXjJ!#HXXmwfcZWZk^zih?GJj;qm(Hqn`qPFV z6W7j|`rrG@O)-x3T5I-f*Oz-`$;}>LFI8)%f9%oJ+W~biL{*mCb*^kZs&W6j%C+<_ zTc2Ldn|Z%H>ALid!aM&(CKa43{LEl1w%z86u)n<#PipT0}MkkKAJdD#yPb*ZysP_*Ud;1LOCMF>><#j>7*M4n4nI@NU8HfDc!~ zKU_PdTJh}XREMbPQMtY|cS-rzE@fFgO^0Ri?x?rrLLQMV@9*AV?-XDDxbvrG#E!IT z-+hbBCSx}f7Dv_`3%uKM!u`JX0AJMD%1I+$1RDJ8!_!0tBysaSb5!Ho1JBl zrs8(rQfje#|FItzC1+@C4H0?JnzOlS-yGgs-Ann`y1zH(TW~?|f&8Ied$*d_)$A`^ zp>waXRQny%y??rGOH>yBUn8^l?C!@&n^*07a403`d|XD@$!^0lHfxT=+wepdU;5zm zPA){wl&k9OWeLLt{O7n^re~f$l&X1RwjI-5q2CFg{`@qEJ(lj8KYQEK2RF9G?D^$q z_)Gf!xx-ro-Z&MVXF{Vj|y{VL`_4zJ!>~l$3=~Xue(Slr?E6X`FnAUbU z96dL6hDoySFCMSsJJ{vj`yzL)n;F%xv;Gf5?5?8UX2;^IePni(`5w48)!yyYg!##z zCvGnMYpy0yT6k%V?$MBhtAFDer|3TCyt z8`iMpb5h}&9TG>6#v15mi|MVpXSS{Llv(n%J?l=?muiN+jcz`B-^=yb8>5)&%(tlp zOK;Bd+i7+-Zz;dD8PChZ`}!E&B8#%OKY2TSWA!idx8I6yF3VZBwD8f^`i8C4 zKW!$p;nkB{gQqB;m0MJkas8D4l3K1|EvD?-OBabcHg*f@4Gz6yfXR7Ly2GMsSmHs(^)SUe6oFZ=Zw4s z|E}!`KNDfXbiS&q`w1T6KZ|?G_9G~AlB~h~? z=?I)$Qfi9dhMM4fD+V*rQ~uxaMn3>`t0>Cso_n?r?hBf_%*DmJa zdz^Rpp!)I0^6pPI9u<>*u$(D#@$!cs*VHUIBb~SHo3`{ZV+T#CC+-hS^tF|`Kn;;C zMob%Pk15(`8?$}$wTQhu!}Z4Jw{{+Skv*Mv&;DPqS#oOAQ&#T6lj-UcZJ!90NaZ^j zo!PWHfoqdl?P9BZ8@BW34@~g3I_RRD?9!_J7#{;=lezqx<3H{XxTP5ON=ZQCmSgWAz) zNs}_gqfNE6`SaD6DjocGcYeXYw%Q#wYJ%>*^1;E!lQS~Kruq9g6)ktqY1;DgW<+}2!W~pDEtX}x& z2&asFoy^i_6OO0PuWeIy?|Tq^|CgB0f8~v@Z!HgVS}=L$hOjjejZ2oOsHmwaDJn9i z&#yIm^ytxrrQYJ3uU?mo|Kao`S8wO0khr+MO`A4FZTj*yzkc_{O(CIyO_LrK9XTS+ z^Efr@c|+*TqSF^G_2f2QJ>9%&lhBmVqmpZUcXe@v9656Bh@pF=C+8}kUoMjuE?RSF zisb8f)$l1d|CjAny>`zkP3~H7(TXEWd|md0Jn3KYbFIPCX^VHo)r*z?jGeshsmwn! z$&d+GbT|Gon7;o@SC`h2unb=fYrX3-kk)y2C)7u84#<=>KhG0`dL^TI{Tdb0NHDy@{(+`CLc z_v{F0=3EowsCxs-YznA`+u(A zUD^|;++qG`^|}3M#7l#gqXrqu;Pnz2u`EfCb{~A>u$ohuC4{m4V!{@m;8h!nAG77? zTc&nfr+PeUtu?GTa5U}m+Ps+e|E@2N-x@MaB=^WMTh*&S{T{yI{;hoX$>aa2uF}_& z`YoqgmITQE|DVlc8`84%XS+eci{2M$7Mu7hi2N;i_`PLzU|e388@$@+tP6tZ}s-v!}DxeBh2=RTlq)lUdgbG z^V`eHzc-{|Z}s;i|BLZw{yTBb__AAf+n2km)sudFn7-lrfw-Dao`PaLZ(1kLob3~1 z`Om7*qHX!TUZ?-sEoOh))PK+V@;z~SrLFRh*llJj|7kEe3f`DuDRz?i`NKK;u0Oi< z@9xC8S$?+XPZ<8W6}OCQ{XHWY?QL`S*=lc@7V_{y`|W^~C-0YLZhtT>Q$KNbp}6SV zmYse(-}+n?>oK_=oM%`XCYrh^xN2Fg`_7fT8mDABzp(z>xoBl-#y$p)4bD_2l%)lOJbJUSso5Ch@P{x3m7IKRn&NS8GoG zFU#ZMZ?wP8^lq&A%zeuLxLE!Mi8YoHFHg^(|IB-4e1!0J{pv;M_Z)bkZT@qUQNrc3 zCyj$e*Ga(E0!(MR6e9L(nbeyzr{(wU37W5;zpU{5na{^Ret*Drf6mWrpL-{apGbZ> zx2yEEj{d~ud@>dZl|6gh=5@O~J|*;fckS=8_`14%M<2F|$8Go{n6+QKT*v(9B^~~& zPjvM^oSkEFfBRvB^ZWMb#J4>){$aE%?dP|USs_P%?wcv8xozJR!_T$VCsG;@X?_2> zX<6Ehk|&p4rN7N=S-MX&`1p@ye&&1578yUgQ58|1_|0d2rR(wO(;l8Tw%>mvd`Hc$ z{6}YAs@?feS-9ZS4C5V|F)lN;R`>4=v?M^x$oyscK_w>UHPvT@zzyp>3%s|_{)DzP2P-`!H$JHHLv#zy4k?`(}w?yShEDLTi_oQ&V9?&v>lS;C^7 z|I&VLuHULXdDEY(CuUmQkI|FQH@nf0P=528m8({%Rw&CVK@I!D8)rT$7qa)MsUw`5|q`}KG$6W5b_>=`NT7Q4^_@h(&zt}Qut|hCoSU#sjnAj7! zm&%r>4c7lz{mkJK-C~k&j0-G-j_vPA3{IbUDtN-*%hewa zn_lj%wyG4#vOyI}}^2l4=fB*=*Ke%%A=JWxzK6)0@s#e^35(?N531ZtsoXxb@usmh;VM zJya@Dv^SD{j#XQ+zsQ4y^Z(0zU($d4q%!M{q92o=iT!2O_%q+S;Kj|SJCe_Qe(}@$ zo_cm%?+f#DcT%gB&DNYgdMl@R`r9cVJ!WR=9jgEFB_(Wk+*eJegMtRuoX_@NclopX zC->90hqiA>d6-tbmFDn zzpnSY$L!Jtl{GduO?h9Z-D{L{vvIpKyKP_on?o(kuX*3s(8ZJCAtZD2Q-z13U&-mu(uI^X8yv)gSq z+^E_3Li3GTe2tfJc~$HUZNZaMpWl|Ym~{5z^NQ;Dk`Hw9(TXXP+PU`@i@phxg5}&oj4(@VuMbacc6jyxU(MUM;_UuaVt&x?=8M z;|bBs7uU+ao3l%LfBaUV^ZM_4CpJ&^jn&y|Sbo!uY1^&#Y3>K^JPi3@$G3}h`qNq6 z5^OX5UyDBc7pFPn+oiNQg*U(MDE8@!_4&3l*^cY;wFh?=Rv#XziA-l}&eN=KNdmc2>gFl~3MPHU8JV*ELsv zLHH?wJzIWq=CPhm%V!cH~)Y`oGOOA>u&w zMCL7xj(hVS*q+w@``vhY&M~RQYo5Fpey4PqMf?3>-^v>Ys~5jE{d(W=?Eb#+L?P_bHY~F z*KoZK)^nfqm&3Vqkn;?f6ldk(54s4r6~J_$=6$QQ{wTDoSdZM(nLpk2S<=G^A9>urRumrvI%UKz;Jqn>y{%~(Cp@aez%4-en$o-lD%%gwgwXZPud&HUey z?{as;yyZ-HyZ`h?@ji4`w@;L?Pte(POl9Hp|4Lc2(<;7BpV{)A`Tpjdb(>@@B--}hk!T}Rzb+I~kPn>Eh$UNCnFOB`+ovnXc{=QlC^_4#d zM+D2+&NcIA+Vr z_kZ%Aa;Nan1#9*Q6+WBe?i*bBed|tc>uq7b&$2V0-DGZdY>83jnchnq&RKnTy?#{n zg6zf6XZtHJGeoCPUAcbGrpM7uZtM7S4N& zEsxctEL!&4&9iHrvHX%1i+{Yidp(Y&_mCKG?x!mj%y-1!nq0DCzAGpje3{{MELU*6 zamo7ayH>F(PK(DdE#uB8?XWFE~dmOkQRQZqS29x6+gsaP7D$ zki`gA3mRa5!LfpgPuBSCLfELhi8N#AfwOG^+ySs5cPDoT(SjEdYVw5~mJmjBX(wHfda#2yWEn#bkRCLRD*_J8wJK$LE57f+ zd%e3RPP2O#i3v>Q;P}5|O5R6zDUD0!FX#Wb>Cf4{#V))478~P3`I(C&SGR2S<-WCi z+KPY!Z<*I#lX}VFa+pQ%W%7!@-0>`n9QQ2>V{||Kply9d>7Qww|2f|Nu+6ICzZ2?x z+56(0i(L$<8c|O*UWpi}b4wP?wOADTWcL01>HRJ9XT@h7*J}JLCi!c6{mwxcb^K2T-}ev+p-Vm zXUs8i`Pe1Hx%bN@?~Mfy4|z!Cz4g{s`fww;|K=s@@^?AUlf$P)ZM?NX%j%J+fjYOL zQDDHwV@HleSXX{Z0d40izgsH)?afVPNWTLl#IC%79K8-F|<|L@W#zMq|Yp7_b= z?Z2v};AHbjKzmB9c;qaTOrd{|?f)^C->X#LmVaMP(e_&2789oG@9&QO`1ttam0H zQfTHBr4J9=<&zFJu})JzA^l_`yIci>ZPgcs`L|Y9w9IeWuT* z5~fU>_UQlL{r^EHP~Y;~@GC)jDbtgZ-;ew4w{ewUSm5~L`g(cLai#3?H5WedRJPYV z(MnX$cCmi@>B5_X|Id8+bkXCD=KuH~&+Y$ro;u|<2eKu>qVA8ysZ*y4zPt#WGG)qx z&FAe_7w-T6@Ar*uxwB(==T|

    EZ8x{K7(K!-NA2HBYtUH%`t|c_i|-cGap?hd}3H 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 literal 0 HcmV?d00001 diff --git a/akka-docs/images/akka-as-library-4.png b/akka-docs/images/akka-as-library-4.png new file mode 100644 index 0000000000000000000000000000000000000000..e9adb4e10402430788f2ce3280092cf8b24df4db GIT binary patch literal 39979 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�~hvN0CS8pz#^)qGjlgCf0 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 -^ + .. image:: ../images/akka-as-library-1.png + + Actors as services with Software Transactional Memory (STM) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As in the above, but with the addition of using the STM module to allow transactional memory across many Actors (no persistence, just in-memory). -``_ -^ + .. image:: ../images/akka-as-library-2.png + Actors as services with Persistence module as cache ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As in the above, but with the addition of using the Persistence module to allow transactional persistent cache. This use case scenario you would still use a regular relational database (RDBMS) but use Akka’s transactional persistent storage as a performant scalable cache alongside the RDBMS. -``_ -^ + .. image:: ../images/akka-as-library-3.png + Actors as services with Persistence module as primary storage/Service of Record (SoR) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As in the above, but with the addition of using the Persistence module as the primary storage/SoR. In this use case you wouldn’t use a RDBMS at all but rely on one of the Akka backends (Cassandra, Terracotta, Redis, MongoDB etc.) as transactional persistent storage. This is great if have either high performance, scalability or high-availability requirements where a RDBMS would be either single point of failure or single point of bottleneck or just be too slow. If the storage API (Maps, Vectors or Refs) is too constrained for some use cases we can bypass it and use the storage directly. However, please note that then we will lose the transactional semantics. -``_ -^ + .. image:: ../images/akka-as-library-4.png + Actors as REST/Comet (push) services ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can also expose your library Actors directly as REST (`JAX `_`-RS `_) or Comet (`Atmosphere `_) services by deploying the ‘AkkaServlet’ in your servlet container. In order for this to work in each define a so-called “boot” class which bootstraps the Actor configuration, wiring and startup. This is done in the ‘akka.conf’ file. -``_ -- + .. image:: ../images/akka-as-library-5.png + Using Akka as a stand alone microkernel --------------------------------------- Akka can also be run as a stand-alone microkernel. It implements a full enterprise stack: -^ Web/REST/Comet layer ^^^^^^^^^^^^^^^^^^^^ Akka currently embeds the `Grizzly/GlassFish `_ servlet container (but will soon be pluggable with Jetty as well) which allows to build REST-based using `JAX `_`-RS `_ and Comet-based services using `Atmosphere `_ as well as regular Web applications using JAX-RS’s `implicit views `_ (see also `James Strachan’s article `_). -^ Service layer ^^^^^^^^^^^^^ @@ -82,13 +79,14 @@ The service layer is implemented using fault tolerant, asynchronous, throttled m Persistence layer ^^^^^^^^^^^^^^^^^ - Implemented using pluggable storage engines for both partitioned distributed massively scalable storage (like Cassandra) as well as single node storage (like MongoDB). A different storage and gives also provides different consistency/availability trade-offs implementing either Eventually Consistency (BASE) or Atomicity (ACID). +Implemented using pluggable storage engines for both partitioned distributed massively scalable storage (like Cassandra) as well as single node storage (like MongoDB). A different storage and gives also provides different consistency/availability trade-offs implementing either Eventually Consistency (BASE) or Atomicity (ACID). Monitoring and Management layer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Providing both JMX management and monitoring as well as w3c logging. - ``_ +Providing both JMX management and monitoring as well as w3c logging. + + .. image:: ../images/akka-as-kernel.png Use BivySack for packaging your application ------------------------------------------- diff --git a/akka-docs/intro/index.rst b/akka-docs/intro/index.rst index c3b167fbd7..ea27f70ffe 100644 --- a/akka-docs/intro/index.rst +++ b/akka-docs/intro/index.rst @@ -9,4 +9,5 @@ Introduction getting-started-first-scala getting-started-first-scala-eclipse getting-started-first-java + deployment-scenarios From 4f4af3cda879d04e0956c0874d6df1076f04cfb1 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 3 May 2011 17:07:21 +0200 Subject: [PATCH 132/233] Moved dataflow from pending --- akka-docs/{pending/dataflow-java.rst => java/dataflow.rst} | 0 akka-docs/java/index.rst | 1 + akka-docs/{pending/dataflow-scala.rst => scala/dataflow.rst} | 0 akka-docs/scala/index.rst | 1 + 4 files changed, 2 insertions(+) rename akka-docs/{pending/dataflow-java.rst => java/dataflow.rst} (100%) rename akka-docs/{pending/dataflow-scala.rst => scala/dataflow.rst} (100%) diff --git a/akka-docs/pending/dataflow-java.rst b/akka-docs/java/dataflow.rst similarity index 100% rename from akka-docs/pending/dataflow-java.rst rename to akka-docs/java/dataflow.rst diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst index 2fdac42c34..c968af9c16 100644 --- a/akka-docs/java/index.rst +++ b/akka-docs/java/index.rst @@ -7,6 +7,7 @@ Java API untyped-actors typed-actors actor-registry + dataflow stm transactors remote-actors diff --git a/akka-docs/pending/dataflow-scala.rst b/akka-docs/scala/dataflow.rst similarity index 100% rename from akka-docs/pending/dataflow-scala.rst rename to akka-docs/scala/dataflow.rst diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index 780e7b7c25..99d460aa94 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -8,6 +8,7 @@ Scala API typed-actors actor-registry futures + dataflow agents stm transactors From 131890fc428a7f38c5305229e881afc6cc988b58 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 3 May 2011 17:10:26 +0200 Subject: [PATCH 133/233] Cleanup --- akka-docs/java/dataflow.rst | 1 + akka-docs/scala/dataflow.rst | 1 + 2 files changed, 2 insertions(+) diff --git a/akka-docs/java/dataflow.rst b/akka-docs/java/dataflow.rst index a5f1929431..429b10ce19 100644 --- a/akka-docs/java/dataflow.rst +++ b/akka-docs/java/dataflow.rst @@ -138,6 +138,7 @@ Shows how to shutdown dataflow variables and bind threads to values to be able t Example in Akka: .. code-block:: java + import static akka.dataflow.DataFlow.*; import akka.japi.Effect; diff --git a/akka-docs/scala/dataflow.rst b/akka-docs/scala/dataflow.rst index c935537cae..8b19b0703d 100644 --- a/akka-docs/scala/dataflow.rst +++ b/akka-docs/scala/dataflow.rst @@ -185,6 +185,7 @@ Shows how to shutdown dataflow variables and bind threads to values to be able t Example in Akka: .. code-block:: scala + import akka.dataflow.DataFlow._ // create four 'Int' data flow variables From c0cecfc716962f88bebb997474688999a69d20e5 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Tue, 3 May 2011 12:50:18 -0600 Subject: [PATCH 134/233] Bring Future docs up to date --- akka-docs/scala/futures.rst | 66 ++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/akka-docs/scala/futures.rst b/akka-docs/scala/futures.rst index 4bea4fa389..8d8c2771f5 100644 --- a/akka-docs/scala/futures.rst +++ b/akka-docs/scala/futures.rst @@ -16,11 +16,9 @@ Using an ``Actor``\'s ``!!!`` method to send a message will return a Future. To .. code-block:: scala val future = actor !!! msg - val result: Any = future.apply - // or simply - val result: Any = future() + val result: Any = future.get() -This will cause the current thread to block and wait for the ``Actor`` to 'complete' the ``Future`` with its reply. Due to the dynamic nature of Akka's ``Actor``\s this result will be untyped and will default to ``Nothing``. The safest way to deal with this is to cast the result to an ``Any`` as is shown in the above example. You can also use the expected result type instead of ``Any``, but if an unexpected type were to be returned you will get a ``ClassCastException``. For more elegant ways to deal with this and to use the result without blocking, refer to `Functional Futures`_. +This will cause the current thread to block and wait for the ``Actor`` to 'complete' the ``Future`` with it's reply. Due to the dynamic nature of Akka's ``Actor``\s this result will be untyped and will default to ``Nothing``. The safest way to deal with this is to cast the result to an ``Any`` as is shown in the above example. You can also use the expected result type instead of ``Any``, but if an unexpected type were to be returned you will get a ``ClassCastException``. For more elegant ways to deal with this and to use the result without blocking, refer to `Functional Futures`_. Use Directly ------------ @@ -34,7 +32,7 @@ A common use case within Akka is to have some computation performed concurrently val future = Future { "Hello" + "World" } - val result = future() + val result = future.get() In the above code the block passed to ``Future`` will be executed by the default ``Dispatcher``, with the return value of the block used to complete the ``Future`` (in this case, the result would be the string: "HelloWorld"). Unlike a ``Future`` that is returned from an ``Actor``, this ``Future`` is properly typed, and we also avoid the overhead of managing an ``Actor``. @@ -58,11 +56,11 @@ The first method for working with ``Future`` functionally is ``map``. This metho x.length } - val result = f2() + val result = f2.get() -In this example we are joining two strings together within a Future. Instead of waiting for this to complete, we apply our Function that calculates the length of the string using the 'map' method. Now we have a second Future that will contain an Int. When our original Future completes, it will also apply our Function and complete the second Future with its result. When we finally await the result, it will contain the number 10. Our original Future still contains the string "HelloWorld" and is unaffected by the 'map'. +In this example we are joining two strings together within a Future. Instead of waiting for this to complete, we apply our function that calculates the length of the string using the ``map`` method. Now we have a second Future that will eventually contain an ``Int``. When our original ``Future`` completes, it will also apply our function and complete the second Future with it's result. When we finally ``get`` the result, it will contain the number 10. Our original Future still contains the string "HelloWorld" and is unaffected by the ``map``. -Something to note when using these methods: if the Future is still being processed when one of these methods are called, it will be the completing thread that actually does the work. If the Future is already complete though, it will be run in our current thread. For example: +Something to note when using these methods: if the ``Future`` is still being processed when one of these methods are called, it will be the completing thread that actually does the work. If the ``Future`` is already complete though, it will be run in our current thread. For example: .. code-block:: scala @@ -75,9 +73,9 @@ Something to note when using these methods: if the Future is still being process x.length } - val result = fs() + val result = f2.get() -The original Future will take at least 1 second to execute due to sleep, which means it is still being processed at the time we call 'map'. The Function we provide gets stored within the Future and later executed by the dispatcher when the result is ready. +The original ``Future`` will take at least 1 second to execute now, which means it is still being processed at the time we call ``map``. The function we provide gets stored within the ``Future`` and later executed automatically by the dispatcher when the result is ready. If we do the opposite: @@ -93,11 +91,11 @@ If we do the opposite: x.length } - val result = fs() + val result = f2.get() -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. +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 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': +Normally this works quite well 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 @@ -109,14 +107,14 @@ Normally this works quite well as it means there is very little overhead to runn Future(x.length) } - val result = fs() + val result = f2.get() Now our second Future is executed concurrently as well. This technique can also be used to combine the results of several Futures into a single calculation, which will be better explained in the following sections. For Comprehensions ^^^^^^^^^^^^^^^^^^ -Since Future has a 'map' and 'flatMap' method it can be easily used in a for comprehension: +Since ``Future`` has a ``map`` and ``flatMap`` method it can be easily used in a 'for comprehension': .. code-block:: scala @@ -126,14 +124,28 @@ Since Future has a 'map' and 'flatMap' method it can be easily used in a for com c <- Future(a - 1) // 5 - 1 = 4 } yield b * c // 6 * 4 = 24 - val result = f() + val result = f.get() -Something to keep in mind when doing this is even though it looks like parts of the above example can run in parallel, each step of the for comprehension is run sequentially. This will happen on separate threads for each step but there isn't much benefit over running the calculations all within a single Future. The real benefit comes when the Futures are created first, and then combining them together. +Something to keep in mind when doing this is even though it looks like parts of the above example can run in parallel, each step of the for comprehension is run sequentially. This will happen on separate threads for each step but there isn't much benefit over running the calculations all within a single Future. The real benefit comes when the ``Future``\s are created first, and then combining them together. Composing Futures ^^^^^^^^^^^^^^^^^ -The example for comprehension above is an example of composing Futures. A common use case for this is combining the replies of several Actors into a single calculation without resorting to calling 'await' to block for each result. For example: +The example for comprehension above is an example of composing ``Future``\s. A common use case for this is combining the replies of several ``Actor``\s into a single calculation without resorting to calling ``get`` or ``await`` to block for each result. First an example of using ``get``: + +.. code-block:: scala + + val f1 = actor1 !!! msg1 + val f2 = actor2 !!! msg2 + + val a: Int = f1.get() + val b: Int = f2.get() + + val f3 = actor3 !!! (a + b) + + val result: String = f3.get() + +Here we wait for the results from the first 2 ``Actor``\s before sending that result to the third ``Actor``. We called ``get`` 3 times, which caused our little program to block 3 times before getting our final result. Now compare that to this example: .. code-block:: scala @@ -146,11 +158,11 @@ The example for comprehension above is an example of composing Futures. A common c: String <- actor3 !!! (a + b) } yield c - val result = f3() + val result = f3.get() -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 (note that we don't block to get these results!), they are being added together and sent to a third actor, which replies with a String, which we assign to 'result'. +Here we have 2 actors processing a single message each. Once the 2 results are available (note that we don't block to get these results!), 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 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, for a subclass T of Traversable, T[Future[A]] into a Future[T[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, for a subclass ``T`` of ``Traversable``, ``T[Future[A]]`` into a ``Future[T[A]]``. For example: .. code-block:: scala @@ -161,23 +173,23 @@ This is fine when dealing with a known amount of Actors, but can grow unwieldy i val futureList = Future.sequence(listOfFutures) // Find the sum of the odd numbers - val oddSum = futureList.map(_.sum).apply + val oddSum = futureList.map(_.sum).get() -To better explain what happened in the example, Future.sequence is taking the List[Future[Int]] and turning it into a Future[List[Int]]. We can then use 'map' to work with the List[Int] directly, and we find the sum of the List. +To better explain what happened in the example, ``Future.sequence`` is taking the ``List[Future[Int]]`` and turning it into a ``Future[List[Int]]``. We can then use ``map`` to work with the ``List[Int]`` directly, and we find the sum of the ``List``. -The 'traverse' method is similar to 'sequence', but it takes a T[A] and a Function T => Future[B] to return a Future[T[B]], where T is again a subclass of Traversable. For example, to use 'traverse' to sum the first 100 odd numbers: +The ``traverse`` method is similar to ``sequence``, but it takes a ``T[A]`` and a function ``T => Future[B]`` to return a ``Future[T[B]]``, where ``T`` is again a subclass of Traversable. For example, to use ``traverse`` to sum the first 100 odd numbers: .. code-block:: scala - val oddSum = Future.traverse((1 to 100).toList)(x => Future(x * 2 - 1)).map(_.sum).apply + val oddSum = Future.traverse((1 to 100).toList)(x => Future(x * 2 - 1)).map(_.sum).get() This is the same result as this example: .. code-block:: scala - val oddSum = Future.sequence((1 to 100).toList.map(x => Future(x * 2 - 1))).map(_.sum).apply + val oddSum = Future.sequence((1 to 100).toList.map(x => Future(x * 2 - 1))).map(_.sum).get() -But it may be faster to use 'traverse' as it doesn't have to create an intermediate List[Future[Int]]. +But it may be faster to use ``traverse`` as it doesn't have to create an intermediate ``List[Future[Int]]``. This is just a sample of what can be done, but to use more advanced techniques it is easier to take advantage of Scalaz, which Akka has support for in its akka-scalaz module. From 13e98f0fa7b2654f99bd5d2206ee23fdab76730c Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Tue, 3 May 2011 13:08:25 -0600 Subject: [PATCH 135/233] Add exception handling section --- akka-docs/scala/futures.rst | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/akka-docs/scala/futures.rst b/akka-docs/scala/futures.rst index 8d8c2771f5..331bb1cdba 100644 --- a/akka-docs/scala/futures.rst +++ b/akka-docs/scala/futures.rst @@ -198,12 +198,17 @@ Scalaz Akka also has a `Scalaz module `_ for a more complete support of programming in a functional style. -Exceptions (TODO) ------------------ +Exceptions +---------- -Handling exceptions. +Since the result of a ``Future`` is created concurrently to the rest of the program, exceptions must be handled differently. It doesn't matter if an ``Actor`` or the dispatcher is completing the ``Future``, if an ``Exception`` is caught the ``Future`` will contain it instead of a valid result. If a ``Future`` does contain an ``Exception``, calling ``get`` will cause it to be thrown again so it can be handled properly. -Fine Tuning (TODO) ------------------- +It is also possible to handle an ``Exception`` by returning a different result. This is done with the ``failure`` method. For example: -Dispatchers and timeouts +.. code-block:: scala + + val future = actor !!! msg1 failure { + case e: ArithmeticException => 0 + } + +In this example, if an ``ArithmeticException`` was thrown while the ``Actor`` processed the message, our ``Future`` would have a result of 0. The ``failure`` method works very similarly to the standard try/catch blocks, so multiple ``Exception``\s can be handled in this manner, and if an ``Exception`` is not handled this way it will be behave as if we hadn't used the ``failure`` method. From 85bf38dcc6d3d33bf8523f82f9b97981b7971d09 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Tue, 3 May 2011 14:19:20 -0600 Subject: [PATCH 136/233] Enable continuations plugin within sbt console --- project/build/AkkaProject.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 4336f5a661..1c3520ada0 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -10,7 +10,7 @@ import sbt._ import sbt.CompileOrder._ import spde._ -class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { +class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) with AutoCompilerPlugins { // ------------------------------------------------------------------------------------------------------------------- // Compile settings @@ -26,6 +26,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { override def compileOptions = super.compileOptions ++ scalaCompileSettings.map(CompileOption) override def javaCompileOptions = super.javaCompileOptions ++ javaCompileSettings.map(JavaCompileOption) + override def consoleOptions = super.consoleOptions ++ compileOptions("-P:continuations:enable") // ------------------------------------------------------------------------------------------------------------------- // All repositories *must* go here! See ModuleConigurations below. From 7afe35c042f3facc0f8c4a097ac703fe1697d43c Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Tue, 3 May 2011 14:34:28 -0600 Subject: [PATCH 137/233] Begin updating Dataflow docs --- akka-docs/scala/dataflow.rst | 76 ++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/akka-docs/scala/dataflow.rst b/akka-docs/scala/dataflow.rst index 8b19b0703d..9ffbe95bdb 100644 --- a/akka-docs/scala/dataflow.rst +++ b/akka-docs/scala/dataflow.rst @@ -4,9 +4,7 @@ Dataflow Concurrency (Scala) Description ----------- -**IMPORTANT: As of Akka 1.1, Akka Future, CompletableFuture and DefaultCompletableFuture have all the functionality of DataFlowVariables, they also support non-blocking composition and advanced features like fold and reduce, Akka DataFlowVariable is therefor deprecated and will probably resurface in the following release as a DSL on top of Futures.** - -Akka implements `Oz-style dataflow concurrency `_ through dataflow (single assignment) variables and lightweight (event-based) processes/threads. +Akka implements `Oz-style dataflow concurrency `_ by using a special API for `Futures `_ that allows single assignment variables and multiple lightweight (event-based) processes/threads. Dataflow concurrency is deterministic. This means that it will always behave the same. If you run it once and it yields output 5 then it will do that **every time**, run it 10 million times, same result. If it on the other hand deadlocks the first time you run it, then it will deadlock **every single time** you run it. Also, there is **no difference** between sequential code and concurrent code. These properties makes it very easy to reason about concurrency. The limitation is that the code needs to be side-effect free, e.g. deterministic. You can't use exceptions, time, random etc., but need to treat the part of your program that uses dataflow concurrency as a pure function with input and output. @@ -17,18 +15,32 @@ The documentation is not as complete as it should be, something we will improve * ``_ * ``_ +Getting Started +--------------- + +Scala's Delimited Continuations plugin is required to use the Dataflow API. To enable the plugin when using sbt, your project must inherit the ``AutoCompilerPlugins`` trait and contain a bit of configuration as is seen in this example: + +.. code-block:: scala + + import sbt._ + + class MyAkkaProject(info: ProjectInfo) extends DefaultProject(info) with AkkaProject with AutoCompilerPlugins { + val continuationsPlugin = compilerPlugin("org.scala-lang.plugins" % "continuations" % "2.9.0.RC3") + override def compileOptions = super.compileOptions ++ compileOptions("-P:continuations:enable") + } + Dataflow Variables ------------------ Dataflow Variable defines three different operations: -1. Define a Dataflow Variable +1. Define a Dataflow Variable (with a timeout) .. code-block:: scala - val x = new DataFlowVariable[Int] + val x = new DefaultCompletableFuture[Int](5000) -2. Wait for Dataflow Variable to be bound +2. Wait for Dataflow Variable to be bound (must be contained within a ``Future.flow`` block as described in the next section) .. code-block:: scala @@ -40,32 +52,47 @@ Dataflow Variable defines three different operations: x << 3 -A Dataflow Variable can only be bound once. Subsequent attempts to bind the variable will throw an exception. - -You can also shutdown a dataflow variable like this: +4. Bind Dataflow Variable with a Future (must be contained within a ``Future.flow`` block as described in the next section) .. code-block:: scala - x.shutdown + x << y -Threads -------- +A Dataflow Variable can only be bound once. Subsequent attempts to bind the variable will be ignored. -You can easily create millions lightweight (event-driven) threads on a regular workstation. +Dataflow Delimiter +------------------ + +Dataflow is implemented in Akka using Scala's Delimited Continuations. To use the Dataflow API the code must be contained within a ``Future.flow`` block. For example: .. code-block:: scala - thread { ... } + import Future.flow -You can also set the thread to a reference to be able to control its life-cycle: + val a = Future( ... ) + val b = Future( ... ) + val c = new DefaultCompletableFuture[Int](5000) + + flow { + c << (a() + b()) + } + + val result = c.get() + +The ``flow`` method also returns a ``Future`` for the result of the contained expression, so the previous example could also be written like this: .. code-block:: scala - val t = thread { ... } + import Future.flow - ... // time passes + val a = Future( ... ) + val b = Future( ... ) - t ! 'exit // shut down the thread + val c = flow { + a() + b() + } + + val result = c.get() Examples -------- @@ -84,7 +111,7 @@ To run these examples: :: - Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_22). + Welcome to Scala version 2.9.0.RC1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_25). Type in expressions to have them evaluated. Type :help for more information. @@ -116,16 +143,17 @@ Example in Akka: .. code-block:: scala - import akka.dataflow.DataFlow._ + import akka.dispatch._ + import Future.flow - val x, y, z = new DataFlowVariable[Int] + val x, y, z = new DefaultCompletableFuture[Int](5000) - thread { + flow { z << x() + y() println("z = " + z()) } - thread { x << 40 } - thread { y << 2 } + x << 40 + y << 2 Example of using DataFlowVariable with recursion ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 5b18f18ffda28987adc85ed6345690c1459c31b0 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Tue, 3 May 2011 18:44:27 -0600 Subject: [PATCH 138/233] Fix bug with 'Future << x', must be used within a 'flow' block now --- .../test/scala/akka/dispatch/FutureSpec.scala | 28 ++++++++++++++----- .../src/main/scala/akka/dispatch/Future.scala | 7 ++--- 2 files changed, 23 insertions(+), 12 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 e8d20919a9..cf96a6791f 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -462,7 +462,7 @@ class FutureSpec extends JUnitSuite { assert(ly.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS)) assert(!lz.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS)) - x << 5 + flow { x << 5 } assert(y.get === 5) assert(z.get === 5) @@ -512,12 +512,12 @@ class FutureSpec extends JUnitSuite { assert(List(one, two, simpleResult).forall(_.isCompleted == false)) - one << 1 + flow { one << 1 } assert(one.isCompleted) assert(List(two, simpleResult).forall(_.isCompleted == false)) - two << 9 + flow { two << 9 } assert(List(one, two).forall(_.isCompleted == true)) assert(simpleResult.get === 10) @@ -541,13 +541,13 @@ class FutureSpec extends JUnitSuite { assert(!lz.isOpen) assert(List(x1,x2,y1,y2).forall(_.isCompleted == false)) - y1 << 1 // When this is set, it should cascade down the line + flow { y1 << 1 } // When this is set, it should cascade down the line assert(ly.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS)) assert(x1.get === 1) assert(!lz.isOpen) - y2 << 9 // When this is set, it should cascade down the line + flow { y2 << 9 } // When this is set, it should cascade down the line assert(lz.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS)) assert(x2.get === 9) @@ -595,7 +595,7 @@ class FutureSpec extends JUnitSuite { assert(!ly.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS)) assert(!lz.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS)) - x << 5 + flow { x << 5 } assert(y.get === 5) intercept[java.lang.ArithmeticException](result.get) @@ -644,11 +644,25 @@ class FutureSpec extends JUnitSuite { assert(!checkType(rInt, manifest[String])) assert(!checkType(rInt, manifest[Nothing])) assert(!checkType(rInt, manifest[Any])) - + rString.await rInt.await } + @Test def futureFlowSimpleAssign { + import Future.flow + + val x, y, z = new DefaultCompletableFuture[Int](5000) + + flow { + z << x() + y() + } + flow { x << 40 } + flow { y << 2 } + + assert(z.get === 42) + } + @Test def ticket812FutureDispatchCleanup { val dispatcher = implicitly[MessageDispatcher] assert(dispatcher.pendingFutures === 0) diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 0f09a7535a..7a0f08c6a7 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -243,7 +243,7 @@ object Future { */ def channel(timeout: Long = Actor.TIMEOUT) = new Channel[Any] { val future = empty[Any](timeout) - def !(msg: Any) = future << msg + def !(msg: Any) = future completeWithResult msg } /** @@ -645,10 +645,7 @@ trait CompletableFuture[T] extends Future[T] { this } - /** - * Alias for complete(Right(value)). - */ - final def << (value: T): Future[T] = complete(Right(value)) + final def << (value: T): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] => Future[Any]) => cont(complete(Right(value))) } final def << (other: Future[T]): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] => Future[Any]) => val fr = new DefaultCompletableFuture[Any](Actor.TIMEOUT) From 8752eb5452ddd87ffacf96771e4367971020b213 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Tue, 3 May 2011 18:48:42 -0600 Subject: [PATCH 139/233] Add Promise factory object for creating new CompletableFutures --- 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 7a0f08c6a7..9146a5282f 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -292,7 +292,7 @@ object Future { * The Delimited Continuations compiler plugin must be enabled in order to use this method. */ def flow[A](body: => A @cps[Future[Any]], timeout: Long = Actor.TIMEOUT): Future[A] = { - val future = new DefaultCompletableFuture[A](timeout) + val future = Promise[A](timeout) (reset(future.asInstanceOf[CompletableFuture[Any]].completeWithResult(body)): Future[Any]) onComplete { f => val opte = f.exception if (opte.isDefined) future completeWithException (opte.get) @@ -613,6 +613,14 @@ sealed trait Future[+T] { } +object Promise { + + def apply[A](timeout: Long): CompletableFuture[A] = new DefaultCompletableFuture[A](timeout) + + def apply[A](): CompletableFuture[A] = apply(Actor.TIMEOUT) + +} + /** * Essentially this is the Promise (or write-side) of a Future (read-side). */ From d0b27c43b7fa29375f51d9b87825643c346205ff Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Tue, 3 May 2011 18:52:58 -0600 Subject: [PATCH 140/233] Use Promise in tests --- .../src/test/scala/akka/dispatch/FutureSpec.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 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 cf96a6791f..cd5500e128 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -447,7 +447,7 @@ class FutureSpec extends JUnitSuite { @Test def futureCompletingWithContinuations { import Future.flow - val x, y, z = new DefaultCompletableFuture[Int](Actor.TIMEOUT) + val x, y, z = Promise[Int]() val ly, lz = new StandardLatch val result = flow { @@ -469,7 +469,7 @@ class FutureSpec extends JUnitSuite { assert(lz.isOpen) assert(result.get === 10) - val a, b, c = new DefaultCompletableFuture[Int](Actor.TIMEOUT) + val a, b, c = Promise[Int]() val result2 = flow { val n = (a << c).result.get + 10 @@ -490,7 +490,7 @@ class FutureSpec extends JUnitSuite { @Test def shouldNotAddOrRunCallbacksAfterFailureToBeCompletedBeforeExpiry { val latch = new StandardLatch - val f = new DefaultCompletableFuture[Int](0) + val f = Promise[Int](0) Thread.sleep(25) f.onComplete( _ => latch.open ) //Shouldn't throw any exception here @@ -505,7 +505,7 @@ class FutureSpec extends JUnitSuite { @Test def futureDataFlowShouldEmulateBlocking1 { import Future.flow - val one, two = new DefaultCompletableFuture[Int](1000 * 60) + val one, two = Promise[Int](1000 * 60) val simpleResult = flow { one() + two() } @@ -526,7 +526,7 @@ class FutureSpec extends JUnitSuite { @Test def futureDataFlowShouldEmulateBlocking2 { import Future.flow - val x1, x2, y1, y2 = new DefaultCompletableFuture[Int](1000 * 60) + val x1, x2, y1, y2 = Promise[Int](1000 * 60) val lx, ly, lz = new StandardLatch val result = flow { lx.open() @@ -580,7 +580,7 @@ class FutureSpec extends JUnitSuite { @Test def futureCompletingWithContinuationsFailure { import Future.flow - val x, y, z = new DefaultCompletableFuture[Int](Actor.TIMEOUT) + val x, y, z = Promise[Int]() val ly, lz = new StandardLatch val result = flow { @@ -652,7 +652,7 @@ class FutureSpec extends JUnitSuite { @Test def futureFlowSimpleAssign { import Future.flow - val x, y, z = new DefaultCompletableFuture[Int](5000) + val x, y, z = Promise[Int]() flow { z << x() + y() From d451381164281c75e61576d8f5133f0f09435981 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Tue, 3 May 2011 19:17:12 -0600 Subject: [PATCH 141/233] Dataflow examples all migrated to new api, all tested to work in sbt console --- akka-docs/scala/dataflow.rst | 69 ++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/akka-docs/scala/dataflow.rst b/akka-docs/scala/dataflow.rst index 9ffbe95bdb..82da73ce33 100644 --- a/akka-docs/scala/dataflow.rst +++ b/akka-docs/scala/dataflow.rst @@ -32,13 +32,13 @@ Scala's Delimited Continuations plugin is required to use the Dataflow API. To e Dataflow Variables ------------------ -Dataflow Variable defines three different operations: +Dataflow Variable defines four different operations: -1. Define a Dataflow Variable (with a timeout) +1. Define a Dataflow Variable .. code-block:: scala - val x = new DefaultCompletableFuture[Int](5000) + val x = Promise[Int]() 2. Wait for Dataflow Variable to be bound (must be contained within a ``Future.flow`` block as described in the next section) @@ -46,7 +46,7 @@ Dataflow Variable defines three different operations: x() -3. Bind Dataflow Variable +3. Bind Dataflow Variable (must be contained within a ``Future.flow`` block as described in the next section) .. code-block:: scala @@ -71,7 +71,7 @@ Dataflow is implemented in Akka using Scala's Delimited Continuations. To use th val a = Future( ... ) val b = Future( ... ) - val c = new DefaultCompletableFuture[Int](5000) + val c = Promise[Int]() flow { c << (a() + b()) @@ -146,14 +146,14 @@ Example in Akka: import akka.dispatch._ import Future.flow - val x, y, z = new DefaultCompletableFuture[Int](5000) + val x, y, z = Promise[Int]() flow { z << x() + y() println("z = " + z()) } - x << 40 - y << 2 + flow { x << 40 } + flow { y << 2 } Example of using DataFlowVariable with recursion ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -187,39 +187,41 @@ Example in Akka: .. code-block:: scala - import akka.dataflow.DataFlow._ + import akka.dispatch._ + import Future.flow - def ints(n: Int, max: Int): List[Int] = + def ints(n: Int, max: Int): List[Int] = { if (n == max) Nil else n :: ints(n + 1, max) + } - def sum(s: Int, stream: List[Int]): List[Int] = stream match { + def sum(s: Int, stream: List[Int]): List[Int] = stream match { case Nil => s :: Nil case h :: t => s :: sum(h + s, t) } - val x = new DataFlowVariable[List[Int]] - val y = new DataFlowVariable[List[Int]] + val x, y = Promise[List[Int]]() - thread { x << ints(0, 1000) } - thread { y << sum(0, x()) } - thread { println("List of sums: " + y()) } + flow { x << ints(0, 1000) } + flow { y << sum(0, x()) } + flow { println("List of sums: " + y()) } -Example on life-cycle management of DataFlowVariables -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Example using concurrent Futures +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Shows how to shutdown dataflow variables and bind threads to values to be able to interact with them (exit etc.). +Shows how to have a calculation run in another thread. Example in Akka: .. code-block:: scala - import akka.dataflow.DataFlow._ + import akka.dispatch._ + import Future.flow // create four 'Int' data flow variables - val x, y, z, v = new DataFlowVariable[Int] + val x, y, z, v = Promise[Int]() - val main = thread { + flow { println("Thread 'main'") x << 1 @@ -234,28 +236,19 @@ Example in Akka: z << y println("'z' set to 'y': " + z()) } - - // main completed, shut down the data flow variables - x.shutdown - y.shutdown - z.shutdown - v.shutdown } - val setY = thread { - println("Thread 'setY', sleeping...") - Thread.sleep(5000) - y << 2 + flow { + y << Future { + println("Thread 'setY', sleeping") + Thread.sleep(2000) + 2 + } println("'y' set to: " + y()) } - val setV = thread { + flow { println("Thread 'setV'") v << y println("'v' set to 'y': " + v()) } - - // shut down the threads - main ! 'exit - setY ! 'exit - setV ! 'exit From 10637dc2031c1de6414181a973175d5ffc70c254 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Wed, 4 May 2011 13:50:42 +1200 Subject: [PATCH 142/233] Update release scripts --- project/scripts/find-replace.sh | 2 +- project/scripts/release | 6 +----- project/scripts/test-release | 2 -- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/project/scripts/find-replace.sh b/project/scripts/find-replace.sh index e399ce3116..85a23a965c 100644 --- a/project/scripts/find-replace.sh +++ b/project/scripts/find-replace.sh @@ -22,7 +22,7 @@ echo "Find and replace: $FIND --> $REPLACE" # Exclude directories from search -excludedirs=".git dist deploy embedded-repo lib_managed project/boot project/scripts src_managed target" +excludedirs=".git dist deploy embedded-repo lib_managed project/boot project/scripts src_managed target akka-docs" echo "Excluding directories: $excludedirs" diff --git a/project/scripts/release b/project/scripts/release index 7de585dff8..4fa1cef623 100644 --- a/project/scripts/release +++ b/project/scripts/release @@ -1,12 +1,8 @@ +sh git checkout -b release-{{release.arg1}} clean -sh rm -rf dist -sh rm -rf deploy script find-replace.sh {{project.version}} {{release.arg1}} script find-replace.sh //[[:space:]]*release:[[:space:]]* reload build-release sh git commit -am 'Update version for release {{project.version}}' sh git tag -m 'Version {{project.version}}' v{{project.version}} -sh git revert -n HEAD -sh git commit -am 'Restoring SNAPSHOT version' -reload diff --git a/project/scripts/test-release b/project/scripts/test-release index 6a5994065a..4e821f15f5 100644 --- a/project/scripts/test-release +++ b/project/scripts/test-release @@ -1,6 +1,4 @@ clean -sh rm -rf dist -sh rm -rf deploy clean-lib script find-replace.sh {{project.version}} {{test-release.arg1}} script find-replace.sh //[[:space:]]*release:[[:space:]]* From 753c3bb68734c39ec47715a6ee01007eab6c3568 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Tue, 3 May 2011 21:29:32 -0600 Subject: [PATCH 143/233] Remove hardcoded Scala version for continuations plugin, fixes #834 --- 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 1c3520ada0..3e2e4a0338 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -230,7 +230,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) with Aut class AkkaActorProject(info: ProjectInfo) extends AkkaDefaultProject(info) with OsgiProject with AutoCompilerPlugins { override def bndExportPackage = super.bndExportPackage ++ Seq("com.eaio.*;version=3.2") - val cont = compilerPlugin("org.scala-lang.plugins" % "continuations" % "2.9.0.RC1") + val cont = compilerPlugin("org.scala-lang.plugins" % "continuations" % buildScalaVersion) override def compileOptions = super.compileOptions ++ compileOptions("-P:continuations:enable") } From 696b7bc9e74bd112323095b6c791b1670c904893 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Wed, 4 May 2011 08:46:30 +0200 Subject: [PATCH 144/233] Added explicit sjson.json.Serializer.SJSON --- akka-docs/scala/serialization.rst | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/akka-docs/scala/serialization.rst b/akka-docs/scala/serialization.rst index b229b57942..7a9541b69a 100644 --- a/akka-docs/scala/serialization.rst +++ b/akka-docs/scala/serialization.rst @@ -519,6 +519,8 @@ where Address is a custom class defined by the user. Using SJSON, I can store it .. code-block:: scala + val serializer = sjson.json.Serializer.SJSON + addr should equal( serializer.in[Address](serializer.out(addr))) @@ -540,6 +542,8 @@ What you get back from is a JsValue, an abstraction of the JSON object model. Fo .. code-block:: scala + val serializer = sjson.json.Serializer.SJSON + val a = serializer.in[AnyRef](serializer.out(addr)) // use extractors @@ -587,6 +591,8 @@ With SJSON, I can do the following: val c = Contact("Bob", Map("residence" -> a1, "office" -> a2, "club" -> a3)) val co = serializer.out(c) + val serializer = sjson.json.Serializer.SJSON + // with class specified c should equal(serializer.in[Contact](co)) @@ -669,6 +675,8 @@ The annotation @JSONProperty can be used to selectively ignore fields. When I se .. code-block:: scala + val serializer = sjson.json.Serializer.SJSON + it("should ignore issn field") { val j = Journal(100, "IEEE Computer", "Alex Payne", "012-456372") serializer.in[Journal](serializer.out(j)).asInstanceOf[Journal].issn should equal(null) @@ -699,6 +707,8 @@ SJSON will pick up during serialization. Now we can say: .. code-block:: scala + val serializer = sjson.json.Serializer.SJSON + val c = Contact("Bob", Map("residence" -> a1, "office" -> a2, "club" -> a3)) val co = serializer.out(c) @@ -727,6 +737,8 @@ Serialization works ok with optional members annotated as above. .. code-block:: scala + val serializer = sjson.json.Serializer.SJSON + describe("Bean with optional bean member serialization") { it("should serialize with Option defined") { val c = new ContactWithOptionalAddr("Debasish Ghosh", @@ -774,13 +786,15 @@ and the serialization code like the following: val res = sjson.json.Serializer.SJSON.in[D](json) val res1: D = res.asInstanceOf[D] println(res1) - } + } q } Note that the type hint on class D says A, but the actual instances that have been put into the object before serialization is one of the derived classes (B). During de-serialization, we have no idea of what can be inside D. The serializer.in API will fail since all hint it has is for A, which is abstract. In such cases, we need to handle the de-serialization by using extractors over the underlying data structure that we use for storing JSON objects, which is JsValue. Here's an example: .. code-block:: scala + val serializer = sjson.json.Serializer.SJSON + val test1 = new D(List(B("hello1"))) val json = serializer.out(test1) From e6f58c79cbee946c25f98eac98ab0e6c47cbe818 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 4 May 2011 13:57:40 +0200 Subject: [PATCH 145/233] Fixing use-cases documentation in reST --- akka-docs/intro/index.rst | 1 + akka-docs/intro/use-cases.rst | 48 +++++++++++++++++++++++++++++++++ akka-docs/pending/use-cases.rst | 31 --------------------- 3 files changed, 49 insertions(+), 31 deletions(-) create mode 100644 akka-docs/intro/use-cases.rst delete mode 100644 akka-docs/pending/use-cases.rst diff --git a/akka-docs/intro/index.rst b/akka-docs/intro/index.rst index ea27f70ffe..c95579750e 100644 --- a/akka-docs/intro/index.rst +++ b/akka-docs/intro/index.rst @@ -6,6 +6,7 @@ Introduction what-is-akka why-akka + use-cases getting-started-first-scala getting-started-first-scala-eclipse getting-started-first-java diff --git a/akka-docs/intro/use-cases.rst b/akka-docs/intro/use-cases.rst new file mode 100644 index 0000000000..fd434e89cc --- /dev/null +++ b/akka-docs/intro/use-cases.rst @@ -0,0 +1,48 @@ +Examples of use-cases for Akka +============================== + +There is a great discussion on use-cases for Akka with some good write-ups by production users `here `_ + +Here are some of the areas where Akka is being deployed into production +----------------------------------------------------------------------- + +**Transaction processing (Online Gaming, Finance/Banking, Trading, Statistics, Betting, Social Media, Telecom)** +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Scale up, scale out, fault-tolerance / HA + +**Service backend (any industry, any app)** +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Service REST, SOAP, Cometd, WebSockets etc + Act as message hub / integration layer + Scale up, scale out, fault-tolerance / HA + +**Concurrency/parallelism (any app)** +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Correct + Simple to work with and understand + Just add the jars to your existing JVM project (use Scala, Java, Groovy or JRuby) + +**Simulation** +^^^^^^^^^^^^^^ + Master/Worker, Compute Grid, MapReduce etc. + +**Batch processing (any industry)** +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Camel integration to hook up with batch data sources + Actors divide and conquer the batch workloads + +**Communications Hub (Telecom, Web media, Mobile media)** +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Scale up, scale out, fault-tolerance / HA + +**Gaming and Betting (MOM, online gaming, betting)** +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Scale up, scale out, fault-tolerance / HA + +**Business Intelligence/Data Mining/general purpose crunching** +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Scale up, scale out, fault-tolerance / HA + +**Complex Event Stream Processing** +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Scale up, scale out, fault-tolerance / HA diff --git a/akka-docs/pending/use-cases.rst b/akka-docs/pending/use-cases.rst deleted file mode 100644 index 8647d0b17c..0000000000 --- a/akka-docs/pending/use-cases.rst +++ /dev/null @@ -1,31 +0,0 @@ -Examples of use-cases for Akka -============================== - -There is a great discussion on use-cases for Akka with some good write-ups by production users here: ``_ - -Here are some of the areas where Akka is being deployed into production ------------------------------------------------------------------------ - -# **Transaction processing (Online Gaming, Finance/Banking, Trading, Statistics, Betting, Social Media, Telecom)** -** Scale up, scale out, fault-tolerance / HA -# **Service backend (any industry, any app)** -** Service REST, SOAP, Cometd, WebSockets etc -** Act as message hub / integration layer -** Scale up, scale out, fault-tolerance / HA -# **Concurrency/parallelism (any app)** -** Correct -** Simple to work with and understand -** Just add the jars to your existing JVM project (use Scala, Java, Groovy or JRuby) -# **Simulation** -** Master/Worker, Compute Grid, MapReduce etc. -# **Batch processing (any industry)** -** Camel integration to hook up with batch data sources -** Actors divide and conquer the batch workloads -# **Communications Hub (Telecom, Web media, Mobile media)** -** Scale up, scale out, fault-tolerance / HA -# **Gaming and Betting (MOM, online gaming, betting)** -** Scale up, scale out, fault-tolerance / HA -# **Business Intelligence/Data Mining/general purpose crunching** -** Scale up, scale out, fault-tolerance / HA -# **Complex Event Stream Processing** -** Scale up, scale out, fault-tolerance / HA From d8add4c68c661d17f7afa99fb49f50df2ffee8ed Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 4 May 2011 15:26:43 +0200 Subject: [PATCH 146/233] Fixing Akka boot config version printing --- akka-actor/src/main/scala/akka/util/AkkaLoader.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/AkkaLoader.scala b/akka-actor/src/main/scala/akka/util/AkkaLoader.scala index 2a2ee13db7..164f2da095 100644 --- a/akka-actor/src/main/scala/akka/util/AkkaLoader.scala +++ b/akka-actor/src/main/scala/akka/util/AkkaLoader.scala @@ -67,7 +67,7 @@ class AkkaLoader { println(" ttt tt ttt ttt ttt ttt ttt ttt") println(" tttttttt ttt ttt ttt ttt tttttttt") println("==================================================") - println(" Running version {}", Config.VERSION) + println(" Running version " + Config.VERSION) println("==================================================") } } From f930a727aed46d8ffcceadfb08861403d1b11c2b Mon Sep 17 00:00:00 2001 From: Debasish Ghosh Date: Wed, 4 May 2011 20:28:45 +0530 Subject: [PATCH 147/233] updated sjson ver to RC3 --- project/build/AkkaProject.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 3e2e4a0338..1090224483 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -123,8 +123,8 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) with Aut lazy val protobuf = "com.google.protobuf" % "protobuf-java" % "2.3.0" % "compile" //New BSD - lazy val sjson = "net.debasishg" % "sjson_2.9.0.RC1" % "0.11" % "compile" //ApacheV2 - lazy val sjson_test = "net.debasishg" % "sjson_2.9.0.RC1" % "0.11" % "test" //ApacheV2 + lazy val sjson = "net.debasishg" % "sjson_2.9.0.RC3" % "0.11" % "compile" //ApacheV2 + lazy val sjson_test = "net.debasishg" % "sjson_2.9.0.RC3" % "0.11" % "test" //ApacheV2 lazy val slf4j = "org.slf4j" % "slf4j-api" % SLF4J_VERSION lazy val logback = "ch.qos.logback" % "logback-classic" % "0.9.28" % "runtime" From 276f30d30f85b7efe9efbc1ef7e27f436b8acbac Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 4 May 2011 18:00:24 +0200 Subject: [PATCH 148/233] Fixing ticket #837, broken remote actor refs when serialized --- .../serialization/SerializationProtocol.scala | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/akka-remote/src/main/scala/akka/serialization/SerializationProtocol.scala b/akka-remote/src/main/scala/akka/serialization/SerializationProtocol.scala index f41351f5bc..d92cff5ad7 100644 --- a/akka-remote/src/main/scala/akka/serialization/SerializationProtocol.scala +++ b/akka-remote/src/main/scala/akka/serialization/SerializationProtocol.scala @@ -260,17 +260,25 @@ object RemoteActorSerialization { /** * Serializes the ActorRef instance into a Protocol Buffers (protobuf) Message. */ - def toRemoteActorRefProtocol(ar: ActorRef): RemoteActorRefProtocol = { - import ar._ - - Actor.remote.registerByUuid(ar) - - RemoteActorRefProtocol.newBuilder - .setClassOrServiceName("uuid:"+uuid.toString) - .setActorClassname(actorClassName) - .setHomeAddress(ActorSerialization.toAddressProtocol(ar)) - .setTimeout(timeout) + def toRemoteActorRefProtocol(actor: ActorRef): RemoteActorRefProtocol = actor match { + case r: RemoteActorRef => + RemoteActorRefProtocol.newBuilder + .setClassOrServiceName(r.id) + .setActorClassname(r.actorClassName) + .setHomeAddress(ActorSerialization.toAddressProtocol(r)) + .setTimeout(r.timeout) .build + case ar: LocalActorRef => + import ar._ + + Actor.remote.registerByUuid(ar) + + RemoteActorRefProtocol.newBuilder + .setClassOrServiceName("uuid:"+uuid.toString) + .setActorClassname(actorClassName) + .setHomeAddress(ActorSerialization.toAddressProtocol(ar)) + .setTimeout(timeout) + .build } def createRemoteMessageProtocolBuilder( From c46c4cc279b0216b19bd5ac4684a04f6de63e574 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Wed, 4 May 2011 20:49:35 +0200 Subject: [PATCH 149/233] Fixed toRemoteActorRefProtocol --- akka-docs/scala/serialization.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/akka-docs/scala/serialization.rst b/akka-docs/scala/serialization.rst index 7a9541b69a..8ac5e1c8e1 100644 --- a/akka-docs/scala/serialization.rst +++ b/akka-docs/scala/serialization.rst @@ -217,9 +217,11 @@ Here is an example of how to serialize an Actor. .. code-block:: scala + import akka.serialization.RemoteActorSerialization._ + val actor1 = actorOf[MyActor] - val bytes = toBinary(actor1) + val bytes = toRemoteActorRefProtocol(actor1).toByteArray To deserialize the ``ActorRef`` to a ``RemoteActorRef`` you need to use the ``fromBinaryToRemoteActorRef(bytes: Array[Byte])`` method on the ``ActorRef`` companion object: From aaa8b163f33a565c1fffa2a95079315b8e839897 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 4 May 2011 22:16:06 +0200 Subject: [PATCH 150/233] Adding tests for the actor ref serialization bug, 837 --- .../scala/akka/dispatch/MailboxHandling.scala | 3 +-- .../serialization/SerializationProtocol.scala | 8 +++--- .../SerializableTypeClassActorSpec.scala | 26 +++++++++++++++++++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala b/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala index 388d8f10b0..3b3032ad90 100644 --- a/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala +++ b/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala @@ -4,10 +4,9 @@ package akka.dispatch -import akka.actor.{Actor, ActorType, ActorRef, ActorInitializationException} import akka.AkkaException -import java.util.{Queue, List, Comparator, PriorityQueue} +import java.util.{Comparator, PriorityQueue} import java.util.concurrent._ import akka.util._ diff --git a/akka-remote/src/main/scala/akka/serialization/SerializationProtocol.scala b/akka-remote/src/main/scala/akka/serialization/SerializationProtocol.scala index d92cff5ad7..6ff41cfda1 100644 --- a/akka-remote/src/main/scala/akka/serialization/SerializationProtocol.scala +++ b/akka-remote/src/main/scala/akka/serialization/SerializationProtocol.scala @@ -269,15 +269,13 @@ object RemoteActorSerialization { .setTimeout(r.timeout) .build case ar: LocalActorRef => - import ar._ - Actor.remote.registerByUuid(ar) RemoteActorRefProtocol.newBuilder - .setClassOrServiceName("uuid:"+uuid.toString) - .setActorClassname(actorClassName) + .setClassOrServiceName("uuid:"+ar.uuid.toString) + .setActorClassname(ar.actorClassName) .setHomeAddress(ActorSerialization.toAddressProtocol(ar)) - .setTimeout(timeout) + .setTimeout(ar.timeout) .build } diff --git a/akka-remote/src/test/scala/serialization/SerializableTypeClassActorSpec.scala b/akka-remote/src/test/scala/serialization/SerializableTypeClassActorSpec.scala index 39584726f9..c4b766e7cd 100644 --- a/akka-remote/src/test/scala/serialization/SerializableTypeClassActorSpec.scala +++ b/akka-remote/src/test/scala/serialization/SerializableTypeClassActorSpec.scala @@ -186,6 +186,7 @@ class SerializableTypeClassActorSpec extends } */ } + describe("Custom serializable actors") { it("should serialize and de-serialize") { import BinaryFormatMyActorWithSerializableMessages._ @@ -208,6 +209,31 @@ class SerializableTypeClassActorSpec extends (actor3 !! "hello-reply").getOrElse("_") should equal("world") } } + + describe("ActorRef serialization") { + it("should serialize and deserialize local actor refs ") { + val a = actorOf[MyActorWithDualCounter].start + val out = RemoteActorSerialization.toRemoteActorRefProtocol(a).toByteArray + val in = RemoteActorSerialization.fromBinaryToRemoteActorRef(out) + + in.id should equal("uuid:"+a.uuid) + in.actorClassName should equal(a.actorClassName) + in.timeout should equal(a.timeout) + in.homeAddress should equal(Some(Actor.remote.address)) + a.stop + } + + it("should serialize and deserialize remote actor refs ") { + val a = Actor.remote.actorFor("foo", "localhost", 6666) + val out = RemoteActorSerialization.toRemoteActorRefProtocol(a).toByteArray + val in = RemoteActorSerialization.fromBinaryToRemoteActorRef(out) + + in.id should equal(a.id) + in.actorClassName should equal(a.actorClassName) + in.timeout should equal(a.timeout) + in.homeAddress should equal(a.homeAddress) + } + } } class MyActorWithDualCounter extends Actor { From dafd04ad4887d6b48bd53f48cca1c5f2a989c8c7 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 5 May 2011 11:02:41 +1200 Subject: [PATCH 151/233] Update to scala RC3 --- project/build.properties | 2 +- project/build/AkkaProject.scala | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/project/build.properties b/project/build.properties index 8275b16e18..ccf3e56038 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1,5 +1,5 @@ project.organization=se.scalablesolutions.akka project.name=akka project.version=1.1-SNAPSHOT -build.scala.versions=2.9.0.RC1 +build.scala.versions=2.9.0.RC3 sbt.version=0.7.6.RC0 diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 1090224483..f3fd2c19af 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -61,7 +61,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) with Aut lazy val jerseyModuleConfig = ModuleConfiguration("com.sun.jersey", JavaNetRepo) lazy val multiverseModuleConfig = ModuleConfiguration("org.multiverse", CodehausRepo) lazy val nettyModuleConfig = ModuleConfiguration("org.jboss.netty", JBossRepo) - lazy val scalaTestModuleConfig = ModuleConfiguration("org.scalatest", ScalaToolsSnapshotRepo) + lazy val scalaTestModuleConfig = ModuleConfiguration("org.scalatest", ScalaToolsRelRepo) lazy val spdeModuleConfig = ModuleConfiguration("us.technically.spde", DatabinderRepo) lazy val processingModuleConfig = ModuleConfiguration("org.processing", DatabinderRepo) lazy val sjsonModuleConfig = ModuleConfiguration("net.debasishg", ScalaToolsRelRepo) @@ -78,7 +78,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) with Aut lazy val JACKSON_VERSION = "1.7.1" lazy val JERSEY_VERSION = "1.3" lazy val MULTIVERSE_VERSION = "0.6.2" - lazy val SCALATEST_VERSION = "1.4-SNAPSHOT" + lazy val SCALATEST_VERSION = "1.4.RC3" lazy val JETTY_VERSION = "7.4.0.v20110414" lazy val JAVAX_SERVLET_VERSION = "3.0" lazy val SLF4J_VERSION = "1.6.0" @@ -123,8 +123,8 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) with Aut lazy val protobuf = "com.google.protobuf" % "protobuf-java" % "2.3.0" % "compile" //New BSD - lazy val sjson = "net.debasishg" % "sjson_2.9.0.RC3" % "0.11" % "compile" //ApacheV2 - lazy val sjson_test = "net.debasishg" % "sjson_2.9.0.RC3" % "0.11" % "test" //ApacheV2 + lazy val sjson = "net.debasishg" %% "sjson" % "0.11" % "compile" //ApacheV2 + lazy val sjson_test = "net.debasishg" %% "sjson" % "0.11" % "test" //ApacheV2 lazy val slf4j = "org.slf4j" % "slf4j-api" % SLF4J_VERSION lazy val logback = "ch.qos.logback" % "logback-classic" % "0.9.28" % "runtime" @@ -138,7 +138,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) with Aut lazy val junit = "junit" % "junit" % "4.5" % "test" //Common Public License 1.0 lazy val mockito = "org.mockito" % "mockito-all" % "1.8.1" % "test" //MIT - lazy val scalatest = "org.scalatest" % "scalatest" % SCALATEST_VERSION % "test" //ApacheV2 + lazy val scalatest = "org.scalatest" %% "scalatest" % SCALATEST_VERSION % "test" //ApacheV2 } // ------------------------------------------------------------------------------------------------------------------- From aad5f67c6bcee66a83ad004eebd5f8a0c52027bc Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 5 May 2011 13:13:49 +1200 Subject: [PATCH 152/233] Make akka parent project an actual parent project --- project/build/AkkaProject.scala | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index f3fd2c19af..a5e0f44611 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -10,7 +10,7 @@ import sbt._ import sbt.CompileOrder._ import spde._ -class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) with AutoCompilerPlugins { +class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) { // ------------------------------------------------------------------------------------------------------------------- // Compile settings @@ -24,10 +24,6 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) with Aut val javaCompileSettings = Seq("-Xlint:unchecked") - override def compileOptions = super.compileOptions ++ scalaCompileSettings.map(CompileOption) - override def javaCompileOptions = super.javaCompileOptions ++ javaCompileSettings.map(JavaCompileOption) - override def consoleOptions = super.consoleOptions ++ compileOptions("-P:continuations:enable") - // ------------------------------------------------------------------------------------------------------------------- // All repositories *must* go here! See ModuleConigurations below. // ------------------------------------------------------------------------------------------------------------------- @@ -162,12 +158,6 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) with Aut override def disableCrossPaths = true - //Exclude slf4j1.5.11 from the classpath, it's conflicting... - override def fullClasspath(config: Configuration): PathFinder = { - super.fullClasspath(config) --- - (super.fullClasspath(config) ** "slf4j*1.5.11.jar") - } - // ------------------------------------------------------------ // Publishing // ------------------------------------------------------------ @@ -309,7 +299,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) with Aut } // ------------------------------------------------------------------------------------------------------------------- - // Examples + // Samples // ------------------------------------------------------------------------------------------------------------------- class AkkaSampleAntsProject(info: ProjectInfo) extends DefaultSpdeProject(info) { From 0ac0a1876c116f6324fc76abcb6f1f11e773ec33 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 5 May 2011 14:51:36 +1200 Subject: [PATCH 153/233] Generate combined scaladoc across subprojects --- project/build/AkkaProject.scala | 16 +++++++++----- project/build/DocParentProject.scala | 32 ++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 project/build/DocParentProject.scala diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index a5e0f44611..8c11ccb3d2 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -10,7 +10,7 @@ import sbt._ import sbt.CompileOrder._ import spde._ -class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) { +class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with DocParentProject { // ------------------------------------------------------------------------------------------------------------------- // Compile settings @@ -158,9 +158,15 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) { override def disableCrossPaths = true - // ------------------------------------------------------------ + // ------------------------------------------------------------------------------------------------------------------- + // Scaladocs + // ------------------------------------------------------------------------------------------------------------------- + + override def docProjectDependencies = dependencies.toList - akka_samples + + // ------------------------------------------------------------------------------------------------------------------- // Publishing - // ------------------------------------------------------------ + // ------------------------------------------------------------------------------------------------------------------- override def managedStyle = ManagedStyle.Maven @@ -197,9 +203,9 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) { override def deliverProjectDependencies = super.deliverProjectDependencies.toList - akka_samples.projectID - akka_tutorials.projectID - // ------------------------------------------------------------ + // ------------------------------------------------------------------------------------------------------------------- // Build release - // ------------------------------------------------------------ + // ------------------------------------------------------------------------------------------------------------------- val localReleasePath = outputPath / "release" / version.toString val localReleaseRepository = Resolver.file("Local Release", localReleasePath / "repository" asFile) diff --git a/project/build/DocParentProject.scala b/project/build/DocParentProject.scala new file mode 100644 index 0000000000..ebe339ab2f --- /dev/null +++ b/project/build/DocParentProject.scala @@ -0,0 +1,32 @@ +import sbt._ + +trait DocParentProject extends ParentProject { + def docOutputPath = outputPath / "doc" / "main" / "api" + + def docProjectDependencies = topologicalSort.dropRight(1) + + def docMainSources = + docProjectDependencies.map { + case sp: ScalaPaths => sp.mainSources + case _ => Path.emptyPathFinder + }.foldLeft(Path.emptyPathFinder)(_ +++ _) + + def docCompileClasspath = + docProjectDependencies.map { + case bsp: BasicScalaProject => bsp.compileClasspath + case _ => Path.emptyPathFinder + }.foldLeft(Path.emptyPathFinder)(_ +++ _) + + def docLabel = "main" + + def docMaxErrors = 100 + + def docOptions: Seq[String] = Seq.empty + + lazy val doc = docAction describedAs ("Create combined scaladoc for all subprojects") + + def docAction = task { + val scaladoc = new Scaladoc(docMaxErrors, buildCompiler) + scaladoc(docLabel, docMainSources.get, docCompileClasspath.get, docOutputPath, docOptions, log) + } +} From 8303c4545b542f0efd46dbbd9b616436614d3763 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 5 May 2011 15:20:22 +1200 Subject: [PATCH 154/233] Don't git ignore project/build --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 91c3a65819..5d4e21b9e6 100755 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,6 @@ TAGS akka.tmproj reports dist -build target deploy/*.jar data From ad4190651e1e3bd44df6acdd6ea63d8fe35345ba Mon Sep 17 00:00:00 2001 From: Roland Date: Thu, 5 May 2011 19:58:49 +0200 Subject: [PATCH 155/233] add import statements to FSM docs --- akka-docs/scala/fsm.rst | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/akka-docs/scala/fsm.rst b/akka-docs/scala/fsm.rst index 89dedb52ec..8f4b6187ce 100644 --- a/akka-docs/scala/fsm.rst +++ b/akka-docs/scala/fsm.rst @@ -114,8 +114,12 @@ Now we can create a lock FSM that takes :class:`LockState` as a state and a .. code-block:: scala + import akka.actor.{Actor, FSM} + class Lock(code: String) extends Actor with FSM[LockState, String] { + import FSM._ + val emptyCode = "" startWith(Locked, emptyCode) @@ -318,6 +322,12 @@ The parentheses are not actually needed in all cases, but they visually distinguish between modifiers and their arguments and therefore make the code even more pleasant to read for foreigners. +.. note:: + + Please note that the ``return`` statement may not be used in :meth:`when` + blocks or similar; this is a Scala restriction. Either refactor your code + using ``if () ... else ...`` or move it into a method definition. + Monitoring Transitions ---------------------- @@ -435,7 +445,8 @@ state data which is available during termination handling. It should be noted that :func:`stop` does not abort the actions and stop the FSM immediately. The stop action must be returned from the event handler in - the same way as a state transition. + the same way as a state transition (but note that the ``return`` statement + may not be used within a :meth:`when` block). .. code-block:: scala @@ -463,7 +474,7 @@ invocation of :func:`onTermination` replaces the previously installed handler. Examples ======== -A bigger FSM example can be found in the sources: +A bigger FSM example contrasted with Actor's :meth:`become`/:meth:`unbecome` can be found in the sources: * `Dining Hakkers using FSM `_ * `Dining Hakkers using become `_ From 289733a735b9f933331e2d88030787919cdb82e8 Mon Sep 17 00:00:00 2001 From: Roland Date: Thu, 5 May 2011 20:21:15 +0200 Subject: [PATCH 156/233] clarify messages in TestActorRef example, fixes #840 --- akka-docs/scala/testing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/akka-docs/scala/testing.rst b/akka-docs/scala/testing.rst index c0e36ada78..6a16037b61 100644 --- a/akka-docs/scala/testing.rst +++ b/akka-docs/scala/testing.rst @@ -102,8 +102,8 @@ into a :class:`TestActorRef`. .. code-block:: scala val actorRef = TestActorRef(new MyActor) - val result = actorRef !! msg - result must be (expected) + val result = actorRef !! Say42 // hypothetical message stimulating a '42' answer + result must be (42) 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 From 803b7658aac11bfc8c44042767c852fecb8a591d Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Fri, 6 May 2011 08:12:20 +1200 Subject: [PATCH 157/233] Add sh action to parent project --- project/build/AkkaProject.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 8c11ccb3d2..640f88d1c4 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -10,7 +10,7 @@ import sbt._ import sbt.CompileOrder._ import spde._ -class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with DocParentProject { +class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with ExecProject with DocParentProject { // ------------------------------------------------------------------------------------------------------------------- // Compile settings @@ -158,6 +158,9 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with DocP override def disableCrossPaths = true + // add the sh action since it doesn't exist in ParentProject + lazy val sh = task { args => execOut { Process("sh" :: "-c" :: args.mkString(" ") :: Nil) } } + // ------------------------------------------------------------------------------------------------------------------- // Scaladocs // ------------------------------------------------------------------------------------------------------------------- From 60484e23410f8f2703b993f38cb9b63ec6352d85 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Fri, 6 May 2011 11:28:10 +1200 Subject: [PATCH 158/233] Update find-replace script --- project/scripts/find-replace.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/project/scripts/find-replace.sh b/project/scripts/find-replace.sh index 85a23a965c..884ff53a2a 100644 --- a/project/scripts/find-replace.sh +++ b/project/scripts/find-replace.sh @@ -37,7 +37,10 @@ excludeopts="${excludeopts} \) -prune -o" # Replace in files -search="find . -type f ${excludeopts} -print0 | xargs -0 grep -Il \"${FIND}\"" +search="find . ${excludeopts} -type f -print0 | xargs -0 grep -Il \"${FIND}\"" + +echo $search +echo files=$(eval "$search") @@ -61,7 +64,10 @@ echo # Replace in file names -search="find . -type f ${excludeopts} -name \"*${FIND}*\" -print0" +search="find . ${excludeopts} -type f -name \"*${FIND}*\" -print" + +echo $search +echo files=$(eval "$search") From 6f17d2cf6d6aa780138e96adf28ad378570cc788 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Fri, 6 May 2011 11:55:35 +1200 Subject: [PATCH 159/233] Fix @deprecated warnings --- .../src/main/scala/akka/actor/ActorRef.scala | 30 +++++++++---------- .../main/scala/akka/dataflow/DataFlow.scala | 2 +- .../src/main/scala/akka/dispatch/Future.scala | 10 +++---- .../akka/dispatch/ThreadPoolBuilder.scala | 2 +- .../remoteinterface/RemoteInterface.scala | 12 ++++---- .../main/scala/akka/actor/TypedActor.scala | 22 +++++++------- 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index 08e99206c2..e2c391e300 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -109,7 +109,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal * Defines the default timeout for '!!' and '!!!' invocations, * e.g. the timeout for the future returned by the call to '!!' and '!!!'. */ - @deprecated("Will be replaced by implicit-scoped timeout on all methods that needs it, will default to timeout specified in config") + @deprecated("Will be replaced by implicit-scoped timeout on all methods that needs it, will default to timeout specified in config", "1.1") @BeanProperty @volatile var timeout: Long = Actor.TIMEOUT @@ -184,13 +184,13 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal /** * Returns on which node this actor lives if None it lives in the local ActorRegistry */ - @deprecated("Remoting will become fully transparent in the future") + @deprecated("Remoting will become fully transparent in the future", "1.1") def homeAddress: Option[InetSocketAddress] /** * Java API.

    */ - @deprecated("Remoting will become fully transparent in the future") + @deprecated("Remoting will become fully transparent in the future", "1.1") def getHomeAddress(): InetSocketAddress = homeAddress getOrElse null /** @@ -256,7 +256,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal /** * Is the actor able to handle the message passed in as arguments? */ - @deprecated("Will be removed without replacement, it's just not reliable in the face of `become` and `unbecome`") + @deprecated("Will be removed without replacement, it's just not reliable in the face of `become` and `unbecome`", "1.1") def isDefinedAt(message: Any): Boolean = actor.isDefinedAt(message) /** @@ -382,27 +382,27 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal /** * Returns the class for the Actor instance that is managed by the ActorRef. */ - @deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`") + @deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`", "1.1") def actorClass: Class[_ <: Actor] /** * Akka Java API.

    * Returns the class for the Actor instance that is managed by the ActorRef. */ - @deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`") + @deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`", "1.1") def getActorClass(): Class[_ <: Actor] = actorClass /** * Returns the class name for the Actor instance that is managed by the ActorRef. */ - @deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`") + @deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`", "1.1") def actorClassName: String /** * Akka Java API.

    * Returns the class name for the Actor instance that is managed by the ActorRef. */ - @deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`") + @deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`", "1.1") def getActorClassName(): String = actorClassName /** @@ -456,7 +456,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal *

    * To be invoked from within the actor itself. */ - @deprecated("Will be removed after 1.1, use Actor.actorOf instead") + @deprecated("Will be removed after 1.1, use Actor.actorOf instead", "1.1") def spawn(clazz: Class[_ <: Actor]): ActorRef /** @@ -464,7 +464,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal *

    * To be invoked from within the actor itself. */ - @deprecated("Will be removed after 1.1, client managed actors will be removed") + @deprecated("Will be removed after 1.1, client managed actors will be removed", "1.1") def spawnRemote(clazz: Class[_ <: Actor], hostname: String, port: Int, timeout: Long): ActorRef /** @@ -472,7 +472,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal *

    * To be invoked from within the actor itself. */ - @deprecated("Will be removed after 1.1, use use Actor.remote.actorOf instead and then link on success") + @deprecated("Will be removed after 1.1, use use Actor.remote.actorOf instead and then link on success", "1.1") def spawnLink(clazz: Class[_ <: Actor]): ActorRef /** @@ -480,7 +480,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal *

    * To be invoked from within the actor itself. */ - @deprecated("Will be removed after 1.1, client managed actors will be removed") + @deprecated("Will be removed after 1.1, client managed actors will be removed", "1.1") def spawnLinkRemote(clazz: Class[_ <: Actor], hostname: String, port: Int, timeout: Long): ActorRef /** @@ -644,13 +644,13 @@ class LocalActorRef private[akka] ( /** * Returns the class for the Actor instance that is managed by the ActorRef. */ - @deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`") + @deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`", "1.1") def actorClass: Class[_ <: Actor] = actor.getClass.asInstanceOf[Class[_ <: Actor]] /** * Returns the class name for the Actor instance that is managed by the ActorRef. */ - @deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`") + @deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`", "1.1") def actorClassName: String = actorClass.getName /** @@ -1184,7 +1184,7 @@ private[akka] case class RemoteActorRef private[akka] ( protected[akka] def registerSupervisorAsRemoteActor: Option[Uuid] = None // ==== NOT SUPPORTED ==== - @deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`") + @deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`", "1.1") def actorClass: Class[_ <: Actor] = unsupported def dispatcher_=(md: MessageDispatcher): Unit = unsupported def dispatcher: MessageDispatcher = unsupported diff --git a/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala b/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala index 258bc4fff0..13438132e6 100644 --- a/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala +++ b/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala @@ -66,7 +66,7 @@ object DataFlow { /** * @author Jonas Bonér */ - @deprecated("Superceeded by Future and CompletableFuture as of 1.1") + @deprecated("Superceeded by Future and CompletableFuture as of 1.1", "1.1") sealed class DataFlowVariable[T <: Any](timeoutMs: Long) { import DataFlowVariable._ diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 9146a5282f..4ec877b8bb 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -206,27 +206,27 @@ object Futures { /** * (Blocking!) */ - @deprecated("Will be removed after 1.1, if you must block, use: futures.foreach(_.await)") + @deprecated("Will be removed after 1.1, if you must block, use: futures.foreach(_.await)", "1.1") def awaitAll(futures: List[Future[_]]): Unit = futures.foreach(_.await) /** * Returns the First Future that is completed (blocking!) */ - @deprecated("Will be removed after 1.1, if you must block, use: firstCompletedOf(futures).await") + @deprecated("Will be removed after 1.1, if you must block, use: firstCompletedOf(futures).await", "1.1") def awaitOne(futures: List[Future[_]], timeout: Long = Long.MaxValue): Future[_] = firstCompletedOf[Any](futures, timeout).await /** * Applies the supplied function to the specified collection of Futures after awaiting each future to be completed */ - @deprecated("Will be removed after 1.1, if you must block, use: futures map { f => fun(f.await) }") + @deprecated("Will be removed after 1.1, if you must block, use: futures map { f => fun(f.await) }", "1.1") def awaitMap[A,B](in: Traversable[Future[A]])(fun: (Future[A]) => B): Traversable[B] = in map { f => fun(f.await) } /** * Returns Future.resultOrException of the first completed of the 2 Futures provided (blocking!) */ - @deprecated("Will be removed after 1.1, if you must block, use: firstCompletedOf(List(f1,f2)).await.resultOrException") + @deprecated("Will be removed after 1.1, if you must block, use: firstCompletedOf(List(f1,f2)).await.resultOrException", "1.1") def awaitEither[T](f1: Future[T], f2: Future[T]): Option[T] = firstCompletedOf[T](List(f1,f2)).await.resultOrException } @@ -349,7 +349,7 @@ sealed trait Future[+T] { * 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.") + @deprecated("Will be removed after 1.1, it's dangerous and can cause deadlocks, agony and insanity.", "1.1") def awaitBlocking : Future[T] /** diff --git a/akka-actor/src/main/scala/akka/dispatch/ThreadPoolBuilder.scala b/akka-actor/src/main/scala/akka/dispatch/ThreadPoolBuilder.scala index 0bcc0662e0..842a8f86d8 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ThreadPoolBuilder.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ThreadPoolBuilder.scala @@ -89,7 +89,7 @@ case class ThreadPoolConfigDispatcherBuilder(dispatcherFactory: (ThreadPoolConfi def build = dispatcherFactory(config) //TODO remove this, for backwards compat only - @deprecated("Use .build instead") def buildThreadPool = build + @deprecated("Use .build instead", "1.1") def buildThreadPool = build def withNewBoundedThreadPoolWithLinkedBlockingQueueWithUnboundedCapacity(bounds: Int): ThreadPoolConfigDispatcherBuilder = this.copy(config = config.copy(flowHandler = flowHandler(bounds), queueFactory = linkedBlockingQueue())) diff --git a/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala b/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala index 9c631576ef..8feb774188 100644 --- a/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala +++ b/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala @@ -167,7 +167,7 @@ abstract class RemoteSupport extends ListenerManagement with RemoteServerModule * val actor = actorOf(classOf[MyActor],"www.akka.io", 2552).start() * */ - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") def actorOf(factory: => Actor, host: String, port: Int): ActorRef = Actor.remote.clientManagedActorOf(() => factory, host, port) @@ -186,7 +186,7 @@ abstract class RemoteSupport extends ListenerManagement with RemoteServerModule * val actor = actorOf(classOf[MyActor],"www.akka.io",2552).start() * */ - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") def actorOf(clazz: Class[_ <: Actor], host: String, port: Int): ActorRef = clientManagedActorOf(() => createActorFromClass(clazz), host, port) @@ -205,7 +205,7 @@ abstract class RemoteSupport extends ListenerManagement with RemoteServerModule * val actor = actorOf[MyActor]("www.akka.io",2552).start() * */ - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") def actorOf[T <: Actor : Manifest](host: String, port: Int): ActorRef = clientManagedActorOf(() => createActorFromClass(manifest.erasure), host, port) @@ -447,7 +447,7 @@ trait RemoteClientModule extends RemoteModule { self: RemoteModule => def typedActorFor[T](intfClass: Class[T], serviceId: String, implClassName: String, timeout: Long, hostname: String, port: Int, loader: ClassLoader): T = typedActorFor(intfClass, serviceId, implClassName, timeout, hostname, port, Some(loader)) - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") def clientManagedActorOf(factory: () => Actor, host: String, port: Int): ActorRef @@ -487,9 +487,9 @@ trait RemoteClientModule extends RemoteModule { self: RemoteModule => private[akka] def deregisterSupervisorForActor(actorRef: ActorRef): ActorRef - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") private[akka] def registerClientManagedActor(hostname: String, port: Int, uuid: Uuid): Unit - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") private[akka] def unregisterClientManagedActor(hostname: String, port: Int, uuid: Uuid): Unit } 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 4cc1892e65..36af9b9784 100644 --- a/akka-typed-actor/src/main/scala/akka/actor/TypedActor.scala +++ b/akka-typed-actor/src/main/scala/akka/actor/TypedActor.scala @@ -412,12 +412,12 @@ object TypedActorConfiguration { new TypedActorConfiguration().timeout(Duration(timeout, "millis")) } - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") def apply(host: String, port: Int) : TypedActorConfiguration = { new TypedActorConfiguration().makeRemote(host, port) } - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") def apply(host: String, port: Int, timeout: Long) : TypedActorConfiguration = { new TypedActorConfiguration().makeRemote(host, port).timeout(Duration(timeout, "millis")) } @@ -447,10 +447,10 @@ final class TypedActorConfiguration { this } - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") def makeRemote(hostname: String, port: Int): TypedActorConfiguration = makeRemote(new InetSocketAddress(hostname, port)) - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") def makeRemote(remoteAddress: InetSocketAddress): TypedActorConfiguration = { _host = Some(remoteAddress) this @@ -518,7 +518,7 @@ object TypedActor { * @param host hostname of the remote server * @param port port of the remote server */ - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") def newRemoteInstance[T](intfClass: Class[T], targetClass: Class[_], hostname: String, port: Int): T = { newInstance(intfClass, targetClass, TypedActorConfiguration(hostname, port)) } @@ -530,7 +530,7 @@ object TypedActor { * @param host hostname of the remote server * @param port port of the remote server */ - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") def newRemoteInstance[T](intfClass: Class[T], factory: => AnyRef, hostname: String, port: Int): T = { newInstance(intfClass, factory, TypedActorConfiguration(hostname, port)) } @@ -563,7 +563,7 @@ object TypedActor { * @param host hostname of the remote server * @param port port of the remote server */ - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") def newRemoteInstance[T](intfClass: Class[T], targetClass: Class[_], timeout: Long, hostname: String, port: Int): T = { newInstance(intfClass, targetClass, TypedActorConfiguration(hostname, port, timeout)) } @@ -576,7 +576,7 @@ object TypedActor { * @param host hostname of the remote server * @param port port of the remote server */ - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") def newRemoteInstance[T](intfClass: Class[T], factory: => AnyRef, timeout: Long, hostname: String, port: Int): T = { newInstance(intfClass, factory, TypedActorConfiguration(hostname, port, timeout)) } @@ -593,7 +593,7 @@ object TypedActor { /** * Creates an ActorRef, can be local only or client-managed-remote */ - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") private[akka] def createActorRef(typedActor: => TypedActor, config: TypedActorConfiguration): ActorRef = { config match { case null => actorOf(typedActor) @@ -669,14 +669,14 @@ object TypedActor { /** * Java API. */ - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") def newRemoteInstance[T](intfClass: Class[T], factory: TypedActorFactory, hostname: String, port: Int) : T = newRemoteInstance(intfClass, factory.create, hostname, port) /** * Java API. */ - @deprecated("Will be removed after 1.1") + @deprecated("Will be removed after 1.1", "1.1") def newRemoteInstance[T](intfClass: Class[T], factory: TypedActorFactory, timeout: Long, hostname: String, port: Int) : T = newRemoteInstance(intfClass, factory.create, timeout, hostname, port) From 7f9dd2d49f7e088228f98c6e4083a61e801d9b44 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Fri, 6 May 2011 12:31:02 +1200 Subject: [PATCH 160/233] Fix scaladoc errors --- .../src/main/scala/akka/actor/ActorRegistry.scala | 10 +++++----- .../src/main/scala/akka/actor/TypedActor.scala | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/ActorRegistry.scala b/akka-actor/src/main/scala/akka/actor/ActorRegistry.scala index c833c6d360..310aa19c78 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRegistry.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRegistry.scala @@ -286,7 +286,7 @@ class Index[K <: AnyRef,V <: AnyRef : Manifest] { /** * Associates the value of type V with the key of type K - * @returns true if the value didn't exist for the key previously, and false otherwise + * @return true if the value didn't exist for the key previously, and false otherwise */ def put(key: K, value: V): Boolean = { //Tailrecursive spin-locking put @@ -328,7 +328,7 @@ class Index[K <: AnyRef,V <: AnyRef : Manifest] { } /** - * @returns a _new_ array of all existing values for the given key at the time of the call + * @return a _new_ array of all existing values for the given key at the time of the call */ def values(key: K): Array[V] = { val set: JSet[V] = container get key @@ -337,7 +337,7 @@ class Index[K <: AnyRef,V <: AnyRef : Manifest] { } /** - * @returns Some(value) for the first matching value where the supplied function returns true for the given key, + * @return Some(value) for the first matching value where the supplied function returns true for the given key, * if no matches it returns None */ def findValue(key: K)(f: (V) => Boolean): Option[V] = { @@ -359,7 +359,7 @@ class Index[K <: AnyRef,V <: AnyRef : Manifest] { /** * Disassociates the value of type V from the key of type K - * @returns true if the value was disassociated from the key and false if it wasn't previously associated with the key + * @return true if the value was disassociated from the key and false if it wasn't previously associated with the key */ def remove(key: K, value: V): Boolean = { val set = container get key @@ -377,7 +377,7 @@ class Index[K <: AnyRef,V <: AnyRef : Manifest] { } /** - * @returns true if the underlying containers is empty, may report false negatives when the last remove is underway + * @return true if the underlying containers is empty, may report false negatives when the last remove is underway */ def isEmpty: Boolean = container.isEmpty 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 36af9b9784..77d0d1e64d 100644 --- a/akka-typed-actor/src/main/scala/akka/actor/TypedActor.scala +++ b/akka-typed-actor/src/main/scala/akka/actor/TypedActor.scala @@ -559,7 +559,7 @@ object TypedActor { * Factory method for remote typed actor. * @param intfClass interface the typed actor implements * @param targetClass implementation class of the typed actor - * @paramm timeout timeout for future + * @param timeout timeout for future * @param host hostname of the remote server * @param port port of the remote server */ @@ -572,7 +572,7 @@ object TypedActor { * Factory method for remote typed actor. * @param intfClass interface the typed actor implements * @param factory factory method that constructs the typed actor - * @paramm timeout timeout for future + * @param timeout timeout for future * @param host hostname of the remote server * @param port port of the remote server */ @@ -585,7 +585,7 @@ object TypedActor { * Factory method for typed actor. * @param intfClass interface the typed actor implements * @param factory factory method that constructs the typed actor - * @paramm config configuration object forthe typed actor + * @param config configuration object for the typed actor */ def newInstance[T](intfClass: Class[T], factory: => AnyRef, config: TypedActorConfiguration): T = newInstance(intfClass, createActorRef(newTypedActor(factory),config), config) @@ -607,7 +607,7 @@ object TypedActor { * Factory method for typed actor. * @param intfClass interface the typed actor implements * @param targetClass implementation class of the typed actor - * @paramm config configuration object forthe typed actor + * @param config configuration object for the typed actor */ def newInstance[T](intfClass: Class[T], targetClass: Class[_], config: TypedActorConfiguration): T = newInstance(intfClass, createActorRef(newTypedActor(targetClass),config), config) From f01db73f7f05cb90e28a1d30ba51ff6c10a25a2c Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Fri, 6 May 2011 12:59:16 +1200 Subject: [PATCH 161/233] Fix up the migration guide --- akka-docs/general/issue-tracking.rst | 4 +- .../general/migration-guide-1.0.x-1.1.x.rst | 83 ++++++++++++++----- akka-docs/general/migration-guides.rst | 6 +- akka-docs/{pending => scala}/http.rst | 12 ++- akka-docs/scala/index.rst | 1 + 5 files changed, 74 insertions(+), 32 deletions(-) rename akka-docs/{pending => scala}/http.rst (99%) diff --git a/akka-docs/general/issue-tracking.rst b/akka-docs/general/issue-tracking.rst index b0137c3ecf..a57e2f9c99 100644 --- a/akka-docs/general/issue-tracking.rst +++ b/akka-docs/general/issue-tracking.rst @@ -28,11 +28,11 @@ In order to create tickets you need to do the following: `Register here `_ then log in For Akka tickets: -`Link to create new ticket `_ +`Link to create new ticket `__ For Akka Modules tickets: -`Link to create new ticket `_ +`Link to create new ticket `__ Thanks a lot for reporting bugs and suggesting features. 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 e9b27c5032..44f09ea244 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 @@ -1,41 +1,78 @@ -Migration Guide 1.0.x to 1.1.x -=================================== + +.. _migration-1.1: + +################################ + Migration Guide 1.0.x to 1.1.x +################################ **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.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: +- is now dependency free, with the exception of the dependency on the + ``scala-library.jar`` -.. code-block:: scala +- 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. - lazy val logback = "ch.qos.logback" % "logback-classic" % "0.9.28" % "runtime" + 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" % "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). -# 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. + -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 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 are instructions in + the :ref:`http-module` 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 + +.. _ListWriter: https://github.com/jboner/akka/blob/v1.0/akka-http/src/main/scala/akka/http/ListWriter.scala -# ``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. +- The TestKit moved into the akka-testkit subproject and correspondingly into the + ``akka.testkit`` package. diff --git a/akka-docs/general/migration-guides.rst b/akka-docs/general/migration-guides.rst index ed9c1de270..31e0593f71 100644 --- a/akka-docs/general/migration-guides.rst +++ b/akka-docs/general/migration-guides.rst @@ -4,7 +4,7 @@ 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-1.0.x-1.1.x + migration-guide-0.9.x-0.10.x + migration-guide-0.8.x-0.9.x + migration-guide-0.7.x-0.8.x diff --git a/akka-docs/pending/http.rst b/akka-docs/scala/http.rst similarity index 99% rename from akka-docs/pending/http.rst rename to akka-docs/scala/http.rst index 9de34d05e7..75bd9ca7c9 100644 --- a/akka-docs/pending/http.rst +++ b/akka-docs/scala/http.rst @@ -1,10 +1,14 @@ -HTTP -==== + +.. _http-module: + +###### + HTTP +###### Module stability: **SOLID** -When using Akkas embedded servlet container: --------------------------------------------- +When using Akkas embedded servlet container +------------------------------------------- Akka supports the JSR for REST called JAX-RS (JSR-311). It allows you to create interaction with your actors through HTTP + REST diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index 99d460aa94..530d125fa2 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -17,5 +17,6 @@ Scala API fault-tolerance dispatchers fsm + http testing tutorial-chat-server From 9ace0a97da3944fc18060cbb538f1597b98cb510 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Fri, 6 May 2011 14:01:21 +1200 Subject: [PATCH 162/233] Bump version to 1.2-SNAPSHOT --- akka-actor-tests/src/test/scala/akka/config/ConfigSpec.scala | 2 +- akka-actor/src/main/scala/akka/config/Config.scala | 2 +- akka-tutorials/akka-tutorial-first/pom.xml | 2 +- .../src/main/java/akka/tutorial/first/java/Pi.java | 4 ++-- akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala | 4 ++-- .../src/main/java/akka/tutorial/java/second/Pi.java | 4 ++-- akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala | 4 ++-- config/akka-reference.conf | 2 +- project/build.properties | 2 +- scripts/run_akka.sh | 4 ++-- 10 files changed, 15 insertions(+), 15 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/config/ConfigSpec.scala b/akka-actor-tests/src/test/scala/akka/config/ConfigSpec.scala index c7a574a7f0..db7c40be23 100644 --- a/akka-actor-tests/src/test/scala/akka/config/ConfigSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/config/ConfigSpec.scala @@ -18,7 +18,7 @@ class ConfigSpec extends WordSpec with MustMatchers { getList("akka.boot") must equal(Nil) getString("akka.time-unit") must equal(Some("seconds")) - getString("akka.version") must equal(Some("1.1-SNAPSHOT")) + getString("akka.version") must equal(Some("1.2-SNAPSHOT")) getString("akka.actor.default-dispatcher.type") must equal(Some("GlobalExecutorBasedEventDriven")) getInt("akka.actor.default-dispatcher.keep-alive-time") must equal(Some(60)) diff --git a/akka-actor/src/main/scala/akka/config/Config.scala b/akka-actor/src/main/scala/akka/config/Config.scala index 1b5d8f774e..341a69ae3f 100644 --- a/akka-actor/src/main/scala/akka/config/Config.scala +++ b/akka-actor/src/main/scala/akka/config/Config.scala @@ -15,7 +15,7 @@ class ModuleNotAvailableException(message: String, cause: Throwable = null) exte * @author Jonas Bonér */ object Config { - val VERSION = "1.1-SNAPSHOT" + val VERSION = "1.2-SNAPSHOT" val HOME = { val envHome = System.getenv("AKKA_HOME") match { diff --git a/akka-tutorials/akka-tutorial-first/pom.xml b/akka-tutorials/akka-tutorial-first/pom.xml index f3d9589815..e6b1758877 100644 --- a/akka-tutorials/akka-tutorial-first/pom.xml +++ b/akka-tutorials/akka-tutorial-first/pom.xml @@ -15,7 +15,7 @@ se.scalablesolutions.akka akka-actor - 1.1-SNAPSHOT + 1.2-SNAPSHOT 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 034dec5178..24264b52a7 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 @@ -27,8 +27,8 @@ import java.util.concurrent.CountDownLatch; *

      *   $ cd akka-1.1
      *   $ export AKKA_HOME=`pwd`
    - *   $ javac -cp dist/akka-actor-1.1-SNAPSHOT.jar:scala-library.jar akka/tutorial/first/java/Pi.java
    - *   $ java -cp dist/akka-actor-1.1-SNAPSHOT.jar:scala-library.jar:. akka.tutorial.first.java.Pi
    + *   $ javac -cp dist/akka-actor-1.2-SNAPSHOT.jar:scala-library.jar akka/tutorial/first/java/Pi.java
    + *   $ java -cp dist/akka-actor-1.2-SNAPSHOT.jar:scala-library.jar:. akka.tutorial.first.java.Pi
      *   $ ...
      * 
    *

    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 41f562791a..6164ae00cb 100644 --- a/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala +++ b/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala @@ -21,8 +21,8 @@ import java.util.concurrent.CountDownLatch *

      *   $ cd akka-1.1
      *   $ export AKKA_HOME=`pwd`
    - *   $ scalac -cp dist/akka-actor-1.1-SNAPSHOT.jar Pi.scala
    - *   $ java -cp dist/akka-actor-1.1-SNAPSHOT.jar:scala-library.jar:. akka.tutorial.first.scala.Pi
    + *   $ scalac -cp dist/akka-actor-1.2-SNAPSHOT.jar Pi.scala
    + *   $ java -cp dist/akka-actor-1.2-SNAPSHOT.jar:scala-library.jar:. akka.tutorial.first.scala.Pi
      *   $ ...
      * 
    *

    diff --git a/akka-tutorials/akka-tutorial-second/src/main/java/akka/tutorial/java/second/Pi.java b/akka-tutorials/akka-tutorial-second/src/main/java/akka/tutorial/java/second/Pi.java index 6397c09148..1829428f5b 100644 --- a/akka-tutorials/akka-tutorial-second/src/main/java/akka/tutorial/java/second/Pi.java +++ b/akka-tutorials/akka-tutorial-second/src/main/java/akka/tutorial/java/second/Pi.java @@ -29,8 +29,8 @@ import akka.routing.UntypedLoadBalancer; *

      *   $ cd akka-1.1
      *   $ export AKKA_HOME=`pwd`
    - *   $ javac -cp dist/akka-actor-1.1-SNAPSHOT.jar:scala-library.jar akka/tutorial/java/second/Pi.java
    - *   $ java -cp dist/akka-actor-1.1-SNAPSHOT.jar:scala-library.jar:. akka.tutorial.java.second.Pi
    + *   $ javac -cp dist/akka-actor-1.2-SNAPSHOT.jar:scala-library.jar akka/tutorial/java/second/Pi.java
    + *   $ java -cp dist/akka-actor-1.2-SNAPSHOT.jar:scala-library.jar:. akka.tutorial.java.second.Pi
      *   $ ...
      * 
    *

    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 35d29f8f6c..f671605359 100644 --- a/akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala +++ b/akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala @@ -22,8 +22,8 @@ import System.{currentTimeMillis => now} *

      *   $ cd akka-1.1
      *   $ export AKKA_HOME=`pwd`
    - *   $ scalac -cp dist/akka-actor-1.1-SNAPSHOT.jar Pi.scala
    - *   $ java -cp dist/akka-actor-1.1-SNAPSHOT.jar:scala-library.jar:. akka.tutorial.second.Pi
    + *   $ scalac -cp dist/akka-actor-1.2-SNAPSHOT.jar Pi.scala
    + *   $ java -cp dist/akka-actor-1.2-SNAPSHOT.jar:scala-library.jar:. akka.tutorial.second.Pi
      *   $ ...
      * 
    *

    diff --git a/config/akka-reference.conf b/config/akka-reference.conf index 9a647c6ad5..3a44e12e5a 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -6,7 +6,7 @@ # Modify as needed. akka { - version = "1.1-SNAPSHOT" # Akka version, checked against the runtime version of Akka. + version = "1.2-SNAPSHOT" # Akka version, checked against the runtime version of Akka. enabled-modules = [] # Comma separated list of the enabled modules. Options: ["remote", "camel", "http"] diff --git a/project/build.properties b/project/build.properties index ccf3e56038..2f760f3270 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1,5 +1,5 @@ project.organization=se.scalablesolutions.akka project.name=akka -project.version=1.1-SNAPSHOT +project.version=1.2-SNAPSHOT build.scala.versions=2.9.0.RC3 sbt.version=0.7.6.RC0 diff --git a/scripts/run_akka.sh b/scripts/run_akka.sh index a58c021482..1d7ce97c21 100755 --- a/scripts/run_akka.sh +++ b/scripts/run_akka.sh @@ -1,6 +1,6 @@ #!/bin/bash cd $AKKA_HOME -VERSION=1.1-SNAPSHOT +VERSION=1.2-SNAPSHOT TARGET_DIR=dist/$VERSION/$1 shift 1 VMARGS=$@ @@ -13,4 +13,4 @@ else fi export AKKA_HOME=`pwd` -java -jar ${VMARGS} ${VERSION}.jar \ No newline at end of file +java -jar ${VMARGS} ${VERSION}.jar From 9fa6c322f9a9f85a8765042facedbe4fd8d3d626 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Fri, 6 May 2011 15:40:05 +1200 Subject: [PATCH 163/233] Add links to snapshot scaladoc --- akka-docs/general/index.rst | 1 + akka-docs/general/scaladoc.rst | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 akka-docs/general/scaladoc.rst diff --git a/akka-docs/general/index.rst b/akka-docs/general/index.rst index 4cd57b37b7..a437095478 100644 --- a/akka-docs/general/index.rst +++ b/akka-docs/general/index.rst @@ -9,5 +9,6 @@ General building-akka configuration event-handler + scaladoc issue-tracking licenses diff --git a/akka-docs/general/scaladoc.rst b/akka-docs/general/scaladoc.rst new file mode 100644 index 0000000000..a972225416 --- /dev/null +++ b/akka-docs/general/scaladoc.rst @@ -0,0 +1,13 @@ + +.. _scaladoc: + +########## + Scaladoc +########## + +Automatically published Scaladoc API for the latest SNAPSHOT version of Akka can +be found here: + +http://akka.io/api/akka/snapshot + +http://akka.io/api/akka-modules/snapshot From 6000b0594f4632fb93a8e228d01784e6c3d6e683 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Fri, 6 May 2011 15:56:23 +1200 Subject: [PATCH 164/233] Add released API links --- akka-docs/general/scaladoc.rst | 35 +++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/akka-docs/general/scaladoc.rst b/akka-docs/general/scaladoc.rst index a972225416..312aaec5e5 100644 --- a/akka-docs/general/scaladoc.rst +++ b/akka-docs/general/scaladoc.rst @@ -1,13 +1,38 @@ .. _scaladoc: -########## - Scaladoc -########## +############## + Scaladoc API +############## + + +Akka Snapshot +============= Automatically published Scaladoc API for the latest SNAPSHOT version of Akka can be found here: -http://akka.io/api/akka/snapshot +- Akka - http://akka.io/api/akka/snapshot -http://akka.io/api/akka-modules/snapshot +- Akka Modules - http://akka.io/api/akka-modules/snapshot + + +Release Versions +================ + +1.1-RC1 +------- + +- Akka 1.1-RC1 - http://akka.io/api/akka/1.1-RC1/ +- Akka Modules 1.1-RC1 - http://akka.io/api/akka-modules/1.1-RC1/ + +1.0 +--- + +- Akka 1.0 - http://akka.io/api/1.0/ + +Older +----- + +- Akka 0.10 - http://www.scalablesolutions.se/akka/api/0.10/ +- Akka 0.9 - http://www.scalablesolutions.se/akka/api/0.9/ From 897884bf1ce3fb22f5d451f525b8ecdcc93919c4 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Thu, 5 May 2011 21:19:18 +0200 Subject: [PATCH 165/233] method dispatch (cherry picked from commit 1494a32cc8621c7cd53f1d6ed54d4da99b543bc1) --- 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 acd99b7fcf..7147f4edad 100644 --- a/akka-docs/java/typed-actors.rst +++ b/akka-docs/java/typed-actors.rst @@ -7,7 +7,7 @@ Typed Actors (Java) 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. +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. Each method dispatch is turned into a message that is put on a queue to be processed by the Typed Actor sequentially one by one. If you are using the `Spring Framework `_ then take a look at Akka's `Spring integration `_. diff --git a/akka-docs/scala/typed-actors.rst b/akka-docs/scala/typed-actors.rst index 74c7f22f1f..1663f844ef 100644 --- a/akka-docs/scala/typed-actors.rst +++ b/akka-docs/scala/typed-actors.rst @@ -7,7 +7,7 @@ Typed Actors (Scala) 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. +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. Each method dispatch is turned into a message that is put on a queue to be processed by the Typed Actor sequentially one by one. If you are using the `Spring Framework `_ then take a look at Akka's `Spring integration `_. From e3189abe54350f543af5a38d3e1f72dbec46bbe4 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 6 May 2011 07:38:49 +0200 Subject: [PATCH 166/233] Docs: Added some missing links (cherry picked from commit a70b7c027fba7f24ff1b7496cf8087bc2e9d5038) --- akka-docs/dev/developer-guidelines.rst | 2 ++ akka-docs/dev/documentation.rst | 6 +++--- akka-docs/dev/sponsors.rst | 4 +++- akka-docs/dev/team.rst | 11 ++++++---- akka-docs/general/issue-tracking.rst | 2 ++ akka-docs/general/licenses.rst | 2 ++ akka-docs/general/migration-guides.rst | 2 ++ akka-docs/index.rst | 28 ++++++++++++++++++++++++-- 8 files changed, 47 insertions(+), 10 deletions(-) diff --git a/akka-docs/dev/developer-guidelines.rst b/akka-docs/dev/developer-guidelines.rst index ab19c370a2..138ff18476 100644 --- a/akka-docs/dev/developer-guidelines.rst +++ b/akka-docs/dev/developer-guidelines.rst @@ -1,3 +1,5 @@ +.. _developer_guidelines: + Developer Guidelines ==================== diff --git a/akka-docs/dev/documentation.rst b/akka-docs/dev/documentation.rst index b0da0bd698..aa44d15291 100644 --- a/akka-docs/dev/documentation.rst +++ b/akka-docs/dev/documentation.rst @@ -3,9 +3,9 @@ .. _documentation: -############### - Documentation -############### +######################### + Documentation Guidelines +######################### .. sidebar:: Contents diff --git a/akka-docs/dev/sponsors.rst b/akka-docs/dev/sponsors.rst index 127ab17ee1..085d35cc0d 100644 --- a/akka-docs/dev/sponsors.rst +++ b/akka-docs/dev/sponsors.rst @@ -1,8 +1,10 @@ +.. _sponsors: + Sponsors ============ YourKit -======= +------- YourKit is kindly supporting open source projects with its full-featured Java Profiler. YourKit, LLC is the creator of innovative and intelligent tools for profiling Java and .NET applications. diff --git a/akka-docs/dev/team.rst b/akka-docs/dev/team.rst index 4038fa9347..53991eb260 100644 --- a/akka-docs/dev/team.rst +++ b/akka-docs/dev/team.rst @@ -1,9 +1,11 @@ +.. _team: + Team ===== -=================== ========================== ================================= +=================== ========================== ==================================== Name Role Email -=================== ========================== ================================= +=================== ========================== ==================================== Jonas Bonér Founder, Despot, Committer jonas AT jonasboner DOT com Viktor Klang Bad cop, Committer viktor DOT klang AT gmail DOT com Debasish Ghosh Committer dghosh AT acm DOT org @@ -21,5 +23,6 @@ Irmo Manie Committer Heiko Seeberger Committer Hiram Chirino Committer Scott Clasen Committer -Roland Kuhn Committer -=================== ========================== ================================= \ No newline at end of file +Roland Kuhn Committer +Patrik Nordwall Committer patrik DOT nordwall AT gmail DOT com +=================== ========================== ==================================== \ No newline at end of file diff --git a/akka-docs/general/issue-tracking.rst b/akka-docs/general/issue-tracking.rst index a57e2f9c99..7c9afd9ade 100644 --- a/akka-docs/general/issue-tracking.rst +++ b/akka-docs/general/issue-tracking.rst @@ -1,3 +1,5 @@ +.. _issue_tracking: + Issue Tracking ============== diff --git a/akka-docs/general/licenses.rst b/akka-docs/general/licenses.rst index 02a3a10cec..b7104d9679 100644 --- a/akka-docs/general/licenses.rst +++ b/akka-docs/general/licenses.rst @@ -1,3 +1,5 @@ +.. _licenses: + Licenses ======== diff --git a/akka-docs/general/migration-guides.rst b/akka-docs/general/migration-guides.rst index 31e0593f71..bf43d939d5 100644 --- a/akka-docs/general/migration-guides.rst +++ b/akka-docs/general/migration-guides.rst @@ -1,3 +1,5 @@ +.. _migration: + Migration Guides ================ diff --git a/akka-docs/index.rst b/akka-docs/index.rst index 692664ce81..39245a1bfb 100644 --- a/akka-docs/index.rst +++ b/akka-docs/index.rst @@ -11,7 +11,31 @@ Contents java/index dev/index -Links -===== +Project Info +============ + +* :ref:`migration` * `Support `_ + +* `Downloads `_ + +* `Source Code `_ + +* `API `_ + +* `Maven Repository `_ + +* `Mailing List `_ + +* :ref:`licenses` + +* :ref:`issue_tracking` + +* :ref:`team` + +* :ref:`developer_guidelines` + +* :ref:`documentation` + +* :ref:`sponsors` \ No newline at end of file From 263934485535984b33d244f192cc58484d78924f Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 6 May 2011 08:45:15 +0200 Subject: [PATCH 167/233] Docs: Moved from pending (cherry picked from commit c10d94439dcca4bb9f08be2bc2f92442bb7b3cd4) --- .../{pending => additional}/articles.rst | 0 .../{pending => additional}/benchmarks.rst | 0 .../companies-using-akka.rst | 0 .../external-sample-projects.rst | 0 akka-docs/additional/index.rst | 14 ++++++++++++++ .../language-bindings.rst | 0 .../Recipes.rst => additional/recipes.rst} | 0 .../stability-matrix.rst} | 0 akka-docs/index.rst | 13 ++++++------- akka-docs/project/index.rst | 7 +++++++ akka-docs/project/links.rst | 19 +++++++++++++++++++ 11 files changed, 46 insertions(+), 7 deletions(-) rename akka-docs/{pending => additional}/articles.rst (100%) rename akka-docs/{pending => additional}/benchmarks.rst (100%) rename akka-docs/{pending => additional}/companies-using-akka.rst (100%) rename akka-docs/{pending => additional}/external-sample-projects.rst (100%) create mode 100644 akka-docs/additional/index.rst rename akka-docs/{pending => additional}/language-bindings.rst (100%) rename akka-docs/{pending/Recipes.rst => additional/recipes.rst} (100%) rename akka-docs/{pending/Feature Stability Matrix.rst => additional/stability-matrix.rst} (100%) create mode 100644 akka-docs/project/index.rst create mode 100644 akka-docs/project/links.rst diff --git a/akka-docs/pending/articles.rst b/akka-docs/additional/articles.rst similarity index 100% rename from akka-docs/pending/articles.rst rename to akka-docs/additional/articles.rst diff --git a/akka-docs/pending/benchmarks.rst b/akka-docs/additional/benchmarks.rst similarity index 100% rename from akka-docs/pending/benchmarks.rst rename to akka-docs/additional/benchmarks.rst diff --git a/akka-docs/pending/companies-using-akka.rst b/akka-docs/additional/companies-using-akka.rst similarity index 100% rename from akka-docs/pending/companies-using-akka.rst rename to akka-docs/additional/companies-using-akka.rst diff --git a/akka-docs/pending/external-sample-projects.rst b/akka-docs/additional/external-sample-projects.rst similarity index 100% rename from akka-docs/pending/external-sample-projects.rst rename to akka-docs/additional/external-sample-projects.rst diff --git a/akka-docs/additional/index.rst b/akka-docs/additional/index.rst new file mode 100644 index 0000000000..b8573a450f --- /dev/null +++ b/akka-docs/additional/index.rst @@ -0,0 +1,14 @@ +Additional Information +====================== + +.. toctree:: + :maxdepth: 2 + + articles + benchmarks + `Support `_ + recipes + external-sample-projects + companies-using-akka + language-bindings + stability-matrix diff --git a/akka-docs/pending/language-bindings.rst b/akka-docs/additional/language-bindings.rst similarity index 100% rename from akka-docs/pending/language-bindings.rst rename to akka-docs/additional/language-bindings.rst diff --git a/akka-docs/pending/Recipes.rst b/akka-docs/additional/recipes.rst similarity index 100% rename from akka-docs/pending/Recipes.rst rename to akka-docs/additional/recipes.rst diff --git a/akka-docs/pending/Feature Stability Matrix.rst b/akka-docs/additional/stability-matrix.rst similarity index 100% rename from akka-docs/pending/Feature Stability Matrix.rst rename to akka-docs/additional/stability-matrix.rst diff --git a/akka-docs/index.rst b/akka-docs/index.rst index 39245a1bfb..c01678f8fd 100644 --- a/akka-docs/index.rst +++ b/akka-docs/index.rst @@ -10,22 +10,20 @@ Contents scala/index java/index dev/index + project/index + additional/index -Project Info -============ +Quick Links +=========== * :ref:`migration` -* `Support `_ - * `Downloads `_ * `Source Code `_ * `API `_ -* `Maven Repository `_ - * `Mailing List `_ * :ref:`licenses` @@ -38,4 +36,5 @@ Project Info * :ref:`documentation` -* :ref:`sponsors` \ No newline at end of file +* :ref:`sponsors` + diff --git a/akka-docs/project/index.rst b/akka-docs/project/index.rst new file mode 100644 index 0000000000..13671bfa28 --- /dev/null +++ b/akka-docs/project/index.rst @@ -0,0 +1,7 @@ +Project Information +=================== + +.. toctree:: + :maxdepth: 2 + + links diff --git a/akka-docs/project/links.rst b/akka-docs/project/links.rst new file mode 100644 index 0000000000..69b7380249 --- /dev/null +++ b/akka-docs/project/links.rst @@ -0,0 +1,19 @@ +.. _links: + +`Support `_ +======================================== + +`Downloads `_ +======================================== + +`Source Code `_ +============================================== + +`API `_ +========================================== + +`Maven Repository `_ +================================================ + +`Mailing List `_ +========================================================== \ No newline at end of file From a59791599316c93bc2e15bc7d72141e2e419bea2 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 6 May 2011 09:03:57 +0200 Subject: [PATCH 168/233] Docs: Restructured toc (cherry picked from commit b6ea22e994ab900dad2661861cd90a7ab969ceb4) --- akka-docs/additional/index.rst | 1 - akka-docs/{general => dev}/building-akka.rst | 0 akka-docs/dev/index.rst | 5 +++-- akka-docs/general/index.rst | 6 +----- akka-docs/index.rst | 13 +------------ akka-docs/project/index.rst | 6 ++++++ akka-docs/{general => project}/issue-tracking.rst | 0 akka-docs/{general => project}/licenses.rst | 0 akka-docs/project/links.rst | 3 --- .../migration-guide-0.7.x-0.8.x.rst | 0 .../migration-guide-0.8.x-0.9.x.rst | 0 .../migration-guide-0.9.x-0.10.x.rst | 0 .../migration-guide-1.0.x-1.1.x.rst | 0 akka-docs/{general => project}/migration-guides.rst | 0 akka-docs/{pending => project}/release-notes.rst | 0 akka-docs/{general => project}/scaladoc.rst | 0 akka-docs/{dev => project}/sponsors.rst | 0 akka-docs/scala/actors.rst | 2 +- 18 files changed, 12 insertions(+), 24 deletions(-) rename akka-docs/{general => dev}/building-akka.rst (100%) rename akka-docs/{general => project}/issue-tracking.rst (100%) rename akka-docs/{general => project}/licenses.rst (100%) rename akka-docs/{general => project}/migration-guide-0.7.x-0.8.x.rst (100%) rename akka-docs/{general => project}/migration-guide-0.8.x-0.9.x.rst (100%) rename akka-docs/{general => project}/migration-guide-0.9.x-0.10.x.rst (100%) rename akka-docs/{general => project}/migration-guide-1.0.x-1.1.x.rst (100%) rename akka-docs/{general => project}/migration-guides.rst (100%) rename akka-docs/{pending => project}/release-notes.rst (100%) rename akka-docs/{general => project}/scaladoc.rst (100%) rename akka-docs/{dev => project}/sponsors.rst (100%) diff --git a/akka-docs/additional/index.rst b/akka-docs/additional/index.rst index b8573a450f..389e68fa35 100644 --- a/akka-docs/additional/index.rst +++ b/akka-docs/additional/index.rst @@ -6,7 +6,6 @@ Additional Information articles benchmarks - `Support `_ recipes external-sample-projects companies-using-akka diff --git a/akka-docs/general/building-akka.rst b/akka-docs/dev/building-akka.rst similarity index 100% rename from akka-docs/general/building-akka.rst rename to akka-docs/dev/building-akka.rst diff --git a/akka-docs/dev/index.rst b/akka-docs/dev/index.rst index a4f1cee593..690ea88664 100644 --- a/akka-docs/dev/index.rst +++ b/akka-docs/dev/index.rst @@ -4,7 +4,8 @@ Information for Developers .. toctree:: :maxdepth: 2 - documentation + building-akka developer-guidelines - sponsors + documentation team + diff --git a/akka-docs/general/index.rst b/akka-docs/general/index.rst index a437095478..eaedbd37f7 100644 --- a/akka-docs/general/index.rst +++ b/akka-docs/general/index.rst @@ -5,10 +5,6 @@ General :maxdepth: 2 jmm - migration-guides - building-akka configuration event-handler - scaladoc - issue-tracking - licenses + diff --git a/akka-docs/index.rst b/akka-docs/index.rst index c01678f8fd..7fbe64f4c3 100644 --- a/akka-docs/index.rst +++ b/akka-docs/index.rst @@ -22,19 +22,8 @@ Quick Links * `Source Code `_ -* `API `_ - -* `Mailing List `_ - -* :ref:`licenses` +* :ref:`scaladoc` * :ref:`issue_tracking` -* :ref:`team` - -* :ref:`developer_guidelines` - -* :ref:`documentation` - -* :ref:`sponsors` diff --git a/akka-docs/project/index.rst b/akka-docs/project/index.rst index 13671bfa28..c429e74379 100644 --- a/akka-docs/project/index.rst +++ b/akka-docs/project/index.rst @@ -4,4 +4,10 @@ Project Information .. toctree:: :maxdepth: 2 + migration-guides + release-notes + scaladoc + issue-tracking + licenses + sponsors links diff --git a/akka-docs/general/issue-tracking.rst b/akka-docs/project/issue-tracking.rst similarity index 100% rename from akka-docs/general/issue-tracking.rst rename to akka-docs/project/issue-tracking.rst diff --git a/akka-docs/general/licenses.rst b/akka-docs/project/licenses.rst similarity index 100% rename from akka-docs/general/licenses.rst rename to akka-docs/project/licenses.rst diff --git a/akka-docs/project/links.rst b/akka-docs/project/links.rst index 69b7380249..7f1c061a26 100644 --- a/akka-docs/project/links.rst +++ b/akka-docs/project/links.rst @@ -9,9 +9,6 @@ `Source Code `_ ============================================== -`API `_ -========================================== - `Maven Repository `_ ================================================ diff --git a/akka-docs/general/migration-guide-0.7.x-0.8.x.rst b/akka-docs/project/migration-guide-0.7.x-0.8.x.rst similarity index 100% rename from akka-docs/general/migration-guide-0.7.x-0.8.x.rst rename to akka-docs/project/migration-guide-0.7.x-0.8.x.rst diff --git a/akka-docs/general/migration-guide-0.8.x-0.9.x.rst b/akka-docs/project/migration-guide-0.8.x-0.9.x.rst similarity index 100% rename from akka-docs/general/migration-guide-0.8.x-0.9.x.rst rename to akka-docs/project/migration-guide-0.8.x-0.9.x.rst diff --git a/akka-docs/general/migration-guide-0.9.x-0.10.x.rst b/akka-docs/project/migration-guide-0.9.x-0.10.x.rst similarity index 100% rename from akka-docs/general/migration-guide-0.9.x-0.10.x.rst rename to akka-docs/project/migration-guide-0.9.x-0.10.x.rst diff --git a/akka-docs/general/migration-guide-1.0.x-1.1.x.rst b/akka-docs/project/migration-guide-1.0.x-1.1.x.rst similarity index 100% rename from akka-docs/general/migration-guide-1.0.x-1.1.x.rst rename to akka-docs/project/migration-guide-1.0.x-1.1.x.rst diff --git a/akka-docs/general/migration-guides.rst b/akka-docs/project/migration-guides.rst similarity index 100% rename from akka-docs/general/migration-guides.rst rename to akka-docs/project/migration-guides.rst diff --git a/akka-docs/pending/release-notes.rst b/akka-docs/project/release-notes.rst similarity index 100% rename from akka-docs/pending/release-notes.rst rename to akka-docs/project/release-notes.rst diff --git a/akka-docs/general/scaladoc.rst b/akka-docs/project/scaladoc.rst similarity index 100% rename from akka-docs/general/scaladoc.rst rename to akka-docs/project/scaladoc.rst diff --git a/akka-docs/dev/sponsors.rst b/akka-docs/project/sponsors.rst similarity index 100% rename from akka-docs/dev/sponsors.rst rename to akka-docs/project/sponsors.rst diff --git a/akka-docs/scala/actors.rst b/akka-docs/scala/actors.rst index ed186e17ba..ddd1e5f439 100644 --- a/akka-docs/scala/actors.rst +++ b/akka-docs/scala/actors.rst @@ -11,7 +11,7 @@ The `Actor Model `_ provides a higher The API of Akka’s Actors is similar to Scala Actors which has borrowed some of its syntax from Erlang. -The Akka 0.9 release introduced a new concept; ActorRef, which requires some refactoring. If you are new to Akka just read along, but if you have used Akka 0.6.x, 0.7.x and 0.8.x then you might be helped by the :doc:`0.8.x => 0.9.x migration guide ` +The Akka 0.9 release introduced a new concept; ActorRef, which requires some refactoring. If you are new to Akka just read along, but if you have used Akka 0.6.x, 0.7.x and 0.8.x then you might be helped by the :doc:`0.8.x => 0.9.x migration guide ` Creating Actors --------------- From ce0aa39e2f5c525cdc1e14242e2337affb3fea58 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 6 May 2011 09:27:50 +0200 Subject: [PATCH 169/233] Docs: Release notes is totally broken (cherry picked from commit 72ba6a69f034f6b7c15680274c6507c936b797e4) --- akka-docs/project/release-notes.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/akka-docs/project/release-notes.rst b/akka-docs/project/release-notes.rst index 692e7ce244..80d4c15dca 100644 --- a/akka-docs/project/release-notes.rst +++ b/akka-docs/project/release-notes.rst @@ -4,13 +4,13 @@ Release Notes Changes listed in no particular order. Current Development 1.1-SNAPSHOT -================================ +---------------------------------------- ||~ =Type= ||~ =Changes= ||~ =By= || || **UPD** || improve FSM DSL: make onTransition syntax nicer || Roland Kuhn || Release 1.1-M1 -============== +-------------------- ||~ =**Type** ||~ @@ -74,7 +74,7 @@ Release 1.1-M1 || **REM** || #686 Remove SBinary || Viktor Klang || Release 1.0-RC6 -=============== +---------------------------------------- ||~ =**Type** ||~ @@ -89,7 +89,7 @@ Release 1.0-RC6 || **FIX** || #629 Stuck upon actor invocation || Viktor Klang || Release 1.0-RC5 -=============== +---------------------------------------- ||~ =**Type** ||~ @@ -109,7 +109,7 @@ Release 1.0-RC5 || **FIX** || ActorRef.forward now doesn't require the sender to be set on the message || Viktor Klang || Release 1.0-RC3 -=============== +---------------------------------------- ||~ =**Type** ||~ @@ -146,7 +146,7 @@ Release 1.0-RC3 || **REM** || object UntypedActor, object ActorRegistry, class RemoteActor, class RemoteUntypedActor, class RemoteUntypedConsumerActor || Viktor Klang || Release 1.0-RC1 -=============== +---------------------------------------- ||~ =**Type** ||~ @@ -218,7 +218,7 @@ Release 1.0-RC1 || **REM** || JGroups integration, doesn't play with cloud services :/ || Viktor Klang || Release 1.0-MILESTONE1 -====================== +---------------------------------------- ||~ =**Type** ||~ @@ -295,7 +295,7 @@ Release 1.0-MILESTONE1 || **ADD** || #265 Java API for AMQP module || Irmo Manie || Release 0.10 - Aug 21 2010 -========================== +---------------------------------------- ``_ @@ -425,7 +425,7 @@ trapExit should pass through self with Exit to supervisor || Irmo Manie || || **FIX** || #340 RedisStorage Map.get does not throw exception when disconnected from redis but returns None || Debasish Ghosh || Release 0.9 - June 2th 2010 -=========================== +---------------------------------------- ||~ =**Type** ||~ @@ -509,7 +509,7 @@ Release 0.9 - June 2th 2010 || **REM** || Shoal cluster module || Viktor Klang || Release 0.8.1 - April 6th 2010 -============================== +---------------------------------------- ||~ =**Type** ||~ @@ -531,7 +531,7 @@ Release 0.8.1 - April 6th 2010 || **FIX** || Moved web initializer to new akka-servlet module || Viktor Klang || Release 0.8 - March 31st 2010 -============================= +---------------------------------------- ||~ =**Type** ||~ @@ -553,7 +553,7 @@ Release 0.8 - March 31st 2010 || **REMOVE** || Actor.send function || Jonas Bonér || Release 0.7 - March 21st 2010 -============================= +---------------------------------------- ||~ =**Type** ||~ @@ -586,7 +586,7 @@ Release 0.7 - March 21st 2010 || **FIX** || Various bugs fixed || Team || Release 0.6 - January 5th 2010 -============================== +---------------------------------------- ||~ =**Type** ||~ From 3a0858acd2d28389de726fb0041626e5fc47cf67 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 6 May 2011 09:35:08 +0200 Subject: [PATCH 170/233] Docs: fixed stability matrix (cherry picked from commit 6ef88eae3b96278e25ef386b657af82d9a1ec28f) --- akka-docs/additional/stability-matrix.rst | 41 ++++++++++++----------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/akka-docs/additional/stability-matrix.rst b/akka-docs/additional/stability-matrix.rst index cdbd6b3ad9..636cab6c5f 100644 --- a/akka-docs/additional/stability-matrix.rst +++ b/akka-docs/additional/stability-matrix.rst @@ -10,22 +10,25 @@ Explanation of the different levels of stability * **Stable** - Ready for use in production environment * **In progress** - Not enough feedback/use to claim it's ready for production use -||~ Feature ||~ Solid ||~ Stable ||~ In progress || -||= ====`Actors (Scala) `_ ==== ||= Solid ||= ||= || -||= ====`Actors (Java) `_ ==== ||= Solid ||= ||= || -||= ====` Typed Actors (Scala) `_ ==== ||= Solid ||= ||= || -||= ====` Typed Actors (Java) `_ ==== ||= Solid ||= ||= || -||= ====`STM (Scala) `_ ==== ||= Solid ||= ||= || -||= ====`STM (Java) `_ ==== ||= Solid ||= ||= || -||= ====`Transactors (Scala) `_ ==== ||= Solid ||= ||= || -||= ====`Transactors (Java) `_ ==== ||= Solid ||= ||= || -||= ====`Remote Actors (Scala) `_ ==== ||= Solid ||= ||= || -||= ====`Remote Actors (Java) `_ ==== ||= Solid ||= ||= || -||= ====`Camel `_ ==== ||= Solid ||= ||= || -||= ====`AMQP `_ ==== ||= Solid ||= ||= || -||= ====`HTTP `_ ==== ||= Solid ||= ||= || -||= ====`Integration Guice `_ ==== ||= ||= Stable ||= || -||= ====`Integration Spring `_ ==== ||= ||= Stable ||= || -||= ====`JTA `_ ==== ||= ||= Stable ||= || -||= ====`Scheduler `_ ==== ||= Solid ||= ||= || -||= ====`Redis Pub Sub `_ ==== ||= ||= ||= In progress || +================================ ============ ============ ============ +Feature Solid Stable In progress +================================ ============ ============ ============ +Actors (Scala) Solid +Actors (Java) Solid +Typed Actors (Scala) Solid +Typed Actors (Java) Solid +STM (Scala) Solid +STM (Java) Solid +Transactors (Scala) Solid +Transactors (Java) Solid +Remote Actors (Scala) Solid +Remote Actors (Java) Solid +Camel Solid +AMQP Solid +HTTP Solid +Integration Guice Stable +Integration Spring Stable +JTA Stable +Scheduler Solid +Redis Pub Sub In progress +================================ ============ ============ ============ From ed97e07bcaa041768ee2de39c2bfdb8207e6b577 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 6 May 2011 09:42:21 +0200 Subject: [PATCH 171/233] Docs: Fixed benchmarks (cherry picked from commit 9099a6ae18f7f3a6edf0bead6f095e74c12030db) --- akka-docs/additional/benchmarks.rst | 36 ++++++++++-------- ...nchmark-akka-sample-trading-throughput.png | Bin 0 -> 14714 bytes akka-docs/intro/index.rst | 2 +- 3 files changed, 21 insertions(+), 17 deletions(-) create mode 100644 akka-docs/images/benchmark-akka-sample-trading-throughput.png diff --git a/akka-docs/additional/benchmarks.rst b/akka-docs/additional/benchmarks.rst index 6352040d32..359914379f 100644 --- a/akka-docs/additional/benchmarks.rst +++ b/akka-docs/additional/benchmarks.rst @@ -4,28 +4,32 @@ Benchmarks Scalability, Throughput and Latency benchmark --------------------------------------------- -``_ + .. image:: ../images/benchmark-akka-sample-trading-throughput.png Simple Trading system. -* `Here is the result with some graphs `_ -* `Here is the article `_ -* `Here is the code `_ + +- `Here is the result with some graphs `_ +- `Here is the article `_ +- `Here is the code `_ Compares: -* Synchronous Scala solution -* Scala library Actors -** Fire-forget -** Request-reply -* Akka -** Request-reply -** Fire-forget with default dispatcher -** Fire-forget with Hawt dispatcher + +- Synchronous Scala solution +- Scala library Actors + + - Fire-forget + - Request-reply + +- Akka + - Request-reply + - Fire-forget with default dispatcher + - Fire-forget with Hawt dispatcher Performance benchmark --------------------- Benchmarking Akka against: -* Scala Library Actors -* Raw Java concurrency -* Jetlang (Java actors lib) -``_ + +- Scala Library Actors +- Raw Java concurrency +- Jetlang (Java actors lib) ``_ diff --git a/akka-docs/images/benchmark-akka-sample-trading-throughput.png b/akka-docs/images/benchmark-akka-sample-trading-throughput.png new file mode 100644 index 0000000000000000000000000000000000000000..52cc7819da7ecaea201f92568771873e64dae55d GIT binary patch literal 14714 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 Date: Fri, 6 May 2011 09:55:57 +0200 Subject: [PATCH 172/233] Docs: Fixed third-party-integrations (cherry picked from commit 720545de2b5ca0c47ae98428f3803109b1ff31c8) --- akka-docs/additional/index.rst | 1 + akka-docs/{pending => additional}/third-party-integrations.rst | 0 2 files changed, 1 insertion(+) rename akka-docs/{pending => additional}/third-party-integrations.rst (100%) diff --git a/akka-docs/additional/index.rst b/akka-docs/additional/index.rst index 389e68fa35..a2090bea43 100644 --- a/akka-docs/additional/index.rst +++ b/akka-docs/additional/index.rst @@ -9,5 +9,6 @@ Additional Information recipes external-sample-projects companies-using-akka + third-party-integrations language-bindings stability-matrix diff --git a/akka-docs/pending/third-party-integrations.rst b/akka-docs/additional/third-party-integrations.rst similarity index 100% rename from akka-docs/pending/third-party-integrations.rst rename to akka-docs/additional/third-party-integrations.rst From 0019015d9ec28195e5d2ac1e88a8214408b2f8f8 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 6 May 2011 09:57:25 +0200 Subject: [PATCH 173/233] Docs: Fixed third-party-integrations (cherry picked from commit 560296035172737efac428fbfb8f2d4e1e5e8cea) --- akka-docs/additional/third-party-integrations.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/akka-docs/additional/third-party-integrations.rst b/akka-docs/additional/third-party-integrations.rst index 579c3123d0..be663c4e4b 100644 --- a/akka-docs/additional/third-party-integrations.rst +++ b/akka-docs/additional/third-party-integrations.rst @@ -2,19 +2,20 @@ Third-party Integrations ======================== The Play! Framework -=================== +------------------- Dustin Whitney has done an Akka integration module for the `Play! framework `_. Detailed instructions here: ``_. There are three screencasts: -# Using Play! with Akka STM: ``_ -# Using Play! with Akka Actors: ``_ -# Using Play! with Akka Remote Actors: ``_ + +- Using Play! with Akka STM: ``_ +- Using Play! with Akka Actors: ``_ +- Using Play! with Akka Remote Actors: ``_ The Pinky REST/MVC Framework -============================ +---------------------------- Peter Hausel has done an Akka integration module for the `Pinky framework `_. From 97105ba62535ada67200f7d6011c60407c697148 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 6 May 2011 10:00:11 +0200 Subject: [PATCH 174/233] Docs: Moved routing from pending (cherry picked from commit d60de88117e18fc8cadb6b845351c4ad95f5dd43) --- akka-docs/java/index.rst | 1 + akka-docs/{pending/routing-java.rst => java/routing.rst} | 0 akka-docs/scala/index.rst | 1 + akka-docs/{pending/routing-scala.rst => scala/routing.rst} | 0 4 files changed, 2 insertions(+) rename akka-docs/{pending/routing-java.rst => java/routing.rst} (100%) rename akka-docs/{pending/routing-scala.rst => scala/routing.rst} (100%) diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst index c968af9c16..734151c5d2 100644 --- a/akka-docs/java/index.rst +++ b/akka-docs/java/index.rst @@ -14,3 +14,4 @@ Java API serialization fault-tolerance dispatchers + routing diff --git a/akka-docs/pending/routing-java.rst b/akka-docs/java/routing.rst similarity index 100% rename from akka-docs/pending/routing-java.rst rename to akka-docs/java/routing.rst diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index 530d125fa2..fad25026af 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -16,6 +16,7 @@ Scala API serialization fault-tolerance dispatchers + routing fsm http testing diff --git a/akka-docs/pending/routing-scala.rst b/akka-docs/scala/routing.rst similarity index 100% rename from akka-docs/pending/routing-scala.rst rename to akka-docs/scala/routing.rst From 18b58d9f63e6ba91bb6f8e60f3305613e577b183 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 6 May 2011 10:09:16 +0200 Subject: [PATCH 175/233] Docs: Cleanup of routing (cherry picked from commit 744d4c2dd626913898ed1456bb5fc287236e83a2) --- akka-docs/java/routing.rst | 15 ++++++++------- akka-docs/scala/routing.rst | 16 +++++++++++----- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/akka-docs/java/routing.rst b/akka-docs/java/routing.rst index 2c818af896..4caae2a7d0 100644 --- a/akka-docs/java/routing.rst +++ b/akka-docs/java/routing.rst @@ -1,8 +1,8 @@ Routing (Java) ============== -**UntypedDispatcher** ---------------------- +UntypedDispatcher +----------------- An UntypedDispatcher is an actor that routes incoming messages to outbound actors. @@ -42,8 +42,8 @@ An UntypedDispatcher is an actor that routes incoming messages to outbound actor dispatcher.sendOneWay("Ping"); //Prints "Pinger: Ping" dispatcher.sendOneWay("Pong"); //Prints "Ponger: Pong" -**UntypedLoadBalancer** ------------------------ +UntypedLoadBalancer +------------------- An UntypedLoadBalancer is an actor that forwards messages it receives to a boundless sequence of destination actors. @@ -88,6 +88,7 @@ An UntypedLoadBalancer is an actor that forwards messages it receives to a bound You can also send a 'new Routing.Broadcast(msg)' message to the router to have it be broadcasted out to all the actors it represents. -``_ -router.sendOneWay(new Routing.Broadcast(new PoisonPill())); -``_ +.. code-block:: java + + router.sendOneWay(new Routing.Broadcast(new PoisonPill())); + diff --git a/akka-docs/scala/routing.rst b/akka-docs/scala/routing.rst index 3885290ccb..6d22f224c0 100644 --- a/akka-docs/scala/routing.rst +++ b/akka-docs/scala/routing.rst @@ -1,4 +1,9 @@ -**Routing / Patterns (Scala)** +Routing (Scala) +=============== + +.. sidebar:: Contents + + .. contents:: :local: Akka-core includes some building blocks to build more complex message flow handlers, they are listed and explained below: @@ -7,7 +12,7 @@ Dispatcher A Dispatcher is an actor that routes incoming messages to outbound actors. -To use it you can either create a Dispatcher through the **dispatcherActor()** factory method +To use it you can either create a Dispatcher through the ``dispatcherActor()` factory method .. code-block:: scala @@ -69,7 +74,7 @@ LoadBalancer A LoadBalancer is an actor that forwards messages it receives to a boundless sequence of destination actors. -Example using the **loadBalancerActor()** factory method: +Example using the ``loadBalancerActor()`` factory method: .. code-block:: scala @@ -147,11 +152,10 @@ Selection ^^^^^^^^^ All pools require a *Selector* to be mixed-in. This trait controls how and how many actors in the pool will receive the incoming message. Define *selectionCount* to some positive number greater than one to route to multiple actors. Currently two are provided: + * `SmallestMailboxSelector `_ - Using the exact same logic as the iterator of the same name, the pooled actor with the fewest number of pending messages will be chosen. * `RoundRobinSelector `_ - Performs a very simple index-based selection, wrapping around the end of the list, very much like the CyclicIterator does. -* - Partial Fills ************* @@ -161,6 +165,7 @@ Capacity ^^^^^^^^ As you'd expect, capacity traits determine how the pool is funded with actors. There are two types of strategies that can be employed: + * `FixedCapacityStrategy `_ - When you mix this into your actor pool, you define a pool size and when the pool is started, it will have that number of actors within to which messages will be delegated. * `BoundedCapacityStrategy `_ - When you mix this into your actor pool, you define upper and lower bounds, and when the pool is started, it will have the minimum number of actors in place to handle messages. You must also mix-in a Capacitor and a Filter when using this strategy (see below). @@ -204,6 +209,7 @@ A *Filter* is a trait that modifies the raw pressure reading returned from a Cap } Here we see how the filter function will have the chance to modify the pressure reading to influence the capacity change. You are free to implement filter() however you like. We provide a `Filter `_ trait that evaluates both a rampup and a backoff subfilter to determine how to use the pressure reading to alter the pool capacity. There are several subfilters available to use, though again you may create whatever makes the most sense for you pool: + * `BasicRampup `_ - When pressure exceeds current capacity, increase the number of actors in the pool by some factor (*rampupRate*) of the current pool size. * `BasicBackoff `_ - When the pressure ratio falls under some predefined amount (*backoffThreshold*), decrease the number of actors in the pool by some factor of the current pool size. * `RunningMeanBackoff `_ - This filter tracks the average pressure-to-capacity over the lifetime of the pool (or since the last time the filter was reset) and will begin to reduce capacity once this mean falls below some predefined amount. The number of actors that will be stopped is determined by some factor of the difference between the current capacity and pressure. The idea behind this filter is to reduce the likelihood of "thrashing" (removing then immediately creating...) pool actors by delaying the backoff until some quiescent stage of the pool. Put another way, use this subfilter to allow quick rampup to handle load and more subtle backoff as that decreases over time. From 9c02cd1d905428dcc3297b43a269896cef757ebf Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 6 May 2011 11:33:32 +0200 Subject: [PATCH 176/233] Docs: Converted release notes (cherry picked from commit 0df737e1c7484047616112af8b6daab44d557e73) --- akka-docs/project/release-notes.rst | 1082 ++++++++++++--------------- 1 file changed, 482 insertions(+), 600 deletions(-) diff --git a/akka-docs/project/release-notes.rst b/akka-docs/project/release-notes.rst index 80d4c15dca..32b088c3a2 100644 --- a/akka-docs/project/release-notes.rst +++ b/akka-docs/project/release-notes.rst @@ -6,651 +6,533 @@ Changes listed in no particular order. Current Development 1.1-SNAPSHOT ---------------------------------------- -||~ =Type= ||~ =Changes= ||~ =By= || -|| **UPD** || improve FSM DSL: make onTransition syntax nicer || Roland Kuhn || +- **UPD** - improve FSM DSL: make onTransition syntax nicer (Roland Kuhn) Release 1.1-M1 -------------------- -||~ =**Type** - ||~ -===== - -**Changes** - ||~ -===== - -**By**= || -|| **ADD** || #647 Extract an akka-camel-typed module out of akka-camel for optional typed actor support || Martin Krasser || -|| **ADD** || #654 Allow consumer actors to acknowledge in-only message exchanges || Martin Krasser || -|| **ADD** || #669 Support self.reply in preRestart and postStop after exception in receive || Martin Krasser || -|| **ADD** || #682 Support for fault-tolerant Producer actors || Martin Krasser || -|| **ADD** || Move TestKit to akka-testkit and add CallingThreadDispatcher || Roland Kuhn || -|| **ADD** || Remote Client message buffering transaction log for buffering messages failed to send due to network problems. Flushes the buffer on reconnect. || Jonas Bonér || -|| **ADD** || Added trait simulate network problems/errors to be used for remote actor testing || Jonas Bonér || -|| **ADD** || Add future and await methods to Agent || Peter Vlugter || -|| **ADD** || #586 Allow explicit reconnect for RemoteClient || Viktor Klang || -|| **ADD** || #587 Dead letter sink queue for messages sent through RemoteClient that didn't get sent due to connection failure || Viktor Klang || -|| **ADD** || #598 actor.id when using akka-spring should be the id of the spring bean || Viktor Klang || -|| **ADD** || #652 Reap expired futures from ActiveRemoteClientHandler || Viktor Klang || -|| **ADD** || #656 Squeeze more out of EBEDD? || Viktor Klang || -|| **ADD** || #715 EventHandler.error should be usable without Throwable || Viktor Klang || -|| **ADD** || #717 Add ExecutionHandler to NettyRemoteServer for more performance and scalability || Viktor Klang || -|| **ADD** || #497 Optimize remote sends done in local scope || Viktor Klang || -|| **ADD** || #633 Add support for Scalaz in akka-modules || Derek Williams || -|| **ADD** || #677 Add map, flatMap, foreach, and filter to Future || Derek Williams || -|| **ADD** || #661 Optimized Future's internals || Derek Williams || -|| **ADD** || #685 Optimize execution of Futures || Derek Williams || -|| **ADD** || #711 Make Future.completeWith work with an uncompleted Future || Derek Williams || -|| **UPD** || #667 Upgrade to Camel 2.7.0 || Martin Krasser || -|| **UPD** || Updated HawtDispatch to 1.1 || Hiram Chirino || -|| **UPD** || #688 Update Akka 1.1-SNAPSHOT to Scala 2.9.0-RC1 || Viktor Klang || -|| **UPD** || #718 Add HawtDispatcher to akka-modules || Viktor Klang || -|| **UPD** || #698 Deprecate client-managed actors || Viktor Klang || -|| **UPD** || #730 Update Akka and Akka Modules to SBT 0.7.6-RC0 || Viktor Klang || -|| **UPD** || #663 Update to latest scalatest || Derek Williams || -|| **FIX** || Misc cleanup, API changes and refactorings || Jonas Bonér || -|| **FIX** || #675 preStart() is called twice when creating new instance of TypedActor || Debasish Ghosh || -|| **FIX** || #704 Write docs for Java Serialization || Debasish Ghosh || -|| **FIX** || #645 Change Futures.awaitAll to not throw FutureTimeoutException but return a List[Option[Any]] || Viktor Klang || -|| **FIX** || #681 Clean exit using server-managed remote actor via client || Viktor Klang || -|| **FIX** || #720 Connection loss when sending to a dead remote actor || Viktor Klang || -|| **FIX** || #593 Move Jetty specific stuff (with deps) from akka-http to akka-kernel || Viktor Klang || -|| **FIX** || #638 ActiveRemoteClientHandler - Unexpected exception from downstream in remote client || Viktor Klang || -|| **FIX** || #655 Remote actors with non-uuid names doesnt work for req./reply-pattern || Viktor Klang || -|| **FIX** || #588 RemoteClient.shutdown does not remove client from Map with clients || Viktor Klang || -|| **FIX** || #672 Remoting breaks if mutual DNS lookup isn't possible || Viktor Klang || -|| **FIX** || #699 Remote typed actor per-session server won't start if called method has no result || Viktor Klang || -|| **FIX** || #702 Handle ReadTimeoutException in akka-remote || Viktor Klang || -|| **FIX** || #708 Fall back to Akka classloader if event-handler class cannot be found. || Viktor Klang || -|| **FIX** || #716 Split akka-http and clean-up dependencies || Viktor Klang || -|| **FIX** || #721 Inability to parse/load the Config should do a System.exit(-1) || Viktor Klang || -|| **FIX** || #722 Race condition in Actor hotswapping || Viktor Klang || -|| **FIX** || #723 MessageSerializer CNFE regression || Viktor Klang || -|| **FIX** || #680 Remote TypedActor behavior differs from local one when sending to generic interfaces || Viktor Klang || -|| **FIX** || #659 Calling await on a Future that is expired and uncompleted should throw an exception || Derek Williams || -|| **REM** || #626 Update and clean up dependencies || Viktor Klang || -|| **REM** || #623 Remove embedded-repo (Akka + Akka Modules) || Viktor Klang || -|| **REM** || #686 Remove SBinary || Viktor Klang || +- **ADD** - #647 Extract an akka-camel-typed module out of akka-camel for optional typed actor support (Martin Krasser) +- **ADD** - #654 Allow consumer actors to acknowledge in-only message exchanges (Martin Krasser) +- **ADD** - #669 Support self.reply in preRestart and postStop after exception in receive (Martin Krasser) +- **ADD** - #682 Support for fault-tolerant Producer actors (Martin Krasser) +- **ADD** - Move TestKit to akka-testkit and add CallingThreadDispatcher (Roland Kuhn) +- **ADD** - Remote Client message buffering transaction log for buffering messages failed to send due to network problems. Flushes the buffer on reconnect. (Jonas Bonér) +- **ADD** - Added trait simulate network problems/errors to be used for remote actor testing (Jonas Bonér) +- **ADD** - Add future and await methods to Agent (Peter Vlugter) +- **ADD** - #586 Allow explicit reconnect for RemoteClient (Viktor Klang) +- **ADD** - #587 Dead letter sink queue for messages sent through RemoteClient that didn't get sent due to connection failure (Viktor Klang) +- **ADD** - #598 actor.id when using akka-spring should be the id of the spring bean (Viktor Klang) +- **ADD** - #652 Reap expired futures from ActiveRemoteClientHandler (Viktor Klang) +- **ADD** - #656 Squeeze more out of EBEDD? (Viktor Klang) +- **ADD** - #715 EventHandler.error should be usable without Throwable (Viktor Klang) +- **ADD** - #717 Add ExecutionHandler to NettyRemoteServer for more performance and scalability (Viktor Klang) +- **ADD** - #497 Optimize remote sends done in local scope (Viktor Klang) +- **ADD** - #633 Add support for Scalaz in akka-modules (Derek Williams) +- **ADD** - #677 Add map, flatMap, foreach, and filter to Future (Derek Williams) +- **ADD** - #661 Optimized Future's internals (Derek Williams) +- **ADD** - #685 Optimize execution of Futures (Derek Williams) +- **ADD** - #711 Make Future.completeWith work with an uncompleted Future (Derek Williams) +- **UPD** - #667 Upgrade to Camel 2.7.0 (Martin Krasser) +- **UPD** - Updated HawtDispatch to 1.1 (Hiram Chirino) +- **UPD** - #688 Update Akka 1.1-SNAPSHOT to Scala 2.9.0-RC1 (Viktor Klang) +- **UPD** - #718 Add HawtDispatcher to akka-modules (Viktor Klang) +- **UPD** - #698 Deprecate client-managed actors (Viktor Klang) +- **UPD** - #730 Update Akka and Akka Modules to SBT 0.7.6-RC0 (Viktor Klang) +- **UPD** - #663 Update to latest scalatest (Derek Williams) +- **FIX** - Misc cleanup, API changes and refactorings (Jonas Bonér) +- **FIX** - #675 preStart() is called twice when creating new instance of TypedActor (Debasish Ghosh) +- **FIX** - #704 Write docs for Java Serialization (Debasish Ghosh) +- **FIX** - #645 Change Futures.awaitAll to not throw FutureTimeoutException but return a List[Option[Any]] (Viktor Klang) +- **FIX** - #681 Clean exit using server-managed remote actor via client (Viktor Klang) +- **FIX** - #720 Connection loss when sending to a dead remote actor (Viktor Klang) +- **FIX** - #593 Move Jetty specific stuff (with deps) from akka-http to akka-kernel (Viktor Klang) +- **FIX** - #638 ActiveRemoteClientHandler - Unexpected exception from downstream in remote client (Viktor Klang) +- **FIX** - #655 Remote actors with non-uuid names doesnt work for req./reply-pattern (Viktor Klang) +- **FIX** - #588 RemoteClient.shutdown does not remove client from Map with clients (Viktor Klang) +- **FIX** - #672 Remoting breaks if mutual DNS lookup isn't possible (Viktor Klang) +- **FIX** - #699 Remote typed actor per-session server won't start if called method has no result (Viktor Klang) +- **FIX** - #702 Handle ReadTimeoutException in akka-remote (Viktor Klang) +- **FIX** - #708 Fall back to Akka classloader if event-handler class cannot be found. (Viktor Klang) +- **FIX** - #716 Split akka-http and clean-up dependencies (Viktor Klang) +- **FIX** - #721 Inability to parse/load the Config should do a System.exit(-1) (Viktor Klang) +- **FIX** - #722 Race condition in Actor hotswapping (Viktor Klang) +- **FIX** - #723 MessageSerializer CNFE regression (Viktor Klang) +- **FIX** - #680 Remote TypedActor behavior differs from local one when sending to generic interfaces (Viktor Klang) +- **FIX** - #659 Calling await on a Future that is expired and uncompleted should throw an exception (Derek Williams) +- **REM** - #626 Update and clean up dependencies (Viktor Klang) +- **REM** - #623 Remove embedded-repo (Akka + Akka Modules) (Viktor Klang) +- **REM** - #686 Remove SBinary (Viktor Klang) Release 1.0-RC6 ---------------------------------------- -||~ =**Type** - ||~ -===== - -**Changes** - ||~ -===== - -**By**= || -|| **FIX** || #628 Supervied TypedActors fails to restart || Viktor Klang || -|| **FIX** || #629 Stuck upon actor invocation || Viktor Klang || +- **FIX** - #628 Supervied TypedActors fails to restart (Viktor Klang) +- **FIX** - #629 Stuck upon actor invocation (Viktor Klang) Release 1.0-RC5 ---------------------------------------- -||~ =**Type** - ||~ -===== - -**Changes** - ||~ -===== - -**By**= || -|| **FIX** || Source JARs published to 'src' instead of 'source' || Odd Moller || -|| **FIX** || #612 Conflict between Spring autostart=true for Consumer actors and || Martin Krasser || -|| **FIX** || #613 Change Akka XML schema URI to http://akka.io/schema/akka || Martin Krasser || -|| **FIX** || Spring XSD namespace changed from 'akkasource.org' to 'akka.io' || Viktor Klang || -|| **FIX** || Checking for remote secure cookie is disabled by default if no akka.conf is loaded || Viktor Klang || -|| **FIX** || Changed Casbah to ScalaToolsRepo for akka-sbt-plugin || Viktor Klang || -|| **FIX** || ActorRef.forward now doesn't require the sender to be set on the message || Viktor Klang || +- **FIX** - Source JARs published to 'src' instead of 'source' || Odd Moller || +- **FIX** - #612 Conflict between Spring autostart=true for Consumer actors and (Martin Krasser) +- **FIX** - #613 Change Akka XML schema URI to http://akka.io/schema/akka (Martin Krasser) +- **FIX** - Spring XSD namespace changed from 'akkasource.org' to 'akka.io' (Viktor Klang) +- **FIX** - Checking for remote secure cookie is disabled by default if no akka.conf is loaded (Viktor Klang) +- **FIX** - Changed Casbah to ScalaToolsRepo for akka-sbt-plugin (Viktor Klang) +- **FIX** - ActorRef.forward now doesn't require the sender to be set on the message (Viktor Klang) Release 1.0-RC3 ---------------------------------------- -||~ =**Type** - ||~ -===== - -**Changes** - ||~ -===== - -**By**= || -|| **ADD** || #568 Add autostart attribute to Spring actor configuration || Viktor Klang || -|| **ADD** || #586 Allow explicit reconnect for remote clients || Viktor Klang || -|| **ADD** || #587 Add possibility for dead letter queues for failed remote sends || Viktor Klang || -|| **ADD** || #497 Optimize remote send in local scope || Viktor Klang || -|| **ADD** || Improved Java Actor API: akka.actor.Actors || Viktor Klang || -|| **ADD** || Improved Scala Actor API: akka.actor.Actor || Viktor Klang || -|| **ADD** || #148 Create a testing framework for testing Actors || Roland Kuhn || -|| **ADD** || Support Replica Set/Replica Pair connection modes with MongoDB Persistence || Brendan McAdams || -|| **ADD** || User configurable Write Concern settings for MongoDB Persistence || Brendan McAdams || -|| **ADD** || Support for configuring MongoDB Persistence with MongoDB's URI Connection String || Brendan McAdams || -|| **ADD** || Support for Authentication with MongoDB Persistence || Brendan McAdams || -|| **FIX** || Misc bug fixes || Team || -|| **FIX** || #603 Race condition in Remote send || Viktor Klang || -|| **FIX** || #594 Log statement in RemoteClientHandler was wrongly formatted || Viktor Klang || -|| **FIX** || #580 Message uuids must be generated || Viktor Klang || -|| **FIX** || #583 Serialization classloader has a visibility issue || Viktor Klang || -|| **FIX** || #598 By default the bean ID should become the actor id for Spring actor configuration || Viktor Klang || -|| **FIX** || #577 RemoteClientHandler swallows certain exceptions || Viktor Klang || -|| **FIX** || #581 Fix edgecase where an exception could not be deserialized || Viktor Klang || -|| **FIX** || MongoDB write success wasn't being properly checked; fixed (integrated w/ new write concern features) || Brendan McAdams || -|| **UPD** || Improvements to FSM module akka.actor.FSM || Manie & Kuhn || -|| **UPD** || Changed Akka URI to http://akka.io. Reflects both XSDs, Maven repositories etc. || Jonas Bonér || -|| **REM** || #574 Remote RemoteClient, RemoteServer and RemoteNode || Viktor Klang || -|| **REM** || object UntypedActor, object ActorRegistry, class RemoteActor, class RemoteUntypedActor, class RemoteUntypedConsumerActor || Viktor Klang || +- **ADD** - #568 Add autostart attribute to Spring actor configuration (Viktor Klang) +- **ADD** - #586 Allow explicit reconnect for remote clients (Viktor Klang) +- **ADD** - #587 Add possibility for dead letter queues for failed remote sends (Viktor Klang) +- **ADD** - #497 Optimize remote send in local scope (Viktor Klang) +- **ADD** - Improved Java Actor API: akka.actor.Actors (Viktor Klang) +- **ADD** - Improved Scala Actor API: akka.actor.Actor (Viktor Klang) +- **ADD** - #148 Create a testing framework for testing Actors (Roland Kuhn) +- **ADD** - Support Replica Set/Replica Pair connection modes with MongoDB Persistence || Brendan McAdams || +- **ADD** - User configurable Write Concern settings for MongoDB Persistence || Brendan McAdams || +- **ADD** - Support for configuring MongoDB Persistence with MongoDB's URI Connection String || Brendan McAdams || +- **ADD** - Support for Authentication with MongoDB Persistence || Brendan McAdams || +- **FIX** - Misc bug fixes || Team || +- **FIX** - #603 Race condition in Remote send (Viktor Klang) +- **FIX** - #594 Log statement in RemoteClientHandler was wrongly formatted (Viktor Klang) +- **FIX** - #580 Message uuids must be generated (Viktor Klang) +- **FIX** - #583 Serialization classloader has a visibility issue (Viktor Klang) +- **FIX** - #598 By default the bean ID should become the actor id for Spring actor configuration (Viktor Klang) +- **FIX** - #577 RemoteClientHandler swallows certain exceptions (Viktor Klang) +- **FIX** - #581 Fix edgecase where an exception could not be deserialized (Viktor Klang) +- **FIX** - MongoDB write success wasn't being properly checked; fixed (integrated w/ new write concern features) || Brendan McAdams || +- **UPD** - Improvements to FSM module akka.actor.FSM || Manie & Kuhn || +- **UPD** - Changed Akka URI to http://akka.io. Reflects both XSDs, Maven repositories etc. (Jonas Bonér) +- **REM** - #574 Remote RemoteClient, RemoteServer and RemoteNode (Viktor Klang) +- **REM** - object UntypedActor, object ActorRegistry, class RemoteActor, class RemoteUntypedActor, class RemoteUntypedConsumerActor (Viktor Klang) Release 1.0-RC1 ---------------------------------------- -||~ =**Type** - ||~ -===== - -**Changes** - ||~ -===== - -**By**= || -|| **ADD** || #477 Added support for Remote Agents || Viktor Klang || -|| **ADD** || #460 Hotswap for Java API (UntypedActor) || Viktor Klang || -|| **ADD** || #471 Added support for TypedActors to return Java Option || Viktor Klang || -|| **ADD** || New design and API for more fluent and intuitive FSM module || Roland Kuhn || -|| **ADD** || Added secure cookie based remote node authentication || Jonas Bonér || -|| **ADD** || Untrusted safe mode for remote server || Jonas Bonér || -|| **ADD** || Refactored config file format - added list of enabled modules etc. || Jonas Bonér || -|| **ADD** || Docs for Dataflow Concurrency || Jonas Bonér || -|| **ADD** || Made remote message frame size configurable || Jonas Bonér || -|| **ADD** || #496 Detect when Remote Client disconnects || Jonas Bonér || -|| **ADD** || #472 Improve API to wait for endpoint activation/deactivation (`more `_ ...) || Martin Krasser || -|| **ADD** || #473 Allow consumer actors to customize their own routes (`more `_ ...) || Martin Krasser || -|| **ADD** || #504 Add session bound server managed remote actors || Paul Pach || -|| **ADD** || DSL for FSM || Irmo Manie || -|| **ADD** || Shared unit test for all dispatchers to enforce Actor Model || Viktor Klang || -|| **ADD** || #522 Make stacking optional for become and HotSwap || Viktor Klang || -|| **ADD** || #524 Make frame size configurable for client&server || Bonér & Klang || -|| **ADD** || #526 Add onComplete callback to Future || Viktor Klang || -|| **ADD** || #536 Document Channel-abstraction for later replies || Viktor Klang || -|| **ADD** || #540 Include self-reference as parameter to HotSwap || Viktor Klang || -|| **ADD** || #546 Include Garrick Evans' Akka-mist into master || Viktor Klang || -|| **ADD** || #438 Support remove operation in PersistentVector || Scott Clasen || -|| **ADD** || #229 Memcached protocol support for Persistence module || Scott Clasen || -|| **ADD** || Amazon SimpleDb support for Persistence module || Scott Clasen || -|| **FIX** || #518 refactor common storage bakend to use bulk puts/gets where possible || Scott Clasen || -|| **FIX** || #532 Prevent persistent datatypes with same uuid from corrupting a TX || Scott Clasen || -|| **FIX** || #464 ThreadPoolBuilder should be rewritten to be an immutable builder || Viktor Klang || -|| **FIX** || #449 Futures.awaitOne now uses onComplete listeners || Viktor Klang || -|| **FIX** || #486 Fixed memory leak caused by Configgy that prevented full unload || Viktor Klang || -|| **FIX** || #488 Fixed race condition in EBEDD restart || Viktor Klang || -|| **FIX** || #492 Fixed race condition in Scheduler || Viktor Klang || -|| **FIX** || #493 Switched to non-https repository for JBoss artifacts || Viktor Klang || -|| **FIX** || #481 Exception when creating an actor now behaves properly when supervised || Viktor Klang || -|| **FIX** || #498 Fixed no-op in supervision DSL || Viktor Klang || -|| **FIX** || #491 reply and reply_? now sets a sender reference || Viktor Klang || -|| **FIX** || #519 NotSerializableError when using Remote Typed Actors || Viktor Klang || -|| **FIX** || #523 Message.toString is called all the time for incomign messages, expensive || Viktor Klang || -|| **FIX** || #537 Make sure top folder is included in sources jar || Viktor Klang || -|| **FIX** || #529 Remove Scala version number from Akka artifact ids || Viktor Klang || -|| **FIX** || #533 Can't set LifeCycle from the Java API || Viktor Klang || -|| **FIX** || #542 Make Future-returning Remote Typed Actor methods use onComplete || Viktor Klang || -|| **FIX** || #479 Do not register listeners when CamelService is turned off by configuration || Martin Krasser || -|| **FIX** || Fixed bug with finding TypedActor by type in ActorRegistry || Jonas Bonér || -|| **FIX** || #515 race condition in FSM StateTimeout Handling || Irmo Manie || -|| **UPD** || Akka package from "se.scalablesolutions.akka" to "akka" || Viktor Klang || -|| **UPD** || Update Netty to 3.2.3.Final || Viktor Klang || -|| **UPD** || #458 Camel to 2.5.0 || Martin Krasser || -|| **UPD** || #458 Spring to 3.0.4.RELEASE || Martin Krasser || -|| **UPD** || #458 Jetty to 7.1.6.v20100715 || Martin Krasser || -|| **UPD** || Update to Scala 2.8.1 || Jonas Bonér || -|| **UPD** || Changed remote server default port to 2552 (AKKA) || Jonas Bonér || -|| **UPD** || Cleaned up and made remote protocol more effifient || Jonas Bonér || -|| **UPD** || #528 RedisPersistentRef should not throw in case of missing key || Debasish Ghosh || -|| **UPD** || #531 Fix RedisStorage add() method in Java API || Debasish Ghosh || -|| **UPD** || #513 Implement snapshot based persistence control in SortedSet || Debasish Ghosh || -|| **UPD** || #547 Update FSM docs || Irmo Manie || -|| **UPD** || #548 Update AMQP docs || Irmo Manie || -|| **REM** || Atmosphere integration, replace with Mist || Klang @ Evans || -|| **REM** || JGroups integration, doesn't play with cloud services :/ || Viktor Klang || +- **ADD** - #477 Added support for Remote Agents (Viktor Klang) +- **ADD** - #460 Hotswap for Java API (UntypedActor) (Viktor Klang) +- **ADD** - #471 Added support for TypedActors to return Java Option (Viktor Klang) +- **ADD** - New design and API for more fluent and intuitive FSM module (Roland Kuhn) +- **ADD** - Added secure cookie based remote node authentication (Jonas Bonér) +- **ADD** - Untrusted safe mode for remote server (Jonas Bonér) +- **ADD** - Refactored config file format - added list of enabled modules etc. (Jonas Bonér) +- **ADD** - Docs for Dataflow Concurrency (Jonas Bonér) +- **ADD** - Made remote message frame size configurable (Jonas Bonér) +- **ADD** - #496 Detect when Remote Client disconnects (Jonas Bonér) +- **ADD** - #472 Improve API to wait for endpoint activation/deactivation (`more `__ ...) (Martin Krasser) +- **ADD** - #473 Allow consumer actors to customize their own routes (`more `__ ...) (Martin Krasser) +- **ADD** - #504 Add session bound server managed remote actors || Paul Pach || +- **ADD** - DSL for FSM (Irmo Manie) +- **ADD** - Shared unit test for all dispatchers to enforce Actor Model (Viktor Klang) +- **ADD** - #522 Make stacking optional for become and HotSwap (Viktor Klang) +- **ADD** - #524 Make frame size configurable for client&server (Bonér & Klang) +- **ADD** - #526 Add onComplete callback to Future (Viktor Klang) +- **ADD** - #536 Document Channel-abstraction for later replies (Viktor Klang) +- **ADD** - #540 Include self-reference as parameter to HotSwap (Viktor Klang) +- **ADD** - #546 Include Garrick Evans' Akka-mist into master (Viktor Klang) +- **ADD** - #438 Support remove operation in PersistentVector (Scott Clasen) +- **ADD** - #229 Memcached protocol support for Persistence module (Scott Clasen) +- **ADD** - Amazon SimpleDb support for Persistence module (Scott Clasen) +- **FIX** - #518 refactor common storage bakend to use bulk puts/gets where possible (Scott Clasen) +- **FIX** - #532 Prevent persistent datatypes with same uuid from corrupting a TX (Scott Clasen) +- **FIX** - #464 ThreadPoolBuilder should be rewritten to be an immutable builder (Viktor Klang) +- **FIX** - #449 Futures.awaitOne now uses onComplete listeners (Viktor Klang) +- **FIX** - #486 Fixed memory leak caused by Configgy that prevented full unload (Viktor Klang) +- **FIX** - #488 Fixed race condition in EBEDD restart (Viktor Klang) +- **FIX** - #492 Fixed race condition in Scheduler (Viktor Klang) +- **FIX** - #493 Switched to non-https repository for JBoss artifacts (Viktor Klang) +- **FIX** - #481 Exception when creating an actor now behaves properly when supervised (Viktor Klang) +- **FIX** - #498 Fixed no-op in supervision DSL (Viktor Klang) +- **FIX** - #491 ``reply`` and ``reply_?`` now sets a sender reference (Viktor Klang) +- **FIX** - #519 NotSerializableError when using Remote Typed Actors (Viktor Klang) +- **FIX** - #523 Message.toString is called all the time for incomign messages, expensive (Viktor Klang) +- **FIX** - #537 Make sure top folder is included in sources jar (Viktor Klang) +- **FIX** - #529 Remove Scala version number from Akka artifact ids (Viktor Klang) +- **FIX** - #533 Can't set LifeCycle from the Java API (Viktor Klang) +- **FIX** - #542 Make Future-returning Remote Typed Actor methods use onComplete (Viktor Klang) +- **FIX** - #479 Do not register listeners when CamelService is turned off by configuration (Martin Krasser) +- **FIX** - Fixed bug with finding TypedActor by type in ActorRegistry (Jonas Bonér) +- **FIX** - #515 race condition in FSM StateTimeout Handling (Irmo Manie) +- **UPD** - Akka package from "se.scalablesolutions.akka" to "akka" (Viktor Klang) +- **UPD** - Update Netty to 3.2.3.Final (Viktor Klang) +- **UPD** - #458 Camel to 2.5.0 (Martin Krasser) +- **UPD** - #458 Spring to 3.0.4.RELEASE (Martin Krasser) +- **UPD** - #458 Jetty to 7.1.6.v20100715 (Martin Krasser) +- **UPD** - Update to Scala 2.8.1 (Jonas Bonér) +- **UPD** - Changed remote server default port to 2552 (AKKA) (Jonas Bonér) +- **UPD** - Cleaned up and made remote protocol more effifient (Jonas Bonér) +- **UPD** - #528 RedisPersistentRef should not throw in case of missing key (Debasish Ghosh) +- **UPD** - #531 Fix RedisStorage add() method in Java API (Debasish Ghosh) +- **UPD** - #513 Implement snapshot based persistence control in SortedSet (Debasish Ghosh) +- **UPD** - #547 Update FSM docs (Irmo Manie) +- **UPD** - #548 Update AMQP docs (Irmo Manie) +- **REM** - Atmosphere integration, replace with Mist (Klang @ Evans) +- **REM** - JGroups integration, doesn't play with cloud services :/ (Viktor Klang) Release 1.0-MILESTONE1 ---------------------------------------- -||~ =**Type** - ||~ -===== - -**Changes** - ||~ -===== - -**By**= || -|| **ADD** || Splitted akka-core up in akka-actor, akka-typed-actor & akka-remote || Jonas Bonér || -|| **ADD** || Added meta-data to network protocol || Jonas Bonér || -|| **ADD** || HotSwap and actor.become now uses a stack of PartialFunctions with API for pushing and popping the stack || Jonas Bonér || -|| **ADD** || #440 Create typed actors with constructor args || Michael Kober || -|| **ADD** || #322 Abstraction for unification of sender and senderFuture for later reply || Michael Kober || -|| **ADD** || #364 Serialization for TypedActor proxy reference || Michael Kober || -|| **ADD** || #423 Support configuration of Akka via Spring || Michael Kober || -|| **FIX** || #426 UUID wrong for remote proxy for server managed actor || Michael Kober || -|| **ADD** || #378 Support for server initiated remote TypedActor and UntypedActor in Spring config || Michael Kober || -||< **ADD** ||< #194 Support for server-managed typed actor ||< Michael Kober || -|| **ADD** || #447 Allow Camel service to be turned off by configuration || Martin Krasser || -|| **ADD** || #457 JavaAPI improvements for akka-camel (please read the `migration guide `_) || Martin Krasser || -|| **ADD** || #465 Dynamic message routing to actors (`more `_ ...) || Martin Krasser || -|| **FIX** || #410 Use log configuration from config directory || Martin Krasser || -|| **FIX** || #343 Some problems with persistent structures || Debasish Ghosh || -|| **FIX** || #430 Refactor / re-implement MongoDB adapter so that it conforms to the guidelines followed in Redis and Cassandra modules || Debasish Ghosh || -|| **FIX** || #436 ScalaJSON serialization does not map Int data types properly when used within a Map || Debasish Ghosh || -|| **ADD** || #230 Update redisclient to be Redis 2.0 compliant || Debasish Ghosh || -|| **FIX** || #435 Mailbox serialization does not retain messages || Debasish Ghosh || -|| **ADD** || #445 Integrate type class based serialization of sjson into Akka || Debasish Ghosh || -|| **FIX** || #480: Regression multibulk replies redis client || Debasish Ghosh || -|| **FIX** || #415 Publish now generate source and doc jars || Viktor Klang || -|| **FIX** || #420 REST endpoints should be able to be processed in parallel || Viktor Klang || -|| **FIX** || #422 Dispatcher config should work for ThreadPoolBuilder-based dispatchers || Viktor Klang || -|| **FIX** || #401 ActorRegistry should not leak memory || Viktor Klang || -|| **FIX** || #250 Performance optimization for ExecutorBasedEventDrivenDispatcher || Viktor Klang || -|| **FIX** || #419 Rename init and shutdown callbacks to preStart and postStop, and remove initTransactionalState || Viktor Klang || -|| **FIX** || #346 Make max no of restarts (and within) are now both optional || Viktor Klang || -|| **FIX** || #424 Actors self.supervisor not set by the time init() is called when started by startLink() || Viktor Klang || -|| **FIX** || #427 spawnLink and startLink now has the same dispatcher semantics || Viktor Klang || -|| **FIX** || #413 Actor shouldn't process more messages when waiting to be restarted (HawtDispatcher still does) || Viktor Klang || -|| **FIX** || !! and !!! now do now not block the actor when used in remote actor || Viktor Klang || -|| **FIX** || RemoteClient now reconnects properly || Viktor Klang || -|| **FIX** || Logger.warn now properly works with varargs || Viktor Klang || -|| **FIX** || #450 Removed ActorRef lifeCycle boilerplate: Some(LifeCycle(Permanent)) => Permanent || Viktor Klang || -|| **FIX** || Moved ActorRef.trapExit into ActorRef.faultHandler and removed Option-boilerplate from faultHandler || Viktor Klang || -|| **FIX** || ThreadBasedDispatcher cheaper for idling actors, also benefits from all that is ExecutorBasedEventDrivenDispatcher || Viktor Klang || -|| **FIX** || Fixing Futures.future, uses Actor.spawn under the hood, specify dispatcher to control where block is executed || Viktor Klang || -|| **FIX** || #469 Akka "dist" now uses a root folder to avoid loitering if unzipped in a folder || Viktor Klang || -|| **FIX** || Removed ScalaConfig, JavaConfig and rewrote Supervision configuration || Viktor Klang || -|| **UPD** || Jersey to 1.3 || Viktor Klang || -|| **UPD** || Atmosphere to 0.6.2 || Viktor Klang || -|| **UPD** || Netty to 3.2.2.Final || Viktor Klang || -|| **ADD** || Changed config file priority loading and added config modes. || Viktor Klang || -|| **ADD** || #411 Bumped Jetty to v 7 and migrated to it's eclipse packages || Viktor Klang || -|| **ADD** || #414 Migrate from Grizzly to Jetty for Akka Microkernel || Viktor Klang || -|| **ADD** || #261 Add Java API for 'routing' module || VIktor Klang || -|| **ADD** || #262 Add Java API for Agent || Viktor Klang || -|| **ADD** || #264 Add Java API for Dataflow || Viktor Klang || -|| **ADD** || Using JerseySimpleBroadcaster instead of JerseyBroadcaster in AkkaBroadcaster || Viktor Klang || -|| **ADD** || #433 Throughput deadline added for ExecutorBasedEventDrivenDispatcher || Viktor Klang || -|| **ADD** || Add possibility to set default cometSupport in akka.conf || Viktor Klang || -|| **ADD** || #451 Added possibility to use akka-http as a standalone REST server || Viktor Klang || -|| **ADD** || #446 Added support for Erlang-style receiveTimeout || Viktor Klang || -|| **ADD** || #462 Added support for suspend/resume of processing individual actors mailbox, should give clearer restart semantics || Viktor Klang || -|| **ADD** || #466 Actor.spawn now takes an implicit dispatcher to specify who should run the block || Viktor Klang || -|| **ADD** || #456 Added map to Future and Futures.awaitMap || Viktor Klang || -|| **REM** || #418 Remove Lift sample module and docs || Viktor Klang || -|| **REM** || Removed all Reactor-based dispatchers || Viktor Klang || -|| **REM** || Removed anonymous actor factories || Viktor Klang || -|| **ADD** || Voldemort support for akka-persistence || Scott Clasen || -|| **ADD** || HBase support for akka-persistence || David Greco || -|| **ADD** || CouchDB support for akka-persistence || Yung-Luen Lan & Kahlen || -|| **ADD** || #265 Java API for AMQP module || Irmo Manie || +- **ADD** - Splitted akka-core up in akka-actor, akka-typed-actor & akka-remote (Jonas Bonér) +- **ADD** - Added meta-data to network protocol (Jonas Bonér) +- **ADD** - HotSwap and actor.become now uses a stack of PartialFunctions with API for pushing and popping the stack (Jonas Bonér) +- **ADD** - #440 Create typed actors with constructor args (Michael Kober) +- **ADD** - #322 Abstraction for unification of sender and senderFuture for later reply (Michael Kober) +- **ADD** - #364 Serialization for TypedActor proxy reference (Michael Kober) +- **ADD** - #423 Support configuration of Akka via Spring (Michael Kober) +- **FIX** - #426 UUID wrong for remote proxy for server managed actor (Michael Kober) +- **ADD** - #378 Support for server initiated remote TypedActor and UntypedActor in Spring config (Michael Kober) +- **ADD** - #194 Support for server-managed typed actor ||< Michael Kober || +- **ADD** - #447 Allow Camel service to be turned off by configuration (Martin Krasser) +- **ADD** - #457 JavaAPI improvements for akka-camel (please read the `migration guide `_) (Martin Krasser) +- **ADD** - #465 Dynamic message routing to actors (`more `__ ...) (Martin Krasser) +- **FIX** - #410 Use log configuration from config directory (Martin Krasser) +- **FIX** - #343 Some problems with persistent structures (Debasish Ghosh) +- **FIX** - #430 Refactor / re-implement MongoDB adapter so that it conforms to the guidelines followed in Redis and Cassandra modules (Debasish Ghosh) +- **FIX** - #436 ScalaJSON serialization does not map Int data types properly when used within a Map (Debasish Ghosh) +- **ADD** - #230 Update redisclient to be Redis 2.0 compliant (Debasish Ghosh) +- **FIX** - #435 Mailbox serialization does not retain messages (Debasish Ghosh) +- **ADD** - #445 Integrate type class based serialization of sjson into Akka (Debasish Ghosh) +- **FIX** - #480: Regression multibulk replies redis client (Debasish Ghosh) +- **FIX** - #415 Publish now generate source and doc jars (Viktor Klang) +- **FIX** - #420 REST endpoints should be able to be processed in parallel (Viktor Klang) +- **FIX** - #422 Dispatcher config should work for ThreadPoolBuilder-based dispatchers (Viktor Klang) +- **FIX** - #401 ActorRegistry should not leak memory (Viktor Klang) +- **FIX** - #250 Performance optimization for ExecutorBasedEventDrivenDispatcher (Viktor Klang) +- **FIX** - #419 Rename init and shutdown callbacks to preStart and postStop, and remove initTransactionalState (Viktor Klang) +- **FIX** - #346 Make max no of restarts (and within) are now both optional (Viktor Klang) +- **FIX** - #424 Actors self.supervisor not set by the time init() is called when started by startLink() (Viktor Klang) +- **FIX** - #427 spawnLink and startLink now has the same dispatcher semantics (Viktor Klang) +- **FIX** - #413 Actor shouldn't process more messages when waiting to be restarted (HawtDispatcher still does) (Viktor Klang) +- **FIX** - !! and !!! now do now not block the actor when used in remote actor (Viktor Klang) +- **FIX** - RemoteClient now reconnects properly (Viktor Klang) +- **FIX** - Logger.warn now properly works with varargs (Viktor Klang) +- **FIX** - #450 Removed ActorRef lifeCycle boilerplate: Some(LifeCycle(Permanent)) => Permanent (Viktor Klang) +- **FIX** - Moved ActorRef.trapExit into ActorRef.faultHandler and removed Option-boilerplate from faultHandler (Viktor Klang) +- **FIX** - ThreadBasedDispatcher cheaper for idling actors, also benefits from all that is ExecutorBasedEventDrivenDispatcher (Viktor Klang) +- **FIX** - Fixing Futures.future, uses Actor.spawn under the hood, specify dispatcher to control where block is executed (Viktor Klang) +- **FIX** - #469 Akka "dist" now uses a root folder to avoid loitering if unzipped in a folder (Viktor Klang) +- **FIX** - Removed ScalaConfig, JavaConfig and rewrote Supervision configuration (Viktor Klang) +- **UPD** - Jersey to 1.3 (Viktor Klang) +- **UPD** - Atmosphere to 0.6.2 (Viktor Klang) +- **UPD** - Netty to 3.2.2.Final (Viktor Klang) +- **ADD** - Changed config file priority loading and added config modes. (Viktor Klang) +- **ADD** - #411 Bumped Jetty to v 7 and migrated to it's eclipse packages (Viktor Klang) +- **ADD** - #414 Migrate from Grizzly to Jetty for Akka Microkernel (Viktor Klang) +- **ADD** - #261 Add Java API for 'routing' module (Viktor Klang) +- **ADD** - #262 Add Java API for Agent (Viktor Klang) +- **ADD** - #264 Add Java API for Dataflow (Viktor Klang) +- **ADD** - Using JerseySimpleBroadcaster instead of JerseyBroadcaster in AkkaBroadcaster (Viktor Klang) +- **ADD** - #433 Throughput deadline added for ExecutorBasedEventDrivenDispatcher (Viktor Klang) +- **ADD** - Add possibility to set default cometSupport in akka.conf (Viktor Klang) +- **ADD** - #451 Added possibility to use akka-http as a standalone REST server (Viktor Klang) +- **ADD** - #446 Added support for Erlang-style receiveTimeout (Viktor Klang) +- **ADD** - #462 Added support for suspend/resume of processing individual actors mailbox, should give clearer restart semantics (Viktor Klang) +- **ADD** - #466 Actor.spawn now takes an implicit dispatcher to specify who should run the block (Viktor Klang) +- **ADD** - #456 Added map to Future and Futures.awaitMap (Viktor Klang) +- **REM** - #418 Remove Lift sample module and docs (Viktor Klang) +- **REM** - Removed all Reactor-based dispatchers (Viktor Klang) +- **REM** - Removed anonymous actor factories (Viktor Klang) +- **ADD** - Voldemort support for akka-persistence (Scott Clasen) +- **ADD** - HBase support for akka-persistence (David Greco) +- **ADD** - CouchDB support for akka-persistence (Yung-Luen Lan & Kahlen) +- **ADD** - #265 Java API for AMQP module (Irmo Manie) Release 0.10 - Aug 21 2010 ---------------------------------------- -``_ - -||~ =**Type** - ||~ -===== - -**Changes** - ||~ -===== - -**By**= || -||< **ADD** ||< Added new Actor type: UntypedActor for Java API ||< Jonas Bonér || -||< **ADD** ||< #26 Deep serialization of Actor including its mailbox ||< Jonas Bonér || -||< **ADD** ||< Rewritten network protocol. More efficient and cleaner. ||< Jonas Bonér || -||< **ADD** ||< Rewritten Java Active Object tests into Scala to be able to run the in SBT. ||< Jonas Bonér || -||< **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 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 || -||< **ADD** ||< Added ActiveObjectContext with sender reference etc ||< Jonas Bonér || -||< **ADD** ||< #293 Changed config format to JSON-style ||< Jonas Bonér || -||< **ADD** ||< #302: Incorporate new ReceiveTimeout in Actor serialization ||< Jonas Bonér || -||< **ADD** ||< Added Java API docs and made it comparable with Scala API docs. 1-1 mirroring ||< Jonas Bonér || -||< **ADD** ||< Renamed Active Object to Typed Actor ||< Jonas Bonér || -||< **ADD** ||< Enhanced Typed Actor: remoting, "real" restart upon failure etc. ||< Jonas Bonér || -||< **ADD** ||< Typed Actor now inherits Actor and is a full citizen in the Actor world. ||< Jonas Bonér || -||< **ADD** ||< Added support for remotely shutting down a remote actor ||< Jonas Bonér || -||< **ADD** ||< #224 Add support for Camel in typed actors (`more `_ ...) ||< Martin Krasser || -||< **ADD** || #282 Producer trait should implement Actor.receive (`more `_ ...) || Martin Krasser || -||< **ADD** || #271 Support for bean scope prototype in akka-spring || Johan Rask || -||< **ADD** || Support for DI of values and bean references on target instance in akka-spring || Johan Rask || -||< **ADD** || #287 Method annotated with @postrestart in ActiveObject is not called during restart || Johan Rask || -|| **ADD** || Support for ApplicationContextAware in akka-spring || Johan Rask || -|| **ADD** || #199 Support shutdown hook in TypedActor || Martin Krasser || -|| **ADD** || #266 Access to typed actors from user-defined Camel routes (`more `_ ...) || Martin Krasser || -|| **ADD** || #268 Revise akka-camel documentation (`more `_ ...) || Martin Krasser || -|| **ADD** || #289 Support for Spring configuration element (`more `_ ...) || Martin Krasser || -|| **ADD** || #296 TypedActor lifecycle management || Martin Krasser || -|| **ADD** || #297 Shutdown routes to typed actors (`more `_ ...) || Martin Krasser || -|| **ADD** || #314 akka-spring to support typed actor lifecycle management (`more `_ ...) || Martin Krasser || -|| **ADD** || #315 akka-spring to support configuration of shutdown callback method (`more `_ ...) || Martin Krasser || -|| **ADD** || Fault-tolerant consumer actors and typed consumer actors (`more `_ ...) || Martin Krasser || -|| **ADD** || #320 Leverage Camel's non-blocking routing engine (`more `_ ...) || Martin Krasser || -|| **ADD** || #335 Producer trait should allow forwarding of results || Martin Krasser || -|| **ADD** || #339 Redesign of Producer trait (pre/post processing hooks, async in-out) (`more `_ ...) || Martin Krasser || -|| **ADD** || Non-blocking, asynchronous routing example for akka-camel (`more `_ ...) || Martin Krasser || -|| **ADD** || #333 Allow applications to wait for endpoints being activated (`more `_ ...) || Martin Krasser || -|| **ADD** || #356 Support @consume annotations on typed actor implementation class || Martin Krasser || -|| **ADD** || #357 Support untyped Java actors as endpoint consumer || Martin Krasser || -|| **ADD** || #366 CamelService should be a singleton || Martin Krasser || -|| **ADD** || #392 Support untyped Java actors as endpoint producer || Martin Krasser || -|| **ADD** || #393 Redesign CamelService singleton to be a CamelServiceManager (`more `_ ...) || Martin Krasser || -|| **ADD** || #295 Refactoring Actor serialization to type classes || Debasish Ghosh || -|| **ADD** || #317 Change documentation for Actor Serialization || Debasish Ghosh || -|| **ADD** || #388 Typeclass serialization of ActorRef/UntypedActor isn't Java friendly || Debasish Ghosh || -|| **ADD** || #292 Add scheduleOnce to Scheduler || Irmo Manie || -|| **ADD** || #308 Initial receive timeout on actor || Irmo Manie || -|| **ADD** || Redesign of AMQP module (`more `_ ...) || Irmo Manie || -|| **ADD** || Added "become(behavior: Option[Receive])" to Actor || Viktor Klang || -|| **ADD** || Added "find[T](f: PartialFunction[ActorRef,T]) : Option[T]" to ActorRegistry || Viktor Klang || -|| **ADD** || #369 Possibility to configure dispatchers in akka.conf || Viktor Klang || -|| **ADD** || #395 Create ability to add listeners to RemoteServer || Viktor Klang || -|| **ADD** || #225 Add possibility to use Scheduler from TypedActor || Viktor Klang || -|| **ADD** || #61 Integrate new persistent datastructures in Scala 2.8 || Peter Vlugter || -|| **ADD** || Expose more of what Multiverse can do || Peter Vlugter || -|| **ADD** || #205 STM transaction settings || Peter Vlugter || -|| **ADD** || #206 STM transaction deferred and compensating || Peter Vlugter || -|| **ADD** || #232 Expose blocking transactions || Peter Vlugter || -|| **ADD** || #249 Expose Multiverse Refs for primitives || Peter Vlugter || -|| **ADD** || #390 Expose transaction propagation level in multiverse || Peter Vlugter || -|| **ADD** || Package objects for importing local/global STM || Peter Vlugter || -|| **ADD** || Java API for the STM || Peter Vlugter || -|| **ADD** || #379 Create STM Atomic templates for Java API || Peter Vlugter || -|| **ADD** || #270 SBT plugin for Akka || Peter Vlugter || -|| **ADD** || #198 support for ThreadBasedDispatcher in Spring config || Michael Kober || -|| **ADD** || #377 support HawtDispatcher in Spring config || Michael Kober || -|| **ADD** || #376 support Spring config for untyped actors || Michael Kober || -|| **ADD** || #200 support WorkStealingDispatcher in Spring config || Michael Kober || -|| **UPD** || #336 RabbitMQ 1.8.1 || Irmo Manie || -|| **UPD** || #288 Netty to 3.2.1.Final || Viktor Klang || -|| **UPD** || Atmosphere to 0.6.1 || Viktor Klang || -|| **UPD** || Lift to 2.8.0-2.1-M1 || Viktor Klang || -|| **UPD** || Camel to 2.4.0 || Martin Krasser || -|| **UPD** || Spring to 3.0.3.RELEASE || Martin Krasser || -|| **UPD** || Multiverse to 0.6 || Peter Vlugter || -|| **FIX** || Fixed bug with stm not being enabled by default when no AKKA_HOME is set || Jonas Bonér || -|| **FIX** || Fixed bug in network manifest serialization || Jonas Bonér || -|| **FIX** || Fixed bug Remote Actors || Jonas Bonér || -|| **FIX** || Fixed memory leak in Active Objects || Jonas Bonér || -|| **FIX** || Fixed indeterministic deadlock in Transactor restart || Jonas Bonér || -|| **FIX** || #325 Fixed bug in STM with dead hanging CountDownCommitBarrier || Jonas Bonér || -|| **FIX** || #316: NoSuchElementException during ActiveObject restart || Jonas Bonér || -|| **FIX** || #256: Tests for ActiveObjectContext || Jonas Bonér || -|| **FIX** || Fixed bug in restart of Actors with 'Temporary' life-cycle || Jonas Bonér || -|| **FIX** || #280 Tests fail if there is no akka.conf set || Jonas Bonér || -|| **FIX** || #286 unwanted transitive dependencies from Geronimo project || Viktor Klang || -|| **FIX** || Atmosphere comet comment to use stream instead of writer || Viktor Klang || -|| **FIX** || #285 akka.conf is now used as defaults for Akka REST servlet init parameters || Viktor Klang || -|| **FIX** || #321 fixed performance regression in ActorRegistry || Viktor Klang || -|| **FIX** || #286 geronimo servlet 2.4 dep is no longer transitively loaded || Viktor Klang || -|| **FIX** || #334 partial lift sample rewrite to fix breakage || Viktor Klang || -|| **FIX** || Fixed a memory leak in ActorRegistry || Viktor Klang || -|| **FIX** || Fixed a race-condition in Cluster || Viktor Klang || -|| **FIX** || #355 Switched to Array instead of List on ActorRegistry return types || Viktor Klang || -|| **FIX** || #352 ActorRegistry.actorsFor(class) now checks isAssignableFrom || Viktor Klang || -|| **FIX** || Fixed a race condition in ActorRegistry.register || Viktor Klang || -|| **FIX** || #337 Switched from Configgy logging to SLF4J, better for OSGi || Viktor Klang || -|| **FIX** || #372 Scheduler now returns Futures to cancel tasks || Viktor Klang || -|| **FIX** || #306 JSON serialization between remote actors is not transparent || Debasish Ghosh || -|| **FIX** || #204 Reduce object creation in STM || Peter Vlugter || -|| **FIX** || #253 Extend Multiverse BasicRef rather than wrap ProgrammaticRef || Peter Vlugter || -|| **REM** || Removed pure POJO-style Typed Actor (old Active Object) || Jonas Bonér || -|| **REM** || Removed Lift as a dependency for Akka-http || Viktor Klang || -|| **REM** || #294 Remove reply and reply_? from Actor || Viktor Klang || -|| **REM** || Removed one field in Actor, should be a minor memory reduction for high actor quantities || Viktor Klang || -|| **FIX** || #301 DI does not work in akka-spring when specifying an interface || Johan Rask || -|| **FIX** || #328 -trapExit should pass through self with Exit to supervisor || Irmo Manie || -|| **FIX** || Fixed warning when deregistering listeners || Martin Krasser || -|| **FIX** || Added camel-jetty-2.4.0.1 to Akka's embedded-repo. -(fixes a concurrency bug in camel-jetty-2.4.0, to be officially released in Camel 2.5.0) || Martin Krasser || -|| **FIX** || #338 RedisStorageBackend fails when redis closes connection to idle client || Debasish Ghosh || -|| **FIX** || #340 RedisStorage Map.get does not throw exception when disconnected from redis but returns None || Debasish Ghosh || +- **ADD** - Added new Actor type: UntypedActor for Java API (Jonas Bonér) +- **ADD** - #26 Deep serialization of Actor including its mailbox (Jonas Bonér) +- **ADD** - Rewritten network protocol. More efficient and cleaner. (Jonas Bonér) +- **ADD** - Rewritten Java Active Object tests into Scala to be able to run the in SBT. (Jonas Bonér) +- **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 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) +- **ADD** - Added ActiveObjectContext with sender reference etc (Jonas Bonér) +- **ADD** - #293 Changed config format to JSON-style (Jonas Bonér) +- **ADD** - #302: Incorporate new ReceiveTimeout in Actor serialization (Jonas Bonér) +- **ADD** - Added Java API docs and made it comparable with Scala API docs. 1-1 mirroring (Jonas Bonér) +- **ADD** - Renamed Active Object to Typed Actor (Jonas Bonér) +- **ADD** - Enhanced Typed Actor: remoting, "real" restart upon failure etc. (Jonas Bonér) +- **ADD** - Typed Actor now inherits Actor and is a full citizen in the Actor world. (Jonas Bonér) +- **ADD** - Added support for remotely shutting down a remote actor (Jonas Bonér) +- **ADD** - #224 Add support for Camel in typed actors (`more `__ ...) (Martin Krasser) +- **ADD** - #282 Producer trait should implement Actor.receive (`more `__...) (Martin Krasser) +- **ADD** - #271 Support for bean scope prototype in akka-spring (Johan Rask) +- **ADD** - Support for DI of values and bean references on target instance in akka-spring (Johan Rask) +- **ADD** - #287 Method annotated with @postrestart in ActiveObject is not called during restart (Johan Rask) +- **ADD** - Support for ApplicationContextAware in akka-spring (Johan Rask) +- **ADD** - #199 Support shutdown hook in TypedActor (Martin Krasser) +- **ADD** - #266 Access to typed actors from user-defined Camel routes (`more `__ ...) (Martin Krasser) +- **ADD** - #268 Revise akka-camel documentation (`more `__ ...) (Martin Krasser) +- **ADD** - #289 Support for Spring configuration element (`more `__ ...) (Martin Krasser) +- **ADD** - #296 TypedActor lifecycle management (Martin Krasser) +- **ADD** - #297 Shutdown routes to typed actors (`more `__ ...) (Martin Krasser) +- **ADD** - #314 akka-spring to support typed actor lifecycle management (`more `__ ...) (Martin Krasser) +- **ADD** - #315 akka-spring to support configuration of shutdown callback method (`more `__ ...) (Martin Krasser) +- **ADD** - Fault-tolerant consumer actors and typed consumer actors (`more `__ ...) (Martin Krasser) +- **ADD** - #320 Leverage Camel's non-blocking routing engine (`more `__ ...) (Martin Krasser) +- **ADD** - #335 Producer trait should allow forwarding of results (Martin Krasser) +- **ADD** - #339 Redesign of Producer trait (pre/post processing hooks, async in-out) (`more `__ ...) (Martin Krasser) +- **ADD** - Non-blocking, asynchronous routing example for akka-camel (`more `__ ...) (Martin Krasser) +- **ADD** - #333 Allow applications to wait for endpoints being activated (`more `__ ...) (Martin Krasser) +- **ADD** - #356 Support @consume annotations on typed actor implementation class (Martin Krasser) +- **ADD** - #357 Support untyped Java actors as endpoint consumer (Martin Krasser) +- **ADD** - #366 CamelService should be a singleton (Martin Krasser) +- **ADD** - #392 Support untyped Java actors as endpoint producer (Martin Krasser) +- **ADD** - #393 Redesign CamelService singleton to be a CamelServiceManager (`more `__ ...) (Martin Krasser) +- **ADD** - #295 Refactoring Actor serialization to type classes (Debasish Ghosh) +- **ADD** - #317 Change documentation for Actor Serialization (Debasish Ghosh) +- **ADD** - #388 Typeclass serialization of ActorRef/UntypedActor isn't Java friendly (Debasish Ghosh) +- **ADD** - #292 Add scheduleOnce to Scheduler (Irmo Manie) +- **ADD** - #308 Initial receive timeout on actor (Irmo Manie) +- **ADD** - Redesign of AMQP module (`more `__ ...) (Irmo Manie) +- **ADD** - Added "become(behavior: Option[Receive])" to Actor (Viktor Klang) +- **ADD** - Added "find[T](f: PartialFunction[ActorRef,T]) : Option[T]" to ActorRegistry (Viktor Klang) +- **ADD** - #369 Possibility to configure dispatchers in akka.conf (Viktor Klang) +- **ADD** - #395 Create ability to add listeners to RemoteServer (Viktor Klang) +- **ADD** - #225 Add possibility to use Scheduler from TypedActor (Viktor Klang) +- **ADD** - #61 Integrate new persistent datastructures in Scala 2.8 (Peter Vlugter) +- **ADD** - Expose more of what Multiverse can do (Peter Vlugter) +- **ADD** - #205 STM transaction settings (Peter Vlugter) +- **ADD** - #206 STM transaction deferred and compensating (Peter Vlugter) +- **ADD** - #232 Expose blocking transactions (Peter Vlugter) +- **ADD** - #249 Expose Multiverse Refs for primitives (Peter Vlugter) +- **ADD** - #390 Expose transaction propagation level in multiverse (Peter Vlugter) +- **ADD** - Package objects for importing local/global STM (Peter Vlugter) +- **ADD** - Java API for the STM (Peter Vlugter) +- **ADD** - #379 Create STM Atomic templates for Java API (Peter Vlugter) +- **ADD** - #270 SBT plugin for Akka (Peter Vlugter) +- **ADD** - #198 support for ThreadBasedDispatcher in Spring config (Michael Kober) +- **ADD** - #377 support HawtDispatcher in Spring config (Michael Kober) +- **ADD** - #376 support Spring config for untyped actors (Michael Kober) +- **ADD** - #200 support WorkStealingDispatcher in Spring config (Michael Kober) +- **UPD** - #336 RabbitMQ 1.8.1 (Irmo Manie) +- **UPD** - #288 Netty to 3.2.1.Final (Viktor Klang) +- **UPD** - Atmosphere to 0.6.1 (Viktor Klang) +- **UPD** - Lift to 2.8.0-2.1-M1 (Viktor Klang) +- **UPD** - Camel to 2.4.0 (Martin Krasser) +- **UPD** - Spring to 3.0.3.RELEASE (Martin Krasser) +- **UPD** - Multiverse to 0.6 (Peter Vlugter) +- **FIX** - Fixed bug with stm not being enabled by default when no AKKA_HOME is set (Jonas Bonér) +- **FIX** - Fixed bug in network manifest serialization (Jonas Bonér) +- **FIX** - Fixed bug Remote Actors (Jonas Bonér) +- **FIX** - Fixed memory leak in Active Objects (Jonas Bonér) +- **FIX** - Fixed indeterministic deadlock in Transactor restart (Jonas Bonér) +- **FIX** - #325 Fixed bug in STM with dead hanging CountDownCommitBarrier (Jonas Bonér) +- **FIX** - #316: NoSuchElementException during ActiveObject restart (Jonas Bonér) +- **FIX** - #256: Tests for ActiveObjectContext (Jonas Bonér) +- **FIX** - Fixed bug in restart of Actors with 'Temporary' life-cycle (Jonas Bonér) +- **FIX** - #280 Tests fail if there is no akka.conf set (Jonas Bonér) +- **FIX** - #286 unwanted transitive dependencies from Geronimo project (Viktor Klang) +- **FIX** - Atmosphere comet comment to use stream instead of writer (Viktor Klang) +- **FIX** - #285 akka.conf is now used as defaults for Akka REST servlet init parameters (Viktor Klang) +- **FIX** - #321 fixed performance regression in ActorRegistry (Viktor Klang) +- **FIX** - #286 geronimo servlet 2.4 dep is no longer transitively loaded (Viktor Klang) +- **FIX** - #334 partial lift sample rewrite to fix breakage (Viktor Klang) +- **FIX** - Fixed a memory leak in ActorRegistry (Viktor Klang) +- **FIX** - Fixed a race-condition in Cluster (Viktor Klang) +- **FIX** - #355 Switched to Array instead of List on ActorRegistry return types (Viktor Klang) +- **FIX** - #352 ActorRegistry.actorsFor(class) now checks isAssignableFrom (Viktor Klang) +- **FIX** - Fixed a race condition in ActorRegistry.register (Viktor Klang) +- **FIX** - #337 Switched from Configgy logging to SLF4J, better for OSGi (Viktor Klang) +- **FIX** - #372 Scheduler now returns Futures to cancel tasks (Viktor Klang) +- **FIX** - #306 JSON serialization between remote actors is not transparent (Debasish Ghosh) +- **FIX** - #204 Reduce object creation in STM (Peter Vlugter) +- **FIX** - #253 Extend Multiverse BasicRef rather than wrap ProgrammaticRef (Peter Vlugter) +- **REM** - Removed pure POJO-style Typed Actor (old Active Object) (Jonas Bonér) +- **REM** - Removed Lift as a dependency for Akka-http (Viktor Klang) +- **REM** - #294 Remove ``reply`` and ``reply_?`` from Actor (Viktor Klang) +- **REM** - Removed one field in Actor, should be a minor memory reduction for high actor quantities (Viktor Klang) +- **FIX** - #301 DI does not work in akka-spring when specifying an interface (Johan Rask) +- **FIX** - #328 trapExit should pass through self with Exit to supervisor (Irmo Manie) +- **FIX** - Fixed warning when deregistering listeners (Martin Krasser) +- **FIX** - Added camel-jetty-2.4.0.1 to Akka's embedded-repo. (fixes a concurrency bug in camel-jetty-2.4.0, to be officially released in Camel 2.5.0) (Martin Krasser) +- **FIX** - #338 RedisStorageBackend fails when redis closes connection to idle client (Debasish Ghosh) +- **FIX** - #340 RedisStorage Map.get does not throw exception when disconnected from redis but returns None (Debasish Ghosh) Release 0.9 - June 2th 2010 ---------------------------------------- -||~ =**Type** - ||~ -===== - -**Changes** - ||~ -===== - -**By**= || -|| || || || -|| **ADD** || Serializable, immutable, network-aware ActorRefs || Jonas Bonér || -|| **ADD** || Optionally JTA-aware STM transactions || Jonas Bonér || -|| **ADD** || Rewritten supervisor management, making use of ActorRef, now really kills the Actor instance and replaces it || Jonas Bonér || -|| **ADD** || Allow linking and unlinking a declaratively configured Supervisor || Jonas Bonér || -|| **ADD** || Remote protocol rewritten to allow passing along sender reference in all situations || Jonas Bonér || -|| **ADD** || #37 API for JTA usage || Jonas Bonér || -|| **ADD** || Added user accessible 'sender' and 'senderFuture' references || Jonas Bonér || -|| **ADD** || Sender actor is now passed along for all message send functions (!, !!, !!!, forward) || Jonas Bonér || -|| **ADD** || Subscription API for listening to RemoteClient failures || Jonas Bonér || -|| **ADD** || Implemented link/unlink for ActiveObjects || Jan Kronquist / Michael Kober || -|| **ADD** || Added alter method to TransactionalRef + added appl(initValue) to Transactional Map/Vector/Ref || Peter Vlugter || -|| **ADD** || Load dependency JARs in JAR deloyed in kernel's ,/deploy dir || Jonas Bonér || -|| **ADD** || Allowing using Akka without specifying AKKA_HOME or path to akka.conf config file || Jonas Bonér || -|| **ADD** || Redisclient now supports PubSub || Debasish Ghosh || -|| **ADD** || Added a sample project under akka-samples for Redis PubSub using Akka actors || Debasish Ghosh || -|| **ADD** || Richer API for Actor.reply || Viktor Klang || -|| **ADD** || Added Listeners to Akka patterns || Viktor Klang || -|| **ADD** || #183 Deactivate endpoints of stopped consumer actors || Martin Krasser || -|| **ADD** || Camel `Message API improvements `_ || Martin Krasser || -|| **ADD** || #83 Send notification to parent supervisor if all actors supervised by supervisor has been permanently killed || Jonas Bonér || -|| **ADD** || #121 Make it possible to dynamically create supervisor hierarchies for Active Objects || Michael Kober || -|| **ADD** || #131 Subscription API for node joining & leaving cluster || Jonas Bonér || -|| **ADD** || #145 Register listener for errors in RemoteClient/RemoteServer || Jonas Bonér || -|| **ADD** || #146 Create an additional distribution with sources || Jonas Bonér || -|| **ADD** || #149 Support loading JARs from META-INF/lib in JARs put into the ./deploy directory || Jonas Bonér || -|| **ADD** || #166 Implement insertVectorStorageEntriesFor in CassandraStorageBackend || Jonas Bonér || -|| **ADD** || #168 Separate ID from Value in Actor; introduce ActorRef || Jonas Bonér || -|| **ADD** || #174 Create sample module for remote actors || Jonas Bonér || -|| **ADD** || #175 Add new sample module with Peter Vlugter's Ant demo || Jonas Bonér || -|| **ADD** || #177 Rewrite remote protocol to make use of new ActorRef || Jonas Bonér || -|| **ADD** || #180 Make use of ActorRef indirection for fault-tolerance management || Jonas Bonér || -|| **ADD** || #184 Upgrade to Netty 3.2.0.CR1 || Jonas Bonér || -|| **ADD** || #185 Rewrite Agent and Supervisor to work with new ActorRef || Jonas Bonér || -|| **ADD** || #188 Change the order of how the akka.conf is detected || Jonas Bonér || -|| **ADD** || #189 Reintroduce 'sender: Option[Actor]' ref in Actor || Jonas Bonér || -|| **ADD** || #203 Upgrade to Scala 2.8 RC2 || Jonas Bonér || -|| **ADD** || #222 Using Akka without AKKA_HOME or akka.conf || Jonas Bonér || -|| **ADD** || #234 Add support for injection and management of ActiveObjectContext with RTTI such as 'sender' and 'senderFuture' references etc. || Jonas Bonér || -|| **ADD** || #236 Upgrade SBinary to Scala 2.8 RC2 || Jonas Bonér || -|| **ADD** || #235 Problem with RedisStorage.getVector(..) data structure storage management || Jonas Bonér || -|| **ADD** || #239 Upgrade to Camel 2.3.0 || Martin Krasser || -|| **ADD** || #242 Upgraded to Scala 2.8 RC3 || Jonas Bonér || -|| **ADD** || #243 Upgraded to Protobuf 2.3.0 || Jonas Bonér || -|| **ADD** || Added option to specify class loader when de-serializing messages and RemoteActorRef in RemoteClient || Jonas Bonér || -|| **ADD** || #238 Upgrading to Cassandra 0.6.1 || Jonas Bonér || -|| **ADD** || Upgraded to Jersey 1.2 || Viktor Klang || -|| **ADD** || Upgraded Atmosphere to 0.6-SNAPSHOT, adding WebSocket support || Viktor Klang || -|| **FIX** || Simplified ActiveObject configuration || Michael Kober || -|| **FIX** || #237 Upgrade Mongo Java driver to 1.4 (the latest stable release) || Debasish Ghosh || -|| **FIX** || #165 Implemented updateVectorStorageEntryFor in Mongo persistence module || Debasish Ghosh || -|| **FIX** || #154: Allow ActiveObjects to use the default timeout in config file || Michael Kober || -|| **FIX** || Active Object methods with @inittransactionalstate should be invoked automatically || Michael Kober || -|| **FIX** || Nested supervisor hierarchy failure propagation bug fixed || Jonas Bonér || -|| **FIX** || Fixed bug on CommitBarrier transaction registration || Jonas Bonér || -|| **FIX** || Merged many modules to reduce total number of modules || Viktor Klang || -|| **FIX** || Future parameterized || Viktor Klang || -|| **FIX** || #191: Workstealing dispatcher didn't work with !! || Viktor Klang || -|| **FIX** || #202: Allow applications to disable stream-caching || Martin Krasser || -|| **FIX** || #119 Problem with Cassandra-backed Vector || Jonas Bonér || -|| **FIX** || #147 Problem replying to remote sender when message sent with ! || Jonas Bonér || -|| **FIX** || #171 initial value of Ref can become null if first transaction rolled back || Jonas Bonér || -|| **FIX** || #172 Fix "broken" Protobuf serialization API || Jonas Bonér || -|| **FIX** || #173 Problem with Vector::slice in CassandraStorage || Jonas Bonér || -|| **FIX** || #190 RemoteClient shutdown ends up in endless loop || Jonas Bonér || -|| **FIX** || #211 Problem with getting CommitBarrierOpenException when using Transaction.Global || Jonas Bonér || -|| **FIX** || #240 Supervised actors not started when starting supervisor || Jonas Bonér || -|| **FIX** || Fixed problem with Transaction.Local not committing to persistent storage || Jonas Bonér || -|| **FIX** || #215: Re-engineered the JAX-RS support || Viktor Klang || -|| **FIX** || Many many bug fixes || Team || -|| **REM** || Shoal cluster module || Viktor Klang || +- **ADD** - Serializable, immutable, network-aware ActorRefs (Jonas Bonér) +- **ADD** - Optionally JTA-aware STM transactions (Jonas Bonér) +- **ADD** - Rewritten supervisor management, making use of ActorRef, now really kills the Actor instance and replaces it (Jonas Bonér) +- **ADD** - Allow linking and unlinking a declaratively configured Supervisor (Jonas Bonér) +- **ADD** - Remote protocol rewritten to allow passing along sender reference in all situations (Jonas Bonér) +- **ADD** - #37 API for JTA usage (Jonas Bonér) +- **ADD** - Added user accessible 'sender' and 'senderFuture' references (Jonas Bonér) +- **ADD** - Sender actor is now passed along for all message send functions (!, !!, !!!, forward) (Jonas Bonér) +- **ADD** - Subscription API for listening to RemoteClient failures (Jonas Bonér) +- **ADD** - Implemented link/unlink for ActiveObjects || Jan Kronquist / Michael Kober || +- **ADD** - Added alter method to TransactionalRef + added appl(initValue) to Transactional Map/Vector/Ref (Peter Vlugter) +- **ADD** - Load dependency JARs in JAR deloyed in kernel's ,/deploy dir (Jonas Bonér) +- **ADD** - Allowing using Akka without specifying AKKA_HOME or path to akka.conf config file (Jonas Bonér) +- **ADD** - Redisclient now supports PubSub (Debasish Ghosh) +- **ADD** - Added a sample project under akka-samples for Redis PubSub using Akka actors (Debasish Ghosh) +- **ADD** - Richer API for Actor.reply (Viktor Klang) +- **ADD** - Added Listeners to Akka patterns (Viktor Klang) +- **ADD** - #183 Deactivate endpoints of stopped consumer actors (Martin Krasser) +- **ADD** - Camel `Message API improvements `_ (Martin Krasser) +- **ADD** - #83 Send notification to parent supervisor if all actors supervised by supervisor has been permanently killed (Jonas Bonér) +- **ADD** - #121 Make it possible to dynamically create supervisor hierarchies for Active Objects (Michael Kober) +- **ADD** - #131 Subscription API for node joining & leaving cluster (Jonas Bonér) +- **ADD** - #145 Register listener for errors in RemoteClient/RemoteServer (Jonas Bonér) +- **ADD** - #146 Create an additional distribution with sources (Jonas Bonér) +- **ADD** - #149 Support loading JARs from META-INF/lib in JARs put into the ./deploy directory (Jonas Bonér) +- **ADD** - #166 Implement insertVectorStorageEntriesFor in CassandraStorageBackend (Jonas Bonér) +- **ADD** - #168 Separate ID from Value in Actor; introduce ActorRef (Jonas Bonér) +- **ADD** - #174 Create sample module for remote actors (Jonas Bonér) +- **ADD** - #175 Add new sample module with Peter Vlugter's Ant demo (Jonas Bonér) +- **ADD** - #177 Rewrite remote protocol to make use of new ActorRef (Jonas Bonér) +- **ADD** - #180 Make use of ActorRef indirection for fault-tolerance management (Jonas Bonér) +- **ADD** - #184 Upgrade to Netty 3.2.0.CR1 (Jonas Bonér) +- **ADD** - #185 Rewrite Agent and Supervisor to work with new ActorRef (Jonas Bonér) +- **ADD** - #188 Change the order of how the akka.conf is detected (Jonas Bonér) +- **ADD** - #189 Reintroduce 'sender: Option[Actor]' ref in Actor (Jonas Bonér) +- **ADD** - #203 Upgrade to Scala 2.8 RC2 (Jonas Bonér) +- **ADD** - #222 Using Akka without AKKA_HOME or akka.conf (Jonas Bonér) +- **ADD** - #234 Add support for injection and management of ActiveObjectContext with RTTI such as 'sender' and 'senderFuture' references etc. (Jonas Bonér) +- **ADD** - #236 Upgrade SBinary to Scala 2.8 RC2 (Jonas Bonér) +- **ADD** - #235 Problem with RedisStorage.getVector(..) data structure storage management (Jonas Bonér) +- **ADD** - #239 Upgrade to Camel 2.3.0 (Martin Krasser) +- **ADD** - #242 Upgraded to Scala 2.8 RC3 (Jonas Bonér) +- **ADD** - #243 Upgraded to Protobuf 2.3.0 (Jonas Bonér) +- **ADD** - Added option to specify class loader when de-serializing messages and RemoteActorRef in RemoteClient (Jonas Bonér) +- **ADD** - #238 Upgrading to Cassandra 0.6.1 (Jonas Bonér) +- **ADD** - Upgraded to Jersey 1.2 (Viktor Klang) +- **ADD** - Upgraded Atmosphere to 0.6-SNAPSHOT, adding WebSocket support (Viktor Klang) +- **FIX** - Simplified ActiveObject configuration (Michael Kober) +- **FIX** - #237 Upgrade Mongo Java driver to 1.4 (the latest stable release) (Debasish Ghosh) +- **FIX** - #165 Implemented updateVectorStorageEntryFor in Mongo persistence module (Debasish Ghosh) +- **FIX** - #154: Allow ActiveObjects to use the default timeout in config file (Michael Kober) +- **FIX** - Active Object methods with @inittransactionalstate should be invoked automatically (Michael Kober) +- **FIX** - Nested supervisor hierarchy failure propagation bug fixed (Jonas Bonér) +- **FIX** - Fixed bug on CommitBarrier transaction registration (Jonas Bonér) +- **FIX** - Merged many modules to reduce total number of modules (Viktor Klang) +- **FIX** - Future parameterized (Viktor Klang) +- **FIX** - #191: Workstealing dispatcher didn't work with !! (Viktor Klang) +- **FIX** - #202: Allow applications to disable stream-caching (Martin Krasser) +- **FIX** - #119 Problem with Cassandra-backed Vector (Jonas Bonér) +- **FIX** - #147 Problem replying to remote sender when message sent with ! (Jonas Bonér) +- **FIX** - #171 initial value of Ref can become null if first transaction rolled back (Jonas Bonér) +- **FIX** - #172 Fix "broken" Protobuf serialization API (Jonas Bonér) +- **FIX** - #173 Problem with Vector::slice in CassandraStorage (Jonas Bonér) +- **FIX** - #190 RemoteClient shutdown ends up in endless loop (Jonas Bonér) +- **FIX** - #211 Problem with getting CommitBarrierOpenException when using Transaction.Global (Jonas Bonér) +- **FIX** - #240 Supervised actors not started when starting supervisor (Jonas Bonér) +- **FIX** - Fixed problem with Transaction.Local not committing to persistent storage (Jonas Bonér) +- **FIX** - #215: Re-engineered the JAX-RS support (Viktor Klang) +- **FIX** - Many many bug fixes || Team || +- **REM** - Shoal cluster module (Viktor Klang) Release 0.8.1 - April 6th 2010 ---------------------------------------- -||~ =**Type** - ||~ -===== - -**Changes** - ||~ -===== - -**By**= || -|| || || || -|| **ADD** || Redis cluster support || Debasish Ghosh || -|| **ADD** || Reply to remote sender from message set with ! || Jonas Bonér || -|| **ADD** || Load-balancer which prefers actors with few messages in mailbox || Jan Van Besien || -|| **ADD** || Added developer mailing list: [akka-dev AT googlegroups DOT com] || Jonas Bonér || -|| **FIX** || Separated thread-local from thread-global transaction API || Jonas Bonér || -|| **FIX** || Fixed bug in using STM outside Actors || Jonas Bonér || -|| **FIX** || Fixed bug in anonymous actors || Jonas Bonér || -|| **FIX** || Moved web initializer to new akka-servlet module || Viktor Klang || +- **ADD** - Redis cluster support (Debasish Ghosh) +- **ADD** - Reply to remote sender from message set with ! (Jonas Bonér) +- **ADD** - Load-balancer which prefers actors with few messages in mailbox || Jan Van Besien || +- **ADD** - Added developer mailing list: [akka-dev AT googlegroups DOT com] (Jonas Bonér) +- **FIX** - Separated thread-local from thread-global transaction API (Jonas Bonér) +- **FIX** - Fixed bug in using STM outside Actors (Jonas Bonér) +- **FIX** - Fixed bug in anonymous actors (Jonas Bonér) +- **FIX** - Moved web initializer to new akka-servlet module (Viktor Klang) Release 0.8 - March 31st 2010 ---------------------------------------- -||~ =**Type** - ||~ -===== - -**Changes** - ||~ -===== - -**By**= || -|| || || || -|| **ADD** || Scala 2.8 based || Viktor Klang || -|| **ADD** || Monadic API for Agents || Jonas Bonér || -|| **ADD** || Agents are transactional || Jonas Bonér || -|| **ADD** || Work-stealing dispatcher || Jan Van Besien || -|| **ADD** || Improved Spring integration || Michael Kober || -|| **FIX** || Various bugfixes || Team || -|| **FIX** || Improved distribution packaging || Jonas Bonér || -|| **REMOVE** || Actor.send function || Jonas Bonér || +- **ADD** - Scala 2.8 based (Viktor Klang) +- **ADD** - Monadic API for Agents (Jonas Bonér) +- **ADD** - Agents are transactional (Jonas Bonér) +- **ADD** - Work-stealing dispatcher || Jan Van Besien || +- **ADD** - Improved Spring integration (Michael Kober) +- **FIX** - Various bugfixes || Team || +- **FIX** - Improved distribution packaging (Jonas Bonér) +- **REMOVE** - Actor.send function (Jonas Bonér) Release 0.7 - March 21st 2010 ---------------------------------------- -||~ =**Type** - ||~ -===== - -**Changes** - ||~ -===== - -**By**= || -|| || || || -|| **ADD** || Rewritten STM now works generically with fire-forget message flows || Jonas Bonér || -|| **ADD** || Apache Camel integration || Martin Krasser || -|| **ADD** || Spring integration || Michael Kober || -|| **ADD** || Server-managed Remote Actors || Jonas Bonér || -|| **ADD** || Clojure-style Agents || Viktor Klang || -|| **ADD** || Shoal cluster backend || Viktor Klang || -|| **ADD** || Redis-based transactional queue storage backend || Debasish Ghosh || -|| **ADD** || Redis-based transactional sorted set storage backend || Debasish Ghosh || -|| **ADD** || Redis-based atomic INC (index) operation || Debasish Ghosh || -|| **ADD** || Distributed Comet || Viktor Klang || -|| **ADD** || Project moved to SBT (simple-build-tool) || Peter Hausel || -|| **ADD** || Futures object with utility methods for Future's || Jonas Bonér || -|| **ADD** || !!! function that returns a Future || Jonas Bonér || -|| **ADD** || Richer ActorRegistry API || Jonas Bonér || -|| **FIX** || Improved event-based dispatcher performance with 40% || Jan Van Besien || -|| **FIX** || Improved remote client pipeline performance || Viktor Klang || -|| **FIX** || Support several Clusters on the same network || Viktor Klang || -|| **FIX** || Structural package refactoring || Jonas Bonér || -|| **FIX** || Various bugs fixed || Team || +- **ADD** - Rewritten STM now works generically with fire-forget message flows (Jonas Bonér) +- **ADD** - Apache Camel integration (Martin Krasser) +- **ADD** - Spring integration (Michael Kober) +- **ADD** - Server-managed Remote Actors (Jonas Bonér) +- **ADD** - Clojure-style Agents (Viktor Klang) +- **ADD** - Shoal cluster backend (Viktor Klang) +- **ADD** - Redis-based transactional queue storage backend (Debasish Ghosh) +- **ADD** - Redis-based transactional sorted set storage backend (Debasish Ghosh) +- **ADD** - Redis-based atomic INC (index) operation (Debasish Ghosh) +- **ADD** - Distributed Comet (Viktor Klang) +- **ADD** - Project moved to SBT (simple-build-tool) || Peter Hausel || +- **ADD** - Futures object with utility methods for Future's (Jonas Bonér) +- **ADD** - !!! function that returns a Future (Jonas Bonér) +- **ADD** - Richer ActorRegistry API (Jonas Bonér) +- **FIX** - Improved event-based dispatcher performance with 40% || Jan Van Besien || +- **FIX** - Improved remote client pipeline performance (Viktor Klang) +- **FIX** - Support several Clusters on the same network (Viktor Klang) +- **FIX** - Structural package refactoring (Jonas Bonér) +- **FIX** - Various bugs fixed || Team || Release 0.6 - January 5th 2010 ---------------------------------------- -||~ =**Type** - ||~ -===== - -**Changes** - ||~ -===== - -**By**= || -|| || || || -|| **ADD** || Clustered Comet using Akka remote actors and clustered membership API || Viktor Klang || -|| **ADD** || Cluster membership API and implementation based on JGroups || Viktor Klang || -|| **ADD** || Security module for HTTP-based authentication and authorization || Viktor Klang || -|| **ADD** || Support for using Scala XML tags in RESTful Actors (scala-jersey) || Viktor Klang || -|| **ADD** || Support for Comet Actors using Atmosphere || Viktor Klang || -|| **ADD** || MongoDB as Akka storage backend || Debasish Ghosh || -|| **ADD** || Redis as Akka storage backend || Debasish Ghosh || -|| **ADD** || Transparent JSON serialization of Scala objects based on SJSON || Debasish Ghosh || -|| **ADD** || Kerberos/SPNEGO support for Security module || Eckhart Hertzler || -|| **ADD** || Implicit sender for remote actors: Remote actors are able to use reply to answer a request || Mikael Högqvist || -|| **ADD** || Support for using the Lift Web framework with Actors || Tim Perrett || -|| **ADD** || Added CassandraSession API (with socket pooling) wrapping Cassandra's Thrift API in Scala and Java APIs || Jonas Bonér || -|| **ADD** || Rewritten STM, now integrated with Multiverse STM || Jonas Bonér || -|| **ADD** || Added STM API for atomic {..} and run {..} orElse {..} || Jonas Bonér || -|| **ADD** || Added STM retry || Jonas Bonér || -|| **ADD** || AMQP integration; abstracted as actors in a supervisor hierarchy. Impl AMQP 0.9.1 || Jonas Bonér || -|| **ADD** || Complete rewrite of the persistence transaction management, now based on Unit of Work and Multiverse STM || Jonas Bonér || -|| **ADD** || Monadic API to TransactionalRef (use it in for-comprehension) || Jonas Bonér || -|| **ADD** || Lightweight actor syntax using one of the Actor.actor(..) methods. F.e: 'val a = actor { case _ => .. }' || Jonas Bonér || -|| **ADD** || Rewritten event-based dispatcher which improved perfomance by 10x, now substantially faster than event-driven Scala Actors || Jonas Bonér || -|| **ADD** || New Scala JSON parser based on sjson || Jonas Bonér || -|| **ADD** || Added zlib compression to remote actors || Jonas Bonér || -|| **ADD** || Added implicit sender reference for fire-forget ('!') message sends || Jonas Bonér || -|| **ADD** || Monadic API to TransactionalRef (use it in for-comprehension) || Jonas Bonér || -|| **ADD** || Smoother web app integration; just add akka.conf to the classpath (WEB-INF/classes), no need for AKKA_HOME or -Dakka.conf=.. || Jonas Bonér || -|| **ADD** || Modularization of distribution into a thin core (actors, remoting and STM) and the rest in submodules || Jonas Bonér || -|| **ADD** || Added 'forward' to Actor, forwards message but keeps original sender address || Jonas Bonér || -|| **ADD** || JSON serialization for Java objects (using Jackson) || Jonas Bonér || -|| **ADD** || JSON serialization for Scala objects (using SJSON) || Jonas Bonér || -|| **ADD** || Added implementation for remote actor reconnect upon failure || Jonas Bonér || -|| **ADD** || Protobuf serialization for Java and Scala objects || Jonas Bonér || -|| **ADD** || SBinary serialization for Scala objects || Jonas Bonér || -|| **ADD** || Protobuf as remote protocol || Jonas Bonér || -|| **ADD** || Updated Cassandra integration and CassandraSession API to v0.4 || Jonas Bonér || -|| **ADD** || CassandraStorage is now works with external Cassandra cluster || Jonas Bonér || -|| **ADD** || ActorRegistry for retrieving Actor instances by class name and by id || Jonas Bonér || -|| **ADD** || SchedulerActor for scheduling periodic tasks || Jonas Bonér || -|| **ADD** || Now start up kernel with 'java -jar dist/akka-0.6.jar' || Jonas Bonér || -|| **ADD** || Added Akka user mailing list: akka-user AT googlegroups DOT com]] || Jonas Bonér || -|| **ADD** || Improved and restructured documentation || Jonas Bonér || -|| **ADD** || New URL: http://akkasource.org || Jonas Bonér || -|| **ADD** || New and much improved docs || Jonas Bonér || -|| **ADD** || Enhanced trapping of failures: 'trapExit = List(classOf[..], classOf[..])' || Jonas Bonér || -|| **ADD** || Upgraded to Netty 3.2, Protobuf 2.2, ScalaTest 1.0, Jersey 1.1.3, Atmosphere 0.4.1, Cassandra 0.4.1, Configgy 1.4 || Jonas Bonér || -|| **FIX** || Lowered actor memory footprint; now an actor consumes ~600 bytes, which mean that you can create 6.5 million on 4 G RAM || Jonas Bonér || -|| **FIX** || Remote actors are now defined by their UUID (not class name) || Jonas Bonér || -|| **FIX** || Fixed dispatcher bugs || Jonas Bonér || -|| **FIX** || Cleaned up Maven scripts and distribution in general || Jonas Bonér || -|| **FIX** || Fixed many many bugs and minor issues || Jonas Bonér || -|| **FIX** || Fixed inconsistencies and uglyness in Actors API || Jonas Bonér || -|| **REMOVE** || Removed concurrent mode || Jonas Bonér || -|| **REMOVE** || Removed embedded Cassandra mode || Jonas Bonér || -|| **REMOVE** || Removed the !? method in Actor (synchronous message send, since it's evil. Use !! with time-out instead. || Jonas Bonér || -|| **REMOVE** || Removed startup scripts and lib dir || Jonas Bonér || -|| **REMOVE** || Removed the 'Transient' life-cycle scope since to close to 'Temporary' in semantics. || Jonas Bonér || -|| **REMOVE** || Removed 'Transient' Actors and restart timeout || Jonas Bonér || +- **ADD** - Clustered Comet using Akka remote actors and clustered membership API (Viktor Klang) +- **ADD** - Cluster membership API and implementation based on JGroups (Viktor Klang) +- **ADD** - Security module for HTTP-based authentication and authorization (Viktor Klang) +- **ADD** - Support for using Scala XML tags in RESTful Actors (scala-jersey) (Viktor Klang) +- **ADD** - Support for Comet Actors using Atmosphere (Viktor Klang) +- **ADD** - MongoDB as Akka storage backend (Debasish Ghosh) +- **ADD** - Redis as Akka storage backend (Debasish Ghosh) +- **ADD** - Transparent JSON serialization of Scala objects based on SJSON (Debasish Ghosh) +- **ADD** - Kerberos/SPNEGO support for Security module || Eckhart Hertzler || +- **ADD** - Implicit sender for remote actors: Remote actors are able to use reply to answer a request || Mikael Högqvist || +- **ADD** - Support for using the Lift Web framework with Actors || Tim Perrett || +- **ADD** - Added CassandraSession API (with socket pooling) wrapping Cassandra's Thrift API in Scala and Java APIs (Jonas Bonér) +- **ADD** - Rewritten STM, now integrated with Multiverse STM (Jonas Bonér) +- **ADD** - Added STM API for atomic {..} and run {..} orElse {..} (Jonas Bonér) +- **ADD** - Added STM retry (Jonas Bonér) +- **ADD** - AMQP integration; abstracted as actors in a supervisor hierarchy. Impl AMQP 0.9.1 (Jonas Bonér) +- **ADD** - Complete rewrite of the persistence transaction management, now based on Unit of Work and Multiverse STM (Jonas Bonér) +- **ADD** - Monadic API to TransactionalRef (use it in for-comprehension) (Jonas Bonér) +- **ADD** - Lightweight actor syntax using one of the Actor.actor(..) methods. F.e: 'val a = actor { case _ => .. }' (Jonas Bonér) +- **ADD** - Rewritten event-based dispatcher which improved perfomance by 10x, now substantially faster than event-driven Scala Actors (Jonas Bonér) +- **ADD** - New Scala JSON parser based on sjson (Jonas Bonér) +- **ADD** - Added zlib compression to remote actors (Jonas Bonér) +- **ADD** - Added implicit sender reference for fire-forget ('!') message sends (Jonas Bonér) +- **ADD** - Monadic API to TransactionalRef (use it in for-comprehension) (Jonas Bonér) +- **ADD** - Smoother web app integration; just add akka.conf to the classpath (WEB-INF/classes), no need for AKKA_HOME or -Dakka.conf=.. (Jonas Bonér) +- **ADD** - Modularization of distribution into a thin core (actors, remoting and STM) and the rest in submodules (Jonas Bonér) +- **ADD** - Added 'forward' to Actor, forwards message but keeps original sender address (Jonas Bonér) +- **ADD** - JSON serialization for Java objects (using Jackson) (Jonas Bonér) +- **ADD** - JSON serialization for Scala objects (using SJSON) (Jonas Bonér) +- **ADD** - Added implementation for remote actor reconnect upon failure (Jonas Bonér) +- **ADD** - Protobuf serialization for Java and Scala objects (Jonas Bonér) +- **ADD** - SBinary serialization for Scala objects (Jonas Bonér) +- **ADD** - Protobuf as remote protocol (Jonas Bonér) +- **ADD** - Updated Cassandra integration and CassandraSession API to v0.4 (Jonas Bonér) +- **ADD** - CassandraStorage is now works with external Cassandra cluster (Jonas Bonér) +- **ADD** - ActorRegistry for retrieving Actor instances by class name and by id (Jonas Bonér) +- **ADD** - SchedulerActor for scheduling periodic tasks (Jonas Bonér) +- **ADD** - Now start up kernel with 'java -jar dist/akka-0.6.jar' (Jonas Bonér) +- **ADD** - Added Akka user mailing list: akka-user AT googlegroups DOT com]] (Jonas Bonér) +- **ADD** - Improved and restructured documentation (Jonas Bonér) +- **ADD** - New URL: http://akkasource.org (Jonas Bonér) +- **ADD** - New and much improved docs (Jonas Bonér) +- **ADD** - Enhanced trapping of failures: 'trapExit = List(classOf[..], classOf[..])' (Jonas Bonér) +- **ADD** - Upgraded to Netty 3.2, Protobuf 2.2, ScalaTest 1.0, Jersey 1.1.3, Atmosphere 0.4.1, Cassandra 0.4.1, Configgy 1.4 (Jonas Bonér) +- **FIX** - Lowered actor memory footprint; now an actor consumes ~600 bytes, which mean that you can create 6.5 million on 4 G RAM (Jonas Bonér) +- **FIX** - Remote actors are now defined by their UUID (not class name) (Jonas Bonér) +- **FIX** - Fixed dispatcher bugs (Jonas Bonér) +- **FIX** - Cleaned up Maven scripts and distribution in general (Jonas Bonér) +- **FIX** - Fixed many many bugs and minor issues (Jonas Bonér) +- **FIX** - Fixed inconsistencies and uglyness in Actors API (Jonas Bonér) +- **REMOVE** - Removed concurrent mode (Jonas Bonér) +- **REMOVE** - Removed embedded Cassandra mode (Jonas Bonér) +- **REMOVE** - Removed the !? method in Actor (synchronous message send, since it's evil. Use !! with time-out instead. (Jonas Bonér) +- **REMOVE** - Removed startup scripts and lib dir (Jonas Bonér) +- **REMOVE** - Removed the 'Transient' life-cycle scope since to close to 'Temporary' in semantics. (Jonas Bonér) +- **REMOVE** - Removed 'Transient' Actors and restart timeout (Jonas Bonér) From 66ec36b4ef2cae993df167b02ef59ddd5980bc98 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Sun, 8 May 2011 08:15:24 +0200 Subject: [PATCH 177/233] Docs: Moved getting-started from pending (cherry picked from commit d3f83b2bed989885f318f3d044668a4fac721a3d) --- akka-docs/{pending => intro}/getting-started.rst | 0 akka-docs/intro/index.rst | 1 + 2 files changed, 1 insertion(+) rename akka-docs/{pending => intro}/getting-started.rst (100%) diff --git a/akka-docs/pending/getting-started.rst b/akka-docs/intro/getting-started.rst similarity index 100% rename from akka-docs/pending/getting-started.rst rename to akka-docs/intro/getting-started.rst diff --git a/akka-docs/intro/index.rst b/akka-docs/intro/index.rst index d83f93036f..b04b877827 100644 --- a/akka-docs/intro/index.rst +++ b/akka-docs/intro/index.rst @@ -6,6 +6,7 @@ Introduction what-is-akka why-akka + getting-started getting-started-first-scala getting-started-first-scala-eclipse getting-started-first-java From e1c5e2bfed291f2c098e64847daa12501f0ec3ab Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Sun, 8 May 2011 10:23:05 +0200 Subject: [PATCH 178/233] Docs: Re-write of Getting Started page (cherry picked from commit 55ef6998dd473f759803e9d6dc862af80b1cfceb) --- akka-docs/additional/add-on-modules.rst | 16 +++ akka-docs/additional/index.rst | 1 + akka-docs/dev/building-akka.rst | 2 + .../intro/getting-started-first-java.rst | 2 + .../intro/getting-started-first-scala.rst | 2 + akka-docs/intro/getting-started.rst | 134 ++++++++++++++---- akka-docs/java/index.rst | 2 + akka-docs/project/links.rst | 21 ++- akka-docs/scala/index.rst | 2 + 9 files changed, 152 insertions(+), 30 deletions(-) create mode 100644 akka-docs/additional/add-on-modules.rst diff --git a/akka-docs/additional/add-on-modules.rst b/akka-docs/additional/add-on-modules.rst new file mode 100644 index 0000000000..bab2f1b174 --- /dev/null +++ b/akka-docs/additional/add-on-modules.rst @@ -0,0 +1,16 @@ +.. _add-on-modules: + +Add-on Modules +============== + +Akka Modules consist of add-on modules outside the core of Akka: + +- ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.) +- ``akka-amqp-1.1.jar`` -- AMQP integration +- ``akka-camel-1.1.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world) +- ``akka-camel-typed-1.1.jar`` -- Apache Camel Typed Actors integration +- ``akka-scalaz-1.1.jar`` -- Support for the Scalaz library +- ``akka-spring-1.1.jar`` -- Spring framework integration +- ``akka-osgi-dependencies-bundle-1.1.jar`` -- OSGi support + +Documentation for Akka Modules is located `here `_. diff --git a/akka-docs/additional/index.rst b/akka-docs/additional/index.rst index a2090bea43..f36b5ce7cd 100644 --- a/akka-docs/additional/index.rst +++ b/akka-docs/additional/index.rst @@ -4,6 +4,7 @@ Additional Information .. toctree:: :maxdepth: 2 + add-on-modules articles benchmarks recipes diff --git a/akka-docs/dev/building-akka.rst b/akka-docs/dev/building-akka.rst index 3ed3b151bc..677ad0e15a 100644 --- a/akka-docs/dev/building-akka.rst +++ b/akka-docs/dev/building-akka.rst @@ -154,6 +154,8 @@ Building Akka Modules See the Akka Modules documentation. +.. _dependencies: + Dependencies ============ diff --git a/akka-docs/intro/getting-started-first-java.rst b/akka-docs/intro/getting-started-first-java.rst index aafe02446f..32e8f36672 100644 --- a/akka-docs/intro/getting-started-first-java.rst +++ b/akka-docs/intro/getting-started-first-java.rst @@ -1,3 +1,5 @@ +.. _getting-started-first-java: + Getting Started Tutorial (Java): First Chapter ============================================== diff --git a/akka-docs/intro/getting-started-first-scala.rst b/akka-docs/intro/getting-started-first-scala.rst index 20e592c4ea..07cfec7548 100644 --- a/akka-docs/intro/getting-started-first-scala.rst +++ b/akka-docs/intro/getting-started-first-scala.rst @@ -1,3 +1,5 @@ +.. _getting-started-first-scala: + Getting Started Tutorial (Scala): First Chapter =============================================== diff --git a/akka-docs/intro/getting-started.rst b/akka-docs/intro/getting-started.rst index d76db9b299..8071c6887d 100644 --- a/akka-docs/intro/getting-started.rst +++ b/akka-docs/intro/getting-started.rst @@ -1,19 +1,78 @@ Getting Started =============== -There are several ways to download Akka. You can download the full distribution with microkernel, which includes all modules. You can download just the core distribution. Or you can use a build tool like Maven or SBT to download dependencies from the Akka Maven repository. +.. sidebar:: Contents -A list of each of the Akka module JARs dependencies can be found `here `_. + .. contents:: :local: + +The best way to start learning Akka is to try the Getting Started Tutorial, which comes in several flavours +depending on you development environment preferences: + +- :ref:`getting-started-first-java` for Java development, either + + - as standalone project, running from the command line, + - or as Maven project and running it from within Maven + +- :ref:`getting-started-first-scala` for Scala development, either + + - as standalone project, running from the command line, + - or as SBT (Simple Build Tool) project and running it from within SBT + +- :ref:`getting-started-first-scala-eclipse` for Scala development with Eclipse + +The Getting Started Tutorial describes everything you need to get going, and you don't need to read the rest of +this page if you study the tutorial. For later look back reference this page describes the +essential parts for getting started with different development environments. + +Prerequisites +------------- + +Akka requires that you have `Jave 1.6 `_ or +later installed on you machine. + +Download +-------- + +There are several ways to download Akka. You can download the full distribution with microkernel, which includes +all modules. You can download just the core distribution. Or you can use a build tool like Maven or SBT to download +dependencies from the Akka Maven repository. + +Modules +------- + +Akka is split up into two different parts: + +* Akka - The core modules. Reflects all the sections under :ref:`scala_api` and :ref:`java_api`. +* Akka Modules - The microkernel and add-on modules, described in :ref:`add-on-modules`. + +Akka is very modular and has many JARs for containing different features. The core distribution has seven modules: + +- ``akka-actor-1.1.jar`` -- Standard Actors +- ``akka-typed-actor-1.1.jar`` -- Typed Actors +- ``akka-remote-1.1.jar`` -- Remote Actors +- ``akka-stm-1.1.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures +- ``akka-http-1.1.jar`` -- Akka Mist for continuation-based asynchronous HTTP and also Jersey integration +- ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener +- ``akka-testkit-1.1.jar`` -- Toolkit for testing Actors + +We also have Akka Modules containing add-on modules outside the core of Akka. + +- ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.) +- ``akka-amqp-1.1.jar`` -- AMQP integration +- ``akka-camel-1.1.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world) +- ``akka-camel-typed-1.1.jar`` -- Apache Camel Typed Actors integration +- ``akka-scalaz-1.1.jar`` -- Support for the Scalaz library +- ``akka-spring-1.1.jar`` -- Spring framework integration +- ``akka-osgi-dependencies-bundle-1.1.jar`` -- OSGi support + + +How to see the JARs dependencies of each Akka module is described in the :ref:`dependencies` section. Worth noting +is that ``akka-actor`` has zero external dependencies (apart from the ``scala-library.jar`` JAR). Using a release distribution ---------------------------- -Akka is split up into two different parts: - -* Akka - The core modules. Reflects all the sections under 'Scala API' and 'Java API' in the navigation bar. -* Akka Modules - The microkernel and add-on modules. Reflects all the sections under 'Add-on modules' in the navigation bar. - -Download the release you need (Akka core or Akka Modules) from ``_ and unzip it. +Download the release you need, Akka core or Akka Modules, from ``_ and unzip it. Microkernel ^^^^^^^^^^^ @@ -21,17 +80,9 @@ Microkernel The Akka Modules distribution includes the microkernel. To run the microkernel: * Set the AKKA_HOME environment variable to the root of the Akka distribution. -* Run ``java -jar akka-modules-1.0.jar``. This will boot up the microkernel and deploy all samples applications from './deploy' dir. +* To start the kernel use the scripts in the ``bin`` directory and deploy all samples applications from ``./deploy`` dir. -For example (bash shell): - -:: - - cd akka-modules-1.0 - export AKKA_HOME=`pwd` - java -jar akka-modules-1.0.jar - -Now you can continue with reading the `tutorial `_ and try to build the tutorial sample project step by step. This can be a good starting point before diving into the reference documentation which can be navigated in the left sidebar. +More information is available in the documentation of the Microkernel in :ref:`add-on-modules`. Using a build tool ------------------ @@ -39,9 +90,14 @@ Using a build tool Akka can be used with build tools that support Maven repositories. The Akka Maven repository can be found at ``_. Using Akka with Maven -^^^^^^^^^^^^^^^^^^^^^ +--------------------- -If you want to use Akka with Maven then you need to add this repository to your ``pom.xml``: +Information about how to use Akka with Maven, including how to create an Akka Maven project from scratch, +can be found in the :ref:`getting-started-first-java`. + +Summary of the essential parts for using Akka with Maven: + +1) Add this repository to your ``pom.xml``: .. code-block:: xml @@ -51,22 +107,30 @@ If you want to use Akka with Maven then you need to add this repository to your http://akka.io/repository/ -Then you can add the Akka dependencies. For example, here is the dependency for Akka Actor 1.0: +2) Add the Akka dependencies. For example, here is the dependency for Akka Actor 1.1: .. code-block:: xml se.scalablesolutions.akka akka-actor - 1.0 + 1.1 + + Using Akka with SBT -^^^^^^^^^^^^^^^^^^^ +------------------- -Akka has an SBT plugin which makes it very easy to get started with Akka and SBT. +Information about how to use Akka with SBT, including how to create an Akka SBT project from scratch, +can be found in the :ref:`getting-started-first-scala`. -The Scala version in your SBT project needs to match the version that Akka is built against. For 1.0 this is 2.8.1. +Summary of the essential parts for using Akka with SBT: + +1) Akka has an SBT plugin which makes it very easy to get started with Akka and SBT. + +The Scala version in your SBT project needs to match the version that Akka is built against. For Akka 1.1 this is +Scala version 2.9.0. To use the plugin, first add a plugin definition to your SBT project by creating project/plugins/Plugins.scala with: @@ -76,12 +140,12 @@ To use the plugin, first add a plugin definition to your SBT project by creating 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.0" + val akkaPlugin = "se.scalablesolutions.akka" % "akka-sbt-plugin" % "1.1" } -*Note: the plugin version matches the Akka version provided. The current release is 1.0.* +*Note: the plugin version matches the Akka version provided. The current release is 1.1.* -Then mix the AkkaProject trait into your project definition. For example: +2) Then mix the AkkaProject trait into your project definition. For example: .. code-block:: scala @@ -106,6 +170,18 @@ If you also want to include other Akka modules there is a convenience method: `` val akkaMongo = akkaModule("persistence-mongo") val akkaRedis = akkaModule("persistence-redis") + +Using Akka with Eclipse +----------------------- + +Information about how to use Akka with Eclipse, including how to create an Akka Eclipse project from scratch, +can be found in the :ref:`getting-started-first-scala-eclipse`. + +Using Akka with IntelliJ IDEA +----------------------------- + +Setup SBT project and then use `sbt-idea `_ to generate IntelliJ IDEA project. + Build from sources ------------------ @@ -114,7 +190,7 @@ Akka uses Git and is hosted at `Github `_. * Akka: clone the Akka repository from ``_ * Akka Modules: clone the Akka Modules repository from ``_ -Continue reading the page on `how to build and run Akka `_ +Continue reading the page on :ref:`building-akka` Need help? ---------- diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst index 734151c5d2..54b2adf201 100644 --- a/akka-docs/java/index.rst +++ b/akka-docs/java/index.rst @@ -1,3 +1,5 @@ +.. _java_api: + Java API ========= diff --git a/akka-docs/project/links.rst b/akka-docs/project/links.rst index 7f1c061a26..3d732ca2f9 100644 --- a/akka-docs/project/links.rst +++ b/akka-docs/project/links.rst @@ -3,14 +3,33 @@ `Support `_ ======================================== +`Scalable Solutions `_ + + `Downloads `_ ======================================== +``_ + + `Source Code `_ ============================================== +Akka uses Git and is hosted at `Github `_. + +* Akka: clone the Akka repository from ``_ +* Akka Modules: clone the Akka Modules repository from ``_ + + `Maven Repository `_ ================================================ +``_ + + `Mailing List `_ -========================================================== \ No newline at end of file +========================================================== + +`Akka User Google Group `_ + +`Akka Developer Google Group `_ \ No newline at end of file diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index fad25026af..51e35b0d54 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -1,3 +1,5 @@ +.. _scala_api: + Scala API ========= From a0f5cbf086ec576fcc2595fb86186fb4b80c8d53 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Sun, 8 May 2011 14:37:57 +0200 Subject: [PATCH 179/233] Docs: Fix of sample in 'Serializer API using reflection' (cherry picked from commit dfc2ba15efe49ee81aecf3b64e5291aa1ec487c0) --- akka-docs/scala/serialization.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/akka-docs/scala/serialization.rst b/akka-docs/scala/serialization.rst index 8ac5e1c8e1..2005ec99a4 100644 --- a/akka-docs/scala/serialization.rst +++ b/akka-docs/scala/serialization.rst @@ -468,21 +468,22 @@ You can also use the Serializer abstraction to serialize using the reflection ba .. code-block:: scala - import akka.serialization.Serializer import scala.reflect.BeanInfo + import akka.serialization.Serializer @BeanInfo case class Foo(name: String) { def this() = this(null) // default constructor is necessary for deserialization } - val foo = new Foo("bar") - val json = Serializer.ScalaJSON.out(foo) + val foo = Foo("bar") - val fooCopy = Serializer.ScalaJSON.in(json) // returns a JsObject as an AnyRef + val json = Serializer.ScalaJSON.toBinary(foo) - val fooCopy2 = Serializer.ScalaJSON.in(new String(json)) // can also take a string as input + val fooCopy = Serializer.ScalaJSON.fromBinary(json) // returns a JsObject as an AnyRef - val fooCopy3 = Serializer.ScalaJSON.in[Foo](json).asInstanceOf[Foo] + val fooCopy2 = Serializer.ScalaJSON.fromJSON(new String(json)) // can also take a string as input + + val fooCopy3 = Serializer.ScalaJSON.fromBinary[Foo](json).asInstanceOf[Foo] Classes without a @BeanInfo annotation cannot be serialized as JSON. So if you see something like that: From 48f3229cff69b4c463b138c809d872078e8dd7d3 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Sun, 8 May 2011 15:14:40 +0200 Subject: [PATCH 180/233] Docs: Moved security from pending (cherry picked from commit efcc7325d0b4063dc225f84535ecf1cacf4fd4ee) --- akka-docs/scala/index.rst | 1 + akka-docs/{pending => scala}/security.rst | 0 2 files changed, 1 insertion(+) rename akka-docs/{pending => scala}/security.rst (100%) diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index 51e35b0d54..c3916ac2a6 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -21,5 +21,6 @@ Scala API routing fsm http + security testing tutorial-chat-server diff --git a/akka-docs/pending/security.rst b/akka-docs/scala/security.rst similarity index 100% rename from akka-docs/pending/security.rst rename to akka-docs/scala/security.rst From 0b010b6984b21cfc217a2f94bbc63355884cbb65 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Sun, 8 May 2011 15:40:06 +0200 Subject: [PATCH 181/233] Docs: Moved slf4j from pending (cherry picked from commit ff4107f8e0531b88db317021eeb56d636ba80bad) --- akka-docs/general/index.rst | 1 + akka-docs/{pending => general}/slf4j.rst | 0 2 files changed, 1 insertion(+) rename akka-docs/{pending => general}/slf4j.rst (100%) diff --git a/akka-docs/general/index.rst b/akka-docs/general/index.rst index eaedbd37f7..ae8a8f30b3 100644 --- a/akka-docs/general/index.rst +++ b/akka-docs/general/index.rst @@ -7,4 +7,5 @@ General jmm configuration event-handler + slf4j diff --git a/akka-docs/pending/slf4j.rst b/akka-docs/general/slf4j.rst similarity index 100% rename from akka-docs/pending/slf4j.rst rename to akka-docs/general/slf4j.rst From 73fd0775f3888b857e8f6f04b3e3575244644c3c Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Sun, 8 May 2011 16:59:17 +0200 Subject: [PATCH 182/233] Docs: Various improvements (cherry picked from commit aad2c29c68a239b8f2512abc5e93d58ca4354a49) --- akka-docs/general/event-handler.rst | 4 +- akka-docs/general/jmm.rst | 3 ++ akka-docs/general/slf4j.rst | 12 ++++- akka-docs/index.rst | 5 ++- akka-docs/java/actor-registry.rst | 10 ++--- akka-docs/java/dataflow.rst | 5 +++ akka-docs/java/typed-actors.rst | 57 +++++++++++++++++++++++- akka-docs/java/untyped-actors.rst | 39 ++++++++-------- akka-docs/project/issue-tracking.rst | 2 +- akka-docs/project/links.rst | 19 ++++---- akka-docs/scala/actor-registry.rst | 12 ++--- akka-docs/scala/actors.rst | 35 ++++++++------- akka-docs/scala/dataflow.rst | 4 ++ akka-docs/scala/futures.rst | 4 ++ akka-docs/scala/http.rst | 11 +++-- akka-docs/scala/remote-actors.rst | 27 ----------- akka-docs/scala/security.rst | 45 ++++++++++--------- akka-docs/scala/tutorial-chat-server.rst | 18 +------- akka-docs/scala/typed-actors.rst | 4 +- 19 files changed, 186 insertions(+), 130 deletions(-) diff --git a/akka-docs/general/event-handler.rst b/akka-docs/general/event-handler.rst index e43bb20eb0..e4396faef5 100644 --- a/akka-docs/general/event-handler.rst +++ b/akka-docs/general/event-handler.rst @@ -1,3 +1,5 @@ +.. _event-handler: + Event Handler ============= @@ -17,7 +19,7 @@ You can configure which event handlers should be registered at boot time. That i event-handler-level = "DEBUG" # Options: ERROR, WARNING, INFO, DEBUG } -The default one logs to STDOUT and is registered by default. It is not intended to be used for production. There is also an SLF4J event handler available in the 'akka-slf4j.jar' module. Read more about it `here `_. +The default one logs to STDOUT and is registered by default. It is not intended to be used for production. There is also an :ref:`slf4j` event handler available in the 'akka-slf4j' module. Example of creating a listener from Scala (from Java you just have to create an 'UntypedActor' and create a handler for these messages): diff --git a/akka-docs/general/jmm.rst b/akka-docs/general/jmm.rst index ed7bffb748..fd65ce3f28 100644 --- a/akka-docs/general/jmm.rst +++ b/akka-docs/general/jmm.rst @@ -2,6 +2,7 @@ Akka and the Java Memory Model ================================ Prior to Java 5, the Java Memory Model (JMM) was broken. It was possible to get all kinds of strange results like unpredictable merged writes made by concurrent executing threads, unexpected reordering of instructions, and even final fields were not guaranteed to be final. With Java 5 and JSR-133, the Java Memory Model is clearly specified. This specification makes it possible to write code that performs, but doesn't cause concurrency problems. The Java Memory Model is specified in 'happens before'-rules, e.g.: + * **monitor lock rule**: a release of a lock happens before every subsequent acquire of the same lock. * **volatile variable rule**: a write of a volatile variable happens before every subsequent read of the same volatile variable @@ -11,10 +12,12 @@ Actors and the Java Memory Model -------------------------------- With the Actors implementation in Akka, there are 2 ways multiple threads can execute actions on shared memory over time: + * if a message is send to an actor (e.g. by another actor). In most cases messages are immutable, but if that message is not a properly constructed immutable object, without happens before rules, the system still could be subject to instruction re-orderings and visibility problems (so a possible source of concurrency errors). * if an actor makes changes to its internal state in one 'receive' method and access that state while processing another message. With the actors model you don't get any guarantee that the same thread will be executing the same actor for different messages. Without a happens before relation between these actions, there could be another source of concurrency errors. To solve the 2 problems above, Akka adds the following 2 'happens before'-rules to the JMM: + * **the actor send rule**: where the send of the message to an actor happens before the receive of the **same** actor. * **the actor subsequent processing rule**: where processing of one message happens before processing of the next message by the **same** actor. diff --git a/akka-docs/general/slf4j.rst b/akka-docs/general/slf4j.rst index 780030a543..a49b731771 100644 --- a/akka-docs/general/slf4j.rst +++ b/akka-docs/general/slf4j.rst @@ -1,7 +1,15 @@ +.. _slf4j: + SLF4J ===== -This module is available in the 'akka-slf4j.jar'. It has one single dependency; the slf4j-api jar. +This module is available in the 'akka-slf4j.jar'. It has one single dependency; the slf4j-api jar. In runtime you +also need a SLF4J backend, we recommend: + + .. code-block:: scala + + lazy val logback = "ch.qos.logback" % "logback-classic" % "0.9.28" % "runtime" + Event Handler ------------- @@ -15,5 +23,5 @@ This module includes a SLF4J Event Handler that works with Akka's standard Event event-handler-level = "DEBUG" } -Read more about how to use the event handler `here `_. +Read more about how to use the :ref:`event-handler`. diff --git a/akka-docs/index.rst b/akka-docs/index.rst index 7fbe64f4c3..fb8e273bf5 100644 --- a/akka-docs/index.rst +++ b/akka-docs/index.rst @@ -13,8 +13,8 @@ Contents project/index additional/index -Quick Links -=========== +Links +===== * :ref:`migration` @@ -26,4 +26,5 @@ Quick Links * :ref:`issue_tracking` +* :ref:`support` diff --git a/akka-docs/java/actor-registry.rst b/akka-docs/java/actor-registry.rst index b495226598..41d50c05c3 100644 --- a/akka-docs/java/actor-registry.rst +++ b/akka-docs/java/actor-registry.rst @@ -8,12 +8,12 @@ ActorRegistry: Finding Actors Actors can be looked up using the 'akka.actor.Actors.registry()' object. Through this registry you can look up actors by: -* uuid com.eaio.uuid.UUID – this uses the ‘uuid’ field in the Actor class, returns the actor reference for the actor with specified uuid, if one exists, otherwise None -* id string – this uses the ‘id’ field in the Actor class, which can be set by the user (default is the class name), returns all actor references to actors with specified id -* parameterized type - returns a 'ActorRef[]' with all actors that are a subtype of this specific type -* specific actor class - returns a 'ActorRef[]' with all actors of this exact class +* uuid com.eaio.uuid.UUID – this uses the ``uuid`` field in the Actor class, returns the actor reference for the actor with specified uuid, if one exists, otherwise None +* id string – this uses the ``id`` field in the Actor class, which can be set by the user (default is the class name), returns all actor references to actors with specified id +* parameterized type - returns a ``ActorRef[]`` with all actors that are a subtype of this specific type +* specific actor class - returns a ``ActorRef[]`` with all actors of this exact class -Actors are automatically registered in the ActorRegistry when they are started and removed when they are stopped. But you can explicitly register and unregister ActorRef's if you need to using the 'register' and 'unregister' methods. +Actors are automatically registered in the ActorRegistry when they are started and removed when they are stopped. But you can explicitly register and unregister ActorRef's if you need to using the ``register`` and ``unregister`` methods. Here is a summary of the API for finding actors: diff --git a/akka-docs/java/dataflow.rst b/akka-docs/java/dataflow.rst index 429b10ce19..52437647a5 100644 --- a/akka-docs/java/dataflow.rst +++ b/akka-docs/java/dataflow.rst @@ -1,6 +1,10 @@ Dataflow Concurrency (Java) =========================== +.. sidebar:: Contents + + .. contents:: :local: + Introduction ------------ @@ -13,6 +17,7 @@ Dataflow concurrency is deterministic. This means that it will always behave the The best way to learn how to program with dataflow variables is to read the fantastic book `Concepts, Techniques, and Models of Computer Programming `_. By Peter Van Roy and Seif Haridi. The documentation is not as complete as it should be, something we will improve shortly. For now, besides above listed resources on dataflow concurrency, I recommend you to read the documentation for the GPars implementation, which is heavily influenced by the Akka implementation: + * ``_ * ``_ diff --git a/akka-docs/java/typed-actors.rst b/akka-docs/java/typed-actors.rst index 7147f4edad..99803186b2 100644 --- a/akka-docs/java/typed-actors.rst +++ b/akka-docs/java/typed-actors.rst @@ -54,8 +54,8 @@ Then you can create an Typed Actor out of it by creating it through the 'TypedAc (RegistrationService) TypedActor.newInstance(RegistrationService.class, RegistrationServiceImpl.class, 1000); // The last parameter defines the timeout for Future calls -**Creating Typed Actors with non-default constructor** -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Creating Typed Actors with non-default constructor +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To create a typed actor that takes constructor arguments use a variant of 'newInstance' or 'newRemoteInstance' that takes an instance of a 'TypedActorFactory' in which you can create the TypedActor in any way you like. If you use this method then make sure that no one can get a reference to the actor instance. Touching actor state directly is bypassing the whole actor dispatching mechanism and create race conditions which can lead to corrupt data. @@ -193,3 +193,56 @@ Akka can help you in this regard. It allows you to turn on an option for seriali } This will make a deep clone (using Java serialization) of all parameters. + +Guice Integration +----------------- + +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 +^^^^^^^^^^^^^^^^^^^^^^ + +You can also plug in external Guice modules and have not-actors wired up as part of the configuration. +Here is an example: + +.. code-block:: java + + import static akka.config.Supervision.*; + import static akka.config.SupervisorConfig.*; + + TypedActorConfigurator manager = new TypedActorConfigurator(); + + manager.configure( + new AllForOneStrategy(new Class[]{Exception.class}, 3, 1000), + new SuperviseTypedActor[] { + new SuperviseTypedActor( + Foo.class, + FooImpl.class, + temporary(), + 1000), + new SuperviseTypedActor( + Bar.class, + BarImpl.class, + permanent(), + 1000) + }) + .addExternalGuiceModule(new AbstractModule() { + protected void configure() { + bind(Ext.class).to(ExtImpl.class).in(Scopes.SINGLETON); + }}) + .configure() + .inject() + .supervise(); + +Retrieve the external Guice dependency +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The external dependency can be retrieved like this: + +.. code-block:: java + + Ext ext = manager.getExternalDependency(Ext.class); + + + diff --git a/akka-docs/java/untyped-actors.rst b/akka-docs/java/untyped-actors.rst index 1b73988aca..c03dd8e50c 100644 --- a/akka-docs/java/untyped-actors.rst +++ b/akka-docs/java/untyped-actors.rst @@ -227,6 +227,27 @@ Here is an example: Reply to messages ----------------- +Reply using the channel +^^^^^^^^^^^^^^^^^^^^^^^ + +If you want to have a handle to an object to whom you can reply to the message, you can use the Channel abstraction. +Simply call getContext().channel() and then you can forward that to others, store it away or otherwise until you want to reply, +which you do by Channel.sendOneWay(msg) + +.. code-block:: java + + public void onReceive(Object message) throws Exception { + if (message instanceof String) { + String msg = (String)message; + if (msg.equals("Hello") && getContext().getSenderFuture().isDefined()) { + // Reply to original sender of message using the channel + getContext().channel().sendOneWay(msg + " from " + getContext().getUuid()); + } + } + } + +We recommend that you as first choice use the channel abstraction instead of the other ways described in the following sections. + Reply using the 'replySafe' and 'replyUnsafe' methods ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -307,24 +328,6 @@ Here is an example of how it can be used: } } -Reply using the channel -^^^^^^^^^^^^^^^^^^^^^^^ - -If you want to have a handle to an object to whom you can reply to the message, you can use the Channel abstraction. -Simply call getContext().channel() and then you can forward that to others, store it away or otherwise until you want to reply, -which you do by Channel.sendOneWay(msg) - -.. code-block:: java - - public void onReceive(Object message) throws Exception { - if (message instanceof String) { - String msg = (String)message; - if (msg.equals("Hello") && getContext().getSenderFuture().isDefined()) { - // Reply to original sender of message using the channel - getContext().channel().sendOneWay(msg + " from " + getContext().getUuid()); - } - } - } Summary of reply semantics and options ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/akka-docs/project/issue-tracking.rst b/akka-docs/project/issue-tracking.rst index 7c9afd9ade..f5d43699f3 100644 --- a/akka-docs/project/issue-tracking.rst +++ b/akka-docs/project/issue-tracking.rst @@ -11,7 +11,7 @@ Browsing Tickets ^^^^^^^ -`You can find the Akka tickets here `_ +`You can find the Akka tickets here `_ `You can find the Akka Modules tickets here `_ diff --git a/akka-docs/project/links.rst b/akka-docs/project/links.rst index 3d732ca2f9..845c739e44 100644 --- a/akka-docs/project/links.rst +++ b/akka-docs/project/links.rst @@ -1,10 +1,17 @@ -.. _links: +.. _support: -`Support `_ -======================================== +`Support `__ +========================================= `Scalable Solutions `_ +`Mailing List `_ +========================================================== + +`Akka User Google Group `_ + +`Akka Developer Google Group `_ + `Downloads `_ ======================================== @@ -27,9 +34,3 @@ Akka uses Git and is hosted at `Github `_. ``_ -`Mailing List `_ -========================================================== - -`Akka User Google Group `_ - -`Akka Developer Google Group `_ \ No newline at end of file diff --git a/akka-docs/scala/actor-registry.rst b/akka-docs/scala/actor-registry.rst index 5f57434501..d812ef9066 100644 --- a/akka-docs/scala/actor-registry.rst +++ b/akka-docs/scala/actor-registry.rst @@ -6,14 +6,14 @@ Module stability: **SOLID** ActorRegistry: Finding Actors ----------------------------- -Actors can be looked up by using the **akka.actor.Actor.registry: akka.actor.ActorRegistry**. Lookups for actors through this registry can be done by: +Actors can be looked up by using the ``akka.actor.Actor.registry: akka.actor.ActorRegistry``. Lookups for actors through this registry can be done by: -* uuid akka.actor.Uuid – this uses the ‘**uuid**’ field in the Actor class, returns the actor reference for the actor with specified uuid, if one exists, otherwise None -* id string – this uses the ‘**id**’ field in the Actor class, which can be set by the user (default is the class name), returns all actor references to actors with specified id -* specific actor class - returns an '**Array[Actor]**' with all actors of this exact class -* parameterized type - returns an '**Array[Actor]**' with all actors that are a subtype of this specific type +* uuid akka.actor.Uuid – this uses the ``uuid`` field in the Actor class, returns the actor reference for the actor with specified uuid, if one exists, otherwise None +* id string – this uses the ``id`` field in the Actor class, which can be set by the user (default is the class name), returns all actor references to actors with specified id +* specific actor class - returns an ``Array[Actor]`` with all actors of this exact class +* parameterized type - returns an ``Array[Actor]`` with all actors that are a subtype of this specific type -Actors are automatically registered in the ActorRegistry when they are started, removed or stopped. You can explicitly register and unregister ActorRef's by using the '**register**' and '**unregister**' methods. The ActorRegistry contains many convenience methods for looking up typed actors. +Actors are automatically registered in the ActorRegistry when they are started, removed or stopped. You can explicitly register and unregister ActorRef's by using the ``register`` and ``unregister`` methods. The ActorRegistry contains many convenience methods for looking up typed actors. Here is a summary of the API for finding actors: diff --git a/akka-docs/scala/actors.rst b/akka-docs/scala/actors.rst index ddd1e5f439..c7f5453a2e 100644 --- a/akka-docs/scala/actors.rst +++ b/akka-docs/scala/actors.rst @@ -260,6 +260,25 @@ Let's start by looking how we can reply to messages in a convenient way using th Reply to messages ----------------- +Reply using the channel +^^^^^^^^^^^^^^^^^^^^^^^ + +If you want to have a handle to an object to whom you can reply to the message, you can use the ``Channel`` abstraction. +Simply call ``self.channel`` and then you can forward that to others, store it away or otherwise until you want to reply, which you do by ``Channel ! response``: + +.. code-block:: scala + + case request => + val result = process(request) + self.channel ! result + +.. code-block:: scala + + case request => + friend forward self.channel + +We recommend that you as first choice use the channel abstraction instead of the other ways described in the following sections. + Reply using the reply and reply\_? methods ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -325,22 +344,6 @@ Here is an example of how it can be used: senderFuture.foreach(_.completeWithException(this, e)) } -Reply using the channel -^^^^^^^^^^^^^^^^^^^^^^^ - -If you want to have a handle to an object to whom you can reply to the message, you can use the ``Channel`` abstraction. -Simply call ``self.channel`` and then you can forward that to others, store it away or otherwise until you want to reply, which you do by ``Channel ! response``: - -.. code-block:: scala - - case request => - val result = process(request) - self.channel ! result - -.. code-block:: scala - - case request => - friend forward self.channel Summary of reply semantics and options ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/akka-docs/scala/dataflow.rst b/akka-docs/scala/dataflow.rst index 82da73ce33..ec99eb0a74 100644 --- a/akka-docs/scala/dataflow.rst +++ b/akka-docs/scala/dataflow.rst @@ -1,6 +1,10 @@ Dataflow Concurrency (Scala) ============================ +.. sidebar:: Contents + + .. contents:: :local: + Description ----------- diff --git a/akka-docs/scala/futures.rst b/akka-docs/scala/futures.rst index 331bb1cdba..690952baa3 100644 --- a/akka-docs/scala/futures.rst +++ b/akka-docs/scala/futures.rst @@ -1,6 +1,10 @@ Futures (Scala) =============== +.. sidebar:: Contents + + .. contents:: :local: + Introduction ------------ diff --git a/akka-docs/scala/http.rst b/akka-docs/scala/http.rst index 75bd9ca7c9..9f15664b70 100644 --- a/akka-docs/scala/http.rst +++ b/akka-docs/scala/http.rst @@ -1,9 +1,12 @@ .. _http-module: -###### - HTTP -###### +HTTP +==== + +.. sidebar:: Contents + + .. contents:: :local: Module stability: **SOLID** @@ -523,3 +526,5 @@ Using the Akka Mist module with the Facebook Graph API and WebGL Example project using Akka Mist with the Facebook Graph API and WebGL ``_ + + diff --git a/akka-docs/scala/remote-actors.rst b/akka-docs/scala/remote-actors.rst index 0f7e68d095..305783238d 100644 --- a/akka-docs/scala/remote-actors.rst +++ b/akka-docs/scala/remote-actors.rst @@ -697,30 +697,3 @@ Using the generated message builder to send the message to a remote actor: .setName("Coltrane") .build -SBinary -^^^^^^^ - -.. code-block:: scala - - case class User(firstNameLastName: Tuple2[String, String], email: String, age: Int) extends Serializable.SBinary[User] { - import sbinary.DefaultProtocol._ - - 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) - } - diff --git a/akka-docs/scala/security.rst b/akka-docs/scala/security.rst index cae23fbdd5..9d186b5ea2 100644 --- a/akka-docs/scala/security.rst +++ b/akka-docs/scala/security.rst @@ -1,11 +1,16 @@ -Security -======== +HTTP Security +============= + +.. sidebar:: Contents + + .. contents:: :local: Module stability: **IN PROGRESS** Akka supports security for access to RESTful Actors through `HTTP Authentication `_. The security is implemented as a jersey ResourceFilter which delegates the actual authentication to an authentication actor. Akka provides authentication via the following authentication schemes: + * `Basic Authentication `_ * `Digest Authentication `_ * `Kerberos SPNEGO Authentication `_ @@ -13,16 +18,14 @@ Akka provides authentication via the following authentication schemes: The authentication is performed by implementations of akka.security.AuthenticationActor. Akka provides a trait for each authentication scheme: + * BasicAuthenticationActor * DigestAuthenticationActor * SpnegoAuthenticationActor -With Akka’s excellent support for distributed databases, it’s a one-liner to do a distributed authentication scheme. - -^ Setup -===== +----- To secure your RESTful actors you need to perform the following steps: @@ -54,6 +57,7 @@ To secure your RESTful actors you need to perform the following steps: 3. Start your authentication actor in your 'Boot' class. The security package consists of the following parts: 4. Secure your RESTful actors using class or resource level annotations: + * @DenyAll * @RolesAllowed(listOfRoles) * @PermitAll @@ -65,6 +69,7 @@ The akka-samples-security module contains a small sample application with sample You can start the sample app using the jetty plugin: mvn jetty:run. The RESTful actor can then be accessed using your browser of choice under: + * permit access only to users having the “chef” role: ``_ * public access: ``_ @@ -72,7 +77,6 @@ You can access the secured resource using any user for basic authentication (whi Digest authentication can be directly enabled in the sample app. Kerberos/SPNEGO authentication is a bit more involved an is described below. -^ Kerberos/SPNEGO Authentication ------------------------------ @@ -83,11 +87,12 @@ In a kerberos enabled environment a user will need to sign on only once. Subsequ Most prominently the kerberos protocol is used to authenticate users in a windows network. When deploying web applications to a corporate intranet an important feature will be to support the single sign on (SSO), which comes to make the application kerberos aware. How does it work (at least for REST actors)? -# When accessing a secured resource the server will check the request for the *Authorization* header as with basic or digest authentication. -# If it is not set, the server will respond with a challenge to “Negotiate”. The negotiation is in fact the NEGO part of the `SPNEGO `_ specification) -# The browser will then try to acquire a so called *service ticket* from a ticket granting service, i.e. the kerberos server -# 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 + +- When accessing a secured resource the server will check the request for the *Authorization* header as with basic or digest authentication. +- If it is not set, the server will respond with a challenge to "Negotiate". The negotiation is in fact the NEGO part of the `SPNEGO `_ specification +- The browser will then try to acquire a so called *service ticket* from a ticket granting service, i.e. the kerberos server +- 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/SPNEGOauthentication actor in the akka.conf like this: @@ -103,8 +108,9 @@ To activate the kerberos/SPNEGO authentication for your REST actor you need to e } Furthermore you must provide the SpnegoAuthenticator with the following information. -# Service principal name: the name of your web application in the kerberos servers user database. This name is always has the form “HTTP/{server}@{realm}” -# Path to the keytab file: this is a kind of certificate for your web application to acquire tickets from the kerberos server + +- Service principal name: the name of your web application in the kerberos servers user database. This name is always has the form ``HTTP/{server}@{realm}`` +- Path to the keytab file: this is a kind of certificate for your web application to acquire tickets from the kerberos server .. code-block:: ruby @@ -122,7 +128,6 @@ Furthermore you must provide the SpnegoAuthenticator with the following informat ... } -^ How to setup kerberos on localhost for Ubuntu --------------------------------------------- @@ -233,9 +238,9 @@ This seems correct. To remove the ticket cache simply type kdestroy. Verifying - Password: eckart@dilbert:~$ -This command will create a keytab file for the service principal named “http.keytab” in the current directory. You can specify other encryption methods than ‘aes256-cts-hmac-sha1-96’, but this is the e default encryption method for the heimdal client, so there is no additional configuration needed. You can specify other encryption types in the krb5.conf. +This command will create a keytab file for the service principal named ``http.keytab`` in the current directory. You can specify other encryption methods than ‘aes256-cts-hmac-sha1-96’, but this is the e default encryption method for the heimdal client, so there is no additional configuration needed. You can specify other encryption types in the krb5.conf. -Note that you might need to install the unlimited strength policy files for java from here:``_ to use the aes256 encryption from your application. +Note that you might need to install the unlimited strength policy files for java from here: ``_ to use the aes256 encryption from your application. Again we can test if the keytab generation worked with the kinit command: @@ -249,13 +254,13 @@ Again we can test if the keytab generation worked with the kinit command: Issued Expires Principal Oct 24 21:59:20 Oct 25 06:59:20 krbtgt/EXAMPLE.COM@EXAMPLE.COM -Now point the configuration of the key in 'akka.conf' to the correct location and set the correct service principal name. The web application should now startup and produce at least a 401 response with a header “WWW-Authenticate” = “Negotiate”. The last step is to configure the browser. +Now point the configuration of the key in 'akka.conf' to the correct location and set the correct service principal name. The web application should now startup and produce at least a 401 response with a header ``WWW-Authenticate`` = "Negotiate". The last step is to configure the browser. 6. Set up Firefox to use Kerberos/SPNEGO -This is done by typing 'about:config'. Filter the config entries for “network.neg” and set the config entries “network.negotiate-auth.delegation-uris” and “network.negotiate-auth.trusted-uris” to “localhost”. +This is done by typing ``about:config``. Filter the config entries for ``network.neg`` and set the config entries ``network.negotiate-auth.delegation-uris`` and ``network.negotiate-auth.trusted-uris`` to ``localhost``. and now … 7. Access the RESTful Actor. 8. Have fun -… but acquire an initial ticket for the user principal first: kinit zaphod +… but acquire an initial ticket for the user principal first: ``kinit zaphod`` diff --git a/akka-docs/scala/tutorial-chat-server.rst b/akka-docs/scala/tutorial-chat-server.rst index daa28d2ca3..3ef4b5b74b 100644 --- a/akka-docs/scala/tutorial-chat-server.rst +++ b/akka-docs/scala/tutorial-chat-server.rst @@ -371,22 +371,8 @@ Akka currently provides three different transactional abstractions; 'Map', 'Vect What you get is transactional memory in which multiple Actors are allowed to read and write to the same memory concurrently and if there is a clash between two transactions then both of them are aborted and retried. Aborting a transaction means that the memory is rolled back to the state it were in when the transaction was started. -In database terms STM gives you 'ACI' semantics; 'Atomicity', 'Consistency' and 'Isolation'. The 'D' in 'ACID'; 'Durability', you can't get with an STM since it is in memory. This however is addressed by the persistence module in Akka. - -Persistence: Storing the chat log ---------------------------------- - -Akka modules provides the possibility of taking the transactional data structures we discussed above and making them persistent. It is an extension to the STM which guarantees that it has the same semantics. - -The `persistence module `_ has pluggable storage back-ends. - -They all implement persistent 'Map', 'Vector' and 'Ref'. Which can be created and retrieved by id through one of the storage modules. - -.. code-block:: scala - - val map = RedisStorage.newMap(id) - val vector = CassandraStorage.newVector(id) - val ref = MongoStorage.newRef(id) +In database terms STM gives you 'ACI' semantics; 'Atomicity', 'Consistency' and 'Isolation'. The 'D' in 'ACID'; 'Durability', you can't get with an STM since it is in memory. +It possible to implement durable persistence for the transactional data structures, but in this sample we keep them in memory. Chat storage: Backed with simple in-memory ------------------------------------------ diff --git a/akka-docs/scala/typed-actors.rst b/akka-docs/scala/typed-actors.rst index 1663f844ef..81d68685f5 100644 --- a/akka-docs/scala/typed-actors.rst +++ b/akka-docs/scala/typed-actors.rst @@ -53,8 +53,8 @@ Then you can create an Typed Actor out of it by creating it through the 'TypedAc val service = TypedActor.newInstance(classOf[RegistrationService], classOf[RegistrationServiceImpl], 1000) // The last parameter defines the timeout for Future calls -**Creating Typed Actors with non-default constructor** -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Creating Typed Actors with non-default constructor +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To create a typed actor that takes constructor arguments use a variant of 'newInstance' or 'newRemoteInstance' that takes a call-by-name block in which you can create the Typed Actor in any way you like. From 20be7c40c635cb111085bfafa372473a8a2d806d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bone=CC=81r?= Date: Sun, 8 May 2011 20:42:40 +0200 Subject: [PATCH 183/233] Minor changes to docs + added new akka logo --- akka-docs/_sphinx/static/akka.png | Bin 5959 -> 7499 bytes akka-docs/_sphinx/static/logo.png | Bin 6767 -> 7499 bytes akka-docs/images/akka-as-kernel.png | Bin 39638 -> 0 bytes akka-docs/images/akka-as-library-1.png | Bin 29399 -> 0 bytes akka-docs/images/akka-as-library-2.png | Bin 36522 -> 0 bytes akka-docs/images/akka-as-library-3.png | Bin 48196 -> 0 bytes akka-docs/images/akka-as-library-4.png | Bin 39979 -> 0 bytes akka-docs/images/akka-as-library-5.png | Bin 44526 -> 0 bytes akka-docs/intro/deployment-scenarios.rst | 63 +---------------------- akka-docs/intro/what-is-akka.rst | 4 +- akka-docs/intro/why-akka.rst | 21 ++++---- 11 files changed, 13 insertions(+), 75 deletions(-) delete mode 100644 akka-docs/images/akka-as-kernel.png delete mode 100644 akka-docs/images/akka-as-library-1.png delete mode 100644 akka-docs/images/akka-as-library-2.png delete mode 100644 akka-docs/images/akka-as-library-3.png delete mode 100644 akka-docs/images/akka-as-library-4.png delete mode 100644 akka-docs/images/akka-as-library-5.png diff --git a/akka-docs/_sphinx/static/akka.png b/akka-docs/_sphinx/static/akka.png index d79821a0470333b5a79225ddfde526d9e6ba8d8c..a6bc9c3b98ed53f4c76165a2a52034a67e5d6ab0 100644 GIT binary patch literal 7499 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 HQ$iB}ON*{e literal 5959 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 diff --git a/akka-docs/_sphinx/static/logo.png b/akka-docs/_sphinx/static/logo.png index 2c36c66a36d62ba21608ec04770c4ddf8d8ccfa9..a6bc9c3b98ed53f4c76165a2a52034a67e5d6ab0 100644 GIT binary patch literal 7499 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 HQ$iB}ON*{e literal 6767 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 diff --git a/akka-docs/images/akka-as-kernel.png b/akka-docs/images/akka-as-kernel.png deleted file mode 100644 index fc3ca9ab5acd4298fe94666b2196ea0844cce0c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39638 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 diff --git a/akka-docs/images/akka-as-library-1.png b/akka-docs/images/akka-as-library-1.png deleted file mode 100644 index 6a9575a475622b013fdb4ef740f6e023ff98c28f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29399 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�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 E08pR@WdHyG diff --git a/akka-docs/images/akka-as-library-2.png b/akka-docs/images/akka-as-library-2.png deleted file mode 100644 index ea1785e61d0448ac6438c221eab3cf489bcfafb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36522 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 r@URw8Fw_o}TUP9_{bP0l+XkKb)|%M diff --git a/akka-docs/images/akka-as-library-3.png b/akka-docs/images/akka-as-library-3.png deleted file mode 100644 index a86468b98796abca66025369e5e22ce3c16379d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48196 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

    EZ8x{K7(K!-NA2HBYtUH%`t|c_i|-cGap?hd}3H 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 diff --git a/akka-docs/images/akka-as-library-4.png b/akka-docs/images/akka-as-library-4.png deleted file mode 100644 index e9adb4e10402430788f2ce3280092cf8b24df4db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39979 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�~hvN0CS8pz#^)qGjlgCf0 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 `_) or Comet (`Atmosphere `_) services by deploying the ‘AkkaServlet’ in your servlet container. In order for this to work in each define a so-called “boot” class which bootstraps the Actor configuration, wiring and startup. This is done in the ‘akka.conf’ file. - - .. image:: ../images/akka-as-library-5.png - - -Using Akka as a stand alone microkernel + Using Akka as a stand alone microkernel --------------------------------------- Akka can also be run as a stand-alone microkernel. It implements a full enterprise stack: - -Web/REST/Comet layer -^^^^^^^^^^^^^^^^^^^^ - -Akka currently embeds the `Grizzly/GlassFish `_ servlet container (but will soon be pluggable with Jetty as well) which allows to build REST-based using `JAX `_`-RS `_ and Comet-based services using `Atmosphere `_ as well as regular Web applications using JAX-RS’s `implicit views `_ (see also `James Strachan’s article `_). - - -Service layer -^^^^^^^^^^^^^ - -The service layer is implemented using fault tolerant, asynchronous, throttled message passing; like `SEDA-in-a-box `_ using Actors. - -Persistence layer -^^^^^^^^^^^^^^^^^ - -Implemented using pluggable storage engines for both partitioned distributed massively scalable storage (like Cassandra) as well as single node storage (like MongoDB). A different storage and gives also provides different consistency/availability trade-offs implementing either Eventually Consistency (BASE) or Atomicity (ACID). - -Monitoring and Management layer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Providing both JMX management and monitoring as well as w3c logging. - - .. image:: ../images/akka-as-kernel.png - Use BivySack for packaging your application ------------------------------------------- diff --git a/akka-docs/intro/what-is-akka.rst b/akka-docs/intro/what-is-akka.rst index 17e20bade7..9aeda53cd8 100644 --- a/akka-docs/intro/what-is-akka.rst +++ b/akka-docs/intro/what-is-akka.rst @@ -6,7 +6,7 @@ What is Akka? **Simpler Scalability, Fault-Tolerance, Concurrency & Remoting through Actors** -We believe that writing correct concurrent, fault-tolerant and scalable applications is too hard. Most of the time it's because we are using the wrong tools and the wrong level of abstraction. Akka is here to change that. Using the Actor Model together with ``Software Transactional Memory`` we raise the abstraction level and provide a better platform to build correct concurrent and scalable applications. For fault-tolerance we adopt the ``Let it crash`` / ``Embrace failure`` model which have been used with great success in the telecom industry to build applications that self-heals, systems that never stop. Actors also provides the abstraction for transparent distribution and the basis for truly scalable and fault-tolerant applications. Akka is Open Source and available under the ``Apache 2 License``. +We believe that writing correct concurrent, fault-tolerant and scalable applications is too hard. Most of the time it's because we are using the wrong tools and the wrong level of abstraction. Akka is here to change that. Using the Actor Model together with Software Transactional Memory we raise the abstraction level and provide a better platform to build correct concurrent and scalable applications. For fault-tolerance we adopt the Let it crash/Embrace failure model which have been used with great success in the telecom industry to build applications that self-heals, systems that never stop. Actors also provides the abstraction for transparent distribution and the basis for truly scalable and fault-tolerant applications. Akka is Open Source and available under the Apache 2 License. Download from ``_ @@ -20,7 +20,7 @@ Akka implements a unique hybrid of: - Asynchronous, non-blocking and highly performant event-driven programming model. - Very lightweight event-driven processes (create ~6.5 million actors on 4GB RAM). -- :ref:`fault-tolerance-java` through supervisor hierarchies with `let-it-crash `_ semantics. Excellent for writing highly fault-tolerant systems that never stop, systems that self-heal. +- :ref:`fault-tolerance-java` through supervisor hierarchies with "let-it-crash" semantics. Excellent for writing highly fault-tolerant systems that never stop, systems that self-heal. - :ref:`stm-java` (STM). (Distributed transactions coming soon). - :ref:`transactors-java`: combine actors and STM into transactional actors. Allows you to compose atomic message flows with automatic retry and rollback. - :ref:`remote-actors-java`: highly performant distributed actors with remote supervision and error management. diff --git a/akka-docs/intro/why-akka.rst b/akka-docs/intro/why-akka.rst index 512a669b2f..4294d7e022 100644 --- a/akka-docs/intro/why-akka.rst +++ b/akka-docs/intro/why-akka.rst @@ -53,16 +53,15 @@ And that's all in the ApacheV2-licensed open source project. On top of that we have a commercial product called Cloudy Akka which provides the following features: -#. Dynamically clustered ActorRegistry with both automatic and manual migration - of actors - -#. Cluster membership and cluster event subscriptions - -#. Durable actor mailboxes of different sizes and shapes - file-backed, - Redis-backed, ZooKeeper-backed, Beanstalkd-backed and with AMQP and JMS-based - in the works - -#. Monitoring influenced by Dapper for cross-machine message tracing and - JMX-exposed statistics +#. Management through Dashboard, JMX and REST +#. Monitoring through Dashboard, JMX and SNMP +#. Dapper-style tracing of messages across components and remote nodes +#. A configurable alert system +#. Real-time statistics +#. Very low overhead monitoring agents (should always be on in production) +#. Consolidation of statistics and logging information to a single node +#. Data analysis through Hadoop +#. Storage of statistics data for later processing +#. Provisioning and rolling upgrades through a dashboard Read more `here `_. From 1b54d013f489e952eeee27a54d7376da45a5cc52 Mon Sep 17 00:00:00 2001 From: Roland Date: Sun, 8 May 2011 22:17:34 +0200 Subject: [PATCH 184/233] clarify time measurement in testkit --- akka-docs/scala/testing.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/akka-docs/scala/testing.rst b/akka-docs/scala/testing.rst index 6a16037b61..aeec3ac57f 100644 --- a/akka-docs/scala/testing.rst +++ b/akka-docs/scala/testing.rst @@ -248,6 +248,11 @@ first; it follows that this examination usually is the last statement in a } } +.. note:: + + All times are measured using ``System.nanoTime``, meaning that they describe + wall time, not CPU time. + Ray Roestenburg has written a great article on using the TestKit: ``_. His full example is also available :ref:`here `. From ca77eb0926525dc2d60ed532bdcd550d4b139abf Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Mon, 9 May 2011 07:55:51 +0200 Subject: [PATCH 185/233] Docs: Removed JTA (cherry picked from commit d92d25b15894e34c0df0715886206fefec862d17) --- akka-docs/additional/stability-matrix.rst | 1 - akka-docs/intro/getting-started.rst | 4 ---- akka-docs/java/stm.rst | 28 ----------------------- akka-docs/scala/stm.rst | 28 ----------------------- 4 files changed, 61 deletions(-) diff --git a/akka-docs/additional/stability-matrix.rst b/akka-docs/additional/stability-matrix.rst index 636cab6c5f..61e5d247fb 100644 --- a/akka-docs/additional/stability-matrix.rst +++ b/akka-docs/additional/stability-matrix.rst @@ -28,7 +28,6 @@ AMQP Solid HTTP Solid Integration Guice Stable Integration Spring Stable -JTA Stable Scheduler Solid Redis Pub Sub In progress ================================ ============ ============ ============ diff --git a/akka-docs/intro/getting-started.rst b/akka-docs/intro/getting-started.rst index 8071c6887d..58632d3940 100644 --- a/akka-docs/intro/getting-started.rst +++ b/akka-docs/intro/getting-started.rst @@ -165,10 +165,6 @@ If you also want to include other Akka modules there is a convenience method: `` val akkaCamel = akkaModule("camel") val akkaCamelTyped = akkaModule("camel-typed") val akkaSpring = akkaModule("spring") - val akkaJta = akkaModule("jta") - val akkaCassandra = akkaModule("persistence-cassandra") - val akkaMongo = akkaModule("persistence-mongo") - val akkaRedis = akkaModule("persistence-redis") Using Akka with Eclipse diff --git a/akka-docs/java/stm.rst b/akka-docs/java/stm.rst index df6761b52a..ebdd553465 100644 --- a/akka-docs/java/stm.rst +++ b/akka-docs/java/stm.rst @@ -529,31 +529,3 @@ This illustration is taken from Rich Hickey's presentation. Copyright Rich Hicke .. image:: ../images/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. - -Akka also has an API for using JTA explicitly. Read the `section on JTA `_ for details. - -You can enable JTA support in the 'stm' section in the config: - -:: - - stm { - jta-aware = off # 'on' means that if there JTA Transaction Manager available then the STM will - # begin (or join), commit or rollback the JTA transaction. Default is 'off'. - } - -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 - } - - diff --git a/akka-docs/scala/stm.rst b/akka-docs/scala/stm.rst index c5bb99bcb4..b11cf828e1 100644 --- a/akka-docs/scala/stm.rst +++ b/akka-docs/scala/stm.rst @@ -529,34 +529,6 @@ This illustration is taken from Rich Hickey's presentation. Copyright Rich Hicke .. image:: ../images/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. - -Akka also has an API for using JTA explicitly. Read the `section on JTA `_ for details. - -You can enable JTA support in the 'stm' section in the config: - -:: - - stm { - jta-aware = off # 'on' means that if there JTA Transaction Manager available then the STM will - # begin (or join), commit or rollback the JTA transaction. Default is 'off'. - } - -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 - } - - Ants simulation sample ---------------------- From 5b08cf59077dfff563d362bbb208021615ca1a26 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Mon, 9 May 2011 08:28:07 +0200 Subject: [PATCH 186/233] Removed jta section from akka-reference.conf (cherry picked from commit 3518f4ffbbdda8ddae26e394604f7e0d22324a38) --- config/akka-reference.conf | 7 ------- 1 file changed, 7 deletions(-) diff --git a/config/akka-reference.conf b/config/akka-reference.conf index 3a44e12e5a..63c1e0f85e 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -73,13 +73,6 @@ akka { trace-level = "none" } - 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 - } - http { hostname = "localhost" port = 9998 From b94b91c14b0cf5365b2c20d149a5cc1a9d546dbe Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 9 May 2011 09:25:18 +0200 Subject: [PATCH 187/233] Update gfx --- .../src/main/scala/akka/util/AkkaLoader.scala | 68 ++++++++++++------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/AkkaLoader.scala b/akka-actor/src/main/scala/akka/util/AkkaLoader.scala index 164f2da095..e6ebc28c3d 100644 --- a/akka-actor/src/main/scala/akka/util/AkkaLoader.scala +++ b/akka-actor/src/main/scala/akka/util/AkkaLoader.scala @@ -43,30 +43,50 @@ class AkkaLoader { } private def printBanner() { - println("==================================================") - println(" t") - println(" t t t") - println(" t t tt t") - println(" tt t t tt t") - println(" t ttttttt t ttt t") - println(" t tt ttt t ttt t") - println(" t t ttt t ttt t t") - println(" tt t ttt ttt ttt t") - println(" t t ttt ttt t tt t") - println(" t ttt ttt t t") - println(" tt ttt ttt t") - println(" ttt ttt") - println(" tttttttt ttt ttt ttt ttt tttttttt") - println(" ttt tt ttt ttt ttt ttt ttt ttt") - println(" ttt ttt ttt ttt ttt ttt ttt ttt") - println(" ttt ttt ttt ttt ttt tt ttt ttt") - println(" tttt ttttttttt tttttttt tttt") - println(" ttttttttt ttt ttt ttt ttt ttttttttt") - println(" ttt ttt ttt ttt ttt ttt ttt ttt") - println(" ttt ttt ttt ttt ttt ttt ttt ttt") - println(" ttt tt ttt ttt ttt ttt ttt ttt") - println(" tttttttt ttt ttt ttt ttt tttttttt") - println("==================================================") + println(""" +============================================================================== + + ZZ: + ZZZZ + ZZZZZZ + ZZZ' ZZZ + ~7 7ZZ' ZZZ + :ZZZ: IZZ' ZZZ + ,OZZZZ.~ZZ? ZZZ + ZZZZ' 'ZZZ$ ZZZ + . $ZZZ ~ZZ$ ZZZ + .=Z?. .ZZZO ~ZZ7 OZZ + .ZZZZ7..:ZZZ~ 7ZZZ ZZZ~ + .$ZZZ$Z+.ZZZZ ZZZ: ZZZ$ + .,ZZZZ?' =ZZO= .OZZ 'ZZZ + .$ZZZZ+ .ZZZZ IZZZ ZZZ$ + .ZZZZZ' .ZZZZ' .ZZZ$ ?ZZZ + .ZZZZZZ' .OZZZ? ?ZZZ 'ZZZ$ + .?ZZZZZZ' .ZZZZ? .ZZZ? 'ZZZO + .+ZZZZZZ?' .7ZZZZ' .ZZZZ :ZZZZ + .ZZZZZZ$' .?ZZZZZ' .~ZZZZ 'ZZZZ. + + + NNNNN $NNNN+ + NNNNN $NNNN+ + NNNNN $NNNN+ + NNNNN $NNNN+ + NNNNN $NNNN+ + =NNNNNNNNND$ NNNNN DDDDDD: $NNNN+ DDDDDN NDDNNNNNNNN, + NNNNNNNNNNNNND NNNNN DNNNNN $NNNN+ 8NNNNN= :NNNNNNNNNNNNNN + NNNNN$ DNNNNN NNNNN $NNNNN~ $NNNN+ NNNNNN NNNNN, :NNNNN+ + ?DN~ NNNNN NNNNN MNNNNN $NNNN+:NNNNN7 $ND =NNNNN + DNNNNN NNNNNDNNNN$ $NNNNDNNNNN :DNNNNN + ZNDNNNNNNNNND NNNNNNNNNND, $NNNNNNNNNNN DNDNNNNNNNNNN + NNNNNNNDDINNNNN NNNNNNNNNNND $NNNNNNNNNNND ONNNNNNND8+NNNNN + :NNNND NNNNN NNNNNN DNNNN, $NNNNNO 7NNNND NNNNNO :NNNNN + DNNNN NNNNN NNNNN DNNNN $NNNN+ 8NNNNN NNNNN $NNNNN + DNNNNO NNNNNN NNNNN NNNNN $NNNN+ NNNNN$ NNNND, ,NNNNND + NNNNNNDDNNNNNNNN NNNNN =NNNNN $NNNN+ DNNNN? DNNNNNNDNNNNNNNND + NNNNNNNNN NNNN$ NNNNN 8NNNND $NNNN+ NNNNN= ,DNNNNNNND NNNNN$ + +============================================================================== +""") println(" Running version " + Config.VERSION) println("==================================================") } From 44fb8bff7d91e9b15c4daa68f83624ede0655b35 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Mon, 9 May 2011 21:26:17 +1200 Subject: [PATCH 188/233] Rework dist yet again --- project/build/AkkaDistProject.scala | 227 +++++++++++++++++++++++++++ project/build/AkkaProject.scala | 30 +++- project/build/DocParentProject.scala | 4 + 3 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 project/build/AkkaDistProject.scala diff --git a/project/build/AkkaDistProject.scala b/project/build/AkkaDistProject.scala new file mode 100644 index 0000000000..440d337f86 --- /dev/null +++ b/project/build/AkkaDistProject.scala @@ -0,0 +1,227 @@ +/** + * Copyright (C) 2009-2011 Scalable Solutions AB + */ + +import sbt._ + +trait AkkaDistBaseProject extends DefaultProject { + def distOutputPath: Path + def distLibPath: Path + def distSrcPath: Path + def distDocPath: Path + def dist: Task +} + +trait AkkaDistProject extends AkkaDistBaseProject { + def distName: String + + val distFullName = distName + "-" + version + val distOutputBasePath = outputPath / "dist" + val distOutputPath = (distOutputBasePath ##) / distFullName + val distScalaLibPath = distOutputPath / "lib" + val distBinPath = distOutputPath / "bin" + val distConfigPath = distOutputPath / "config" + val distDeployPath = distOutputPath / "deploy" + val distLibPath = distOutputPath / "lib" / "akka" + val distSrcPath = distOutputPath / "src" / "akka" + val distDocPath = distOutputPath / "doc" / "akka" + val distDocJarsPath = distDocPath / "api" / "jars" + val distSharePath = Path.userHome / ".ivy2" / "dist" / distFullName + val distArchiveName = distFullName + ".zip" + val distArchive = (distOutputBasePath ##) / distArchiveName + + def distConfigSources = info.projectPath / "config" * "*" + def distScriptSources = info.projectPath / "scripts" * "*" + + lazy val distExclusiveProperty = systemOptional[Boolean]("dist.exclusive", false) + + def distExclusive = distExclusiveProperty.value + + def scalaDependency = if (distExclusive) Path.emptyPathFinder else buildLibraryJar + + def allProjectDependencies = topologicalSort.dropRight(1) + + def distDependencies = { + allProjectDependencies.flatMap( p => p match { + case adp: AkkaDistBaseProject => Some(adp) + case _ => None + }) + } + + def distDependencyJarNames = { + val jarNames = distDependencies.flatMap { dist => + (dist.distLibPath ** "*.jar").get.map(_.name) ++ + (dist.distSrcPath ** "*.jar").get.map(_.name) ++ + (dist.distDocPath ** "*.jar").get.map(_.name) + } + Set(jarNames: _*) + } + + def distClasspath = runClasspath + + def filterOutExcludes(paths: PathFinder) = { + if (distExclusive) { + val exclude = distDependencyJarNames + def include(path: Path) = !exclude(path.name) + paths.filter(include) + } else paths + } + + def dependencyJars(filter: Path => Boolean) = distClasspath.filter(filter) + + def isJar(path: Path) = path.name.endsWith(".jar") + + def isSrcJar(path: Path) = isJar(path) && path.name.contains("-sources") + + def isDocJar(path: Path) = isJar(path) && path.name.contains("-docs") + + def isClassJar(path: Path) = isJar(path) && !isSrcJar(path) && !isDocJar(path) + + def projectDependencies = allProjectDependencies -- distDependencies + + def projectDependencyJars(f: PackagePaths => Path) = { + Path.lazyPathFinder { + projectDependencies.flatMap( p => p match { + case pp: PackagePaths => Some(f(pp)) + case _ => None + }) + } + } + + def distLibs = filterOutExcludes(dependencyJars(isClassJar) +++ projectDependencyJars(_.jarPath)) + + def distSrcJars = filterOutExcludes(dependencyJars(isSrcJar) +++ projectDependencyJars(_.packageSrcJar)) + + def distDocJars = filterOutExcludes(dependencyJars(isDocJar) +++ projectDependencyJars(_.packageDocsJar)) + + def distShareSources = (distOutputPath ##) ** "*" + + lazy val dist = (distAction dependsOn (distBase, `package`, packageSrc, packageDocs) + describedAs("Create a distribution.")) + + def distAction = task { + copyFiles(scalaDependency, distScalaLibPath) orElse + copyFiles(distLibs, distLibPath) orElse + copyFiles(distSrcJars, distSrcPath) orElse + copyFiles(distDocJars, distDocJarsPath) orElse + copyFiles(distConfigSources, distConfigPath) orElse + copyScripts(distScriptSources, distBinPath) orElse + copyPaths(distShareSources, distSharePath) orElse + FileUtilities.zip(List(distOutputPath), distArchive, true, log) + } + + lazy val distBase = distBaseAction dependsOn (distClean) describedAs "Create the dist base." + + def distBaseAction = task { + if (!distExclusive) { + distDependencies.map( dist => { + val allFiles = (dist.distOutputPath ##) ** "*" + copyPaths(allFiles, distOutputPath) + }).foldLeft(None: Option[String])(_ orElse _) + } else None + } + + def distDependencyTasks: Seq[ManagedTask] = distDependencies.map(_.dist) + + lazy val distClean = (distCleanAction dependsOn (distDependencyTasks: _*) + describedAs "Clean the dist target dir.") + + def distCleanAction = task { + FileUtilities.clean(distOutputPath, log) orElse + FileUtilities.clean(distSharePath, log) + } + + def copyFiles(from: PathFinder, to: Path): Option[String] = { + if (from.get.isEmpty) None + else FileUtilities.copyFlat(from.get, to, log).left.toOption + } + + def copyPaths(from: PathFinder, to: Path): Option[String] = { + if (from.get.isEmpty) None + else FileUtilities.copy(from.get, to, log).left.toOption + } + + def copyScripts(from: PathFinder, to: Path): Option[String] = { + from.get.map { script => + val target = to / script.name + FileUtilities.copyFile(script, target, log) orElse + setExecutable(target, script.asFile.canExecute) + }.foldLeft(None: Option[String])(_ orElse _) + } + + def setExecutable(target: Path, executable: Boolean): Option[String] = { + val success = target.asFile.setExecutable(executable, false) + if (success) None else Some("Couldn't set permissions of " + target) + } + + override def disableCrossPaths = true + + def doNothing = task { None } + override def compileAction = doNothing + override def testCompileAction = doNothing + override def testAction = doNothing + override def packageAction = doNothing + override def publishLocalAction = doNothing + override def deliverLocalAction = doNothing + override def publishAction = doNothing + override def deliverAction = doNothing +} + +trait AkkaDistDocProject extends AkkaDistProject { + def distDocName = distName + + def findDocParent(project: Project): DocParentProject = project.info.parent match { + case Some(dpp: DocParentProject) => dpp + case Some(p: Project) => findDocParent(p) + case _ => error("Parent project is not a DocParentProject") + } + + def docParent = findDocParent(this) + + override def distAction = super.distAction dependsOn (distApi, distRstDocs) + + val apiSources = (docParent.docOutputPath ##) ** "*" + val apiPath = distDocPath / "api" / "html" / distDocName + + lazy val distApi = task { + copyPaths(apiSources, apiPath) + } dependsOn (distBase, docParent.doc) + + val rstDocsPath = docParent.info.projectPath / "akka-docs" + + lazy val rstDocs = task { + import Process._ + log.info("Building docs...") + val exitCode = ((new java.lang.ProcessBuilder("make", "clean", "html", "pdf")) directory rstDocsPath.asFile) ! log + if (exitCode > 0) Some("Failed to build docs.") else None + } + + val rstDocsBuildPath = rstDocsPath / "_build" + val rstDocsHtmlSources = (rstDocsBuildPath / "html" ##) ** "*" + val rstDocsPdfSources = (rstDocsBuildPath / "latex" ##) ** "*.pdf" + + val rstDocsOutputPath = distDocPath / "docs" + val rstDocsHtmlPath = rstDocsOutputPath / "html" / distDocName + val rstDocsPdfPath = rstDocsOutputPath / "pdf" + + lazy val distRstDocs = task { + copyPaths(rstDocsHtmlSources, rstDocsHtmlPath) orElse + copyPaths(rstDocsPdfSources, rstDocsPdfPath) + } dependsOn (distBase, rstDocs) +} + +/* + * For wiring together akka and akka-modules. + */ +trait AkkaDistSharedProject extends AkkaDistBaseProject { + def distName: String + + val distFullName = distName + "-" + version + val distOutputPath = Path.userHome / ".ivy2" / "dist" / distFullName + + val distLibPath = distOutputPath / "lib" / "akka" + val distSrcPath = distOutputPath / "src" / "akka" + val distDocPath = distOutputPath / "doc" / "akka" + + lazy val dist = task { None } +} diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 640f88d1c4..e7f5f14acd 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -10,7 +10,7 @@ import sbt._ import sbt.CompileOrder._ import spde._ -class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with ExecProject with DocParentProject { +class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with ExecProject with DocParentProject { akkaParent => // ------------------------------------------------------------------------------------------------------------------- // Compile settings @@ -444,6 +444,34 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec publishTask(publishIvyModule, releaseConfiguration) dependsOn (deliver, publishLocal, makePom) } } + + // ------------------------------------------------------------------------------------------------------------------- + // Distribution + // ------------------------------------------------------------------------------------------------------------------- + + lazy val akkaDist = project("dist", "akka-dist", new AkkaDistParentProject(_)) + + class AkkaDistParentProject(info: ProjectInfo) extends ParentProject(info) { + lazy val akkaActorsDist = project("actors", "akka-dist-actors", new AkkaActorsDistProject(_), akka_actor) + + lazy val akkaCoreDist = project("core", "akka-dist-core", new AkkaCoreDistProject(_), + akkaActorsDist, akka_remote, akka_http, akka_slf4j, akka_testkit, akka_actor_tests) + + def doNothing = task { None } + override def publishLocalAction = doNothing + override def deliverLocalAction = doNothing + override def publishAction = doNothing + override def deliverAction = doNothing + + class AkkaActorsDistProject(info: ProjectInfo) extends DefaultProject(info) with AkkaDistDocProject { + def distName = "akka-actors" + override def distDocName = "akka" + } + + class AkkaCoreDistProject(info: ProjectInfo)extends DefaultProject(info) with AkkaDistProject { + def distName = "akka-core" + } + } } trait OsgiProject extends BNDPlugin { self: DefaultProject => diff --git a/project/build/DocParentProject.scala b/project/build/DocParentProject.scala index ebe339ab2f..8200273ea1 100644 --- a/project/build/DocParentProject.scala +++ b/project/build/DocParentProject.scala @@ -1,3 +1,7 @@ +/** + * Copyright (C) 2009-2011 Scalable Solutions AB + */ + import sbt._ trait DocParentProject extends ParentProject { From 1e87f138cdb86bf7a94e07ac84eeff9556c814c0 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Mon, 9 May 2011 21:47:36 +1200 Subject: [PATCH 189/233] Don't deliver akka dist project --- project/build/AkkaProject.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index e7f5f14acd..a03d1ddde0 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -204,7 +204,10 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec override def artifacts = Set(Artifact(artifactID, "pom", "pom")) - override def deliverProjectDependencies = super.deliverProjectDependencies.toList - akka_samples.projectID - akka_tutorials.projectID + override def deliverProjectDependencies = (super.deliverProjectDependencies.toList + - akka_samples.projectID + - akka_tutorials.projectID + - akkaDist.projectID) // ------------------------------------------------------------------------------------------------------------------- // Build release From 692820e73449132b583548b8c8576f7c95191fe7 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 9 May 2011 17:05:28 +0200 Subject: [PATCH 190/233] Fixing #846 --- akka-docs/additional/index.rst | 2 +- akka-docs/additional/{recipes.rst => recipies.rst} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename akka-docs/additional/{recipes.rst => recipies.rst} (100%) diff --git a/akka-docs/additional/index.rst b/akka-docs/additional/index.rst index f36b5ce7cd..7398db601b 100644 --- a/akka-docs/additional/index.rst +++ b/akka-docs/additional/index.rst @@ -7,7 +7,7 @@ Additional Information add-on-modules articles benchmarks - recipes + recipies external-sample-projects companies-using-akka third-party-integrations diff --git a/akka-docs/additional/recipes.rst b/akka-docs/additional/recipies.rst similarity index 100% rename from akka-docs/additional/recipes.rst rename to akka-docs/additional/recipies.rst From 48f2ceef3e6776f08fd14e475497b8d4a48f7303 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 9 May 2011 17:06:28 +0200 Subject: [PATCH 191/233] Adding some docs to AllForOne and OneForOne --- .../main/scala/akka/config/SupervisionConfig.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/akka-actor/src/main/scala/akka/config/SupervisionConfig.scala b/akka-actor/src/main/scala/akka/config/SupervisionConfig.scala index 6b66f3415d..ee4f93280f 100644 --- a/akka-actor/src/main/scala/akka/config/SupervisionConfig.scala +++ b/akka-actor/src/main/scala/akka/config/SupervisionConfig.scala @@ -46,6 +46,12 @@ object Supervision { if (maxNrOfRetries < 0) None else Some(maxNrOfRetries), if (withinTimeRange < 0) None else Some(withinTimeRange)) } + /** + * Restart all actors linked to the same supervisor when one fails, + * trapExit = which Throwables should be intercepted + * maxNrOfRetries = the number of times an actor is allowed to be restarted + * withinTimeRange = millisecond time window for maxNrOfRetries, negative means no window + */ case class AllForOneStrategy(override val trapExit: List[Class[_ <: Throwable]], maxNrOfRetries: Option[Int] = None, withinTimeRange: Option[Int] = None) extends FaultHandlingStrategy(trapExit) { @@ -68,6 +74,12 @@ object Supervision { if (maxNrOfRetries < 0) None else Some(maxNrOfRetries), if (withinTimeRange < 0) None else Some(withinTimeRange)) } + /** + * Restart an actor when it fails + * trapExit = which Throwables should be intercepted + * maxNrOfRetries = the number of times an actor is allowed to be restarted + * withinTimeRange = millisecond time window for maxNrOfRetries, negative means no window + */ case class OneForOneStrategy(override val trapExit: List[Class[_ <: Throwable]], maxNrOfRetries: Option[Int] = None, withinTimeRange: Option[Int] = None) extends FaultHandlingStrategy(trapExit) { From b72b455f60607b2f6456be576d86e8eeb4f85c11 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 9 May 2011 17:06:37 +0200 Subject: [PATCH 192/233] Removing unused imports --- akka-actor/src/main/scala/akka/routing/Pool.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/akka-actor/src/main/scala/akka/routing/Pool.scala b/akka-actor/src/main/scala/akka/routing/Pool.scala index 5a906df851..c95a80b0fc 100644 --- a/akka-actor/src/main/scala/akka/routing/Pool.scala +++ b/akka-actor/src/main/scala/akka/routing/Pool.scala @@ -5,7 +5,6 @@ package akka.routing import akka.actor.{Actor, ActorRef, PoisonPill} -import java.util.concurrent.TimeUnit /** * Actor pooling @@ -47,7 +46,6 @@ trait ActorPool { */ trait DefaultActorPool extends ActorPool { this: Actor => import ActorPool._ - import collection.mutable.LinkedList import akka.actor.MaximumNumberOfRestartsWithinTimeRangeReached protected var _delegates = Vector[ActorRef]() From b30a1643572f938a7f626abb102b0bab3c8f790a Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 9 May 2011 17:52:30 +0200 Subject: [PATCH 193/233] Removing logging.rst and servlet.rst and moving the guice-integration.rst into the Java section --- .../{pending => java}/guice-integration.rst | 8 ++-- akka-docs/java/index.rst | 1 + akka-docs/pending/logging.rst | 4 -- akka-docs/pending/servlet.rst | 41 ------------------- 4 files changed, 6 insertions(+), 48 deletions(-) rename akka-docs/{pending => java}/guice-integration.rst (93%) delete mode 100644 akka-docs/pending/logging.rst delete mode 100644 akka-docs/pending/servlet.rst diff --git a/akka-docs/pending/guice-integration.rst b/akka-docs/java/guice-integration.rst similarity index 93% rename from akka-docs/pending/guice-integration.rst rename to akka-docs/java/guice-integration.rst index 6392bddfd3..de00b701cb 100644 --- a/akka-docs/pending/guice-integration.rst +++ b/akka-docs/java/guice-integration.rst @@ -45,6 +45,8 @@ Retrieve the external Guice dependency -------------------------------------- The external dependency can be retrieved like this: -``_ -Ext ext = manager.getExternalDependency(Ext.class); -``_ + +.. code-block:: java + + Ext ext = manager.getExternalDependency(Ext.class); + diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst index 54b2adf201..1d09b3e437 100644 --- a/akka-docs/java/index.rst +++ b/akka-docs/java/index.rst @@ -17,3 +17,4 @@ Java API fault-tolerance dispatchers routing + guice-integration diff --git a/akka-docs/pending/logging.rst b/akka-docs/pending/logging.rst deleted file mode 100644 index 833f2f419b..0000000000 --- a/akka-docs/pending/logging.rst +++ /dev/null @@ -1,4 +0,0 @@ -Logging -======= - -Logging has been removed. See the `Event Handler `_. diff --git a/akka-docs/pending/servlet.rst b/akka-docs/pending/servlet.rst deleted file mode 100644 index 6859657a72..0000000000 --- a/akka-docs/pending/servlet.rst +++ /dev/null @@ -1,41 +0,0 @@ -Akka Servlet -============ - -= - -Module stability: **STABLE** - -Akka has a servlet; ‘se.scalablesolutions.akka.comet.AkkaServlet’ that can use to deploy your Akka-based application in an external Servlet container. All you need to do is to add the servlet to the ‘web.xml’, set ‘$AKKA_HOME’ to the root of the distribution (needs the ‘$AKKA_HOME/config/*’ files) and add the JARs in the ‘$AKKA_HOME/lib’ to your classpath (or put them in the ‘WEB-INF/lib’ directory in the WAR file). - -Also, you need to add the Akka initialize/cleanup listener in web.xml - -.. code-block:: xml - - - ... - - se.scalablesolutions.akka.servlet.Initializer - - ... - - -And to support REST actors and/or comet actors, you need to add the following servlet declaration: - -``_ - -... - - Akka - - se.scalablesolutions.akka.comet.AkkaServlet - - se.scalablesolutions.akka.rest.AkkaServlet - - - * - Akka - -... - - -``_ From 867dc0ffc7d12aab640c360e5291ae783ddb1156 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Tue, 10 May 2011 09:57:57 +1200 Subject: [PATCH 194/233] Change the automatic release branch to avoid conflicts --- project/scripts/release | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/scripts/release b/project/scripts/release index 4fa1cef623..2fd97d8693 100644 --- a/project/scripts/release +++ b/project/scripts/release @@ -1,4 +1,4 @@ -sh git checkout -b release-{{release.arg1}} +sh git checkout -b releasing-{{release.arg1}} clean script find-replace.sh {{project.version}} {{release.arg1}} script find-replace.sh //[[:space:]]*release:[[:space:]]* From a6f8a9f3ce33de42b0657d385761570a0e2c446b Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Tue, 10 May 2011 12:32:38 +1200 Subject: [PATCH 195/233] Include docs and api in release process --- project/build/AkkaProject.scala | 35 +++++++++++-- ...kkaDistProject.scala => DistProject.scala} | 49 ++++++++----------- project/build/DocParentProject.scala | 37 +++++++++----- 3 files changed, 74 insertions(+), 47 deletions(-) rename project/build/{AkkaDistProject.scala => DistProject.scala} (82%) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index a03d1ddde0..746f9d2f39 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -165,7 +165,7 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec // Scaladocs // ------------------------------------------------------------------------------------------------------------------- - override def docProjectDependencies = dependencies.toList - akka_samples + override def apiProjectDependencies = dependencies.toList - akka_samples // ------------------------------------------------------------------------------------------------------------------- // Publishing @@ -215,7 +215,6 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec val localReleasePath = outputPath / "release" / version.toString val localReleaseRepository = Resolver.file("Local Release", localReleasePath / "repository" asFile) - val localReleaseDownloads = localReleasePath / "downloads" override def otherRepositories = super.otherRepositories ++ Seq(localReleaseRepository) @@ -224,7 +223,33 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec publishTask(publishIvyModule, releaseConfiguration) dependsOn (deliver, publishLocal, makePom) } - lazy val buildRelease = task { None } dependsOn publishRelease + lazy val buildRelease = task { + log.info("Built release.") + None + } dependsOn (publishRelease, releaseApi, releaseDocs, releaseDownloads) + + lazy val releaseApi = task { + val apiSources = ((apiOutputPath ##) ***) + val apiPath = localReleasePath / "api" / "akka" / version.toString + FileUtilities.copy(apiSources.get, apiPath, log).left.toOption + } dependsOn (api) + + lazy val releaseDocs = task { + val docsBuildPath = docsPath / "_build" + val docsHtmlSources = ((docsBuildPath / "html" ##) ***) + val docsPdfSources = (docsBuildPath / "latex" ##) ** "*.pdf" + val docsOutputPath = localReleasePath / "docs" / "akka" / version.toString + FileUtilities.copy(docsHtmlSources.get, docsOutputPath, log).left.toOption orElse + FileUtilities.copy(docsPdfSources.get, docsOutputPath, log).left.toOption + } dependsOn (docs) + + lazy val releaseDownloads = task { + val distArchive = akkaDist.akkaCoreDist.distArchive + val downloadsPath = localReleasePath / "downloads" + FileUtilities.copy(distArchive.get, downloadsPath, log).left.toOption + } dependsOn (dist) + + lazy val dist = task { None } // dummy task // ------------------------------------------------------------------------------------------------------------------- // akka-actor subproject @@ -466,12 +491,12 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec override def publishAction = doNothing override def deliverAction = doNothing - class AkkaActorsDistProject(info: ProjectInfo) extends DefaultProject(info) with AkkaDistDocProject { + class AkkaActorsDistProject(info: ProjectInfo) extends DefaultProject(info) with DistDocProject { def distName = "akka-actors" override def distDocName = "akka" } - class AkkaCoreDistProject(info: ProjectInfo)extends DefaultProject(info) with AkkaDistProject { + class AkkaCoreDistProject(info: ProjectInfo)extends DefaultProject(info) with DistProject { def distName = "akka-core" } } diff --git a/project/build/AkkaDistProject.scala b/project/build/DistProject.scala similarity index 82% rename from project/build/AkkaDistProject.scala rename to project/build/DistProject.scala index 440d337f86..df6b348396 100644 --- a/project/build/AkkaDistProject.scala +++ b/project/build/DistProject.scala @@ -4,7 +4,7 @@ import sbt._ -trait AkkaDistBaseProject extends DefaultProject { +trait DistBaseProject extends DefaultProject { def distOutputPath: Path def distLibPath: Path def distSrcPath: Path @@ -12,7 +12,7 @@ trait AkkaDistBaseProject extends DefaultProject { def dist: Task } -trait AkkaDistProject extends AkkaDistBaseProject { +trait DistProject extends DistBaseProject { def distName: String val distFullName = distName + "-" + version @@ -43,7 +43,7 @@ trait AkkaDistProject extends AkkaDistBaseProject { def distDependencies = { allProjectDependencies.flatMap( p => p match { - case adp: AkkaDistBaseProject => Some(adp) + case adp: DistBaseProject => Some(adp) case _ => None }) } @@ -94,7 +94,7 @@ trait AkkaDistProject extends AkkaDistBaseProject { def distDocJars = filterOutExcludes(dependencyJars(isDocJar) +++ projectDependencyJars(_.packageDocsJar)) - def distShareSources = (distOutputPath ##) ** "*" + def distShareSources = ((distOutputPath ##) ***) lazy val dist = (distAction dependsOn (distBase, `package`, packageSrc, packageDocs) describedAs("Create a distribution.")) @@ -115,7 +115,7 @@ trait AkkaDistProject extends AkkaDistBaseProject { def distBaseAction = task { if (!distExclusive) { distDependencies.map( dist => { - val allFiles = (dist.distOutputPath ##) ** "*" + val allFiles = ((dist.distOutputPath ##) ***) copyPaths(allFiles, distOutputPath) }).foldLeft(None: Option[String])(_ orElse _) } else None @@ -167,7 +167,7 @@ trait AkkaDistProject extends AkkaDistBaseProject { override def deliverAction = doNothing } -trait AkkaDistDocProject extends AkkaDistProject { +trait DistDocProject extends DistProject { def distDocName = distName def findDocParent(project: Project): DocParentProject = project.info.parent match { @@ -178,42 +178,33 @@ trait AkkaDistDocProject extends AkkaDistProject { def docParent = findDocParent(this) - override def distAction = super.distAction dependsOn (distApi, distRstDocs) + override def distAction = super.distAction dependsOn (distApi, distDocs) - val apiSources = (docParent.docOutputPath ##) ** "*" + val apiSources = ((docParent.apiOutputPath ##) ***) val apiPath = distDocPath / "api" / "html" / distDocName lazy val distApi = task { copyPaths(apiSources, apiPath) - } dependsOn (distBase, docParent.doc) + } dependsOn (distBase, docParent.api) - val rstDocsPath = docParent.info.projectPath / "akka-docs" + val docsBuildPath = docParent.docsPath / "_build" + val docsHtmlSources = ((docsBuildPath / "html" ##) ***) + val docsPdfSources = (docsBuildPath / "latex" ##) ** "*.pdf" - lazy val rstDocs = task { - import Process._ - log.info("Building docs...") - val exitCode = ((new java.lang.ProcessBuilder("make", "clean", "html", "pdf")) directory rstDocsPath.asFile) ! log - if (exitCode > 0) Some("Failed to build docs.") else None - } + val docsOutputPath = distDocPath / "docs" + val docsHtmlPath = docsOutputPath / "html" / distDocName + val docsPdfPath = docsOutputPath / "pdf" - val rstDocsBuildPath = rstDocsPath / "_build" - val rstDocsHtmlSources = (rstDocsBuildPath / "html" ##) ** "*" - val rstDocsPdfSources = (rstDocsBuildPath / "latex" ##) ** "*.pdf" - - val rstDocsOutputPath = distDocPath / "docs" - val rstDocsHtmlPath = rstDocsOutputPath / "html" / distDocName - val rstDocsPdfPath = rstDocsOutputPath / "pdf" - - lazy val distRstDocs = task { - copyPaths(rstDocsHtmlSources, rstDocsHtmlPath) orElse - copyPaths(rstDocsPdfSources, rstDocsPdfPath) - } dependsOn (distBase, rstDocs) + lazy val distDocs = task { + copyPaths(docsHtmlSources, docsHtmlPath) orElse + copyPaths(docsPdfSources, docsPdfPath) + } dependsOn (distBase, docParent.docs) } /* * For wiring together akka and akka-modules. */ -trait AkkaDistSharedProject extends AkkaDistBaseProject { +trait DistSharedProject extends DistBaseProject { def distName: String val distFullName = distName + "-" + version diff --git a/project/build/DocParentProject.scala b/project/build/DocParentProject.scala index 8200273ea1..0bb08c4879 100644 --- a/project/build/DocParentProject.scala +++ b/project/build/DocParentProject.scala @@ -5,32 +5,43 @@ import sbt._ trait DocParentProject extends ParentProject { - def docOutputPath = outputPath / "doc" / "main" / "api" + def apiOutputPath = outputPath / "doc" / "main" / "api" - def docProjectDependencies = topologicalSort.dropRight(1) + def apiProjectDependencies = topologicalSort.dropRight(1) - def docMainSources = - docProjectDependencies.map { + def apiMainSources = + apiProjectDependencies.map { case sp: ScalaPaths => sp.mainSources case _ => Path.emptyPathFinder }.foldLeft(Path.emptyPathFinder)(_ +++ _) - def docCompileClasspath = - docProjectDependencies.map { + def apiCompileClasspath = + apiProjectDependencies.map { case bsp: BasicScalaProject => bsp.compileClasspath case _ => Path.emptyPathFinder }.foldLeft(Path.emptyPathFinder)(_ +++ _) - def docLabel = "main" + def apiLabel = "main" - def docMaxErrors = 100 + def apiMaxErrors = 100 - def docOptions: Seq[String] = Seq.empty + def apiOptions: Seq[String] = Seq.empty - lazy val doc = docAction describedAs ("Create combined scaladoc for all subprojects") + lazy val api = apiAction describedAs ("Create combined scaladoc for all subprojects.") - def docAction = task { - val scaladoc = new Scaladoc(docMaxErrors, buildCompiler) - scaladoc(docLabel, docMainSources.get, docCompileClasspath.get, docOutputPath, docOptions, log) + def apiAction = task { + val scaladoc = new Scaladoc(apiMaxErrors, buildCompiler) + scaladoc(apiLabel, apiMainSources.get, apiCompileClasspath.get, apiOutputPath, apiOptions, log) + } + + val docsPath = info.projectPath / "akka-docs" + + lazy val docs = docsAction describedAs ("Create the reStructuredText documentation.") + + def docsAction = task { + import Process._ + log.info("Building docs...") + val exitCode = ((new java.lang.ProcessBuilder("make", "clean", "html", "pdf")) directory docsPath.asFile) ! log + if (exitCode > 0) Some("Failed to build docs.") else None } } From a769fb3640dacf297cda8c56aae019eb90785330 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Tue, 10 May 2011 14:09:34 +1200 Subject: [PATCH 196/233] Update header in html docs --- akka-docs/_sphinx/static/logo.png | Bin 7499 -> 7305 bytes akka-docs/_sphinx/themes/akka/layout.html | 8 ++++---- .../_sphinx/themes/akka/static/akka.css_t | 7 +++++-- akka-docs/conf.py | 3 --- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/akka-docs/_sphinx/static/logo.png b/akka-docs/_sphinx/static/logo.png index a6bc9c3b98ed53f4c76165a2a52034a67e5d6ab0..558dfed6eb6d2ef0677478a00c546f0b4a484916 100644 GIT binary patch literal 7305 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 literal 7499 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 HQ$iB}ON*{e diff --git a/akka-docs/_sphinx/themes/akka/layout.html b/akka-docs/_sphinx/themes/akka/layout.html index db443c3fe5..15d7a9b951 100644 --- a/akka-docs/_sphinx/themes/akka/layout.html +++ b/akka-docs/_sphinx/themes/akka/layout.html @@ -38,11 +38,11 @@ {%- else %} {%- if logo -%} - + {%- endif -%} -

    - {{ shorttitle|e }}

    -

    {{ title|striptags|e }}

    +

    + {{ shorttitle|e }}

    +

    Version {{ version|e }}

    {%- endif %} {%- endblock %} diff --git a/akka-docs/_sphinx/themes/akka/static/akka.css_t b/akka-docs/_sphinx/themes/akka/static/akka.css_t index f05e86bb6a..146ba31623 100644 --- a/akka-docs/_sphinx/themes/akka/static/akka.css_t +++ b/akka-docs/_sphinx/themes/akka/static/akka.css_t @@ -112,13 +112,16 @@ div.header h2 { font-size: 1.3em; font-weight: normal; letter-spacing: 1px; - text-transform: uppercase; - color: #aaa; + color: {{ theme_headingcolor }}; border: 0; margin-top: -3px; padding: 0; } +div.header img.leftlogo { + float: left; +} + div.header img.rightlogo { float: right; } diff --git a/akka-docs/conf.py b/akka-docs/conf.py index 712a3d10c8..f6a0c67069 100644 --- a/akka-docs/conf.py +++ b/akka-docs/conf.py @@ -28,9 +28,6 @@ show_authors = True # -- Options for HTML output --------------------------------------------------- html_theme = 'akka' -html_theme_options = { - 'full_logo': 'true' - } html_theme_path = ['_sphinx/themes'] html_title = 'Akka Documentation' From 5e63ebc36ea6b92ee9da61acb173c10a7e23a72a Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Tue, 10 May 2011 14:30:00 +1200 Subject: [PATCH 197/233] Fix new api task --- project/build/DocParentProject.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/project/build/DocParentProject.scala b/project/build/DocParentProject.scala index 0bb08c4879..1e7bf0266c 100644 --- a/project/build/DocParentProject.scala +++ b/project/build/DocParentProject.scala @@ -27,13 +27,15 @@ trait DocParentProject extends ParentProject { def apiOptions: Seq[String] = Seq.empty - lazy val api = apiAction describedAs ("Create combined scaladoc for all subprojects.") + lazy val api = apiAction dependsOn (doc) describedAs ("Create combined scaladoc for all subprojects.") def apiAction = task { val scaladoc = new Scaladoc(apiMaxErrors, buildCompiler) scaladoc(apiLabel, apiMainSources.get, apiCompileClasspath.get, apiOutputPath, apiOptions, log) } + lazy val doc = task { None } // dummy task + val docsPath = info.projectPath / "akka-docs" lazy val docs = docsAction describedAs ("Create the reStructuredText documentation.") From c581e4be28f5696c2cc7a70f42ae4b6b6880f339 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Tue, 10 May 2011 14:40:05 +1200 Subject: [PATCH 198/233] Update loader banner and version --- .../src/main/scala/akka/util/AkkaLoader.scala | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/AkkaLoader.scala b/akka-actor/src/main/scala/akka/util/AkkaLoader.scala index e6ebc28c3d..c780cb48f3 100644 --- a/akka-actor/src/main/scala/akka/util/AkkaLoader.scala +++ b/akka-actor/src/main/scala/akka/util/AkkaLoader.scala @@ -65,29 +65,29 @@ class AkkaLoader { .?ZZZZZZ' .ZZZZ? .ZZZ? 'ZZZO .+ZZZZZZ?' .7ZZZZ' .ZZZZ :ZZZZ .ZZZZZZ$' .?ZZZZZ' .~ZZZZ 'ZZZZ. - - - NNNNN $NNNN+ - NNNNN $NNNN+ - NNNNN $NNNN+ - NNNNN $NNNN+ - NNNNN $NNNN+ - =NNNNNNNNND$ NNNNN DDDDDD: $NNNN+ DDDDDN NDDNNNNNNNN, - NNNNNNNNNNNNND NNNNN DNNNNN $NNNN+ 8NNNNN= :NNNNNNNNNNNNNN - NNNNN$ DNNNNN NNNNN $NNNNN~ $NNNN+ NNNNNN NNNNN, :NNNNN+ - ?DN~ NNNNN NNNNN MNNNNN $NNNN+:NNNNN7 $ND =NNNNN - DNNNNN NNNNNDNNNN$ $NNNNDNNNNN :DNNNNN - ZNDNNNNNNNNND NNNNNNNNNND, $NNNNNNNNNNN DNDNNNNNNNNNN - NNNNNNNDDINNNNN NNNNNNNNNNND $NNNNNNNNNNND ONNNNNNND8+NNNNN - :NNNND NNNNN NNNNNN DNNNN, $NNNNNO 7NNNND NNNNNO :NNNNN - DNNNN NNNNN NNNNN DNNNN $NNNN+ 8NNNNN NNNNN $NNNNN - DNNNNO NNNNNN NNNNN NNNNN $NNNN+ NNNNN$ NNNND, ,NNNNND - NNNNNNDDNNNNNNNN NNNNN =NNNNN $NNNN+ DNNNN? DNNNNNNDNNNNNNNND + + + NNNNN $NNNN+ + NNNNN $NNNN+ + NNNNN $NNNN+ + NNNNN $NNNN+ + NNNNN $NNNN+ + =NNNNNNNNND$ NNNNN DDDDDD: $NNNN+ DDDDDN NDDNNNNNNNN, + NNNNNNNNNNNNND NNNNN DNNNNN $NNNN+ 8NNNNN= :NNNNNNNNNNNNNN + NNNNN$ DNNNNN NNNNN $NNNNN~ $NNNN+ NNNNNN NNNNN, :NNNNN+ + ?DN~ NNNNN NNNNN MNNNNN $NNNN+:NNNNN7 $ND =NNNNN + DNNNNN NNNNNDNNNN$ $NNNNDNNNNN :DNNNNN + ZNDNNNNNNNNND NNNNNNNNNND, $NNNNNNNNNNN DNDNNNNNNNNNN + NNNNNNNDDINNNNN NNNNNNNNNNND $NNNNNNNNNNND ONNNNNNND8+NNNNN + :NNNND NNNNN NNNNNN DNNNN, $NNNNNO 7NNNND NNNNNO :NNNNN + DNNNN NNNNN NNNNN DNNNN $NNNN+ 8NNNNN NNNNN $NNNNN + DNNNNO NNNNNN NNNNN NNNNN $NNNN+ NNNNN$ NNNND, ,NNNNND + NNNNNNDDNNNNNNNN NNNNN =NNNNN $NNNN+ DNNNN? DNNNNNNDNNNNNNNND NNNNNNNNN NNNN$ NNNNN 8NNNND $NNNN+ NNNNN= ,DNNNNNNND NNNNN$ ============================================================================== -""") - println(" Running version " + Config.VERSION) - println("==================================================") + Running version %s +============================================================================== +""".format(Config.VERSION)) } } From 8dec4bcbc415d80dd1b1907d9bc099437c4fb65a Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Tue, 10 May 2011 16:37:31 +1200 Subject: [PATCH 199/233] Update tutorials and include source in the distribution --- .../intro/getting-started-first-java.rst | 95 +++++++++++-------- .../getting-started-first-scala-eclipse.rst | 58 ++++++----- .../intro/getting-started-first-scala.rst | 70 ++++++++------ akka-tutorials/akka-tutorial-first/README | 7 ++ akka-tutorials/akka-tutorial-first/pom.xml | 4 +- .../project/build.properties | 5 + .../project/build/Project.scala | 3 + .../project/plugins/Plugins.scala | 6 ++ .../java/akka/tutorial/first/java/Pi.java | 25 ----- .../src/main/scala/Pi.scala | 26 ----- akka-tutorials/akka-tutorial-second/README | 7 ++ akka-tutorials/akka-tutorial-second/pom.xml | 43 +++++++++ .../project/build.properties | 5 + .../project/build/Project.scala | 3 + .../project/plugins/Plugins.scala | 6 ++ .../java/akka/tutorial/java/second/Pi.java | 25 ----- .../src/main/scala/Pi.scala | 26 ----- project/build/AkkaProject.scala | 23 ++++- 18 files changed, 243 insertions(+), 194 deletions(-) create mode 100644 akka-tutorials/akka-tutorial-first/README create mode 100644 akka-tutorials/akka-tutorial-first/project/build.properties create mode 100644 akka-tutorials/akka-tutorial-first/project/build/Project.scala create mode 100644 akka-tutorials/akka-tutorial-first/project/plugins/Plugins.scala create mode 100644 akka-tutorials/akka-tutorial-second/README create mode 100644 akka-tutorials/akka-tutorial-second/pom.xml create mode 100644 akka-tutorials/akka-tutorial-second/project/build.properties create mode 100644 akka-tutorials/akka-tutorial-second/project/build/Project.scala create mode 100644 akka-tutorials/akka-tutorial-second/project/plugins/Plugins.scala diff --git a/akka-docs/intro/getting-started-first-java.rst b/akka-docs/intro/getting-started-first-java.rst index 32e8f36672..920f64521f 100644 --- a/akka-docs/intro/getting-started-first-java.rst +++ b/akka-docs/intro/getting-started-first-java.rst @@ -37,7 +37,7 @@ 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:: +Then you can navigate down to the tutorial:: $ cd akka/akka-tutorials/akka-tutorial-first @@ -58,39 +58,49 @@ You can test your installation by invoking ``java``:: Java(TM) SE Runtime Environment (build 1.6.0_24-b07-334-10M3326) Java HotSpot(TM) 64-Bit Server VM (build 19.1-b02-334, mixed mode) + Downloading and installing Akka ------------------------------- -To build and run the tutorial sample from the command line, you have to download Akka. If you prefer to use SBT to build and run the sample then you can skip this section and jump to the next one. +To build and run the tutorial sample from the command line, you have to download +Akka. If you prefer to use SBT to build and run the sample then you can skip +this section and jump to the next one. -Let's get the ``akka-1.1`` distribution of Akka core (not Akka Modules) from `http://akka.io/downloads `_. Once you have downloaded the distribution unzip it in the folder you would like to have Akka installed in, in my case I choose to install it in ``/Users/jboner/tools/``, simply by unzipping it to this directory. +Let's get the ``akka-actors-1.1.zip`` distribution of Akka from +http://akka.io/downloads/ which includes everything we need for this +tutorial. Once you have downloaded the distribution unzip it in the folder you +would like to have Akka installed in. In my case I choose to install it in +``/Users/jboner/tools/``, simply by unzipping it to this directory. -You need to do one more thing in order to install Akka properly: set the ``AKKA_HOME`` environment variable to the root of the distribution. In my case I'm opening up a shell, navigating down to the distribution, and setting the ``AKKA_HOME`` variable:: +You need to do one more thing in order to install Akka properly: set the +``AKKA_HOME`` environment variable to the root of the distribution. In my case +I'm opening up a shell, navigating down to the distribution, and setting the +``AKKA_HOME`` variable:: - $ cd /Users/jboner/tools/akka-1.1 + $ cd /Users/jboner/tools/akka-actors-1.1 $ export AKKA_HOME=`pwd` $ echo $AKKA_HOME - /Users/jboner/tools/akka-1.1 + /Users/jboner/tools/akka-actors-1.1 The distribution looks like this:: - $ ls -l - total 16944 - drwxr-xr-x 7 jboner staff 238 Apr 6 11:15 . - drwxr-xr-x 28 jboner staff 952 Apr 6 11:16 .. - drwxr-xr-x 17 jboner staff 578 Apr 6 11:16 deploy - drwxr-xr-x 26 jboner staff 884 Apr 6 11:16 dist - drwxr-xr-x 3 jboner staff 102 Apr 6 11:15 lib_managed - -rwxr-xr-x 1 jboner staff 8674105 Apr 6 11:15 scala-library.jar - drwxr-xr-x 4 jboner staff 136 Apr 6 11:16 scripts + $ ls -1 + config + doc + lib + src -- In the ``dist`` directory we have the Akka JARs, including sources and docs. -- In the ``lib_managed/compile`` directory we have Akka's dependency JARs. -- In the ``deploy`` directory we have the sample JARs. -- In the ``scripts`` directory we have scripts for running Akka. -- Finally ``scala-library.jar`` is the JAR for the latest Scala distribution that Akka depends on. +- In the ``config`` directory we have the Akka conf files. +- In the ``doc`` directory we have the documentation, API, doc JARs, and also + the source files for the tutorials. +- In the ``lib`` directory we have the Scala and Akka JARs. +- In the ``src`` directory we have the source JARs for Akka. -The only JAR we will need for this tutorial (apart from the ``scala-library.jar`` JAR) is the ``akka-actor-1.1.jar`` JAR in the ``dist`` directory. This is a self-contained JAR with zero dependencies and contains everything we need to write a system using Actors. + +The only JAR we will need for this tutorial (apart from the +``scala-library.jar`` JAR) is the ``akka-actor-1.1.jar`` JAR in the ``lib/akka`` +directory. This is a self-contained JAR with zero dependencies and contains +everything we need to write a system using Actors. Akka is very modular and has many JARs for containing different features. The core distribution has seven modules: @@ -102,7 +112,10 @@ Akka is very modular and has many JARs for containing different features. The co - ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener for logging with SLF4J - ``akka-testkit-1.1.jar`` -- Toolkit for testing Actors -We also have Akka Modules containing add-on modules outside the core of Akka. You can download the Akka Modules distribution from TODO. It contains Akka core as well. We will not be needing any modules there today, but for your information the module JARs are these: +We also have Akka Modules containing add-on modules outside the core of +Akka. You can download the Akka Modules distribution from TODO. It contains Akka +core as well. We will not be needing any modules there today, but for your +information the module JARs are these: - ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.) - ``akka-amqp-1.1.jar`` -- AMQP integration @@ -112,6 +125,7 @@ We also have Akka Modules containing add-on modules outside the core of Akka. Yo - ``akka-spring-1.1.jar`` -- Spring framework integration - ``akka-osgi-dependencies-bundle-1.1.jar`` -- OSGi support + Downloading and installing Maven -------------------------------- @@ -178,7 +192,7 @@ We also need to edit the ``pom.xml`` build file. Let's add the dependency we nee Akka Akka Maven2 Repository - http://www.scalablesolutions.se/akka/repository/ + http://akka.io/repository/ @@ -695,35 +709,42 @@ Before we package it up and run it, let's take a look at the full code now, with } } + Run it as a command line application ------------------------------------ -To build and run the tutorial from the command line, you need to have the Scala library JAR on the classpath. +If you have not typed in (or copied) the code for the tutorial as +``$AKKA_HOME/tutorial/akka/tutorial/first/java/Pi.java`` then now is the +time. When that's done open up a shell and step in to the Akka distribution +(``cd $AKKA_HOME``). -Scala can be downloaded from `http://www.scala-lang.org/downloads `_. Browse there and download the Scala 2.9.0.RC1 release. If you pick the ``tgz`` or ``zip`` distribution then just unzip it where you want it installed. If you pick the IzPack Installer then double click on it and follow the instructions. +First we need to compile the source file. That is done with Java's compiler +``javac``. Our application depends on the ``akka-actor-1.1.jar`` and the +``scala-library.jar`` JAR files, so let's add them to the compiler classpath +when we compile the source:: -The ``scala-library.jar`` resides in the ``scala-2.9.0.RC1/lib`` directory. Copy that to your project directory. + $ javac -cp lib/scala-library.jar:lib/akka/akka-actor-1.1.jar tutorial/akka/tutorial/first/java/Pi.java -If you have not typed in (or copied) the code for the tutorial as ``$AKKA_HOME/tutorial/akka/tutorial/first/java/Pi.java`` then now is the time. When that's done open up a shell and step in to the Akka distribution (``cd $AKKA_HOME``). - -First we need to compile the source file. That is done with Java's compiler ``javac``. Our application depends on the ``akka-actor-1.1.jar`` and the ``scala-library.jar`` JAR files, so let's add them to the compiler classpath when we compile the source:: - - $ javac -cp dist/akka-actor-1.1.jar:scala-library.jar tutorial/Pi.scala - -When we have compiled the source file we are ready to run the application. This is done with ``java`` but yet again we need to add the ``akka-actor-1.1.jar`` and the ``scala-library.jar`` JAR files to the classpath as well as the classes we compiled ourselves:: +When we have compiled the source file we are ready to run the application. This +is done with ``java`` but yet again we need to add the ``akka-actor-1.1.jar`` +and the ``scala-library.jar`` JAR files to the classpath as well as the classes +we compiled ourselves:: $ java \ - -cp dist/akka-actor-1.1.jar:scala-library.jar:tutorial \ + -cp lib/scala-library.jar:lib/akka/akka-actor-1.1.jar:tutorial \ akka.tutorial.java.first.Pi - AKKA_HOME is defined as [/Users/jboner/src/akka-stuff/akka-core] - loading config from [/Users/jboner/src/akka-stuff/akka-core/config/akka.conf]. + AKKA_HOME is defined as [/Users/jboner/tools/akka-actors-1.1] + loading config from [/Users/jboner/tools/akka-actors-1.1/config/akka.conf]. Pi estimate: 3.1435501812459323 Calculation time: 822 millis Yippee! It is working. -If you have not defined the ``AKKA_HOME`` environment variable then Akka can't find the ``akka.conf`` configuration file and will print out a ``Can’t load akka.conf`` warning. This is ok since it will then just use the defaults. +If you have not defined the ``AKKA_HOME`` environment variable then Akka can't +find the ``akka.conf`` configuration file and will print out a ``Can’t load +akka.conf`` warning. This is ok since it will then just use the defaults. + Run it inside Maven ------------------- diff --git a/akka-docs/intro/getting-started-first-scala-eclipse.rst b/akka-docs/intro/getting-started-first-scala-eclipse.rst index b4380490ef..f34cfb75f3 100644 --- a/akka-docs/intro/getting-started-first-scala-eclipse.rst +++ b/akka-docs/intro/getting-started-first-scala-eclipse.rst @@ -43,39 +43,49 @@ You can test your installation by invoking ``java``:: Java(TM) SE Runtime Environment (build 1.6.0_24-b07-334-10M3326) Java HotSpot(TM) 64-Bit Server VM (build 19.1-b02-334, mixed mode) + Downloading and installing Akka ------------------------------- -To build and run the tutorial sample from the command line, you have to download Akka. If you prefer to use SBT to build and run the sample then you can skip this section and jump to the next one. +To build and run the tutorial sample from the command line, you have to download +Akka. If you prefer to use SBT to build and run the sample then you can skip +this section and jump to the next one. -Let's get the ``akka-1.1`` distribution of Akka core (not Akka Modules) from `http://akka.io/downloads `_. Once you have downloaded the distribution unzip it in the folder you would like to have Akka installed in, in my case I choose to install it in ``/Users/jboner/tools/``, simply by unzipping it to this directory. +Let's get the ``akka-actors-1.1.zip`` distribution of Akka from +http://akka.io/downloads/ which includes everything we need for this +tutorial. Once you have downloaded the distribution unzip it in the folder you +would like to have Akka installed in. In my case I choose to install it in +``/Users/jboner/tools/``, simply by unzipping it to this directory. -You need to do one more thing in order to install Akka properly: set the ``AKKA_HOME`` environment variable to the root of the distribution. In my case I'm opening up a shell, navigating down to the distribution, and setting the ``AKKA_HOME`` variable:: +You need to do one more thing in order to install Akka properly: set the +``AKKA_HOME`` environment variable to the root of the distribution. In my case +I'm opening up a shell, navigating down to the distribution, and setting the +``AKKA_HOME`` variable:: - $ cd /Users/jboner/tools/akka-1.1 + $ cd /Users/jboner/tools/akka-actors-1.1 $ export AKKA_HOME=`pwd` $ echo $AKKA_HOME - /Users/jboner/tools/akka-1.1 + /Users/jboner/tools/akka-actors-1.1 The distribution looks like this:: - $ ls -l - total 16944 - drwxr-xr-x 7 jboner staff 238 Apr 6 11:15 . - drwxr-xr-x 28 jboner staff 952 Apr 6 11:16 .. - drwxr-xr-x 17 jboner staff 578 Apr 6 11:16 deploy - drwxr-xr-x 26 jboner staff 884 Apr 6 11:16 dist - drwxr-xr-x 3 jboner staff 102 Apr 6 11:15 lib_managed - -rwxr-xr-x 1 jboner staff 8674105 Apr 6 11:15 scala-library.jar - drwxr-xr-x 4 jboner staff 136 Apr 6 11:16 scripts + $ ls -1 + config + doc + lib + src -- In the ``dist`` directory we have the Akka JARs, including sources and docs. -- In the ``lib_managed/compile`` directory we have Akka's dependency JARs. -- In the ``deploy`` directory we have the sample JARs. -- In the ``scripts`` directory we have scripts for running Akka. -- Finally ``scala-library.jar`` is the JAR for the latest Scala distribution that Akka depends on. +- In the ``config`` directory we have the Akka conf files. +- In the ``doc`` directory we have the documentation, API, doc JARs, and also + the source files for the tutorials. +- In the ``lib`` directory we have the Scala and Akka JARs. +- In the ``src`` directory we have the source JARs for Akka. -The only JAR we will need for this tutorial (apart from the ``scala-library.jar`` JAR) is the ``akka-actor-1.1.jar`` JAR in the ``dist`` directory. This is a self-contained JAR with zero dependencies and contains everything we need to write a system using Actors. + +The only JAR we will need for this tutorial (apart from the +``scala-library.jar`` JAR) is the ``akka-actor-1.1.jar`` JAR in the ``lib/akka`` +directory. This is a self-contained JAR with zero dependencies and contains +everything we need to write a system using Actors. Akka is very modular and has many JARs for containing different features. The core distribution has seven modules: @@ -84,10 +94,13 @@ Akka is very modular and has many JARs for containing different features. The co - ``akka-remote-1.1.jar`` -- Remote Actors - ``akka-stm-1.1.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures - ``akka-http-1.1.jar`` -- Akka Mist for continuation-based asynchronous HTTP and also Jersey integration -- ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener +- ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener for logging with SLF4J - ``akka-testkit-1.1.jar`` -- Toolkit for testing Actors -We also have Akka Modules containing add-on modules outside the core of Akka. You can download the Akka Modules distribution from TODO. It contains Akka core as well. We will not be needing any modules there today, but for your information the module JARs are these: +We also have Akka Modules containing add-on modules outside the core of +Akka. You can download the Akka Modules distribution from TODO. It contains Akka +core as well. We will not be needing any modules there today, but for your +information the module JARs are these: - ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.) - ``akka-amqp-1.1.jar`` -- AMQP integration @@ -97,6 +110,7 @@ We also have Akka Modules containing add-on modules outside the core of Akka. Yo - ``akka-spring-1.1.jar`` -- Spring framework integration - ``akka-osgi-dependencies-bundle-1.1.jar`` -- OSGi support + Downloading and installing the Scala IDE for Eclipse ---------------------------------------------------- diff --git a/akka-docs/intro/getting-started-first-scala.rst b/akka-docs/intro/getting-started-first-scala.rst index 07cfec7548..b6e5fca400 100644 --- a/akka-docs/intro/getting-started-first-scala.rst +++ b/akka-docs/intro/getting-started-first-scala.rst @@ -58,39 +58,49 @@ You can test your installation by invoking ``java``:: Java(TM) SE Runtime Environment (build 1.6.0_24-b07-334-10M3326) Java HotSpot(TM) 64-Bit Server VM (build 19.1-b02-334, mixed mode) + Downloading and installing Akka ------------------------------- -To build and run the tutorial sample from the command line, you have to download Akka. If you prefer to use SBT to build and run the sample then you can skip this section and jump to the next one. +To build and run the tutorial sample from the command line, you have to download +Akka. If you prefer to use SBT to build and run the sample then you can skip +this section and jump to the next one. -Let's get the ``akka-1.1`` distribution of Akka core (not Akka Modules) from `http://akka.io/downloads `_. Once you have downloaded the distribution unzip it in the folder you would like to have Akka installed in, in my case I choose to install it in ``/Users/jboner/tools/``, simply by unzipping it to this directory. +Let's get the ``akka-actors-1.1.zip`` distribution of Akka from +http://akka.io/downloads/ which includes everything we need for this +tutorial. Once you have downloaded the distribution unzip it in the folder you +would like to have Akka installed in. In my case I choose to install it in +``/Users/jboner/tools/``, simply by unzipping it to this directory. -You need to do one more thing in order to install Akka properly: set the ``AKKA_HOME`` environment variable to the root of the distribution. In my case I'm opening up a shell, navigating down to the distribution, and setting the ``AKKA_HOME`` variable:: +You need to do one more thing in order to install Akka properly: set the +``AKKA_HOME`` environment variable to the root of the distribution. In my case +I'm opening up a shell, navigating down to the distribution, and setting the +``AKKA_HOME`` variable:: - $ cd /Users/jboner/tools/akka-1.1 + $ cd /Users/jboner/tools/akka-actors-1.1 $ export AKKA_HOME=`pwd` $ echo $AKKA_HOME - /Users/jboner/tools/akka-1.1 + /Users/jboner/tools/akka-actors-1.1 The distribution looks like this:: - $ ls -l - total 16944 - drwxr-xr-x 7 jboner staff 238 Apr 6 11:15 . - drwxr-xr-x 28 jboner staff 952 Apr 6 11:16 .. - drwxr-xr-x 17 jboner staff 578 Apr 6 11:16 deploy - drwxr-xr-x 26 jboner staff 884 Apr 6 11:16 dist - drwxr-xr-x 3 jboner staff 102 Apr 6 11:15 lib_managed - -rwxr-xr-x 1 jboner staff 8674105 Apr 6 11:15 scala-library.jar - drwxr-xr-x 4 jboner staff 136 Apr 6 11:16 scripts + $ ls -1 + config + doc + lib + src -- In the ``dist`` directory we have the Akka JARs, including sources and docs. -- In the ``lib_managed/compile`` directory we have Akka's dependency JARs. -- In the ``deploy`` directory we have the sample JARs. -- In the ``scripts`` directory we have scripts for running Akka. -- Finally ``scala-library.jar`` is the JAR for the latest Scala distribution that Akka depends on. +- In the ``config`` directory we have the Akka conf files. +- In the ``doc`` directory we have the documentation, API, doc JARs, and also + the source files for the tutorials. +- In the ``lib`` directory we have the Scala and Akka JARs. +- In the ``src`` directory we have the source JARs for Akka. -The only JAR we will need for this tutorial (apart from the ``scala-library.jar`` JAR) is the ``akka-actor-1.1.jar`` JAR in the ``dist`` directory. This is a self-contained JAR with zero dependencies and contains everything we need to write a system using Actors. + +The only JAR we will need for this tutorial (apart from the +``scala-library.jar`` JAR) is the ``akka-actor-1.1.jar`` JAR in the ``lib/akka`` +directory. This is a self-contained JAR with zero dependencies and contains +everything we need to write a system using Actors. Akka is very modular and has many JARs for containing different features. The core distribution has seven modules: @@ -99,10 +109,13 @@ Akka is very modular and has many JARs for containing different features. The co - ``akka-remote-1.1.jar`` -- Remote Actors - ``akka-stm-1.1.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures - ``akka-http-1.1.jar`` -- Akka Mist for continuation-based asynchronous HTTP and also Jersey integration -- ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener +- ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener for logging with SLF4J - ``akka-testkit-1.1.jar`` -- Toolkit for testing Actors -We also have Akka Modules containing add-on modules outside the core of Akka. You can download the Akka Modules distribution from TODO. It contains Akka core as well. We will not be needing any modules there today, but for your information the module JARs are these: +We also have Akka Modules containing add-on modules outside the core of +Akka. You can download the Akka Modules distribution from TODO. It contains Akka +core as well. We will not be needing any modules there today, but for your +information the module JARs are these: - ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.) - ``akka-amqp-1.1.jar`` -- AMQP integration @@ -112,6 +125,7 @@ We also have Akka Modules containing add-on modules outside the core of Akka. Yo - ``akka-spring-1.1.jar`` -- Spring framework integration - ``akka-osgi-dependencies-bundle-1.1.jar`` -- OSGi support + Downloading and installing Scala -------------------------------- @@ -171,9 +185,7 @@ Now we need to create a project definition using our Akka SBT plugin. We do that import sbt._ - class TutorialOneProject(info: ProjectInfo) extends DefaultProject(info) with AkkaProject { - val akkaRepo = "Akka Repo" at "http://akka.io/repository" - } + class TutorialOneProject(info: ProjectInfo) extends DefaultProject(info) with AkkaProject The magic is in mixing in the ``AkkaProject`` trait. @@ -507,15 +519,15 @@ If you have not typed in (or copied) the code for the tutorial as ``$AKKA_HOME/t First we need to compile the source file. That is done with Scala's compiler ``scalac``. Our application depends on the ``akka-actor-1.1.jar`` JAR file, so let's add that to the compiler classpath when we compile the source:: - $ scalac -cp dist/akka-actor-1.1.jar tutorial/Pi.scala + $ scalac -cp lib/akka/akka-actor-1.1.jar tutorial/Pi.scala When we have compiled the source file we are ready to run the application. This is done with ``java`` but yet again we need to add the ``akka-actor-1.1.jar`` JAR file to the classpath, and this time we also need to add the Scala runtime library ``scala-library.jar`` and the classes we compiled ourselves:: $ java \ - -cp dist/akka-actor-1.1.jar:scala-library.jar:tutorial \ + -cp lib/scala-library.jar:lib/akka/akka-actor-1.1.jar:. \ akka.tutorial.first.scala.Pi - AKKA_HOME is defined as [/Users/jboner/src/akka-stuff/akka-core] - loading config from [/Users/jboner/src/akka-stuff/akka-core/config/akka.conf]. + AKKA_HOME is defined as [/Users/jboner/tools/akka-actors-1.1] + loading config from [/Users/jboner/tools/akka-actors-1.1/config/akka.conf]. Pi estimate: 3.1435501812459323 Calculation time: 858 millis diff --git a/akka-tutorials/akka-tutorial-first/README b/akka-tutorials/akka-tutorial-first/README new file mode 100644 index 0000000000..f4b42f631f --- /dev/null +++ b/akka-tutorials/akka-tutorial-first/README @@ -0,0 +1,7 @@ +================ + First Tutorial +================ + +This is the source code for the first tutorial. + +See the Akka Documentation for information about this tutorial. diff --git a/akka-tutorials/akka-tutorial-first/pom.xml b/akka-tutorials/akka-tutorial-first/pom.xml index e6b1758877..b3e9f7319c 100644 --- a/akka-tutorials/akka-tutorial-first/pom.xml +++ b/akka-tutorials/akka-tutorial-first/pom.xml @@ -8,7 +8,7 @@ akka.tutorial.first.java akka-tutorial-first-java jar - 1.0-SNAPSHOT + 1.2-SNAPSHOT http://akka.io @@ -23,7 +23,7 @@ Akka Akka Maven2 Repository - http://www.scalablesolutions.se/akka/repository/ + http://akka.io/repository/ diff --git a/akka-tutorials/akka-tutorial-first/project/build.properties b/akka-tutorials/akka-tutorial-first/project/build.properties new file mode 100644 index 0000000000..bcee7b19c1 --- /dev/null +++ b/akka-tutorials/akka-tutorial-first/project/build.properties @@ -0,0 +1,5 @@ +project.organization=akka.tutorial.first.scala +project.name=akka-tutorial-first-scala +project.version=1.2-SNAPSHOT +build.scala.versions=2.9.0.RC3 +sbt.version=0.7.6.RC0 diff --git a/akka-tutorials/akka-tutorial-first/project/build/Project.scala b/akka-tutorials/akka-tutorial-first/project/build/Project.scala new file mode 100644 index 0000000000..975f2ce970 --- /dev/null +++ b/akka-tutorials/akka-tutorial-first/project/build/Project.scala @@ -0,0 +1,3 @@ +import sbt._ + +class TutorialOneProject(info: ProjectInfo) extends DefaultProject(info) with AkkaProject diff --git a/akka-tutorials/akka-tutorial-first/project/plugins/Plugins.scala b/akka-tutorials/akka-tutorial-first/project/plugins/Plugins.scala new file mode 100644 index 0000000000..aa263a988e --- /dev/null +++ b/akka-tutorials/akka-tutorial-first/project/plugins/Plugins.scala @@ -0,0 +1,6 @@ +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.2-SNAPSHOT" +} 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 24264b52a7..bb081af751 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 @@ -18,31 +18,6 @@ import akka.routing.UntypedLoadBalancer; import java.util.concurrent.CountDownLatch; -/** - * First part in Akka tutorial for Java. - *

    - * Calculates Pi. - *

    - * Run on command line: - *

    - *   $ cd akka-1.1
    - *   $ export AKKA_HOME=`pwd`
    - *   $ javac -cp dist/akka-actor-1.2-SNAPSHOT.jar:scala-library.jar akka/tutorial/first/java/Pi.java
    - *   $ java -cp dist/akka-actor-1.2-SNAPSHOT.jar:scala-library.jar:. akka.tutorial.first.java.Pi
    - *   $ ...
    - * 
    - *

    - * Run it in Maven: - *

    - *   $ mvn
    - *   > scala:console
    - *   > val pi = new akka.tutorial.first.java.Pi
    - *   > pi.calculate(4, 10000, 10000)
    - *   > ...
    - * 
    - * - * @author Jonas Bonér - */ public class Pi { public static void main(String[] args) throws Exception { 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 6164ae00cb..51fcfac45d 100644 --- a/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala +++ b/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala @@ -12,32 +12,6 @@ import Routing._ import System.{currentTimeMillis => now} import java.util.concurrent.CountDownLatch -/** - * First part in Akka tutorial. - *

    - * Calculates Pi. - *

    - * Run on command line: - *

    - *   $ cd akka-1.1
    - *   $ export AKKA_HOME=`pwd`
    - *   $ scalac -cp dist/akka-actor-1.2-SNAPSHOT.jar Pi.scala
    - *   $ java -cp dist/akka-actor-1.2-SNAPSHOT.jar:scala-library.jar:. akka.tutorial.first.scala.Pi
    - *   $ ...
    - * 
    - *

    - * Run it in SBT: - *

    - *   $ sbt
    - *   > update
    - *   > console
    - *   > akka.tutorial.first.scala.Pi.calculate(nrOfWorkers = 4, nrOfElements = 10000, nrOfMessages = 10000)
    - *   > ...
    - *   > :quit
    - * 
    - * - * @author Jonas Bonér - */ object Pi extends App { calculate(nrOfWorkers = 4, nrOfElements = 10000, nrOfMessages = 10000) diff --git a/akka-tutorials/akka-tutorial-second/README b/akka-tutorials/akka-tutorial-second/README new file mode 100644 index 0000000000..ed31bbca22 --- /dev/null +++ b/akka-tutorials/akka-tutorial-second/README @@ -0,0 +1,7 @@ +================= + Second Tutorial +================= + +This is the source code for the second tutorial. + +See the Akka Documentation for information about this tutorial. diff --git a/akka-tutorials/akka-tutorial-second/pom.xml b/akka-tutorials/akka-tutorial-second/pom.xml new file mode 100644 index 0000000000..cf7c46fd3b --- /dev/null +++ b/akka-tutorials/akka-tutorial-second/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + akka-tutorial-second-java + akka.tutorial.second.java + akka-tutorial-second-java + jar + 1.2-SNAPSHOT + http://akka.io + + + + se.scalablesolutions.akka + akka-actor + 1.2-SNAPSHOT + + + + + + Akka + Akka Maven2 Repository + http://akka.io/repository/ + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + + + + + diff --git a/akka-tutorials/akka-tutorial-second/project/build.properties b/akka-tutorials/akka-tutorial-second/project/build.properties new file mode 100644 index 0000000000..ae73049d36 --- /dev/null +++ b/akka-tutorials/akka-tutorial-second/project/build.properties @@ -0,0 +1,5 @@ +project.organization=akka.tutorial.second.scala +project.name=akka-tutorial-second-scala +project.version=1.2-SNAPSHOT +build.scala.versions=2.9.0.RC3 +sbt.version=0.7.6.RC0 diff --git a/akka-tutorials/akka-tutorial-second/project/build/Project.scala b/akka-tutorials/akka-tutorial-second/project/build/Project.scala new file mode 100644 index 0000000000..1d0b230149 --- /dev/null +++ b/akka-tutorials/akka-tutorial-second/project/build/Project.scala @@ -0,0 +1,3 @@ +import sbt._ + +class TutorialTwoProject(info: ProjectInfo) extends DefaultProject(info) with AkkaProject diff --git a/akka-tutorials/akka-tutorial-second/project/plugins/Plugins.scala b/akka-tutorials/akka-tutorial-second/project/plugins/Plugins.scala new file mode 100644 index 0000000000..aa263a988e --- /dev/null +++ b/akka-tutorials/akka-tutorial-second/project/plugins/Plugins.scala @@ -0,0 +1,6 @@ +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.2-SNAPSHOT" +} diff --git a/akka-tutorials/akka-tutorial-second/src/main/java/akka/tutorial/java/second/Pi.java b/akka-tutorials/akka-tutorial-second/src/main/java/akka/tutorial/java/second/Pi.java index 1829428f5b..c8b47f46a2 100644 --- a/akka-tutorials/akka-tutorial-second/src/main/java/akka/tutorial/java/second/Pi.java +++ b/akka-tutorials/akka-tutorial-second/src/main/java/akka/tutorial/java/second/Pi.java @@ -20,31 +20,6 @@ import akka.routing.InfiniteIterator; import akka.routing.Routing.Broadcast; import akka.routing.UntypedLoadBalancer; -/** - * Second part in Akka tutorial for Java. - *

    - * Calculates Pi. - *

    - * Run on command line: - *

    - *   $ cd akka-1.1
    - *   $ export AKKA_HOME=`pwd`
    - *   $ javac -cp dist/akka-actor-1.2-SNAPSHOT.jar:scala-library.jar akka/tutorial/java/second/Pi.java
    - *   $ java -cp dist/akka-actor-1.2-SNAPSHOT.jar:scala-library.jar:. akka.tutorial.java.second.Pi
    - *   $ ...
    - * 
    - *

    - * Run it in Maven: - *

    - *   $ mvn
    - *   > scala:console
    - *   > val pi = new akka.tutorial.java.second.Pi
    - *   > pi.calculate(4, 10000, 10000)
    - *   > ...
    - * 
    - * - * @author Jonas Bonér - */ public class Pi { public static void main(String[] args) throws Exception { 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 f671605359..ff4b8d9c4a 100644 --- a/akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala +++ b/akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala @@ -13,32 +13,6 @@ import akka.dispatch.Future import System.{currentTimeMillis => now} -/** - * Second part in Akka tutorial. - *

    - * Calculates Pi. - *

    - * Run on command line: - *

    - *   $ cd akka-1.1
    - *   $ export AKKA_HOME=`pwd`
    - *   $ scalac -cp dist/akka-actor-1.2-SNAPSHOT.jar Pi.scala
    - *   $ java -cp dist/akka-actor-1.2-SNAPSHOT.jar:scala-library.jar:. akka.tutorial.second.Pi
    - *   $ ...
    - * 
    - *

    - * Run it in SBT: - *

    - *   $ sbt
    - *   > update
    - *   > console
    - *   > akka.tutorial.second.Pi.calculate(nrOfWorkers = 4, nrOfElements = 10000, nrOfMessages = 10000)
    - *   > ...
    - *   > :quit
    - * 
    - * - * @author Jonas Bonér - */ object Pi extends App { calculate(nrOfWorkers = 4, nrOfElements = 10000, nrOfMessages = 10000) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 746f9d2f39..bab3658c35 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -244,9 +244,9 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec } dependsOn (docs) lazy val releaseDownloads = task { - val distArchive = akkaDist.akkaCoreDist.distArchive + val distArchives = akkaDist.akkaActorsDist.distArchive +++ akkaDist.akkaCoreDist.distArchive val downloadsPath = localReleasePath / "downloads" - FileUtilities.copy(distArchive.get, downloadsPath, log).left.toOption + FileUtilities.copy(distArchives.get, downloadsPath, log).left.toOption } dependsOn (dist) lazy val dist = task { None } // dummy task @@ -494,6 +494,25 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec class AkkaActorsDistProject(info: ProjectInfo) extends DefaultProject(info) with DistDocProject { def distName = "akka-actors" override def distDocName = "akka" + + override def distConfigSources = (akkaParent.info.projectPath / "config") * "*" + + override def distAction = super.distAction dependsOn (distTutorials) + + val distTutorialsPath = distDocPath / "tutorials" + + lazy val distTutorials = task { + val tutorials = Set(akka_tutorials.akka_tutorial_first, + akka_tutorials.akka_tutorial_second) + + tutorials.map { tutorial => + val tutorialPath = (tutorial.info.projectPath ##) + val tutorialFilterOut = ((tutorial.outputPath ##) ***) + val tutorialSources = (tutorialPath ***) --- tutorialFilterOut + val tutorialOutputPath = distTutorialsPath / tutorial.name + copyPaths(tutorialSources, tutorialOutputPath) + }.foldLeft(None: Option[String])(_ orElse _) + } dependsOn (distBase) } class AkkaCoreDistProject(info: ProjectInfo)extends DefaultProject(info) with DistProject { From 5856860061ed881e5e29fcf29f3b6add26aac87c Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Tue, 10 May 2011 18:49:33 +1200 Subject: [PATCH 200/233] Rework modular dist and include in release build --- project/build/AkkaProject.scala | 10 ++++- project/build/DistProject.scala | 65 ++++++++++++++++----------------- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index bab3658c35..497c1cac1d 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -226,7 +226,7 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec lazy val buildRelease = task { log.info("Built release.") None - } dependsOn (publishRelease, releaseApi, releaseDocs, releaseDownloads) + } dependsOn (publishRelease, releaseApi, releaseDocs, releaseDownloads, releaseDist) lazy val releaseApi = task { val apiSources = ((apiOutputPath ##) ***) @@ -249,6 +249,12 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec FileUtilities.copy(distArchives.get, downloadsPath, log).left.toOption } dependsOn (dist) + lazy val releaseDist = task { + val distArchives = akkaDist.akkaActorsDist.distExclusiveArchive +++ akkaDist.akkaCoreDist.distExclusiveArchive + val distPath = localReleasePath / "dist" + FileUtilities.copy(distArchives.get, distPath, log).left.toOption + } dependsOn (dist) + lazy val dist = task { None } // dummy task // ------------------------------------------------------------------------------------------------------------------- @@ -495,7 +501,7 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec def distName = "akka-actors" override def distDocName = "akka" - override def distConfigSources = (akkaParent.info.projectPath / "config") * "*" + override def distConfigSources = (akkaParent.info.projectPath / "config" ##) * "*" override def distAction = super.distAction dependsOn (distTutorials) diff --git a/project/build/DistProject.scala b/project/build/DistProject.scala index df6b348396..5286d582d0 100644 --- a/project/build/DistProject.scala +++ b/project/build/DistProject.scala @@ -30,14 +30,17 @@ trait DistProject extends DistBaseProject { val distArchiveName = distFullName + ".zip" val distArchive = (distOutputBasePath ##) / distArchiveName - def distConfigSources = info.projectPath / "config" * "*" - def distScriptSources = info.projectPath / "scripts" * "*" + val distExclusiveOutputBasePath = distOutputBasePath / "exclusive" + val distExclusiveOutputPath = (distExclusiveOutputBasePath ##) / distFullName + val distExclusiveArchive = (distExclusiveOutputBasePath ##) / distArchiveName - lazy val distExclusiveProperty = systemOptional[Boolean]("dist.exclusive", false) + def distConfigSources = ((info.projectPath / "config" ##) ***) + def distScriptSources = ((info.projectPath / "scripts" ##) ***) - def distExclusive = distExclusiveProperty.value + def distAlwaysExclude(path: Path) = path.name == "scala-library.jar" + def distAlwaysInclude(path: Path) = distConfigSources.get.toList.map(_.name).contains(path.name) - def scalaDependency = if (distExclusive) Path.emptyPathFinder else buildLibraryJar + def scalaDependency = buildLibraryJar def allProjectDependencies = topologicalSort.dropRight(1) @@ -48,25 +51,8 @@ trait DistProject extends DistBaseProject { }) } - def distDependencyJarNames = { - val jarNames = distDependencies.flatMap { dist => - (dist.distLibPath ** "*.jar").get.map(_.name) ++ - (dist.distSrcPath ** "*.jar").get.map(_.name) ++ - (dist.distDocPath ** "*.jar").get.map(_.name) - } - Set(jarNames: _*) - } - def distClasspath = runClasspath - def filterOutExcludes(paths: PathFinder) = { - if (distExclusive) { - val exclude = distDependencyJarNames - def include(path: Path) = !exclude(path.name) - paths.filter(include) - } else paths - } - def dependencyJars(filter: Path => Boolean) = distClasspath.filter(filter) def isJar(path: Path) = path.name.endsWith(".jar") @@ -88,11 +74,11 @@ trait DistProject extends DistBaseProject { } } - def distLibs = filterOutExcludes(dependencyJars(isClassJar) +++ projectDependencyJars(_.jarPath)) + def distLibs = dependencyJars(isClassJar) +++ projectDependencyJars(_.jarPath) - def distSrcJars = filterOutExcludes(dependencyJars(isSrcJar) +++ projectDependencyJars(_.packageSrcJar)) + def distSrcJars = dependencyJars(isSrcJar) +++ projectDependencyJars(_.packageSrcJar) - def distDocJars = filterOutExcludes(dependencyJars(isDocJar) +++ projectDependencyJars(_.packageDocsJar)) + def distDocJars = dependencyJars(isDocJar) +++ projectDependencyJars(_.packageDocsJar) def distShareSources = ((distOutputPath ##) ***) @@ -100,25 +86,36 @@ trait DistProject extends DistBaseProject { describedAs("Create a distribution.")) def distAction = task { + def exclusiveDist = { + val excludePaths = (distDependencies.map(p => ((p.distOutputPath ##) ***)) + .foldLeft(Path.emptyPathFinder)(_ +++ _)) + val excludeRelativePaths = excludePaths.get.toList.map(_.relativePath) + val allDistPaths = ((distOutputPath ##) ***) + val includePaths = allDistPaths.filter(path => { + distAlwaysInclude(path) || !(distAlwaysExclude(path) || excludeRelativePaths.contains(path.relativePath)) + }) + copyPaths(includePaths, distExclusiveOutputPath) orElse + FileUtilities.zip(List(distExclusiveOutputPath), distExclusiveArchive, true, log) + } + copyFiles(scalaDependency, distScalaLibPath) orElse copyFiles(distLibs, distLibPath) orElse copyFiles(distSrcJars, distSrcPath) orElse copyFiles(distDocJars, distDocJarsPath) orElse - copyFiles(distConfigSources, distConfigPath) orElse + copyPaths(distConfigSources, distConfigPath) orElse copyScripts(distScriptSources, distBinPath) orElse copyPaths(distShareSources, distSharePath) orElse - FileUtilities.zip(List(distOutputPath), distArchive, true, log) + FileUtilities.zip(List(distOutputPath), distArchive, true, log) orElse + exclusiveDist } lazy val distBase = distBaseAction dependsOn (distClean) describedAs "Create the dist base." def distBaseAction = task { - if (!distExclusive) { - distDependencies.map( dist => { - val allFiles = ((dist.distOutputPath ##) ***) - copyPaths(allFiles, distOutputPath) - }).foldLeft(None: Option[String])(_ orElse _) - } else None + distDependencies.map( dist => { + val allFiles = ((dist.distOutputPath ##) ***) + copyPaths(allFiles, distOutputPath) + }).foldLeft(None: Option[String])(_ orElse _) } def distDependencyTasks: Seq[ManagedTask] = distDependencies.map(_.dist) @@ -138,7 +135,7 @@ trait DistProject extends DistBaseProject { def copyPaths(from: PathFinder, to: Path): Option[String] = { if (from.get.isEmpty) None - else FileUtilities.copy(from.get, to, log).left.toOption + else FileUtilities.copy(from.get, to, true, log).left.toOption } def copyScripts(from: PathFinder, to: Path): Option[String] = { From aa706a4e1b4a4262035dec6951722421b5be2321 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Tue, 10 May 2011 19:35:24 +1200 Subject: [PATCH 201/233] Update tutorial sbt defs to match main project --- akka-tutorials/akka-tutorial-first/project/build.properties | 4 ++-- akka-tutorials/akka-tutorial-second/project/build.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/akka-tutorials/akka-tutorial-first/project/build.properties b/akka-tutorials/akka-tutorial-first/project/build.properties index bcee7b19c1..e4ff105728 100644 --- a/akka-tutorials/akka-tutorial-first/project/build.properties +++ b/akka-tutorials/akka-tutorial-first/project/build.properties @@ -1,5 +1,5 @@ -project.organization=akka.tutorial.first.scala -project.name=akka-tutorial-first-scala +project.organization=se.scalablesolutions.akka +project.name=akka-tutorial-first project.version=1.2-SNAPSHOT build.scala.versions=2.9.0.RC3 sbt.version=0.7.6.RC0 diff --git a/akka-tutorials/akka-tutorial-second/project/build.properties b/akka-tutorials/akka-tutorial-second/project/build.properties index ae73049d36..0b21be843f 100644 --- a/akka-tutorials/akka-tutorial-second/project/build.properties +++ b/akka-tutorials/akka-tutorial-second/project/build.properties @@ -1,5 +1,5 @@ -project.organization=akka.tutorial.second.scala -project.name=akka-tutorial-second-scala +project.organization=se.scalablesolutions.akka +project.name=akka-tutorial-second project.version=1.2-SNAPSHOT build.scala.versions=2.9.0.RC3 sbt.version=0.7.6.RC0 From b455dffc04d4094aa5c4b4f0171ce5c4bb249fab Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 10 May 2011 09:44:55 +0200 Subject: [PATCH 202/233] Docs: Removed pending --- akka-docs/pending/buildr.rst | 55 ------- akka-docs/pending/guice-integration.rst | 50 ------- akka-docs/pending/logging.rst | 4 - akka-docs/pending/servlet.rst | 41 ------ akka-docs/pending/stm.rst | 65 --------- akka-docs/pending/test.rst | 55 ------- akka-docs/pending/testkit-example.rst | 138 ------------------ akka-docs/pending/testkit.rst | 49 ------- .../pending/tutorial-chat-server-java.rst | 7 - 9 files changed, 464 deletions(-) delete mode 100644 akka-docs/pending/buildr.rst delete mode 100644 akka-docs/pending/guice-integration.rst delete mode 100644 akka-docs/pending/logging.rst delete mode 100644 akka-docs/pending/servlet.rst delete mode 100644 akka-docs/pending/stm.rst delete mode 100644 akka-docs/pending/test.rst delete mode 100644 akka-docs/pending/testkit-example.rst delete mode 100644 akka-docs/pending/testkit.rst delete mode 100644 akka-docs/pending/tutorial-chat-server-java.rst diff --git a/akka-docs/pending/buildr.rst b/akka-docs/pending/buildr.rst deleted file mode 100644 index a684463270..0000000000 --- a/akka-docs/pending/buildr.rst +++ /dev/null @@ -1,55 +0,0 @@ -Using Akka in a Buildr project -============================== - -This is an example on how to use Akka in a project based on Buildr - -.. code-block:: ruby - - require 'buildr/scala' - - VERSION_NUMBER = "0.6" - GROUP = "se.scalablesolutions.akka" - - repositories.remote << "http://www.ibiblio.org/maven2/" - repositories.remote << "http://www.lag.net/repo" - repositories.remote << "http://multiverse.googlecode.com/svn/maven-repository/releases" - - AKKA = group('akka-core', 'akka-comet', 'akka-util','akka-kernel', 'akka-rest', 'akka-util-java', - 'akka-security','akka-persistence-common', 'akka-persistence-redis', - 'akka-amqp', - :under=> 'se.scalablesolutions.akka', - :version => '0.6') - ASPECTJ = "org.codehaus.aspectwerkz:aspectwerkz-nodeps-jdk5:jar:2.1" - SBINARY = "sbinary:sbinary:jar:0.3" - COMMONS_IO = "commons-io:commons-io:jar:1.4" - CONFIGGY = "net.lag:configgy:jar:1.4.7" - JACKSON = group('jackson-core-asl', 'jackson-mapper-asl', - :under=> 'org.codehaus.jackson', - :version => '1.2.1') - MULTIVERSE = "org.multiverse:multiverse-alpha:jar:jar-with-dependencies:0.3" - NETTY = "org.jboss.netty:netty:jar:3.2.0.ALPHA2" - PROTOBUF = "com.google.protobuf:protobuf-java:jar:2.2.0" - REDIS = "com.redis:redisclient:jar:1.0.1" - SJSON = "sjson.json:sjson:jar:0.3" - - Project.local_task "run" - - desc "Akka Chat Sample Module" - define "akka-sample-chat" do - project.version = VERSION_NUMBER - project.group = GROUP - - compile.with AKKA, CONFIGGY - - p artifact(MULTIVERSE).to_s - - package(:jar) - - task "run" do - Java.java "scala.tools.nsc.MainGenericRunner", - :classpath => [ compile.dependencies, compile.target, - ASPECTJ, COMMONS_IO, JACKSON, NETTY, MULTIVERSE, PROTOBUF, REDIS, - SBINARY, SJSON], - :java_args => ["-server"] - end - end diff --git a/akka-docs/pending/guice-integration.rst b/akka-docs/pending/guice-integration.rst deleted file mode 100644 index 6392bddfd3..0000000000 --- a/akka-docs/pending/guice-integration.rst +++ /dev/null @@ -1,50 +0,0 @@ -Guice Integration -================= - -Module stability: **STABLE** - -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 ----------------------- - -You can also plug in external Guice modules and have not-actors wired up as part of the configuration. -Here is an example: - -.. code-block:: java - - import static akka.config.Supervision.*; - import static akka.config.SupervisorConfig.*; - - TypedActorConfigurator manager = new TypedActorConfigurator(); - - manager.configure( - new AllForOneStrategy(new Class[]{Exception.class}, 3, 1000), - new SuperviseTypedActor[] { - new SuperviseTypedActor( - Foo.class, - FooImpl.class, - temporary(), - 1000), - new SuperviseTypedActor( - Bar.class, - BarImpl.class, - permanent(), - 1000) - }) - .addExternalGuiceModule(new AbstractModule() { - protected void configure() { - bind(Ext.class).to(ExtImpl.class).in(Scopes.SINGLETON); - }}) - .configure() - .inject() - .supervise(); - -Retrieve the external Guice dependency --------------------------------------- - -The external dependency can be retrieved like this: -``_ -Ext ext = manager.getExternalDependency(Ext.class); -``_ diff --git a/akka-docs/pending/logging.rst b/akka-docs/pending/logging.rst deleted file mode 100644 index 833f2f419b..0000000000 --- a/akka-docs/pending/logging.rst +++ /dev/null @@ -1,4 +0,0 @@ -Logging -======= - -Logging has been removed. See the `Event Handler `_. diff --git a/akka-docs/pending/servlet.rst b/akka-docs/pending/servlet.rst deleted file mode 100644 index 6859657a72..0000000000 --- a/akka-docs/pending/servlet.rst +++ /dev/null @@ -1,41 +0,0 @@ -Akka Servlet -============ - -= - -Module stability: **STABLE** - -Akka has a servlet; ‘se.scalablesolutions.akka.comet.AkkaServlet’ that can use to deploy your Akka-based application in an external Servlet container. All you need to do is to add the servlet to the ‘web.xml’, set ‘$AKKA_HOME’ to the root of the distribution (needs the ‘$AKKA_HOME/config/*’ files) and add the JARs in the ‘$AKKA_HOME/lib’ to your classpath (or put them in the ‘WEB-INF/lib’ directory in the WAR file). - -Also, you need to add the Akka initialize/cleanup listener in web.xml - -.. code-block:: xml - - - ... - - se.scalablesolutions.akka.servlet.Initializer - - ... - - -And to support REST actors and/or comet actors, you need to add the following servlet declaration: - -``_ - -... - - Akka - - se.scalablesolutions.akka.comet.AkkaServlet - - se.scalablesolutions.akka.rest.AkkaServlet - - - * - Akka - -... - - -``_ diff --git a/akka-docs/pending/stm.rst b/akka-docs/pending/stm.rst deleted file mode 100644 index 54984eecf8..0000000000 --- a/akka-docs/pending/stm.rst +++ /dev/null @@ -1,65 +0,0 @@ -Akka STM -======== - -The Akka Software Transactional Memory implementation - -**Read consistency** -^^^^^^^^^^^^^^^^^^^^ - -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 (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. - -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. - -**Read consistency and the Akka STM** -************************************* - -The AkkaSTM (that is build on top of the Multiverse 0.7 STM) and from Akka 1.1 it doesn’t use a MVCC based implementation because of the scalability limiting central clock. - -It uses 2 different mechanisms: -1) For very short transactions it does a full conflict scan every time a new ref is read. Doing a full conflict scan sounds expensive, but it only involves volatile reads. -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** -**************************** - -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 the Ref is updated it will always increment the conflict counter because it doesn’t know if there are any conflicting readers. - -TODO: - -- Visible reads, semi visible reads -- Read tracking - -- strict isolation -- eager conflict detection -- deferred write, no dirty read possible - -- isolation level -- optimistic -- various levels of pessimistic behavior diff --git a/akka-docs/pending/test.rst b/akka-docs/pending/test.rst deleted file mode 100644 index c845cb36d2..0000000000 --- a/akka-docs/pending/test.rst +++ /dev/null @@ -1,55 +0,0 @@ -Testing of Akka -=============== - -Introduction -============ - -Testing concurrent code using time-outs (like Thread.sleep(..)) is usually a bad idea since it is both slow and error-prone. There are some frameworks that can help, some are listed below. - -Testing Actor Interaction -========================= - -For Actor interaction, making sure certain message arrives in time etc. we recommend you use Akka's built-in `TestKit `_. If you want to roll your own, you will find helpful abstractions in the `java.util.concurrent` package, most notably `BlockingQueue` and `CountDownLatch`. - -Unit testing of Actors -====================== - -If you need to unit test your actors then the best way to do that would be to decouple it from the Actor by putting it in a regular class/trait, test that, and then mix in the Actor trait when you want to create actors. This is necessary since you can't instantiate an Actor class directly with 'new'. But note that you can't test Actor interaction with this, but only local Actor implementation. Here is an example: - -.. code-block:: scala - - // test this - class MyLogic { - def blabla: Unit = { - ... - } - } - - // run this - actorOf(new MyLogic with Actor { - def receive = { - case Bla => blabla - } - }) - -...or define a non-anonymous MyLogicActor class. - -Akka Expect -=========== - -Expect mimic for testing Akka actors. - -``_ - -Awaitility -========== - -Not a Akka specific testing framework but a nice DSL for testing asynchronous code. -Scala and Java API. - -``_ - -ScalaTest Conductor -=================== - -``_ diff --git a/akka-docs/pending/testkit-example.rst b/akka-docs/pending/testkit-example.rst deleted file mode 100644 index 611ba4dea6..0000000000 --- a/akka-docs/pending/testkit-example.rst +++ /dev/null @@ -1,138 +0,0 @@ -Ray Roestenburg's example code from `his blog `_. -``_ -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 ! _) - } - } -} -``_ diff --git a/akka-docs/pending/testkit.rst b/akka-docs/pending/testkit.rst deleted file mode 100644 index 65aeac00b6..0000000000 --- a/akka-docs/pending/testkit.rst +++ /dev/null @@ -1,49 +0,0 @@ -Actor TestKit -============= - -Module Stability: **In Progress** - -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 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 -------- - -The TestKit is a trait which you can mix into your test class to setup a test harness consisting of an test actor, which is implicitly available as sender reference, methods for querying and asserting features of messages received by said actor, and finally methods which provide a DSL for timing assertions. - -Ray Roestenburg has written a great article on using the TestKit: ``_. Here is a short teaser: - -.. code-block:: scala - - class SomeSpec extends WordSpec with MustMatchers with TestKit { - - val worker = actorOf(...) - - "A Worker" must { - "send timely replies" in { - within (50 millis) { - worker ! "some work" - expectMsg("some result") - expectNoMsg - } - } - } - } - -His full example is also available `here `_. - -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 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/tutorial-chat-server-java.rst b/akka-docs/pending/tutorial-chat-server-java.rst deleted file mode 100644 index 4f0daaa0de..0000000000 --- a/akka-docs/pending/tutorial-chat-server-java.rst +++ /dev/null @@ -1,7 +0,0 @@ -Tutorial: write a scalable, fault-tolerant, persistent network chat server and client (Java) -============================================================================================ - -Here is a couple of ports of the Scala API chat sample application in the `Scala tutorial `_. - -``_ -``_ From 1051c077e83094c7f80c22018057b4a593637214 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 10 May 2011 09:53:58 +0200 Subject: [PATCH 203/233] Docs: fixed broken links --- .../_sphinx/static/akka-intellij-code-style.jar | Bin 0 -> 8587 bytes akka-docs/additional/add-on-modules.rst | 2 +- akka-docs/dev/developer-guidelines.rst | 4 ++-- akka-docs/disabled/getting-started-first.rst | 2 +- akka-docs/index.rst | 2 ++ akka-docs/intro/getting-started-first-java.rst | 5 ++++- .../getting-started-first-scala-eclipse.rst | 5 ++++- akka-docs/intro/getting-started-first-scala.rst | 5 ++++- akka-docs/java/dispatchers.rst | 2 +- akka-docs/java/typed-actors.rst | 6 +++--- akka-docs/project/scaladoc.rst | 8 ++++---- akka-docs/scala/actors.rst | 4 +++- akka-docs/scala/dataflow.rst | 2 +- akka-docs/scala/fault-tolerance.rst | 2 ++ akka-docs/scala/futures.rst | 4 +++- akka-docs/scala/testing.rst | 2 ++ akka-docs/scala/tutorial-chat-server.rst | 2 +- akka-docs/scala/typed-actors.rst | 6 +++--- 18 files changed, 41 insertions(+), 22 deletions(-) create mode 100644 akka-docs/_sphinx/static/akka-intellij-code-style.jar diff --git a/akka-docs/_sphinx/static/akka-intellij-code-style.jar b/akka-docs/_sphinx/static/akka-intellij-code-style.jar new file mode 100644 index 0000000000000000000000000000000000000000..55866c22c522de2f21a4fb2a6c26ec30ad3c5486 GIT binary patch literal 8587 zcmWIWW@h1H00GJ33DzJQhPfG77?Sf-Qj1F}b5e`-|D9rB04Ww>;9%fjKvyisz<{hc zF*`dk9;7)QtXr=lH|OmQ$NVM(3AP8;`xm{ObXR8An*^gPA#as9n;MI5wwkQ7N@rE~ zI<`MP@QUkE5#jAKzqmc9{=KyJl8`}EyfL59whr~c8x33AwODjy-0~+E+-b0@TzMsN z^-9S{eB6&7TF*8VxFnhFU^VxWSJ`eM{or#6!ORCbo_QPk#XYRH_z)n^zU=N%vyTcM z8u6X%r;m0yE|6E3xpX)$PR@ck$!gnnxd@SUn`gg193l7NO!%JHA7_|btb1o+W|%9V zx|n_WBLCLFrdykq-h8t8lOO-i70w%WpYGA5Ic*P1k>{f-#}7OoQunA9p1vH%mHnbs?o-sqn83SiSJ{16Yze*SrM3G0#;&PX z)_RjwUxU}qQ=?b2| z$LCL3wyc&pz?+@pgM)Hg3nK$VgCKHznxaL%AOizKenClQeqOPDeo01Z5hx0u#{4gS zv`g^+-@l@IZ%f|Y-7`aAl6R5st->CTea2>6eT&R^u3Ko+i$=8wuCU!ng{`DX*|4Fj; zp&LDOCCnu>1(+kZ2*iDOwC&qV1(Ek2-i) zyk|Kl;@%o?Roc~W%LbVjfyc_lS6jCyG5<2}^JIBtP$+YlTUTPzgkID9Y_Hgq@(Eo! zDfZtcOxvH&abL#1@x6MVe_^x#I-i&e*AE?k_Ji|$!B&UL3<;0TIl=E1+HEpjxnJnn zo;?=o2JJrG2ik&j*!w4GYOE=nGfnYiS6P0!=vAk-Ol99)!GYR%cJ=sNW?TA2)OV9e?BnDm(a#o}Y!E%WZ}OA# z-`ni2?AVf77sj^jXXxgt6HSTVCSJX4t!BP-P4==>o>XshpLKo9LcX6!+I-G`!nvkZ z-gAvYeeVkxPYS6nbDtRU_ujUBd>5u2I~K5+`>$v03yGdp_5oL>Doc1?P&TYBb?V%g z^KjDT8|554?4mrCR=+hOi_V)(Ff}o;-kN83*2CGm{af21pL1J&S1qra%U~73l`Nwx ze%#cIxzqR9sWS)WyUt>H^jr0(^xJDz*_(B8^iQ5YXa2lg$ojwRlSvs`lWu+~pYX@p zH(#jYqv4ZxT@D&u9x4(RDuFsDRAy9MR!RD}%Sfa)h==pg$-SFiy%cNrj^;~Ht-8|N zrz;)Leob%Ec@Ew;rn{P*Dy;WsO16iL05dQS-ab`pX8Id ze_)ErMTzGcJ~LbE472j3CUERl54`8%q4a;-M3;l^=Z~H{Zu#}`c5{>)@5QLlcRUe?ET{jO!>7cf(4s$W?dgX5 z6V#Odb>u1(uZT)((<|k@=^VW4zLdo^!HJ7X73N8K#2r^vsVY_4-sfRHWs{?ylF{bl zo-Upt?y0AKC0yOS(%-Ew{(79;tZDc3Ppn$JB}M7B%dC|Vru6>I&`|~Rl6BA z3|a1M6PC#Em;|WK^nclJD;vOCY$m+S`|l}NmV_nzE9&^U913~*S99syoV1HQaoLRK zs>}ty7ivkG{W^3dxykoq<-x^CUvr8prq5io@0o4sv-y_icZE9k{p7j1>_Teiv#`fa z8^5Z4{(P$8ft!Ec^2n1n z$~DdgPJGW2e6c6;-wlC?-FtqoJ$?FgkVEa^Wk-)cJHKB|MQ%^Yv}VCqM`|AkZx@=&l&dt0#n=a1eRqqJ+Y&egB=F4#5gZS_I!GF7wm zEz=a2PrkY}FN&+(N5% z*@u#y8=vfAc%FUtZO>(CC7EB`-A`l+ZT9!W0m|M4^9b?M2M^7R6CfkGd6W?c3!Vn}#k*p?Ax zuR3Slrab3tnfzHk(%v(!>pa{uJB^J$dqdJZ#y`s+E}CdHbEaI*75&3=6xYfn7gl9S zCArS`$=-eB<)XqTELS|w_w)S8e&qV~wea$MuD9_l5smeNGY;?f*8FnPewyIlc>C+? z&diZ}T`jDmf6PuSk9G2y57t{NKIAnlo_YA2`YYDLUxjIGQiU65tmA!eBJ?2Y*c`jF zI;W+R17F^lF0k&%o!!~br(WLO&meeo#vR>P2etJr&P4t#5{Y^7Sn|x8tsJj+AA9M0 z&2Ynf5gi^IOSV_DFRrvWYIs?!cDm}smxTr2w#i*6_|>~pd*8<-2b1rweirOv%y=(x zK>xd3t18#mk3HhQrdp)6e>mRIq+6KA3k$*}fao7kr6j=9pe zwJ!BBa%xKMf3QR7d%e8M8;xUe1t0$GzL-$#!FbwyzPrHR#cI9Be67lA)PGkqIh^7S zn)JS{^ES&-{~t%XW$za^O-?t;+Lgj))Zd`7oijyL_vQS_C6`;5#T(8%`hEAdBn$Bt ztDo*BGoKzanSV5w3R(JJZ9h_R)Ap$Ge~!<8<7$>Y6nyq}uf_WJ{LgRk z{%u^(Kl5cQ`{eI0HD`#dIoEjEV4m@*b0;#R<}9<~o^<52aCgq5B>wQa=$z~X@rdAk z#+Tw`+O7Q;x2x$Km}Z(V)i?e}U+bl5W&Y;n^W4*QudmvDuQHzxO!;?d zRnLUiF1GK_|4V=Mx`llz#~#hucUDBNiDBo9WL~qLd2h_&!iM|H-50hoEZ!F!STAkt zcRzf~yKDancKuzPk|9vOz3^d4Rf@!}K&=SPKegA-)E+uBlX=FTq;(l#l@sdu3rhbO zHMmaS;e9%zFzV=maxvxfKS3g?W-L~Hdko%HTwEpcSXWSc)`_?2XAiS(>zdFJDVh3Z zb<=&X=hNj>Tz$Efd_M$6U%Q%E|J3ESP3T1*@1W2tH@m&2EqH6kGIj3egMTM%Tw;5B zdAIZy-E)0;>deWXYnFXq8oJJKmM+`rn-LS^W9#?W{5=u(ApWG3`HZ_2jI2+dCYSR+ z6Q3@xe@k5cyuMe>^&fAH+_#xaANe`+cUtP#S${OLUmo!<*%~!f`9;@*0+Z7xAL?d3 zUVOV{`_bPkg&&8fD^?|Lk&oW?Cq-iF8NQbSwV#}-I2OM5eH3ov74JVUIsIqaf5V>I zPjg%>jHi4p|7{o?^G0Oa6_dFKWG7aB+;wne&)!!FraGTio;2m#wDRt;^X2RZ9M*8% zkGtBTySg&sZg^PQvDTPhznGaa8s$X3%$Qmkdj0yFcNWa;7Z*Lc$I2|l)jdh6+vswK zN@H|r_hxUFmdh*!%Uv`*ikfx=8TttcbHpF{uA8(>wLT^*Vq)pR{49?0vLZ%-%4HL8 z%zjcHu}VGQvhLE^&S!$s*aB-;XP#Ez3W?1A$G=IF$GLp(pGV7frtJ2YN_-gk!upLy z#$AECZ{&J^vS|O8_{2PGKO@)6zF=RmLeJDPAuB`eS0Z~1n|XGwXqynAqi&*RJoCqd z$@xjMstYx=ug z(XWN`?GFE5HTUi7zvoxg7D4e)6>bab9-WlSw)%zRs(I%EHp>YESLd;=6h9baQ)rcJltDJnkD` zj~-R!x-GMUYj@?rGSM6NPDI7NU3p_)#&-KTvGa<*{TA&16dXHi=7UZDonEd^d}92- zq{CnhU&HK-e|r=+92fg(^!fdU!;_AcdQ82)Eawzo%FZn-Sq#6`zMniPYa8c;%?VfL zoc|;ExS&XF+s(H@Vb_#?%au&|c_jC-X5Zodv&;M$X4xK`+<*Q0^>AKCBm3La@}6@_ zKZ(7Ru;R(BGo_rh3%oD4^5?9r-j(~uQ>sB}vqs+c5`%?(Nq<((ysNR`?4<{cv3;}7 zFPL`pB>P6yZJ)k-zWI5R`Qy#bBLWPQEdH(y%(=haZ~eK`e#IM}l%6$t{-)VBea)&T z^X6{%ukX0==<)h9d8<^mM@@e#uQ>DW{|}Ll-d8iC*S_vv%%OW;-`Gq3xVqm9M|r(% z{+~jBKRR4^B){k9Ot%HTyH)4iy3nb0QT*-a)$%6i6OBIYEqq$BUjO{Q`o37BWxna} zE>5dGXQ#5pM#*(jsFD2^y>sXK?T;`0KJ{2l-OaS+OYBe7YqQ_lXSd&`??m|Vugfho z;NL6WTrG6?h-qT0B>+S!QLq|IYU-Kb+TEH@C1{_o$Isv+3`cp40EXt$7@(7}s+1 z<{5Q{KdO`O^?CpP*_(5qe%Yr6i}l8guln}{3O$xOvd1)VmB;V<&)iP89NxQQ&#Hx$ zvvuZP+j4J`-rKnbAD8(2e$MeA-f-UWOZ8qm9~%d}|M|bOsQd1gb>bIi95;7wxc4;4 z?a3cDb@s^fv+aI<(D%#!s*}EoT_^qVf+;cPd;gx{yW>=I;@%_i;H`dVR8p2pCOtiN z<@QteP!kWa%S_#;OtYu^d{gF%n|8*gdxCmb&f+aMN)|6-&p(p)GVtivG;!u5N7l)T zhg_8wzR7>2r1TmH_`SNrblc`LnHtxU1*=9U|a6VxyBG79Ajot;y0cxDzy?;&EtM<|Nda&1?e|y_1q4J9k%uGIiY)C z=go&E=Dgf2PQSV%zHmN%o3%4@-Ol@Q*|y%+24ejGes8F{I_bF)`9i>vr9uK_nho&%U0f(x$dvhUH|`VpkDk>Jy)}Keg=jUevn=~ zWC-VL>{Tn+@C}IOV_;xN%gjj)NzEs_4s z)8&zaO4ia-?ccCv^VL$gHMHi7B)C zmIOpC5)V3gTG`^jIjckU*WSMW@Oyqa$h$GMvf(8R3=9((kiDB1egnk|pg<_W;#G8Y z!VC-y2zABDi8+a&;f=S`;{6X9h#bBDQ^b6#K<~wavl-bU6aH}DE||?S-$#~pddb^2 zR!8#R-(ZsxF{&||xc%POJH`34K6_uwSSQL*=vT!*R^uZz|@JV7<;A)l)yII?q7cFYt@3!HPiQl;{ro9i(TzDOD?QGu8 zqQKQ=0%ts*aN19KdEtwkjT}phC(ESUDFZd9Tim`=i}p|d70*)J+S)(=W!ZycHVaE-xrAJI zKA)pg;PcevHZ$Y0{2#VOD*UE9zw@N$oxAggGcWA^j+J|k#h4jYy_yhOzz6^-8t_J7qi*TcV@eTkJRTkIvsG?ER+6y_ItybA9vpl zn!uY8tRZ>f_-}SE2fZtsCcJLg_oR8Z`mMJXc21F;eFqp$My=k(m67XkxzG8;%gG*b zP7d0!9!pXVyVkC0*;6>#pzlufBR9|cOH(-07It%e-=y0n6e781ik>A~$Vo#B2vm*{m`xIaOpw&BY{=RFS6VSlRBe#(J%98cH`$}S+5;G9^G$SxXLN# zWXJVOx>HzK|Kkj+|zNi_Ueet)r{PDm^@s|&M%9+EXYc2Kfrpc+} znMpSdPCZ_!`sJ#WWukHP{+?LV?l`k~H~sQwEPeUstXNL&%&&(sD-Ol!oXO9a)xYQs zf7E^J+j{q{<$Q}{?%gacm3ht%ihvXCJ9j!VGB8YGMvj1_fL@e=Vo8(;C{Il)PAw|S zOinEZXR7Gv;@jJV>dvn}uwoXU?PKPhFTL}!ju*aBoB69q$MWTwnZ|M*ExKAF2bdP9 znojoh*5iQoMf84LF;^ zes_XKBfF~Di_iV`f3|$z^m}3-TiPAY3pby=zoXvOt@p|;a&^DN+=XdH`OlYjYi`)M zD$~r;`_A5FuV$<=n= zH*NKcGNzoE5U6?4WbQmeuVcLBo3Gz|vb$Y9f7i6r;w;xb9>3ReXM!bL`2nSG8@E1t zcxQQGmXOtb$N3NS`&>_4aS;4}IY3~3lj6ncQvwgNb!T>*e>r9KHXbXD1((DVy>7k# z<0dJ3YwEY>8$=#to;asEcl~_5Aa>0S-2~mVf6|P>tE}?G2eV2GtuMH z75jtE`5hc5HBQ-VIk{DV=h;Sv$xDA_Jk{Xl2xb@5VV>HqGxJvI!$(q+XZ%R+;>_s& ztFgoARn+{#n{Ly>kL%WUrtt2xJQbq)Tjs+=rZ&IMfVi`B65pt; z-q@v9UY?L}DX%gtWp{dl)5bim(2k<$b>}j!Zp?l2ym;A*n3?Jo&)63CXubb#|0Z5; z&OS?>rRy&}5dCy8()!Tlhqn(Go4;Hg=ean1ug+Af&sm%1gbQXJQwZ^Fw_0s>S$yZl zws#FKfmZw1@vmK%_jTfRhdsd=4YiJmM!U98NdEC+iMa}s(}M}DhV42Vn6v2UEuRzlg;D_y~ma>=KOl~|G$2_<(%KSe@zQ|XT+R)Gg;_mix;!O$J&&4Eh}Ap zn%7S|_+PW-y7yb5RljGeOuN5cQSW=v^fMcSK6c0XFaMNrW@**OocA~5^x2=Ql}7Q! zMqd{Gc~(1$Ws`Skxzo*M^C~sJPWcv-F0j#=qvS^U(FBZb&&M3!a``u8o=A%=RX8LOzQ8R&tKxZu(c~QxH`lGXbzt?@gK{!F-mCZA zvpcZFZiA2X<|#{Z!Y0jMdt+Iy<2v^|h0QA$<$jrDG%aQ7@m04?qh9}RTAK3i&q~gP z&0APnFX{Z9|8UaFudlLBEm@&fJm-}5PCXq(iRi4jZ3niB#>ckqc&)=aUF%@b#;udH z{bgCFOJ3!vjos_ zYZ@F0e$pV|I$_z987?>LE{DZDtBosOx_4f;sFm4cWvzz%hNjd$?_bkTEO*@;KX1$3 zGvZTPA0&DFir)5M17|q*`Z$lQ^`{qK{`~mw!w(PktMt#JXwM z+B)CK7ri$3^?0EY7Z^SIB+?-89-f1GQEd)^#(ewHuq{Z4=I)BdrqE^$87 zo?~B^OP;YWYdF96b6H2c;$HCy`afe5%JO@qd!t_IUb&xq)?VInVV$U@55Jw#zDG%c zjdvd%IW{TTGq0<7A&>W~S4XlAD4!SoP=5P}|KjTbA5N{0x{$kib?%9+tJj(F?v%L3 z(sUwV@zju5@wnYbJ5)}UE&DE3EZKZ1(p2Z6N4s>}j@|D$)}`bus68QZPwhgkiM3wp zq*?P%c^|KQnd)+7>dXC8vuC`#eX4HZLk|8(v$yLM))f~oe0lHSWp<0dyD!VGUj90M z)2jX9S8LewTz#wWgeN_>;k)B==bKpgZHGC3r28)2pZxRq!EYM1)l>G_di{?}@VpZz zf9HO=w06$*zeSIyT}pn-ukh~uY1W2wpEoz>|GCZjob^hyLh0|dhrUI#N_=HVefVj` z@vfbl--{K!@7P$S^l`?l+w-EQ++FnMvD3{xS$XkQdD|b)PW;evrd7J-pihLF&R&&O z_Rbgl99ZkP&pO_6S>}G9ed`ioEtS{=q4o%EhXXx_Y|VbJy(4qtc0ci&j z)i*D_x+3MT=;Sxs|JPaTobHEOvl2 zIY4cC1_t!$Nm&L42G6{b)SR45F9lB*S4Rc+ocyH39EIT2l9J54^x^<-MkX!>h-UP; zYS6?ssCR_GpqXm$M0GKAnwybHgaOjp1ZhT|G6hX^BQ)v~oc0De1ZEb<7eDn79XHS< zE5fY5j98{zLE2&dz}CA%Xg@5-fYH}OcmveiL+(T=A~Z5EFucdmi>2>}>=g7iI3%E9 zW^NKf?q7lQ!9oC}54lwj>WRU0F)-Xh(FgCBp&NkQDg`zE5eBRfLADsy6+qSxYP%vQ zF;LqTp`_. +Documentation for Akka Modules is located `here `_. diff --git a/akka-docs/dev/developer-guidelines.rst b/akka-docs/dev/developer-guidelines.rst index 138ff18476..be83c7bd04 100644 --- a/akka-docs/dev/developer-guidelines.rst +++ b/akka-docs/dev/developer-guidelines.rst @@ -9,7 +9,7 @@ Code Style The Akka code style follows `this document `_ . Here is a code style settings file for ``IntelliJ IDEA``: -`Download `_ +`Download <../_static/akka-intellij-code-style.jar>`_ Please follow the code style. Look at the code around you and mimic. @@ -26,7 +26,7 @@ There is a testing standard that should be followed: `Ticket001Spec `_. It enables assertions concerning replies received and their timing, there is more documentation in the ``_ module. +There is a useful test kit for testing actors: `akka.util.TestKit `_. It enables assertions concerning replies received and their timing, there is more documentation in the :ref:`akka-testkit` module. NetworkFailureTest ^^^^^^^^^^^^^^^^^^ diff --git a/akka-docs/disabled/getting-started-first.rst b/akka-docs/disabled/getting-started-first.rst index 63683a8c17..7ff96ed0ff 100644 --- a/akka-docs/disabled/getting-started-first.rst +++ b/akka-docs/disabled/getting-started-first.rst @@ -80,7 +80,7 @@ Akka is very modular and has many JARs for containing different features. The co - ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener - ``akka-testkit-1.1.jar`` -- Toolkit for testing Actors -We also have Akka Modules containing add-on modules outside the core of Akka. You can download the Akka Modules distribution from TODO. It contains Akka core as well. We will not be needing any modules there today, but for your information the module JARs are these: +We also have Akka Modules containing add-on modules outside the core of Akka. You can download the Akka Modules distribution from ``_. It contains Akka core as well. We will not be needing any modules there today, but for your information the module JARs are these: - ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.) - ``akka-amqp-1.1.jar`` -- AMQP integration diff --git a/akka-docs/index.rst b/akka-docs/index.rst index fb8e273bf5..47416d444b 100644 --- a/akka-docs/index.rst +++ b/akka-docs/index.rst @@ -24,6 +24,8 @@ Links * :ref:`scaladoc` +* `Akka Modules Documentation `_. + * :ref:`issue_tracking` * :ref:`support` diff --git a/akka-docs/intro/getting-started-first-java.rst b/akka-docs/intro/getting-started-first-java.rst index 32e8f36672..b16ba62323 100644 --- a/akka-docs/intro/getting-started-first-java.rst +++ b/akka-docs/intro/getting-started-first-java.rst @@ -102,7 +102,10 @@ Akka is very modular and has many JARs for containing different features. The co - ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener for logging with SLF4J - ``akka-testkit-1.1.jar`` -- Toolkit for testing Actors -We also have Akka Modules containing add-on modules outside the core of Akka. You can download the Akka Modules distribution from TODO. It contains Akka core as well. We will not be needing any modules there today, but for your information the module JARs are these: +We also have Akka Modules containing add-on modules outside the core of +Akka. You can download the Akka Modules distribution from ``_. It contains Akka +core as well. We will not be needing any modules there today, but for your +information the module JARs are these: - ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.) - ``akka-amqp-1.1.jar`` -- AMQP integration diff --git a/akka-docs/intro/getting-started-first-scala-eclipse.rst b/akka-docs/intro/getting-started-first-scala-eclipse.rst index b4380490ef..c30644fc01 100644 --- a/akka-docs/intro/getting-started-first-scala-eclipse.rst +++ b/akka-docs/intro/getting-started-first-scala-eclipse.rst @@ -87,7 +87,10 @@ Akka is very modular and has many JARs for containing different features. The co - ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener - ``akka-testkit-1.1.jar`` -- Toolkit for testing Actors -We also have Akka Modules containing add-on modules outside the core of Akka. You can download the Akka Modules distribution from TODO. It contains Akka core as well. We will not be needing any modules there today, but for your information the module JARs are these: +We also have Akka Modules containing add-on modules outside the core of +Akka. You can download the Akka Modules distribution from ``_. It contains Akka +core as well. We will not be needing any modules there today, but for your +information the module JARs are these: - ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.) - ``akka-amqp-1.1.jar`` -- AMQP integration diff --git a/akka-docs/intro/getting-started-first-scala.rst b/akka-docs/intro/getting-started-first-scala.rst index 07cfec7548..7fd47db7e0 100644 --- a/akka-docs/intro/getting-started-first-scala.rst +++ b/akka-docs/intro/getting-started-first-scala.rst @@ -102,7 +102,10 @@ Akka is very modular and has many JARs for containing different features. The co - ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener - ``akka-testkit-1.1.jar`` -- Toolkit for testing Actors -We also have Akka Modules containing add-on modules outside the core of Akka. You can download the Akka Modules distribution from TODO. It contains Akka core as well. We will not be needing any modules there today, but for your information the module JARs are these: +We also have Akka Modules containing add-on modules outside the core of +Akka. You can download the Akka Modules distribution from ``_. It contains Akka +core as well. We will not be needing any modules there today, but for your +information the module JARs are these: - ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.) - ``akka-amqp-1.1.jar`` -- AMQP integration diff --git a/akka-docs/java/dispatchers.rst b/akka-docs/java/dispatchers.rst index a7fe7ce19a..578fcd4ff5 100644 --- a/akka-docs/java/dispatchers.rst +++ b/akka-docs/java/dispatchers.rst @@ -129,7 +129,7 @@ Setting this to a higher number will increase throughput but lower fairness, and If you don't define a the 'throughput' option in the configuration file then the default value of '5' will be used. -Browse the `ScalaDoc `_ or look at the code for all the options available. +Browse the :ref:`scaladoc` or look at the code for all the options available. Priority event-based ^^^^^^^^^^^^^^^^^^^^ diff --git a/akka-docs/java/typed-actors.rst b/akka-docs/java/typed-actors.rst index 99803186b2..eee1ef16a0 100644 --- a/akka-docs/java/typed-actors.rst +++ b/akka-docs/java/typed-actors.rst @@ -84,7 +84,7 @@ Using a configuration object: RegistrationService service = (RegistrationService) TypedActor.newInstance(RegistrationService.class, config); -However, often you will not use these factory methods but declaratively define the Typed Actors as part of a supervisor hierarchy. More on that in the `Fault Tolerance `_ section. +However, often you will not use these factory methods but declaratively define the Typed Actors as part of a supervisor hierarchy. More on that in the :ref:`fault-tolerance-java` section. Sending messages ---------------- @@ -144,7 +144,7 @@ Here is an example: Stopping Typed Actors --------------------- -Once Typed Actors have been created with one of the TypedActor.newInstance methods they need to be stopped with TypedActor.stop to free resources allocated by the created Typed Actor (this is not needed when the Typed Actor is `supervised `_). +Once Typed Actors have been created with one of the TypedActor.newInstance methods they need to be stopped with TypedActor.stop to free resources allocated by the created Typed Actor (this is not needed when the Typed Actor is supervised). .. code-block:: java @@ -156,7 +156,7 @@ Once Typed Actors have been created with one of the TypedActor.newInstance metho // Free Typed Actor resources TypedActor.stop(service); -When the Typed Actor defines a `shutdown callback `_ method it will be invoked on TypedActor.stop. +When the Typed Actor defines a shutdown callback method (:ref:`fault-tolerance-java`) it will be invoked on TypedActor.stop. How to use the TypedActorContext for runtime information access --------------------------------------------------------------- diff --git a/akka-docs/project/scaladoc.rst b/akka-docs/project/scaladoc.rst index 312aaec5e5..deda2419b2 100644 --- a/akka-docs/project/scaladoc.rst +++ b/akka-docs/project/scaladoc.rst @@ -20,11 +20,11 @@ be found here: Release Versions ================ -1.1-RC1 -------- +1.1 +--- -- Akka 1.1-RC1 - http://akka.io/api/akka/1.1-RC1/ -- Akka Modules 1.1-RC1 - http://akka.io/api/akka-modules/1.1-RC1/ +- Akka 1.1 - http://akka.io/api/akka/1.1/ +- Akka Modules 1.1 - http://akka.io/api/akka-modules/1.1/ 1.0 --- diff --git a/akka-docs/scala/actors.rst b/akka-docs/scala/actors.rst index c7f5453a2e..bd550b807b 100644 --- a/akka-docs/scala/actors.rst +++ b/akka-docs/scala/actors.rst @@ -1,3 +1,5 @@ +.. _actors-scala: + Actors (Scala) ============== @@ -184,7 +186,7 @@ Using ``!!!`` will send a message to the receiving Actor asynchronously and will val future = actor !!! "Hello" -See `Futures `_ for more information. +See :ref:`futures-scala` for more information. Forward message ^^^^^^^^^^^^^^^ diff --git a/akka-docs/scala/dataflow.rst b/akka-docs/scala/dataflow.rst index ec99eb0a74..1262d9c089 100644 --- a/akka-docs/scala/dataflow.rst +++ b/akka-docs/scala/dataflow.rst @@ -8,7 +8,7 @@ Dataflow Concurrency (Scala) Description ----------- -Akka implements `Oz-style dataflow concurrency `_ by using a special API for `Futures `_ that allows single assignment variables and multiple lightweight (event-based) processes/threads. +Akka implements `Oz-style dataflow concurrency `_ by using a special API for :ref:`futures-scala` that allows single assignment variables and multiple lightweight (event-based) processes/threads. Dataflow concurrency is deterministic. This means that it will always behave the same. If you run it once and it yields output 5 then it will do that **every time**, run it 10 million times, same result. If it on the other hand deadlocks the first time you run it, then it will deadlock **every single time** you run it. Also, there is **no difference** between sequential code and concurrent code. These properties makes it very easy to reason about concurrency. The limitation is that the code needs to be side-effect free, e.g. deterministic. You can't use exceptions, time, random etc., but need to treat the part of your program that uses dataflow concurrency as a pure function with input and output. diff --git a/akka-docs/scala/fault-tolerance.rst b/akka-docs/scala/fault-tolerance.rst index 7276c5cdaf..c7ac83fd7e 100644 --- a/akka-docs/scala/fault-tolerance.rst +++ b/akka-docs/scala/fault-tolerance.rst @@ -1,3 +1,5 @@ +.. _fault-tolerance-scala: + Fault Tolerance Through Supervisor Hierarchies (Scala) ====================================================== diff --git a/akka-docs/scala/futures.rst b/akka-docs/scala/futures.rst index 690952baa3..7ac7f535bd 100644 --- a/akka-docs/scala/futures.rst +++ b/akka-docs/scala/futures.rst @@ -1,3 +1,5 @@ +.. _futures-scala: + Futures (Scala) =============== @@ -200,7 +202,7 @@ This is just a sample of what can be done, but to use more advanced techniques i Scalaz ^^^^^^ -Akka also has a `Scalaz module `_ for a more complete support of programming in a functional style. +Akka also has a Scalaz module (:ref:`add-on-modules`) for a more complete support of programming in a functional style. Exceptions ---------- diff --git a/akka-docs/scala/testing.rst b/akka-docs/scala/testing.rst index aeec3ac57f..eee3a2b029 100644 --- a/akka-docs/scala/testing.rst +++ b/akka-docs/scala/testing.rst @@ -1,3 +1,5 @@ +.. _akka-testkit: + ##################### Testing Actor Systems ##################### diff --git a/akka-docs/scala/tutorial-chat-server.rst b/akka-docs/scala/tutorial-chat-server.rst index 3ef4b5b74b..f54888ff31 100644 --- a/akka-docs/scala/tutorial-chat-server.rst +++ b/akka-docs/scala/tutorial-chat-server.rst @@ -36,7 +36,7 @@ Actors encapsulate state and behavior into a lightweight process/thread. In a se Creating Actors --------------- -Akka has both a `Scala API `_ and a `Java API `_. In this article we will only look at the Scala API since that is the most expressive one. The article assumes some basic Scala knowledge, but even if you don't know Scala I don't think it will not be too hard to follow along anyway. +Akka has both a Scala API (:ref:`actors-scala`) and a Java API (:ref:`untyped-actors-java`). In this article we will only look at the Scala API since that is the most expressive one. The article assumes some basic Scala knowledge, but even if you don't know Scala I don't think it will not be too hard to follow along anyway. Akka has adopted the same style of writing Actors as Erlang in which each Actor has an explicit message handler which does pattern matching to match on the incoming messages. diff --git a/akka-docs/scala/typed-actors.rst b/akka-docs/scala/typed-actors.rst index 81d68685f5..d2963ddae7 100644 --- a/akka-docs/scala/typed-actors.rst +++ b/akka-docs/scala/typed-actors.rst @@ -80,7 +80,7 @@ Using a configuration object: val service = TypedActor.newInstance(classOf[RegistrationService], classOf[RegistrationServiceImpl], config) -However, often you will not use these factory methods but declaratively define the Typed Actors as part of a supervisor hierarchy. More on that in the `Fault Tolerance `_ section. +However, often you will not use these factory methods but declaratively define the Typed Actors as part of a supervisor hierarchy. More on that in the :ref:`fault-tolerance-scala` section. Sending messages ---------------- @@ -137,7 +137,7 @@ Here is an example: Stopping Typed Actors --------------------- -Once Typed Actors have been created with one of the TypedActor.newInstance methods they need to be stopped with TypedActor.stop to free resources allocated by the created Typed Actor (this is not needed when the Typed Actor is `supervised `_). +Once Typed Actors have been created with one of the TypedActor.newInstance methods they need to be stopped with TypedActor.stop to free resources allocated by the created Typed Actor (this is not needed when the Typed Actor is supervised). .. code-block:: scala @@ -149,7 +149,7 @@ Once Typed Actors have been created with one of the TypedActor.newInstance metho // Free Typed Actor resources TypedActor.stop(service) -When the Typed Actor defines a `shutdown callback `_ method it will be invoked on TypedActor.stop. +When the Typed Actor defines a shutdown callback method (:ref:`fault-tolerance-scala`) it will be invoked on TypedActor.stop. How to use the TypedActorContext for runtime information access --------------------------------------------------------------- From 789475a46811b9c40d916b83afb533ac6a85b2df Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 10 May 2011 10:02:06 +0200 Subject: [PATCH 204/233] Docs: fixed wrong heading --- akka-docs/intro/deployment-scenarios.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-docs/intro/deployment-scenarios.rst b/akka-docs/intro/deployment-scenarios.rst index f36be61ff1..7694a2d337 100644 --- a/akka-docs/intro/deployment-scenarios.rst +++ b/akka-docs/intro/deployment-scenarios.rst @@ -22,7 +22,7 @@ Actors as services The simplest way you can use Akka is to use the actors as services in your Web application. All that’s needed to do that is to put the Akka charts as well as its dependency jars into ‘WEB-INF/lib’. You also need to put the ‘akka.conf’ config file in the ‘$AKKA_HOME/config’ directory. Now you can create your Actors as regular services referenced from your Web application. You should also be able to use the Remoting service, e.g. be able to make certain Actors remote on other hosts. Please note that remoting service does not speak HTTP over port 80, but a custom protocol over the port is specified in ‘akka.conf’. - Using Akka as a stand alone microkernel +Using Akka as a stand alone microkernel --------------------------------------- Akka can also be run as a stand-alone microkernel. It implements a full enterprise stack: From fe85ae111d0dd8340b1adcfaec1be6cfb33c3abb Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 10 May 2011 10:24:48 +0200 Subject: [PATCH 205/233] Docs: Guice Integration in two places, removed from typed-actors (cherry picked from commit 5b1a610c57e42aeb28a6a1d9c3eb922e1871d334) --- akka-docs/java/typed-actors.rst | 52 --------------------------------- 1 file changed, 52 deletions(-) diff --git a/akka-docs/java/typed-actors.rst b/akka-docs/java/typed-actors.rst index eee1ef16a0..8de961e515 100644 --- a/akka-docs/java/typed-actors.rst +++ b/akka-docs/java/typed-actors.rst @@ -194,55 +194,3 @@ Akka can help you in this regard. It allows you to turn on an option for seriali This will make a deep clone (using Java serialization) of all parameters. -Guice Integration ------------------ - -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 -^^^^^^^^^^^^^^^^^^^^^^ - -You can also plug in external Guice modules and have not-actors wired up as part of the configuration. -Here is an example: - -.. code-block:: java - - import static akka.config.Supervision.*; - import static akka.config.SupervisorConfig.*; - - TypedActorConfigurator manager = new TypedActorConfigurator(); - - manager.configure( - new AllForOneStrategy(new Class[]{Exception.class}, 3, 1000), - new SuperviseTypedActor[] { - new SuperviseTypedActor( - Foo.class, - FooImpl.class, - temporary(), - 1000), - new SuperviseTypedActor( - Bar.class, - BarImpl.class, - permanent(), - 1000) - }) - .addExternalGuiceModule(new AbstractModule() { - protected void configure() { - bind(Ext.class).to(ExtImpl.class).in(Scopes.SINGLETON); - }}) - .configure() - .inject() - .supervise(); - -Retrieve the external Guice dependency -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The external dependency can be retrieved like this: - -.. code-block:: java - - Ext ext = manager.getExternalDependency(Ext.class); - - - From a3499bc5356e8f1dee91c8b01087f93b996f5872 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 10 May 2011 15:54:25 +0200 Subject: [PATCH 206/233] Docs: Some minor corrections (cherry picked from commit 52a0b2c6b89b4887f84f052dd85c458a8f4fb68a) --- akka-docs/dev/building-akka.rst | 2 +- akka-docs/disabled/getting-started-first.rst | 12 ++++++------ akka-docs/intro/getting-started-first-java.rst | 14 ++++++++------ .../getting-started-first-scala-eclipse.rst | 18 +++++++++--------- .../intro/getting-started-first-scala.rst | 12 ++++++------ akka-docs/intro/getting-started.rst | 2 +- akka-docs/scala/dataflow.rst | 2 +- akka-docs/scala/tutorial-chat-server.rst | 2 +- 8 files changed, 33 insertions(+), 31 deletions(-) diff --git a/akka-docs/dev/building-akka.rst b/akka-docs/dev/building-akka.rst index 677ad0e15a..add552f93d 100644 --- a/akka-docs/dev/building-akka.rst +++ b/akka-docs/dev/building-akka.rst @@ -124,7 +124,7 @@ convenient. 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 + [info] Building project akka 1.1-SNAPSHOT against Scala 2.9.0 [info] using AkkaParentProject with sbt 0.7.6.RC0 and Scala 2.7.7 > update [info] diff --git a/akka-docs/disabled/getting-started-first.rst b/akka-docs/disabled/getting-started-first.rst index 7ff96ed0ff..3fa245febc 100644 --- a/akka-docs/disabled/getting-started-first.rst +++ b/akka-docs/disabled/getting-started-first.rst @@ -34,7 +34,7 @@ __ https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first Prerequisites ------------- -This tutorial assumes that you have Jave 1.6 or later installed on you machine and ``java`` on your ``PATH``. You also need to know how to run commands in a shell (ZSH, Bash, DOS etc.) and a decent text editor or IDE to type in the Scala code. +This tutorial assumes that you have Java 1.6 or later installed on you machine and ``java`` on your ``PATH``. You also need to know how to run commands in a shell (ZSH, Bash, DOS etc.) and a decent text editor or IDE to type in the Scala code. Downloading and installing Akka ------------------------------- @@ -95,16 +95,16 @@ Downloading and installing Scala To build and run the tutorial sample from the command line, you have to install the Scala distribution. If you prefer to use SBT to build and run the sample then you can skip this section and jump to the next one. -Scala can be downloaded from `http://www.scala-lang.org/downloads `_. Browse there and download the Scala 2.9.0.RC1 release. If you pick the ``tgz`` or ``zip`` distribution then just unzip it where you want it installed. If you pick the IzPack Installer then double click on it and follow the instructions. +Scala can be downloaded from `http://www.scala-lang.org/downloads `_. Browse there and download the Scala 2.9.0 release. If you pick the ``tgz`` or ``zip`` distribution then just unzip it where you want it installed. If you pick the IzPack Installer then double click on it and follow the instructions. -You also need to make sure that the ``scala-2.9.0.RC1/bin`` (if that is the directory where you installed Scala) is on your ``PATH``:: +You also need to make sure that the ``scala-2.9.0/bin`` (if that is the directory where you installed Scala) is on your ``PATH``:: - $ export PATH=$PATH:scala-2.9.0.RC1/bin + $ export PATH=$PATH:scala-2.9.0/bin You can test your installation by invoking scala:: $ scala -version - Scala code runner version 2.9.0.RC1 -- Copyright 2002-2011, LAMP/EPFL + Scala code runner version 2.9.0.final -- Copyright 2002-2011, LAMP/EPFL Looks like we are all good. Finally let's create a source file ``Pi.scala`` for the tutorial and put it in the root of the Akka distribution in the ``tutorial`` directory (you have to create it first). @@ -133,7 +133,7 @@ If you have not already done so, now is the time to create an SBT project for ou Name: Tutorial 1 Organization: Hakkers Inc Version [1.0]: - Scala version [2.9.0.RC1]: + Scala version [2.9.0]: sbt version [0.7.6.RC0]: Now we have the basis for an SBT project. Akka has an SBT Plugin making it very easy to use Akka is an SBT-based project so let's use that. diff --git a/akka-docs/intro/getting-started-first-java.rst b/akka-docs/intro/getting-started-first-java.rst index df7f352eab..6b4b724216 100644 --- a/akka-docs/intro/getting-started-first-java.rst +++ b/akka-docs/intro/getting-started-first-java.rst @@ -6,7 +6,7 @@ Getting Started Tutorial (Java): First Chapter Introduction ------------ -Welcome to the first tutorial on how to get started with Akka and Java. We assume that you already know what Akka and Java are and will now focus on the steps necessary to start your first project. +Welcome to the first tutorial on how to get started with `Akka `_ and Java. We assume that you already know what Akka and Java are and will now focus on the steps necessary to start your first project. There are two variations of this first tutorial: @@ -165,7 +165,9 @@ Here is the layout that Maven created:: As you can see we already have a Java source file called ``App.java``, let's now rename it to ``Pi.java``. -We also need to edit the ``pom.xml`` build file. Let's add the dependency we need as well as the Maven repository it should download it from. It should now look something like this:: +We also need to edit the ``pom.xml`` build file. Let's add the dependency we need as well as the Maven repository it should download it from. It should now look something like this: + +.. code-block:: xml `_, 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. @@ -139,16 +139,16 @@ Downloading and installing Scala To build and run the tutorial sample from the command line, you have to install the Scala distribution. If you prefer to use Eclipse to build and run the sample then you can skip this section and jump to the next one. -Scala can be downloaded from `http://www.scala-lang.org/downloads `_. Browse there and download the Scala 2.9.0.RC1 release. If you pick the ``tgz`` or ``zip`` distribution then just unzip it where you want it installed. If you pick the IzPack Installer then double click on it and follow the instructions. +Scala can be downloaded from `http://www.scala-lang.org/downloads `_. Browse there and download the Scala 2.9.0 release. If you pick the ``tgz`` or ``zip`` distribution then just unzip it where you want it installed. If you pick the IzPack Installer then double click on it and follow the instructions. -You also need to make sure that the ``scala-2.9.0.RC1/bin`` (if that is the directory where you installed Scala) is on your ``PATH``:: +You also need to make sure that the ``scala-2.9.0/bin`` (if that is the directory where you installed Scala) is on your ``PATH``:: - $ export PATH=$PATH:scala-2.9.0.RC1/bin + $ export PATH=$PATH:scala-2.9.0/bin You can test your installation by invoking scala:: $ scala -version - Scala code runner version 2.9.0.RC1 -- Copyright 2002-2011, LAMP/EPFL + Scala code runner version 2.9.0.final -- Copyright 2002-2011, LAMP/EPFL Looks like we are all good. Finally let's create a source file ``Pi.scala`` for the tutorial and put it in the root of the Akka distribution in the ``tutorial`` directory (you have to create it first). @@ -164,7 +164,7 @@ If you have not already done so, now is the time to create an Eclipse project fo 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``:: +If you are an `SBT `_ user, you can follow the :ref:`getting-started-first-scala-download-sbt` instruction 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._ @@ -190,7 +190,7 @@ and then update your SBT project definition by mixing in ``Eclipsify`` in your p 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] Building project AkkaPi 1.0 against Scala 2.9.0 [info] using MySbtProject with sbt 0.7.4 and Scala 2.7.7 [info] [info] == eclipse == @@ -412,8 +412,8 @@ 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]. + AKKA_HOME is defined as [/Users/jboner/tools/akka-actors-1.1] + loading config from [/Users/jboner/tools/akka-actors-1.1/config/akka.conf]. Pi estimate: 3.1435501812459323 Calculation time: 858 millis diff --git a/akka-docs/intro/getting-started-first-scala.rst b/akka-docs/intro/getting-started-first-scala.rst index c390548661..c8124ca2bf 100644 --- a/akka-docs/intro/getting-started-first-scala.rst +++ b/akka-docs/intro/getting-started-first-scala.rst @@ -44,7 +44,7 @@ Then you can navigate down to the tutorial:: Prerequisites ------------- -This tutorial assumes that you have Jave 1.6 or later installed on you machine and ``java`` on your ``PATH``. You also need to know how to run commands in a shell (ZSH, Bash, DOS etc.) and a decent text editor or IDE to type in the Scala code. +This tutorial assumes that you have Java 1.6 or later installed on you machine and ``java`` on your ``PATH``. You also need to know how to run commands in a shell (ZSH, Bash, DOS etc.) and a decent text editor or IDE to type in the Scala code. You need to make sure that ``$JAVA_HOME`` environment variable is set to the root of the Java distribution. You also need to make sure that the ``$JAVA_HOME/bin`` is on your ``PATH``:: @@ -131,16 +131,16 @@ Downloading and installing Scala To build and run the tutorial sample from the command line, you have to install the Scala distribution. If you prefer to use SBT to build and run the sample then you can skip this section and jump to the next one. -Scala can be downloaded from `http://www.scala-lang.org/downloads `_. Browse there and download the Scala 2.9.0.RC1 release. If you pick the ``tgz`` or ``zip`` distribution then just unzip it where you want it installed. If you pick the IzPack Installer then double click on it and follow the instructions. +Scala can be downloaded from `http://www.scala-lang.org/downloads `_. Browse there and download the Scala 2.9.0 release. If you pick the ``tgz`` or ``zip`` distribution then just unzip it where you want it installed. If you pick the IzPack Installer then double click on it and follow the instructions. -You also need to make sure that the ``scala-2.9.0.RC1/bin`` (if that is the directory where you installed Scala) is on your ``PATH``:: +You also need to make sure that the ``scala-2.9.0/bin`` (if that is the directory where you installed Scala) is on your ``PATH``:: - $ export PATH=$PATH:scala-2.9.0.RC1/bin + $ export PATH=$PATH:scala-2.9.0/bin You can test your installation by invoking scala:: $ scala -version - Scala code runner version 2.9.0.RC1 -- Copyright 2002-2011, LAMP/EPFL + Scala code runner version 2.9.0.final -- Copyright 2002-2011, LAMP/EPFL Looks like we are all good. Finally let's create a source file ``Pi.scala`` for the tutorial and put it in the root of the Akka distribution in the ``tutorial`` directory (you have to create it first). @@ -167,7 +167,7 @@ If you have not already done so, now is the time to create an SBT project for ou Name: Tutorial 1 Organization: Hakkers Inc Version [1.0]: - Scala version [2.9.0.RC1]: + Scala version [2.9.0]: sbt version [0.7.6.RC0]: Now we have the basis for an SBT project. Akka has an SBT Plugin making it very easy to use Akka is an SBT-based project so let's use that. diff --git a/akka-docs/intro/getting-started.rst b/akka-docs/intro/getting-started.rst index 58632d3940..ee8e1ee2aa 100644 --- a/akka-docs/intro/getting-started.rst +++ b/akka-docs/intro/getting-started.rst @@ -27,7 +27,7 @@ essential parts for getting started with different development environments. Prerequisites ------------- -Akka requires that you have `Jave 1.6 `_ or +Akka requires that you have `Java 1.6 `_ or later installed on you machine. Download diff --git a/akka-docs/scala/dataflow.rst b/akka-docs/scala/dataflow.rst index 1262d9c089..af6622b976 100644 --- a/akka-docs/scala/dataflow.rst +++ b/akka-docs/scala/dataflow.rst @@ -115,7 +115,7 @@ To run these examples: :: - Welcome to Scala version 2.9.0.RC1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_25). + Welcome to Scala version 2.9.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_25). Type in expressions to have them evaluated. Type :help for more information. diff --git a/akka-docs/scala/tutorial-chat-server.rst b/akka-docs/scala/tutorial-chat-server.rst index f54888ff31..78a93157a8 100644 --- a/akka-docs/scala/tutorial-chat-server.rst +++ b/akka-docs/scala/tutorial-chat-server.rst @@ -97,7 +97,7 @@ First we need to create an SBT project for our tutorial. You do that by stepping Name: Chat Organization: Hakkers Inc Version [1.0]: - Scala version [2.9.0.RC1]: + Scala version [2.9.0]: 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:: From 207a3743f53f2de2bc44dcaf73db9a0040e6f569 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 10 May 2011 16:47:51 +0200 Subject: [PATCH 207/233] Adding Future docs for Java API and fixing warning for routing.rst --- akka-docs/java/futures.rst | 193 ++++++++++++++++++++++++++++++++++++ akka-docs/java/index.rst | 1 + akka-docs/scala/routing.rst | 2 +- 3 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 akka-docs/java/futures.rst diff --git a/akka-docs/java/futures.rst b/akka-docs/java/futures.rst new file mode 100644 index 0000000000..43e6de2586 --- /dev/null +++ b/akka-docs/java/futures.rst @@ -0,0 +1,193 @@ +.. _futures-java: + +Futures (Java) +=============== + +.. sidebar:: Contents + + .. contents:: :local: + +Introduction +------------ + +In Akka, a `Future `_ is a data structure used to retrieve the result of some concurrent operation. This operation is usually performed by an ``Actor`` or by the ``Dispatcher`` directly. This result can be accessed synchronously (blocking) or asynchronously (non-blocking). + +Use with Actors +--------------- + +There are generally two ways of getting a reply from an ``UntypedActor``: the first is by a sent message (``actorRef.sendOneWay(msg);``), which only works if the original sender was an ``UntypedActor``) and the second is through a ``Future``. + +Using the ``ActorRef``\'s ``sendRequestReplyFuture`` method to send a message will return a Future. To wait for and retrieve the actual result the simplest method is: + +.. code-block:: java + + Future[Object] future = actorRef.sendRequestReplyFuture[Object](msg); + Object result = future.get(); //Block until result is available, usually bad practice + +This will cause the current thread to block and wait for the ``UntypedActor`` to 'complete' the ``Future`` with it's reply. Due to the dynamic nature of Akka's ``UntypedActor``\s this result can be anything. The safest way to deal with this is to specify the result to an ``Object`` as is shown in the above example. You can also use the expected result type instead of ``Any``, but if an unexpected type were to be returned you will get a ``ClassCastException``. For more elegant ways to deal with this and to use the result without blocking, refer to `Functional Futures`_. + +Use Directly +------------ + +A common use case within Akka is to have some computation performed concurrently without needing the extra utility of an ``UntypedActor``. If you find yourself creating a pool of ``UntypedActor``\s for the sole reason of performing a calculation in parallel, there is an easier (and faster) way: + +.. code-block:: java + + import static akka.dispatch.Futures.future; + import java.util.concurrent.Callable; + + Future f = future(new Callable() { + public String call() { + return "Hello" + "World!"; + } + }); + String result = f.get(); //Blocks until timeout, default timeout is set in akka.conf, otherwise 5 seconds + +In the above code the block passed to ``future`` will be executed by the default ``Dispatcher``, with the return value of the block used to complete the ``Future`` (in this case, the result would be the string: "HelloWorld"). Unlike a ``Future`` that is returned from an ``UntypedActor``, this ``Future`` is properly typed, and we also avoid the overhead of managing an ``UntypedActor``. + +Functional Futures +------------------ + +A recent addition to Akka's ``Future`` is several monadic methods that are very similar to the ones used by ``Scala``'s collections. These allow you to create 'pipelines' or 'streams' that the result will travel through. + +Future is a Monad +^^^^^^^^^^^^^^^^^ + +The first method for working with ``Future`` functionally is ``map``. This method takes a ``Function`` which performs some operation on the result of the ``Future``, and returning a new result. The return value of the ``map`` method is another ``Future`` that will contain the new result: + +.. code-block:: java + + import static akka.dispatch.Futures.future; + import static akka.japi.Function; + import java.util.concurrent.Callable; + + Future f1 = future(new Callable() { + public String call() { + return "Hello" + "World"; + } + }); + + Future f2 = f1.map(new Function() { + public Integer apply(String s) { + return s.length(); + } + }); + + Integer result = f2.get(); + +In this example we are joining two strings together within a Future. Instead of waiting for f1 to complete, we apply our function that calculates the length of the string using the ``map`` method. Now we have a second Future, f2, that will eventually contain an ``Integer``. When our original ``Future``, f1, completes, it will also apply our function and complete the second Future with it's result. When we finally ``get`` the result, it will contain the number 10. Our original Future still contains the string "HelloWorld" and is unaffected by the ``map``. + +Something to note when using these methods: if the ``Future`` is still being processed when one of these methods are called, it will be the completing thread that actually does the work. If the ``Future`` is already complete though, it will be run in our current thread. For example: + +.. code-block:: java + + import static akka.dispatch.Futures.future; + import static akka.japi.Function; + import java.util.concurrent.Callable; + + Future f1 = future(new Callable() { + public String call() { + Thread.sleep(1000); + return "Hello" + "World"; + } + }); + + Future f2 = f1.map(new Function() { + public Integer apply(String s) { + return s.length(); + } + }); + + Integer result = f2.get(); + +The original ``Future`` will take at least 1 second to execute now, which means it is still being processed at the time we call ``map``. The function we provide gets stored within the ``Future`` and later executed automatically by the dispatcher when the result is ready. + +If we do the opposite: + +.. code-block:: java + + import static akka.dispatch.Futures.future; + import static akka.japi.Function; + import java.util.concurrent.Callable; + + Future f1 = future(new Callable() { + public String call() { + return "Hello" + "World"; + } + }); + + Thread.sleep(1000); + + Future f2 = f1.map(new Function() { + public Integer apply(String s) { + return s.length(); + } + }); + + Integer result = f2.get(); + +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 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:: java + + import static akka.dispatch.Futures.future; + import static akka.japi.Function; + import java.util.concurrent.Callable; + + Future f1 = future(new Callable() { + public String call() { + return "Hello" + "World"; + } + }); + + Future f2 = f1.flatMap(new Function>() { + public Future apply(final String s) { + return future( + new Callable() { + public Integer call() { + return s.length(); + } + }); + } + }); + + Integer result = f2.get(); + +Now our second Future is executed concurrently as well. This technique can also be used to combine the results of several Futures into a single calculation, which will be better explained in the following sections. + +Composing Futures +^^^^^^^^^^^^^^^^^ + +It is very often desirable to be able to combine different Futures with eachother, below are some examples on how that can be done in a non-blocking fashion. + +.. code-block:: java + + import static akka.dispatch.Futures.sequence; + import java.util.LinkedList; + + LinkedList> listOfFutureInts = ... //Some source generating a list of Future:s + + // now we have a Future[List[Int]] + Future> futureListOfInts = sequence(listOfFutureInts); + + // Find the sum of the odd numbers + Long totalSum = futureListOfInts.map( + new Function, Long>() { + public Long apply(LinkedList ints) { + long sum = 0; + for(Integer i : ints) + sum += i; + return sum; + } + }).get(); + +To better explain what happened in the example, ``Future.sequence`` is taking the ``LinkedList>`` and turning it into a ``Future>``. We can then use ``map`` to work with the ``LinkedList`` directly, and we aggregate the sum of the ``LinkedList``. + +This is just a sample of what can be done. + +Exceptions +---------- + +Since the result of a ``Future`` is created concurrently to the rest of the program, exceptions must be handled differently. It doesn't matter if an ``UntypedActor`` or the dispatcher is completing the ``Future``, if an ``Exception`` is caught the ``Future`` will contain it instead of a valid result. If a ``Future`` does contain an ``Exception``, calling ``get`` will cause it to be thrown again so it can be handled properly. \ No newline at end of file diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst index 1d09b3e437..60c8698890 100644 --- a/akka-docs/java/index.rst +++ b/akka-docs/java/index.rst @@ -9,6 +9,7 @@ Java API untyped-actors typed-actors actor-registry + futures dataflow stm transactors diff --git a/akka-docs/scala/routing.rst b/akka-docs/scala/routing.rst index 6d22f224c0..6f90f4fcce 100644 --- a/akka-docs/scala/routing.rst +++ b/akka-docs/scala/routing.rst @@ -12,7 +12,7 @@ Dispatcher A Dispatcher is an actor that routes incoming messages to outbound actors. -To use it you can either create a Dispatcher through the ``dispatcherActor()` factory method +To use it you can either create a Dispatcher through the ``dispatcherActor()`` factory method .. code-block:: scala From 76b4592debe3279920096251311581381761bf3e Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 10 May 2011 17:36:33 +0200 Subject: [PATCH 208/233] Docs: Added other-doc, links to documentation for other versions (cherry picked from commit d2b634ee085bd7ff69c27626390ae944f71eeaa9) --- akka-docs/index.rst | 2 ++ akka-docs/project/index.rst | 1 + akka-docs/project/other-doc.rst | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 akka-docs/project/other-doc.rst diff --git a/akka-docs/index.rst b/akka-docs/index.rst index 47416d444b..c39fc47bbd 100644 --- a/akka-docs/index.rst +++ b/akka-docs/index.rst @@ -24,6 +24,8 @@ Links * :ref:`scaladoc` +* :ref:`other-doc` + * `Akka Modules Documentation `_. * :ref:`issue_tracking` diff --git a/akka-docs/project/index.rst b/akka-docs/project/index.rst index c429e74379..b814fd07f1 100644 --- a/akka-docs/project/index.rst +++ b/akka-docs/project/index.rst @@ -7,6 +7,7 @@ Project Information migration-guides release-notes scaladoc + other-doc issue-tracking licenses sponsors diff --git a/akka-docs/project/other-doc.rst b/akka-docs/project/other-doc.rst new file mode 100644 index 0000000000..4f2729c23e --- /dev/null +++ b/akka-docs/project/other-doc.rst @@ -0,0 +1,33 @@ + +.. _other-doc: + +################################## + Documentation for Other Versions +################################## + + +Akka Snapshot +============= + +Automatically published documentation for the latest SNAPSHOT version of Akka can +be found here: http://akka.io/docs/akka/snapshot/ + +Release Versions +================ + +1.1 +--- + +- Akka 1.1 - http://akka.io/docs/akka/1.1/ +- Akka Modules 1.1 - http://akka.io/docs/akka-modules/1.1/ + +1.0 +--- + +- Akka 1.0 - http://akka.io/docs/akka-1.0/Home.html (or in `PDF format `__) + +Older +----- + +- Akka 0.10 - http://www.scalablesolutions.se/akka/docs/akka-0.10/ + From 1b58247b1ad6aac2428fd907193d6f11ee71c582 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 10 May 2011 17:43:09 +0200 Subject: [PATCH 209/233] Docs: fixed missing ref (cherry picked from commit 210261e70e270d37c939fa3e551b598d87164faa) --- akka-docs/intro/getting-started-first-scala.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/akka-docs/intro/getting-started-first-scala.rst b/akka-docs/intro/getting-started-first-scala.rst index c8124ca2bf..f72d5812e9 100644 --- a/akka-docs/intro/getting-started-first-scala.rst +++ b/akka-docs/intro/getting-started-first-scala.rst @@ -146,6 +146,8 @@ Looks like we are all good. Finally let's create a source file ``Pi.scala`` for Some tools require you to set the ``SCALA_HOME`` environment variable to the root of the Scala distribution, however Akka does not require that. +.. _getting-started-first-scala-download-sbt: + Downloading and installing SBT ------------------------------ From f3b6e535cff8f01ba2fa0f67a6bd72434c05167c Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 10 May 2011 18:21:45 +0200 Subject: [PATCH 210/233] Adding future docs for fold and reduce for both Java and Scala API --- akka-docs/java/futures.rst | 39 +++++++++++++++++++++++++++++++++++++ akka-docs/scala/futures.rst | 24 +++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/akka-docs/java/futures.rst b/akka-docs/java/futures.rst index 43e6de2586..8aa8af25f5 100644 --- a/akka-docs/java/futures.rst +++ b/akka-docs/java/futures.rst @@ -185,6 +185,45 @@ It is very often desirable to be able to combine different Futures with eachothe To better explain what happened in the example, ``Future.sequence`` is taking the ``LinkedList>`` and turning it into a ``Future>``. We can then use ``map`` to work with the ``LinkedList`` directly, and we aggregate the sum of the ``LinkedList``. +Then there's a method that's called ``fold`` that takes a start-value, a sequence of ``Future``:s and a function from the type of the start-value, a timeout, and the type of the futures and returns something with the same type as the start-value, and then applies the function to all elements in the sequence of futures, non-blockingly, the execution will run on the Thread of the last completing Future in the sequence. + +.. code-block:: java + + import static akka.dispatch.Futures.fold; + import java.util.Iterable; + + Iterable> futures = ... //A sequence of Futures, in this case Strings + + Future result = fold("", 15000, futures, new Function2(){ //Start value is the empty string, timeout is 15 seconds + public String apply(String r, String t) { + return r + t; //Just concatenate + } + }); + + result.get(); // Will produce a String that says "testtesttesttest"(... and so on). + +That's all it takes! + + +If the sequence passed to ``fold`` is empty, it will return the start-value, in the case above, that will be 0. In some cases you don't have a start-value and you're able to use the value of the first completing Future in the sequence as the start-value, you can use ``reduce``, it works like this: + +.. code-block:: java + + import static akka.dispatch.Futures.reduce; + import java.util.Iterable; + + Iterable> futures = ... //A sequence of Futures, in this case Strings + + Future result = reduce(futures, 15000, new Function2(){ //Timeout is 15 seconds + public String apply(String r, String t) { + return r + t; //Just concatenate + } + }); + + result.get(); // Will produce a String that says "testtesttesttest"(... and so on). + +Same as with ``fold``, the execution will be done by the Thread that completes the last of the Futures, you can also parallize it by chunking your futures into sub-sequences and reduce them, and then reduce the reduced results again. + This is just a sample of what can be done. Exceptions diff --git a/akka-docs/scala/futures.rst b/akka-docs/scala/futures.rst index 7ac7f535bd..f64c864564 100644 --- a/akka-docs/scala/futures.rst +++ b/akka-docs/scala/futures.rst @@ -197,6 +197,30 @@ This is the same result as this example: But it may be faster to use ``traverse`` as it doesn't have to create an intermediate ``List[Future[Int]]``. + +Then there's a method that's called ``fold`` that takes a start-value, a sequence of ``Future``:s and a function from the type of the start-value and the type of the futures and returns something with the same type as the start-value, and then applies the function to all elements in the sequence of futures, non-blockingly, the execution will run on the Thread of the last completing Future in the sequence. + +.. code-block:: scala + + val futures = for(i <- 1 to 1000) yield Future(i * 2) // Create a sequence of Futures + + val futureSum = Futures.fold(0)(futures)(_ + _) + +That's all it takes! + + +If the sequence passed to ``fold`` is empty, it will return the start-value, in the case above, that will be 0. In some cases you don't have a start-value and you're able to use the value of the first completing Future in the sequence as the start-value, you can use ``reduce``, it works like this: + +.. code-block:: scala + + val futures = for(i <- 1 to 1000) yield Future(i * 2) // Create a sequence of Futures + + val futureSum = Futures.reduce(futures)(_ + _) + +Same as with ``fold``, the execution will be done by the Thread that completes the last of the Futures, you can also parallize it by chunking your futures into sub-sequences and reduce them, and then reduce the reduced results again. + + + This is just a sample of what can be done, but to use more advanced techniques it is easier to take advantage of Scalaz, which Akka has support for in its akka-scalaz module. Scalaz From 0896807cabbfd9a551a6a651ee151037a4e08a6e Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 10 May 2011 18:56:22 +0200 Subject: [PATCH 211/233] Adding docs for Futures.traverse and fixing imports --- akka-docs/java/futures.rst | 39 +++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/akka-docs/java/futures.rst b/akka-docs/java/futures.rst index 8aa8af25f5..8e66257443 100644 --- a/akka-docs/java/futures.rst +++ b/akka-docs/java/futures.rst @@ -33,6 +33,7 @@ A common use case within Akka is to have some computation performed concurrently .. code-block:: java + import akka.dispatch.Future; import static akka.dispatch.Futures.future; import java.util.concurrent.Callable; @@ -56,7 +57,8 @@ Future is a Monad The first method for working with ``Future`` functionally is ``map``. This method takes a ``Function`` which performs some operation on the result of the ``Future``, and returning a new result. The return value of the ``map`` method is another ``Future`` that will contain the new result: .. code-block:: java - + + import akka.dispatch.Future; import static akka.dispatch.Futures.future; import static akka.japi.Function; import java.util.concurrent.Callable; @@ -81,6 +83,7 @@ Something to note when using these methods: if the ``Future`` is still being pro .. code-block:: java + import akka.dispatch.Future; import static akka.dispatch.Futures.future; import static akka.japi.Function; import java.util.concurrent.Callable; @@ -106,6 +109,7 @@ If we do the opposite: .. code-block:: java + import akka.dispatch.Future; import static akka.dispatch.Futures.future; import static akka.japi.Function; import java.util.concurrent.Callable; @@ -132,6 +136,7 @@ Normally this works quite well as it means there is very little overhead to runn .. code-block:: java + import akka.dispatch.Future; import static akka.dispatch.Futures.future; import static akka.japi.Function; import java.util.concurrent.Callable; @@ -164,7 +169,9 @@ It is very often desirable to be able to combine different Futures with eachothe .. code-block:: java + import akka.dispatch.Future; import static akka.dispatch.Futures.sequence; + import akka.japi.Function; import java.util.LinkedList; LinkedList> listOfFutureInts = ... //Some source generating a list of Future:s @@ -185,12 +192,40 @@ It is very often desirable to be able to combine different Futures with eachothe To better explain what happened in the example, ``Future.sequence`` is taking the ``LinkedList>`` and turning it into a ``Future>``. We can then use ``map`` to work with the ``LinkedList`` directly, and we aggregate the sum of the ``LinkedList``. +The ``traverse`` method is similar to ``sequence``, but it takes a sequence of ``A``s and applies a function from ``A`` to ``Future`` and returns a ``Future>``, enabling parallel ``map`` over the sequence, if you use ``Futures.future`` to create the ``Future``. + +.. code-block:: java + + import akka.dispatch.Future; + import static akka.dispatch.Futures.traverse; + import static akka.dispatch.Futures.future; + import java.util.LinkedList; + import akka.japi.Function; + + LinkedList listStrings = ... //Just a list of Strings + + Future> result = traverse(listStrings, new Function>(){ + public Future apply(final String r) { + return future(new Callable() { + public String call() { + return r.toUpperCase(); + } + }); + } + }); + + result.get(); //Returns a the list of strings as upper case + +It's as simple as that! + Then there's a method that's called ``fold`` that takes a start-value, a sequence of ``Future``:s and a function from the type of the start-value, a timeout, and the type of the futures and returns something with the same type as the start-value, and then applies the function to all elements in the sequence of futures, non-blockingly, the execution will run on the Thread of the last completing Future in the sequence. .. code-block:: java + import akka.dispatch.Future; import static akka.dispatch.Futures.fold; import java.util.Iterable; + import akka.japi.Function2; Iterable> futures = ... //A sequence of Futures, in this case Strings @@ -209,8 +244,10 @@ If the sequence passed to ``fold`` is empty, it will return the start-value, in .. code-block:: java + import akka.dispatch.Future; import static akka.dispatch.Futures.reduce; import java.util.Iterable; + import akka.japi.Function2; Iterable> futures = ... //A sequence of Futures, in this case Strings From c0efbc690fa927347344b7c03116fa6443c6ab6e Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 10 May 2011 19:19:13 +0200 Subject: [PATCH 212/233] Adding some tests to make sure that the Future Java API doesn`t change breakingly --- .../java/akka/dispatch/JavaFutureTests.java | 71 ++++++++++++++++++- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/akka-actor-tests/src/test/java/akka/dispatch/JavaFutureTests.java b/akka-actor-tests/src/test/java/akka/dispatch/JavaFutureTests.java index cdec7f5631..157d6ca6d6 100644 --- a/akka-actor-tests/src/test/java/akka/dispatch/JavaFutureTests.java +++ b/akka-actor-tests/src/test/java/akka/dispatch/JavaFutureTests.java @@ -5,12 +5,11 @@ import static org.junit.Assert.*; import java.util.concurrent.Callable; import java.util.LinkedList; import akka.japi.Function; +import akka.japi.Function2; import akka.japi.Procedure; import scala.Some; import scala.Right; -import static akka.dispatch.Futures.future; -import static akka.dispatch.Futures.traverse; -import static akka.dispatch.Futures.sequence; +import static akka.dispatch.Futures.*; public class JavaFutureTests { @@ -49,4 +48,70 @@ public class JavaFutureTests { assertEquals(futureList.get(), listExpected); } + // TODO: Improve this test, perhaps with an Actor + @Test public void foldForJavaApiMustWork() { + LinkedList> listFutures = new LinkedList>(); + StringBuilder expected = new StringBuilder(); + + for (int i = 0; i < 10; i++) { + expected.append("test"); + listFutures.add(future(new Callable() { + public String call() { + return "test"; + } + })); + } + + Future result = fold("", 15000,listFutures, new Function2(){ + public String apply(String r, String t) { + return r + t; + } + }); + + assertEquals(result.get(), expected.toString()); + } + + @Test public void reduceForJavaApiMustWork() { + LinkedList> listFutures = new LinkedList>(); + StringBuilder expected = new StringBuilder(); + + for (int i = 0; i < 10; i++) { + expected.append("test"); + listFutures.add(future(new Callable() { + public String call() { + return "test"; + } + })); + } + + Future result = reduce(listFutures, 15000, new Function2(){ + public String apply(String r, String t) { + return r + t; + } + }); + + assertEquals(result.get(), expected.toString()); + } + + @Test public void traverseForJavaApiMustWork() { + LinkedList listStrings = new LinkedList(); + LinkedList expectedStrings = new LinkedList(); + + for (int i = 0; i < 10; i++) { + expectedStrings.add("TEST"); + listStrings.add("test"); + } + + Future> result = traverse(listStrings, new Function>(){ + public Future apply(final String r) { + return future(new Callable() { + public String call() { + return r.toUpperCase(); + } + }); + } + }); + + assertEquals(result.get(), expectedStrings); + } } From bc38831febd55e5c728355f63feb6f0e7066713b Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 10 May 2011 21:26:59 +0200 Subject: [PATCH 213/233] Docs: Added links to PDF, removed links to old versions (cherry picked from commit f0b3b8bbb39353ac582fb569624aeba414708d3a) --- akka-docs/project/other-doc.rst | 15 ++++++++------- akka-docs/project/scaladoc.rst | 5 ----- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/akka-docs/project/other-doc.rst b/akka-docs/project/other-doc.rst index 4f2729c23e..f24d4e0e53 100644 --- a/akka-docs/project/other-doc.rst +++ b/akka-docs/project/other-doc.rst @@ -10,7 +10,12 @@ Akka Snapshot ============= Automatically published documentation for the latest SNAPSHOT version of Akka can -be found here: http://akka.io/docs/akka/snapshot/ +be found here: + +- Akka - http://akka.io/docs/akka/snapshot/ (or in `PDF format `__) +- Akka Modules - http://akka.io/docs/akka-modules/snapshot/ (or in `PDF format `__) + + Release Versions ================ @@ -18,16 +23,12 @@ Release Versions 1.1 --- -- Akka 1.1 - http://akka.io/docs/akka/1.1/ -- Akka Modules 1.1 - http://akka.io/docs/akka-modules/1.1/ +- Akka 1.1 - http://akka.io/docs/akka/1.1/ (or in `PDF format (or in `PDF format `__) +- Akka Modules 1.1 - http://akka.io/docs/akka-modules/1.1/ (or in `PDF format `__) 1.0 --- - Akka 1.0 - http://akka.io/docs/akka-1.0/Home.html (or in `PDF format `__) -Older ------ - -- Akka 0.10 - http://www.scalablesolutions.se/akka/docs/akka-0.10/ diff --git a/akka-docs/project/scaladoc.rst b/akka-docs/project/scaladoc.rst index deda2419b2..01d95ecd18 100644 --- a/akka-docs/project/scaladoc.rst +++ b/akka-docs/project/scaladoc.rst @@ -31,8 +31,3 @@ Release Versions - Akka 1.0 - http://akka.io/api/1.0/ -Older ------ - -- Akka 0.10 - http://www.scalablesolutions.se/akka/api/0.10/ -- Akka 0.9 - http://www.scalablesolutions.se/akka/api/0.9/ From 650f9be399c9111da7bb0e47d6f8ca7fc917a435 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 10 May 2011 21:27:38 +0200 Subject: [PATCH 214/233] Added PDF link at top banner (cherry picked from commit 655f9051fcb144c29ebf0e993047dc3010547948) --- akka-docs/_sphinx/themes/akka/layout.html | 1 + akka-docs/_sphinx/themes/akka/static/akka.css_t | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/akka-docs/_sphinx/themes/akka/layout.html b/akka-docs/_sphinx/themes/akka/layout.html index 15d7a9b951..d64cad8d9f 100644 --- a/akka-docs/_sphinx/themes/akka/layout.html +++ b/akka-docs/_sphinx/themes/akka/layout.html @@ -43,6 +43,7 @@

    {{ shorttitle|e }}

    Version {{ version|e }}

    +

    PDF

    {%- endif %} {%- endblock %} diff --git a/akka-docs/_sphinx/themes/akka/static/akka.css_t b/akka-docs/_sphinx/themes/akka/static/akka.css_t index 146ba31623..5020439d8f 100644 --- a/akka-docs/_sphinx/themes/akka/static/akka.css_t +++ b/akka-docs/_sphinx/themes/akka/static/akka.css_t @@ -126,6 +126,12 @@ div.header img.rightlogo { float: right; } +div.header h2.rightheading { + position: relative; + top:-45px; + float: right; +} + div.title { font-size: 1.3em; From 5350d4e8615c31aa3982fd44efcb630ac33f083d Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Wed, 11 May 2011 09:00:46 +1200 Subject: [PATCH 215/233] Adjust docs html header --- akka-docs/_sphinx/themes/akka/layout.html | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/akka-docs/_sphinx/themes/akka/layout.html b/akka-docs/_sphinx/themes/akka/layout.html index d64cad8d9f..929dec3d9c 100644 --- a/akka-docs/_sphinx/themes/akka/layout.html +++ b/akka-docs/_sphinx/themes/akka/layout.html @@ -32,19 +32,12 @@ {% block content %}
    {%- block akkaheader %} - {%- if theme_full_logo != "false" %} - - - - {%- else %} {%- if logo -%} - + {%- endif -%} -

    - {{ shorttitle|e }}

    -

    Version {{ version|e }}

    -

    PDF

    - {%- endif %} +

    {{ shorttitle|e }}

    +

    Version {{ version|e }}

    +

    PDF

    {%- endblock %}
    From 54b31d0b1cac5a77d72490ba86adb133f74848fc Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Wed, 11 May 2011 09:07:57 +1200 Subject: [PATCH 216/233] Update pdf links --- akka-docs/project/other-doc.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/akka-docs/project/other-doc.rst b/akka-docs/project/other-doc.rst index f24d4e0e53..7abfc8c8df 100644 --- a/akka-docs/project/other-doc.rst +++ b/akka-docs/project/other-doc.rst @@ -12,8 +12,8 @@ Akka Snapshot Automatically published documentation for the latest SNAPSHOT version of Akka can be found here: -- Akka - http://akka.io/docs/akka/snapshot/ (or in `PDF format `__) -- Akka Modules - http://akka.io/docs/akka-modules/snapshot/ (or in `PDF format `__) +- Akka - http://akka.io/docs/akka/snapshot/ (or in `PDF format `__) +- Akka Modules - http://akka.io/docs/akka-modules/snapshot/ (or in `PDF format `__) @@ -23,8 +23,8 @@ Release Versions 1.1 --- -- Akka 1.1 - http://akka.io/docs/akka/1.1/ (or in `PDF format (or in `PDF format `__) -- Akka Modules 1.1 - http://akka.io/docs/akka-modules/1.1/ (or in `PDF format `__) +- Akka 1.1 - http://akka.io/docs/akka/1.1/ (or in `PDF format `__) +- Akka Modules 1.1 - http://akka.io/docs/akka-modules/1.1/ (or in `PDF format `__) 1.0 --- From c70250b952ee874d7adf8138b5c043ec65348f04 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Wed, 11 May 2011 09:58:06 +1200 Subject: [PATCH 217/233] Some updates to docs --- akka-docs/index.rst | 2 +- akka-docs/intro/deployment-scenarios.rst | 67 ++++++++++++++++++------ akka-docs/intro/getting-started.rst | 2 +- akka-docs/intro/what-is-akka.rst | 61 +++++++++++++++------ akka-docs/java/index.rst | 2 +- akka-docs/scala/index.rst | 2 +- 6 files changed, 100 insertions(+), 36 deletions(-) diff --git a/akka-docs/index.rst b/akka-docs/index.rst index c39fc47bbd..9919049e77 100644 --- a/akka-docs/index.rst +++ b/akka-docs/index.rst @@ -26,7 +26,7 @@ Links * :ref:`other-doc` -* `Akka Modules Documentation `_. +* `Akka Modules Documentation `_ * :ref:`issue_tracking` diff --git a/akka-docs/intro/deployment-scenarios.rst b/akka-docs/intro/deployment-scenarios.rst index 7694a2d337..a8343d5b33 100644 --- a/akka-docs/intro/deployment-scenarios.rst +++ b/akka-docs/intro/deployment-scenarios.rst @@ -1,37 +1,74 @@ + .. _deployment-scenarios: -Use-case and Deployment Scenarios -================================= +################################### + Use-case and Deployment Scenarios +################################### -**How and in which use-case and deployment scenarios can I use Akka?** +How can I use and deploy Akka? +============================== Akka can be used in two different ways: -* As a library: used as a regular JAR on the classpath and/or in a web app, to be put into ‘WEB-INF/lib’ -* As a microkernel: stand-alone microkernel, embedding a servlet container along with many other services. +- As a library: used as a regular JAR on the classpath and/or in a web app, to + be put into ``WEB-INF/lib`` + +- As a microkernel: stand-alone microkernel, embedding a servlet container along + with many other services + Using Akka as library --------------------- -This is most likely what you want if you are building Web applications. -There are several ways you can use Akka in Library mode by adding more and more modules to the stack. +This is most likely what you want if you are building Web applications. There +are several ways you can use Akka in Library mode by adding more and more +modules to the stack. Actors as services ^^^^^^^^^^^^^^^^^^ -The simplest way you can use Akka is to use the actors as services in your Web application. All that’s needed to do that is to put the Akka charts as well as its dependency jars into ‘WEB-INF/lib’. You also need to put the ‘akka.conf’ config file in the ‘$AKKA_HOME/config’ directory. -Now you can create your Actors as regular services referenced from your Web application. You should also be able to use the Remoting service, e.g. be able to make certain Actors remote on other hosts. Please note that remoting service does not speak HTTP over port 80, but a custom protocol over the port is specified in ‘akka.conf’. +The simplest way you can use Akka is to use the actors as services in your Web +application. All that’s needed to do that is to put the Akka charts as well as +its dependency jars into ``WEB-INF/lib``. You also need to put the ``akka.conf`` +config file in the ``$AKKA_HOME/config`` directory. Now you can create your +Actors as regular services referenced from your Web application. You should also +be able to use the Remoting service, e.g. be able to make certain Actors remote +on other hosts. Please note that remoting service does not speak HTTP over port +80, but a custom protocol over the port is specified in ``akka.conf``. + Using Akka as a stand alone microkernel --------------------------------------- -Akka can also be run as a stand-alone microkernel. It implements a full enterprise stack: +Akka can also be run as a stand-alone microkernel. It implements a full +enterprise stack. See the :ref:`add-on-modules` for more information. -Use BivySack for packaging your application -------------------------------------------- +Using the Akka sbt plugin to package your application +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -"BivySack" For Akka - SBT plugin which creates a full akka microkernel deployment for your project. +The Akka sbt plugin can create a full Akka microkernel deployment for your sbt +project. -Quick and dirty SBT Plugin for creating Akka Microkernel deployments of your SBT Project. This creates a proper "akka deploy" setup with all of your dependencies and configuration files loaded, with a bootable version of your project that you can run cleanly. +To use the plugin, first add a plugin definition to your SBT project by creating +``project/plugins/Plugins.scala`` with:: -Read more about it here ``_. + 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" + } + +Then mix the ``AkkaKernelProject`` trait into your project definition. For +example:: + + class MyProject(info: ProjectInfo) extends DefaultProject(info) with AkkaKernelProject + +This will automatically add all the Akka dependencies needed for a microkernel +deployment (download them with ``sbt update``). + +Place your config files in ``src/main/config``. + +To build a microkernel deployment use the ``dist`` task:: + + sbt dist diff --git a/akka-docs/intro/getting-started.rst b/akka-docs/intro/getting-started.rst index ee8e1ee2aa..46219aec7d 100644 --- a/akka-docs/intro/getting-started.rst +++ b/akka-docs/intro/getting-started.rst @@ -42,7 +42,7 @@ Modules Akka is split up into two different parts: -* Akka - The core modules. Reflects all the sections under :ref:`scala_api` and :ref:`java_api`. +* Akka - The core modules. Reflects all the sections under :ref:`scala-api` and :ref:`java-api`. * Akka Modules - The microkernel and add-on modules, described in :ref:`add-on-modules`. Akka is very modular and has many JARs for containing different features. The core distribution has seven modules: diff --git a/akka-docs/intro/what-is-akka.rst b/akka-docs/intro/what-is-akka.rst index 9aeda53cd8..dbfcf63916 100644 --- a/akka-docs/intro/what-is-akka.rst +++ b/akka-docs/intro/what-is-akka.rst @@ -1,18 +1,31 @@ -What is Akka? -============= -**Akka** --------- +.. _what-is-akka: + +############### + What is Akka? +############### + **Simpler Scalability, Fault-Tolerance, Concurrency & Remoting through Actors** -We believe that writing correct concurrent, fault-tolerant and scalable applications is too hard. Most of the time it's because we are using the wrong tools and the wrong level of abstraction. Akka is here to change that. Using the Actor Model together with Software Transactional Memory we raise the abstraction level and provide a better platform to build correct concurrent and scalable applications. For fault-tolerance we adopt the Let it crash/Embrace failure model which have been used with great success in the telecom industry to build applications that self-heals, systems that never stop. Actors also provides the abstraction for transparent distribution and the basis for truly scalable and fault-tolerant applications. Akka is Open Source and available under the Apache 2 License. +We believe that writing correct concurrent, fault-tolerant and scalable +applications is too hard. Most of the time it's because we are using the wrong +tools and the wrong level of abstraction. Akka is here to change that. Using the +Actor Model together with Software Transactional Memory we raise the abstraction +level and provide a better platform to build correct concurrent and scalable +applications. For fault-tolerance we adopt the Let it crash/Embrace failure +model which have been used with great success in the telecom industry to build +applications that self-heals, systems that never stop. Actors also provides the +abstraction for transparent distribution and the basis for truly scalable and +fault-tolerant applications. Akka is Open Source and available under the Apache +2 License. -Download from ``_ +Download from http://akka.io/downloads/ -Akka implements a unique hybrid of: ------------------------------------ + +Akka implements a unique hybrid +=============================== - :ref:`untyped-actors-java`, which gives you: @@ -20,16 +33,30 @@ Akka implements a unique hybrid of: - Asynchronous, non-blocking and highly performant event-driven programming model. - Very lightweight event-driven processes (create ~6.5 million actors on 4GB RAM). -- :ref:`fault-tolerance-java` through supervisor hierarchies with "let-it-crash" semantics. Excellent for writing highly fault-tolerant systems that never stop, systems that self-heal. +- :ref:`fault-tolerance-java` through supervisor hierarchies with "let-it-crash" + semantics. Excellent for writing highly fault-tolerant systems that never + stop, systems that self-heal. + - :ref:`stm-java` (STM). (Distributed transactions coming soon). -- :ref:`transactors-java`: combine actors and STM into transactional actors. Allows you to compose atomic message flows with automatic retry and rollback. -- :ref:`remote-actors-java`: highly performant distributed actors with remote supervision and error management. -- Java and Scala API. -Akka can be used in two different ways: ---------------------------------------- +- :ref:`transactors-java`: combine actors and STM into transactional + actors. Allows you to compose atomic message flows with automatic retry and + rollback. -- As a library: used by a web app, to be put into ‘WEB-INF/lib’ or as a regular JAR on your classpath. -- As a microkernel: stand-alone kernel, embedding a servlet container and all the other modules. +- :ref:`remote-actors-java`: highly performant distributed actors with remote + supervision and error management. -See the ref:`deployment-scenarios` for details. +- :ref:`java-api` and :ref:`scala-api` + + +Akka can be used in two different ways +====================================== + +- As a library: used by a web app, to be put into ‘WEB-INF/lib’ or as a regular + JAR on your classpath. + +- As a microkernel: stand-alone kernel, embedding a servlet container and all + the other modules. + + +See the :ref:`deployment-scenarios` for details. diff --git a/akka-docs/java/index.rst b/akka-docs/java/index.rst index 60c8698890..d9e900fd22 100644 --- a/akka-docs/java/index.rst +++ b/akka-docs/java/index.rst @@ -1,4 +1,4 @@ -.. _java_api: +.. _java-api: Java API ========= diff --git a/akka-docs/scala/index.rst b/akka-docs/scala/index.rst index c3916ac2a6..7998ae2765 100644 --- a/akka-docs/scala/index.rst +++ b/akka-docs/scala/index.rst @@ -1,4 +1,4 @@ -.. _scala_api: +.. _scala-api: Scala API ========= From c2e3d94895b4e454c9f6ea2e8b42c76807ad0a6a Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Wed, 11 May 2011 14:41:58 +1200 Subject: [PATCH 218/233] Fix nightly build by not publishing tutorials --- project/build/AkkaProject.scala | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 497c1cac1d..a5a8364c10 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -397,9 +397,18 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec // Tutorials // ------------------------------------------------------------------------------------------------------------------- - class AkkaTutorialFirstProject(info: ProjectInfo) extends AkkaDefaultProject(info) + class AkkaTutorialFirstProject(info: ProjectInfo) extends AkkaTutorialProject(info) - class AkkaTutorialSecondProject(info: ProjectInfo) extends AkkaDefaultProject(info) + class AkkaTutorialSecondProject(info: ProjectInfo) extends AkkaTutorialProject(info) + + class AkkaTutorialProject(info: ProjectInfo) extends AkkaDefaultProject(info) { + def doNothing = task { None } + override def publishLocalAction = doNothing + override def deliverLocalAction = doNothing + override def publishAction = doNothing + override def deliverAction = doNothing + override lazy val publishRelease = doNothing + } class AkkaTutorialsParentProject(info: ProjectInfo) extends ParentProject(info) { override def disableCrossPaths = true @@ -410,10 +419,12 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec lazy val akka_tutorial_second = project("akka-tutorial-second", "akka-tutorial-second", new AkkaTutorialSecondProject(_), akka_actor) - lazy val publishRelease = { - val releaseConfiguration = new DefaultPublishConfiguration(localReleaseRepository, "release", false) - publishTask(publishIvyModule, releaseConfiguration) dependsOn (deliver, publishLocal, makePom) - } + def doNothing = task { None } + override def publishLocalAction = doNothing + override def deliverLocalAction = doNothing + override def publishAction = doNothing + override def deliverAction = doNothing + lazy val publishRelease = doNothing } // ------------------------------------------------------------------------------------------------------------------- From ae9a13d6b5401b666fdfe4e0684ac53b2be1a0d1 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Wed, 11 May 2011 15:10:41 +1200 Subject: [PATCH 219/233] Update docs version to 1.2-SNAPSHOT --- akka-docs/_sphinx/themes/akka/layout.html | 2 +- akka-docs/additional/add-on-modules.rst | 2 +- akka-docs/conf.py | 4 ++-- akka-docs/dev/building-akka.rst | 4 ++-- akka-docs/index.rst | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/akka-docs/_sphinx/themes/akka/layout.html b/akka-docs/_sphinx/themes/akka/layout.html index 929dec3d9c..0bd735c446 100644 --- a/akka-docs/_sphinx/themes/akka/layout.html +++ b/akka-docs/_sphinx/themes/akka/layout.html @@ -37,7 +37,7 @@ {%- endif -%}

    {{ shorttitle|e }}

    Version {{ version|e }}

    -

    PDF

    +

    PDF

    {%- endblock %}
    diff --git a/akka-docs/additional/add-on-modules.rst b/akka-docs/additional/add-on-modules.rst index 981bc1ac6c..bab2f1b174 100644 --- a/akka-docs/additional/add-on-modules.rst +++ b/akka-docs/additional/add-on-modules.rst @@ -13,4 +13,4 @@ Akka Modules consist of add-on modules outside the core of Akka: - ``akka-spring-1.1.jar`` -- Spring framework integration - ``akka-osgi-dependencies-bundle-1.1.jar`` -- OSGi support -Documentation for Akka Modules is located `here `_. +Documentation for Akka Modules is located `here `_. diff --git a/akka-docs/conf.py b/akka-docs/conf.py index f6a0c67069..2065d4aefb 100644 --- a/akka-docs/conf.py +++ b/akka-docs/conf.py @@ -17,8 +17,8 @@ exclude_patterns = ['_build', 'pending', 'disabled'] project = u'Akka' copyright = u'2009-2011, Scalable Solutions AB' -version = '1.1' -release = '1.1' +version = '1.2-SNAPSHOT' +release = '1.2-SNAPSHOT' pygments_style = 'simple' highlight_language = 'scala' diff --git a/akka-docs/dev/building-akka.rst b/akka-docs/dev/building-akka.rst index add552f93d..2622051447 100644 --- a/akka-docs/dev/building-akka.rst +++ b/akka-docs/dev/building-akka.rst @@ -124,8 +124,8 @@ convenient. 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 - [info] using AkkaParentProject with sbt 0.7.6.RC0 and Scala 2.7.7 + [info] Building project akka 1.2-SNAPSHOT against Scala 2.9.0 + [info] using AkkaParentProject with sbt 0.7.6 and Scala 2.7.7 > update [info] [info] == akka-actor / update == diff --git a/akka-docs/index.rst b/akka-docs/index.rst index 9919049e77..738b8e636f 100644 --- a/akka-docs/index.rst +++ b/akka-docs/index.rst @@ -26,7 +26,7 @@ Links * :ref:`other-doc` -* `Akka Modules Documentation `_ +* `Akka Modules Documentation `_ * :ref:`issue_tracking` From 86414fe2c1748bc9a601003c1a7580244dd6ec99 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Wed, 11 May 2011 18:03:04 +1200 Subject: [PATCH 220/233] Exclude all dist projects from publishing --- project/build/DistProject.scala | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/project/build/DistProject.scala b/project/build/DistProject.scala index 5286d582d0..de1cb0cdf9 100644 --- a/project/build/DistProject.scala +++ b/project/build/DistProject.scala @@ -10,6 +10,18 @@ trait DistBaseProject extends DefaultProject { def distSrcPath: Path def distDocPath: Path def dist: Task + + override def disableCrossPaths = true + + def doNothing = task { None } + override def compileAction = doNothing + override def testCompileAction = doNothing + override def testAction = doNothing + override def packageAction = doNothing + override def publishLocalAction = doNothing + override def deliverLocalAction = doNothing + override def publishAction = doNothing + override def deliverAction = doNothing } trait DistProject extends DistBaseProject { @@ -150,18 +162,6 @@ trait DistProject extends DistBaseProject { val success = target.asFile.setExecutable(executable, false) if (success) None else Some("Couldn't set permissions of " + target) } - - override def disableCrossPaths = true - - def doNothing = task { None } - override def compileAction = doNothing - override def testCompileAction = doNothing - override def testAction = doNothing - override def packageAction = doNothing - override def publishLocalAction = doNothing - override def deliverLocalAction = doNothing - override def publishAction = doNothing - override def deliverAction = doNothing } trait DistDocProject extends DistProject { From 3184a7038b97dc17aef6d6810cd596321317c481 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 11 May 2011 12:11:56 +0200 Subject: [PATCH 221/233] Updating Netty to 3.2.4, closing ticket #838 --- 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 a5a8364c10..3fc4fa2a43 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -113,7 +113,7 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec lazy val multiverse = "org.multiverse" % "multiverse-alpha" % MULTIVERSE_VERSION % "compile" //ApacheV2 lazy val multiverse_test = "org.multiverse" % "multiverse-alpha" % MULTIVERSE_VERSION % "test" //ApacheV2 - lazy val netty = "org.jboss.netty" % "netty" % "3.2.3.Final" % "compile" //ApacheV2 + lazy val netty = "org.jboss.netty" % "netty" % "3.2.4.Final" % "compile" //ApacheV2 lazy val osgi_core = "org.osgi" % "org.osgi.core" % "4.2.0" //ApacheV2 From 3ab4cab6606144b1ad99524f65c5163d58d0af0d Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 11 May 2011 12:28:19 +0200 Subject: [PATCH 222/233] Updating Jackson to 1.8.0 - closing ticket #839 --- 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 3fc4fa2a43..b7791fb6e6 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -71,7 +71,7 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec // Versions // ------------------------------------------------------------------------------------------------------------------- - lazy val JACKSON_VERSION = "1.7.1" + lazy val JACKSON_VERSION = "1.8.0" lazy val JERSEY_VERSION = "1.3" lazy val MULTIVERSE_VERSION = "0.6.2" lazy val SCALATEST_VERSION = "1.4.RC3" From 5b54de7f4814519c0668e4fa4d6d0c4584a69ca0 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 11 May 2011 13:29:29 +0200 Subject: [PATCH 223/233] Changing signature of traverse and sequence to return java.lang.Iterable, updated docs as well, closing #847 --- .../java/akka/dispatch/JavaFutureTests.java | 5 +++-- .../src/main/scala/akka/dispatch/Future.scala | 16 +++++++------- akka-docs/java/futures.rst | 22 +++++++++---------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/akka-actor-tests/src/test/java/akka/dispatch/JavaFutureTests.java b/akka-actor-tests/src/test/java/akka/dispatch/JavaFutureTests.java index 157d6ca6d6..e908335666 100644 --- a/akka-actor-tests/src/test/java/akka/dispatch/JavaFutureTests.java +++ b/akka-actor-tests/src/test/java/akka/dispatch/JavaFutureTests.java @@ -4,6 +4,7 @@ import org.junit.Test; import static org.junit.Assert.*; import java.util.concurrent.Callable; import java.util.LinkedList; +import java.lang.Iterable; import akka.japi.Function; import akka.japi.Function2; import akka.japi.Procedure; @@ -43,7 +44,7 @@ public class JavaFutureTests { })); } - Future> futureList = sequence(listFutures); + Future> futureList = sequence(listFutures); assertEquals(futureList.get(), listExpected); } @@ -102,7 +103,7 @@ public class JavaFutureTests { listStrings.add("test"); } - Future> result = traverse(listStrings, new Function>(){ + Future> result = traverse(listStrings, new Function>(){ public Future apply(final String r) { return future(new Callable() { public String call() { diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 4ec877b8bb..7458ed9f51 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -159,10 +159,10 @@ object Futures { /** * Java API. - * Simple version of Futures.traverse. Transforms a java.lang.Iterable[Future[A]] into a Future[java.util.LinkedList[A]]. + * Simple version of Futures.traverse. Transforms a java.lang.Iterable[Future[A]] into a Future[java.lang.Iterable[A]]. * Useful for reducing many Futures into a single Future. */ - def sequence[A](in: JIterable[Future[A]], timeout: Long): Future[JLinkedList[A]] = + def sequence[A](in: JIterable[Future[A]], timeout: Long): Future[JIterable[A]] = scala.collection.JavaConversions.iterableAsScalaIterable(in).foldLeft(Future(new JLinkedList[A]()))((fr, fa) => for (r <- fr; a <- fa) yield { r add a @@ -171,18 +171,18 @@ object Futures { /** * Java API. - * Simple version of Futures.traverse. Transforms a java.lang.Iterable[Future[A]] into a Future[java.util.LinkedList[A]]. + * Simple version of Futures.traverse. Transforms a java.lang.Iterable[Future[A]] into a Future[java.lang.Iterable[A]]. * Useful for reducing many Futures into a single Future. */ - def sequence[A](in: JIterable[Future[A]]): Future[JLinkedList[A]] = sequence(in, Actor.TIMEOUT) + def sequence[A](in: JIterable[Future[A]]): Future[JIterable[A]] = sequence(in, Actor.TIMEOUT) /** * Java API. - * Transforms a java.lang.Iterable[A] into a Future[java.util.LinkedList[B]] using the provided Function A => Future[B]. + * Transforms a java.lang.Iterable[A] into a Future[java.lang.Iterable[B]] using the provided Function A => Future[B]. * This is useful for performing a parallel map. For example, to apply a function to all items of a list * in parallel. */ - def traverse[A, B](in: JIterable[A], timeout: Long, fn: JFunc[A,Future[B]]): Future[JLinkedList[B]] = + def traverse[A, B](in: JIterable[A], timeout: Long, fn: JFunc[A,Future[B]]): Future[JIterable[B]] = scala.collection.JavaConversions.iterableAsScalaIterable(in).foldLeft(Future(new JLinkedList[B]())){(fr, a) => val fb = fn(a) for (r <- fr; b <- fb) yield { @@ -193,11 +193,11 @@ object Futures { /** * Java API. - * Transforms a java.lang.Iterable[A] into a Future[java.util.LinkedList[B]] using the provided Function A => Future[B]. + * Transforms a java.lang.Iterable[A] into a Future[java.lang.Iterable[B]] using the provided Function A => Future[B]. * This is useful for performing a parallel map. For example, to apply a function to all items of a list * in parallel. */ - def traverse[A, B](in: JIterable[A], fn: JFunc[A,Future[B]]): Future[JLinkedList[B]] = traverse(in, Actor.TIMEOUT, fn) + def traverse[A, B](in: JIterable[A], fn: JFunc[A,Future[B]]): Future[JIterable[B]] = traverse(in, Actor.TIMEOUT, fn) // ===================================== // Deprecations diff --git a/akka-docs/java/futures.rst b/akka-docs/java/futures.rst index 8e66257443..660dc6bd05 100644 --- a/akka-docs/java/futures.rst +++ b/akka-docs/java/futures.rst @@ -172,12 +172,12 @@ It is very often desirable to be able to combine different Futures with eachothe import akka.dispatch.Future; import static akka.dispatch.Futures.sequence; import akka.japi.Function; - import java.util.LinkedList; + import java.lang.Iterable; - LinkedList> listOfFutureInts = ... //Some source generating a list of Future:s + Iterable> listOfFutureInts = ... //Some source generating a sequence of Future:s - // now we have a Future[List[Int]] - Future> futureListOfInts = sequence(listOfFutureInts); + // now we have a Future[Iterable[Int]] + Future> futureListOfInts = sequence(listOfFutureInts); // Find the sum of the odd numbers Long totalSum = futureListOfInts.map( @@ -190,21 +190,21 @@ It is very often desirable to be able to combine different Futures with eachothe } }).get(); -To better explain what happened in the example, ``Future.sequence`` is taking the ``LinkedList>`` and turning it into a ``Future>``. We can then use ``map`` to work with the ``LinkedList`` directly, and we aggregate the sum of the ``LinkedList``. +To better explain what happened in the example, ``Future.sequence`` is taking the ``Iterable>`` and turning it into a ``Future>``. We can then use ``map`` to work with the ``Iterable`` directly, and we aggregate the sum of the ``Iterable``. -The ``traverse`` method is similar to ``sequence``, but it takes a sequence of ``A``s and applies a function from ``A`` to ``Future`` and returns a ``Future>``, enabling parallel ``map`` over the sequence, if you use ``Futures.future`` to create the ``Future``. +The ``traverse`` method is similar to ``sequence``, but it takes a sequence of ``A``s and applies a function from ``A`` to ``Future`` and returns a ``Future>``, enabling parallel ``map`` over the sequence, if you use ``Futures.future`` to create the ``Future``. .. code-block:: java import akka.dispatch.Future; import static akka.dispatch.Futures.traverse; import static akka.dispatch.Futures.future; - import java.util.LinkedList; + import java.lang.Iterable; import akka.japi.Function; - LinkedList listStrings = ... //Just a list of Strings + Iterable listStrings = ... //Just a sequence of Strings - Future> result = traverse(listStrings, new Function>(){ + Future> result = traverse(listStrings, new Function>(){ public Future apply(final String r) { return future(new Callable() { public String call() { @@ -214,7 +214,7 @@ The ``traverse`` method is similar to ``sequence``, but it takes a sequence of ` } }); - result.get(); //Returns a the list of strings as upper case + result.get(); //Returns the sequence of strings as upper case It's as simple as that! @@ -224,7 +224,7 @@ Then there's a method that's called ``fold`` that takes a start-value, a sequenc import akka.dispatch.Future; import static akka.dispatch.Futures.fold; - import java.util.Iterable; + import java.lang.Iterable; import akka.japi.Function2; Iterable> futures = ... //A sequence of Futures, in this case Strings From 62209d0b3bc43b3eea646bbd2e5b1cb4305635cb Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Thu, 12 May 2011 14:08:37 +0200 Subject: [PATCH 224/233] Removing the use of currentTimeMillis for the restart logic, closing ticket #845 --- .../src/main/scala/akka/actor/ActorRef.scala | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index e2c391e300..9e75a91c4b 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -600,7 +600,7 @@ class LocalActorRef private[akka] ( @volatile private var maxNrOfRetriesCount: Int = 0 @volatile - private var restartsWithinTimeRangeTimestamp: Long = 0L + private var restartTimeWindowStartNanos: Long = 0L @volatile private var _mailbox: AnyRef = _ @volatile @@ -892,30 +892,32 @@ class LocalActorRef private[akka] ( } private def requestRestartPermission(maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]): Boolean = { + val denied = if (maxNrOfRetries.isEmpty && withinTimeRange.isEmpty) { //Immortal false } else if (withinTimeRange.isEmpty) { // restrict number of restarts - maxNrOfRetriesCount += 1 //Increment number of retries - maxNrOfRetriesCount > maxNrOfRetries.get + val retries = maxNrOfRetriesCount + 1 + maxNrOfRetriesCount = retries //Increment number of retries + retries > maxNrOfRetries.get } else { // cannot restart more than N within M timerange - maxNrOfRetriesCount += 1 //Increment number of retries - val windowStart = restartsWithinTimeRangeTimestamp - val now = System.currentTimeMillis - val retries = maxNrOfRetriesCount + val retries = maxNrOfRetriesCount + 1 + + val windowStart = restartTimeWindowStartNanos + val now = System.nanoTime //We are within the time window if it isn't the first restart, or if the window hasn't closed val insideWindow = if (windowStart == 0) false - else (now - windowStart) <= withinTimeRange.get - - //The actor is dead if it dies X times within the window of restart - val unrestartable = insideWindow && retries > maxNrOfRetries.getOrElse(1) + else (now - windowStart) <= TimeUnit.MILLISECONDS.toNanos(withinTimeRange.get) if (windowStart == 0 || !insideWindow) //(Re-)set the start of the window - restartsWithinTimeRangeTimestamp = now + restartTimeWindowStartNanos = now - if (windowStart != 0 && !insideWindow) //Reset number of restarts if window has expired - maxNrOfRetriesCount = 1 + //Reset number of restarts if window has expired, otherwise, increment it + maxNrOfRetriesCount = if (windowStart != 0 && !insideWindow) 1 else retries //Increment number of retries - unrestartable + val restartCountLimit = if (maxNrOfRetries.isDefined) maxNrOfRetries.get else 1 + + //The actor is dead if it dies X times within the window of restart + insideWindow && retries > restartCountLimit } denied == false //If we weren't denied, we have a go From d3e29e8ffac34084e8a9bb493258177f23d06e3e Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Thu, 12 May 2011 14:33:17 +0200 Subject: [PATCH 225/233] Silencing the output of the source of the configuration, can be re-endabled through setting system property `akka.output.config.source` to anything but null, closing ticket #848 --- .../src/main/scala/akka/config/Config.scala | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/akka-actor/src/main/scala/akka/config/Config.scala b/akka-actor/src/main/scala/akka/config/Config.scala index 341a69ae3f..3075338952 100644 --- a/akka-actor/src/main/scala/akka/config/Config.scala +++ b/akka-actor/src/main/scala/akka/config/Config.scala @@ -31,7 +31,7 @@ object Config { envHome orElse systemHome } - val config: Configuration = try { + val config: Configuration = { val confName = { val envConf = System.getenv("AKKA_MODE") match { case null | "" => None @@ -46,42 +46,46 @@ object Config { (envConf orElse systemConf).map("akka." + _ + ".conf").getOrElse("akka.conf") } - val newInstance = + val (newInstance, source) = if (System.getProperty("akka.config", "") != "") { val configFile = System.getProperty("akka.config", "") - println("Loading config from -Dakka.config=" + configFile) - Configuration.fromFile(configFile) + (() => Configuration.fromFile(configFile), "Loading config from -Dakka.config=" + configFile) } else if (getClass.getClassLoader.getResource(confName) ne null) { - println("Loading config [" + confName + "] from the application classpath.") - Configuration.fromResource(confName, getClass.getClassLoader) + (() => Configuration.fromResource(confName, getClass.getClassLoader), "Loading config [" + confName + "] from the application classpath.") } else if (HOME.isDefined) { val configFile = HOME.get + "/config/" + confName - println("AKKA_HOME is defined as [" + HOME.get + "], loading config from [" + configFile + "].") - Configuration.fromFile(configFile) + (() => Configuration.fromFile(configFile), "AKKA_HOME is defined as [" + HOME.get + "], loading config from [" + configFile + "].") } else { - println( - "\nCan't load '" + confName + "'." + + (() => Configuration.fromString("akka {}"), // default empty config + "\nCan't load '" + confName + "'." + "\nOne of the three ways of locating the '" + confName + "' file needs to be defined:" + "\n\t1. Define the '-Dakka.config=...' system property option." + "\n\t2. Put the '" + confName + "' file on the classpath." + "\n\t3. Define 'AKKA_HOME' environment variable pointing to the root of the Akka distribution." + "\nI have no way of finding the '" + confName + "' configuration file." + "\nUsing default values everywhere.") - Configuration.fromString("akka {}") // default empty config } - val configVersion = newInstance.getString("akka.version", VERSION) - if (configVersion != VERSION) - throw new ConfigurationException( - "Akka JAR version [" + VERSION + "] is different than the provided config version [" + configVersion + "]") + try { + val i = newInstance() - newInstance - } catch { - case e => - System.err.println("Couldn't parse config, fatal error.") - e.printStackTrace(System.err) - System.exit(-1) - throw e + val configVersion = i.getString("akka.version", VERSION) + if (configVersion != VERSION) + throw new ConfigurationException( + "Akka JAR version [" + VERSION + "] is different than the provided config version [" + configVersion + "]") + + if(System.getProperty("akka.output.config.source") ne null) + System.out.println(source) + + i + } catch { + case e => + System.err.println("Couldn't parse config, fatal error.") + System.err.println("Config source: " + source) + e.printStackTrace(System.err) + System.exit(-1) + throw e + } } val CONFIG_VERSION = config.getString("akka.version", VERSION) From 53cf5079fdfd44ba487b87659e51bf310c2c818b Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Thu, 12 May 2011 16:14:38 +0200 Subject: [PATCH 226/233] Reusing `akka.output.config.source` system property to output default values when set to non-null value, closing ticket #573 --- .../src/main/scala/akka/config/Config.scala | 2 +- .../scala/akka/config/Configuration.scala | 33 ++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/akka-actor/src/main/scala/akka/config/Config.scala b/akka-actor/src/main/scala/akka/config/Config.scala index 3075338952..eef0c5d8e0 100644 --- a/akka-actor/src/main/scala/akka/config/Config.scala +++ b/akka-actor/src/main/scala/akka/config/Config.scala @@ -74,7 +74,7 @@ object Config { throw new ConfigurationException( "Akka JAR version [" + VERSION + "] is different than the provided config version [" + configVersion + "]") - if(System.getProperty("akka.output.config.source") ne null) + if(Configuration.outputConfigSources) System.out.println(source) i diff --git a/akka-actor/src/main/scala/akka/config/Configuration.scala b/akka-actor/src/main/scala/akka/config/Configuration.scala index d06a9eb88e..27109452c4 100644 --- a/akka-actor/src/main/scala/akka/config/Configuration.scala +++ b/akka-actor/src/main/scala/akka/config/Configuration.scala @@ -15,6 +15,8 @@ object Configuration { val DefaultPath = new File(".").getCanonicalPath val DefaultImporter = new FilesystemImporter(DefaultPath) + val outputConfigSources = System.getProperty("akka.output.config.source") ne null + def load(data: String, importer: Importer = DefaultImporter): Configuration = { val parser = new ConfigParser(importer = importer) new Configuration(parser parse data) @@ -60,6 +62,13 @@ class Configuration(val map: Map[String, Any]) { private val trueValues = Set("true", "on") private val falseValues = Set("false", "off") + private def outputIfDesiredAndReturnInput[T](key: String, t: T): T = { + if (Configuration.outputConfigSources) + println("Akka config is using default value for: " + key) + + t + } + def contains(key: String): Boolean = map contains key def keys: Iterable[String] = map.keys @@ -72,7 +81,8 @@ class Configuration(val map: Map[String, Any]) { } } - def getAny(key: String, defaultValue: Any): Any = getAny(key).getOrElse(defaultValue) + def getAny(key: String, defaultValue: Any): Any = + getAny(key).getOrElse(outputIfDesiredAndReturnInput(key, defaultValue)) def getSeqAny(key: String): Seq[Any] = { try { @@ -84,7 +94,8 @@ class Configuration(val map: Map[String, Any]) { def getString(key: String): Option[String] = map.get(key).map(_.toString) - def getString(key: String, defaultValue: String): String = getString(key).getOrElse(defaultValue) + def getString(key: String, defaultValue: String): String = + getString(key).getOrElse(outputIfDesiredAndReturnInput(key, defaultValue)) def getList(key: String): Seq[String] = { try { @@ -102,7 +113,8 @@ class Configuration(val map: Map[String, Any]) { } } - def getInt(key: String, defaultValue: Int): Int = getInt(key).getOrElse(defaultValue) + def getInt(key: String, defaultValue: Int): Int = + getInt(key).getOrElse(outputIfDesiredAndReturnInput(key, defaultValue)) def getLong(key: String): Option[Long] = { try { @@ -112,7 +124,8 @@ class Configuration(val map: Map[String, Any]) { } } - def getLong(key: String, defaultValue: Long): Long = getLong(key).getOrElse(defaultValue) + def getLong(key: String, defaultValue: Long): Long = + getLong(key).getOrElse(outputIfDesiredAndReturnInput(key, defaultValue)) def getFloat(key: String): Option[Float] = { try { @@ -122,7 +135,8 @@ class Configuration(val map: Map[String, Any]) { } } - def getFloat(key: String, defaultValue: Float): Float = getFloat(key).getOrElse(defaultValue) + def getFloat(key: String, defaultValue: Float): Float = + getFloat(key).getOrElse(outputIfDesiredAndReturnInput(key, defaultValue)) def getDouble(key: String): Option[Double] = { try { @@ -132,7 +146,8 @@ class Configuration(val map: Map[String, Any]) { } } - def getDouble(key: String, defaultValue: Double): Double = getDouble(key).getOrElse(defaultValue) + def getDouble(key: String, defaultValue: Double): Double = + getDouble(key).getOrElse(outputIfDesiredAndReturnInput(key, defaultValue)) def getBoolean(key: String): Option[Boolean] = { getString(key) flatMap { s => @@ -142,11 +157,13 @@ class Configuration(val map: Map[String, Any]) { } } - def getBoolean(key: String, defaultValue: Boolean): Boolean = getBool(key).getOrElse(defaultValue) + def getBoolean(key: String, defaultValue: Boolean): Boolean = + getBool(key).getOrElse(outputIfDesiredAndReturnInput(key, defaultValue)) def getBool(key: String): Option[Boolean] = getBoolean(key) - def getBool(key: String, defaultValue: Boolean): Boolean = getBoolean(key, defaultValue) + def getBool(key: String, defaultValue: Boolean): Boolean = + getBoolean(key, defaultValue) def apply(key: String): String = getString(key) match { case None => throw new ConfigurationException("undefined config: " + key) From fd26f284ab92d2e04a546ea0492f9e467d1dd179 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Fri, 13 May 2011 02:41:28 +1200 Subject: [PATCH 227/233] Update to scala 2.9.0 and sbt 0.7.7 --- akka-docs/scala/dataflow.rst | 2 +- akka-tutorials/akka-tutorial-first/project/build.properties | 4 ++-- akka-tutorials/akka-tutorial-second/project/build.properties | 4 ++-- project/build.properties | 5 +++-- project/build/AkkaProject.scala | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/akka-docs/scala/dataflow.rst b/akka-docs/scala/dataflow.rst index af6622b976..1d46f0164f 100644 --- a/akka-docs/scala/dataflow.rst +++ b/akka-docs/scala/dataflow.rst @@ -29,7 +29,7 @@ Scala's Delimited Continuations plugin is required to use the Dataflow API. To e import sbt._ class MyAkkaProject(info: ProjectInfo) extends DefaultProject(info) with AkkaProject with AutoCompilerPlugins { - val continuationsPlugin = compilerPlugin("org.scala-lang.plugins" % "continuations" % "2.9.0.RC3") + val continuationsPlugin = compilerPlugin("org.scala-lang.plugins" % "continuations" % "2.9.0") override def compileOptions = super.compileOptions ++ compileOptions("-P:continuations:enable") } diff --git a/akka-tutorials/akka-tutorial-first/project/build.properties b/akka-tutorials/akka-tutorial-first/project/build.properties index e4ff105728..efe6111c40 100644 --- a/akka-tutorials/akka-tutorial-first/project/build.properties +++ b/akka-tutorials/akka-tutorial-first/project/build.properties @@ -1,5 +1,5 @@ project.organization=se.scalablesolutions.akka project.name=akka-tutorial-first project.version=1.2-SNAPSHOT -build.scala.versions=2.9.0.RC3 -sbt.version=0.7.6.RC0 +build.scala.versions=2.9.0 +sbt.version=0.7.7 diff --git a/akka-tutorials/akka-tutorial-second/project/build.properties b/akka-tutorials/akka-tutorial-second/project/build.properties index 0b21be843f..7c84e8368b 100644 --- a/akka-tutorials/akka-tutorial-second/project/build.properties +++ b/akka-tutorials/akka-tutorial-second/project/build.properties @@ -1,5 +1,5 @@ project.organization=se.scalablesolutions.akka project.name=akka-tutorial-second project.version=1.2-SNAPSHOT -build.scala.versions=2.9.0.RC3 -sbt.version=0.7.6.RC0 +build.scala.versions=2.9.0 +sbt.version=0.7.7 diff --git a/project/build.properties b/project/build.properties index 2f760f3270..fbcb83ae2d 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1,5 +1,6 @@ project.organization=se.scalablesolutions.akka project.name=akka project.version=1.2-SNAPSHOT -build.scala.versions=2.9.0.RC3 -sbt.version=0.7.6.RC0 +build.scala.versions=2.9.0 +sbt.version=0.7.7 + diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index b7791fb6e6..e681acb1bd 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -74,7 +74,7 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec lazy val JACKSON_VERSION = "1.8.0" lazy val JERSEY_VERSION = "1.3" lazy val MULTIVERSE_VERSION = "0.6.2" - lazy val SCALATEST_VERSION = "1.4.RC3" + lazy val SCALATEST_VERSION = "1.4.1" lazy val JETTY_VERSION = "7.4.0.v20110414" lazy val JAVAX_SERVLET_VERSION = "3.0" lazy val SLF4J_VERSION = "1.6.0" From bab44290e04c6b2c18f6092f2c61e7d623adccd5 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 13 May 2011 13:50:07 +0200 Subject: [PATCH 228/233] Removing weird text about remote actors, closing ticket #851 --- akka-actor/src/main/scala/akka/actor/Actor.scala | 2 -- 1 file changed, 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 972a87d5cf..0dff2f779a 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -184,7 +184,6 @@ object Actor extends ListenerManagement { * that creates the Actor. Please note that this function can be invoked multiple * times if for example the Actor is supervised and needs to be restarted. *

    - * This function should NOT be used for remote actors. *

        *   import Actor._
        *   val actor = actorOf(new MyActor)
    @@ -204,7 +203,6 @@ object Actor extends ListenerManagement {
        * that creates the Actor. Please note that this function can be invoked multiple
        * times if for example the Actor is supervised and needs to be restarted.
        * 

    - * This function should NOT be used for remote actors. * JAVA API */ def actorOf(creator: Creator[Actor]): ActorRef = new LocalActorRef(() => creator.create, None) From f53abcf105cfaf7907e58e9f8f86d3643c5d340e Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 13 May 2011 16:37:25 +0200 Subject: [PATCH 229/233] Rewriting TypedActor.future to be more clean --- .../src/main/scala/akka/actor/TypedActor.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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 77d0d1e64d..086addfb99 100644 --- a/akka-typed-actor/src/main/scala/akka/actor/TypedActor.scala +++ b/akka-typed-actor/src/main/scala/akka/actor/TypedActor.scala @@ -203,11 +203,10 @@ abstract class TypedActor extends Actor with Proxyable { * Integer result = future.get(); *

    */ - def future[T](value: T): Future[T] = - self.senderFuture - .map{f => f.completeWithResult(value); f } - .getOrElse(throw new IllegalActorStateException("No sender future in scope")) - .asInstanceOf[Future[T]] + def future[T](value: T): Future[T] = self.senderFuture match { + case None => throw new IllegalActorStateException("No sender future in scope") + case Some(f) => f.completeWithResult(value).asInstanceOf[Future[T]] + } def receive = { case joinPoint: JoinPoint => From 04541c30096e3ab5cb7a568c1de63324a7003661 Mon Sep 17 00:00:00 2001 From: Roland Date: Sun, 15 May 2011 00:24:12 +0200 Subject: [PATCH 230/233] add summary of system properties to configuration.rst --- akka-docs/general/configuration.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/akka-docs/general/configuration.rst b/akka-docs/general/configuration.rst index bd293383d8..bd6c9510e3 100644 --- a/akka-docs/general/configuration.rst +++ b/akka-docs/general/configuration.rst @@ -5,6 +5,9 @@ Configuration .. contents:: :local: +.. _-Dakka.config: +.. _-Dakka.home: + Specifying the configuration file --------------------------------- @@ -65,6 +68,8 @@ A custom ``akka.conf`` might look like this: } } +.. _-Dakka.mode: + Specifying files for different modes ------------------------------------ @@ -101,3 +106,20 @@ akka.dev.conf: event-handler-level = "DEBUG" } +.. _-Dakka.output.config.source: + +Showing Configuration Source +---------------------------- + +If the system property ``akka.output.config.source`` is set to anything but +null, then the source from which Akka reads its configuration is printed to the +console during application startup. + +Summary of System Properties +---------------------------- + +* :ref:`akka.home <-Dakka.home>` (``AKKA_HOME``): where Akka searches for configuration +* :ref:`akka.config <-Dakka.config>`: explicit configuration file location +* :ref:`akka.mode <-Dakka.mode>` (``AKKA_MODE``): modify configuration file name for multiple profiles +* :ref:`akka.output.config.source <-Dakka.output.config.source>`: whether to print configuration source to console + From 08f663c07d83b458b8cab1e71c62237fbb56b37b Mon Sep 17 00:00:00 2001 From: alarmnummer Date: Sun, 15 May 2011 19:06:23 +0200 Subject: [PATCH 231/233] - removed unused imports --- akka-actor/src/main/scala/akka/actor/package.scala | 2 -- akka-actor/src/main/scala/akka/dataflow/DataFlow.scala | 2 +- .../akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala | 6 ++---- .../ExecutorBasedEventDrivenWorkStealingDispatcher.scala | 4 ---- .../main/scala/akka/dispatch/ThreadBasedDispatcher.scala | 6 +----- 5 files changed, 4 insertions(+), 16 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/package.scala b/akka-actor/src/main/scala/akka/actor/package.scala index 0a781649eb..01ef989f2a 100644 --- a/akka-actor/src/main/scala/akka/actor/package.scala +++ b/akka-actor/src/main/scala/akka/actor/package.scala @@ -4,8 +4,6 @@ package akka -import actor.{ScalaActorRef, ActorRef} - package object actor { implicit def actorRef2Scala(ref: ActorRef): ScalaActorRef = ref.asInstanceOf[ScalaActorRef] diff --git a/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala b/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala index 13438132e6..3cbb11b5bf 100644 --- a/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala +++ b/akka-actor/src/main/scala/akka/dataflow/DataFlow.scala @@ -5,7 +5,7 @@ package akka.dataflow import java.util.concurrent.atomic.AtomicReference -import java.util.concurrent.{ConcurrentLinkedQueue, LinkedBlockingQueue} +import java.util.concurrent.{ConcurrentLinkedQueue} import akka.event.EventHandler import akka.actor.{Actor, ActorRef} diff --git a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala index dca2f2f822..bc03607fdc 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala @@ -5,12 +5,10 @@ package akka.dispatch import akka.event.EventHandler -import akka.actor.{ActorRef, IllegalActorStateException} -import akka.util.{ReflectiveAccess, Switch} +import akka.actor.{ActorRef} -import java.util.Queue import java.util.concurrent.atomic.AtomicReference -import java.util.concurrent.{ TimeUnit, ExecutorService, RejectedExecutionException, ConcurrentLinkedQueue, LinkedBlockingQueue} +import java.util.concurrent.{ TimeUnit, ExecutorService, RejectedExecutionException, ConcurrentLinkedQueue} /** * Default settings are: diff --git a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala index 7829e47712..637aea8e60 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala @@ -5,11 +5,7 @@ package akka.dispatch import akka.actor.{ActorRef, Actor, IllegalActorStateException} -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 /** diff --git a/akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala index 9ed0ce8ef1..ae2c2ecfc3 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala @@ -4,13 +4,9 @@ package akka.dispatch -import akka.actor.{Actor, ActorRef} -import akka.config.Config.config +import akka.actor.{ActorRef} import akka.util.Duration -import java.util.Queue -import java.util.concurrent.{ConcurrentLinkedQueue, BlockingQueue, TimeUnit, LinkedBlockingQueue} -import akka.actor import java.util.concurrent.atomic.AtomicReference /** From 5f51b72df5076a823e6b373ea8a599f22504f884 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Mon, 16 May 2011 08:34:55 +0200 Subject: [PATCH 232/233] Changed Scalable Solutions to Typesafe --- akka-docs/conf.py | 2 +- akka-docs/intro/getting-started.rst | 2 +- akka-docs/intro/why-akka.rst | 2 +- akka-docs/project/links.rst | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/akka-docs/conf.py b/akka-docs/conf.py index 2065d4aefb..760a226ed6 100644 --- a/akka-docs/conf.py +++ b/akka-docs/conf.py @@ -16,7 +16,7 @@ master_doc = 'index' exclude_patterns = ['_build', 'pending', 'disabled'] project = u'Akka' -copyright = u'2009-2011, Scalable Solutions AB' +copyright = u'2011, Typesafe Inc' version = '1.2-SNAPSHOT' release = '1.2-SNAPSHOT' diff --git a/akka-docs/intro/getting-started.rst b/akka-docs/intro/getting-started.rst index 46219aec7d..049dc103e2 100644 --- a/akka-docs/intro/getting-started.rst +++ b/akka-docs/intro/getting-started.rst @@ -193,6 +193,6 @@ Need help? If you have questions you can get help on the `Akka Mailing List `_. -You can also ask for `commercial support `_. +You can also ask for `commercial support `_. Thanks for being a part of the Akka community. diff --git a/akka-docs/intro/why-akka.rst b/akka-docs/intro/why-akka.rst index 4294d7e022..08c82b2bc4 100644 --- a/akka-docs/intro/why-akka.rst +++ b/akka-docs/intro/why-akka.rst @@ -64,4 +64,4 @@ features: #. Storage of statistics data for later processing #. Provisioning and rolling upgrades through a dashboard -Read more `here `_. +Read more `here `_. diff --git a/akka-docs/project/links.rst b/akka-docs/project/links.rst index 845c739e44..7ebe839d8c 100644 --- a/akka-docs/project/links.rst +++ b/akka-docs/project/links.rst @@ -1,9 +1,9 @@ .. _support: -`Support `__ +`Support `__ ========================================= -`Scalable Solutions `_ +`Typesafe `_ `Mailing List `_ ========================================================== From 62427f518c217e685c14577c8e5749d9b07c1f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Mon, 16 May 2011 00:11:09 -0700 Subject: [PATCH 233/233] Added dataflow article --- akka-docs/additional/articles.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/akka-docs/additional/articles.rst b/akka-docs/additional/articles.rst index 06f01f9a7d..2e22142e63 100644 --- a/akka-docs/additional/articles.rst +++ b/akka-docs/additional/articles.rst @@ -21,6 +21,8 @@ Videos Articles -------- +`Scatter-Gather with Akka Dataflow `_ + `Actor-Based Continuations with Akka and Swarm `_ `Mimicking Twitter Using an Akka-Based Event-Driven Architecture `_