From fd5afde4ffef5f31b056154f8b4ef06eb68af277 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 13 Jun 2011 13:28:29 +0200 Subject: [PATCH 01/13] Adding 'ask' to replace 'sendRequestReplyFuture' and removing sendRequestReply --- .../src/main/scala/akka/actor/ActorRef.scala | 49 +++---------------- akka-docs/cluster/durable-mailbox.rst | 2 +- akka-docs/java/futures.rst | 4 +- akka-docs/java/untyped-actors.rst | 10 ++-- akka-spring/src/test/resources/akka-test.conf | 2 +- .../example/UntypedCoordinatedExample.java | 4 +- .../example/UntypedTransactorExample.java | 4 +- .../test/UntypedCoordinatedIncrementTest.java | 4 +- .../test/UntypedTransactorTest.java | 4 +- .../java/akka/tutorial/java/second/Pi.java | 2 +- config/akka-reference.conf | 2 +- 11 files changed, 25 insertions(+), 62 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index c410956cd2..1f2a6e7725 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -274,54 +274,17 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] with S /** * Akka Java API.

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

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

- * Sends a message asynchronously and waits on a future for a reply message under the hood. - *

- * It waits on the reply either until it receives it or until the timeout expires - * (which will throw an ActorTimeoutException). E.g. send-and-receive-eventually semantics. - *

- * NOTE: - * Use this method with care. In most cases it is better to use 'sendOneWay' together with 'getContext().getSender()' to - * implement request/response message exchanges. - *

- * If you are sending messages using sendRequestReply then you have to use getContext().reply(..) - * to send a reply message to the original sender. If not then the sender will block until the timeout expires. - */ - def sendRequestReply(message: AnyRef, timeout: Long, sender: ActorRef): AnyRef = { - !!(message, timeout)(Option(sender)).getOrElse(throw new ActorTimeoutException( - "Message [" + message + - "]\n\tfrom [" + (if (sender ne null) sender.address else "nowhere") + - "]\n\twith timeout [" + timeout + - "]\n\ttimed out.")) - .asInstanceOf[AnyRef] - } - - /** - * Akka Java API.

- * @see sendRequestReplyFuture(message: AnyRef, sender: ActorRef): Future[_] + * @see ask(message: AnyRef, sender: ActorRef): Future[_] * Uses the Actors default timeout (setTimeout()) and omits the sender */ - def sendRequestReplyFuture[T <: AnyRef](message: AnyRef): Future[T] = sendRequestReplyFuture(message, timeout, null).asInstanceOf[Future[T]] + def ask[T <: AnyRef](message: AnyRef): Future[T] = ask(message, timeout, null).asInstanceOf[Future[T]] /** * Akka Java API.

- * @see sendRequestReplyFuture(message: AnyRef, sender: ActorRef): Future[_] + * @see ask(message: AnyRef, sender: ActorRef): Future[_] * Uses the Actors default timeout (setTimeout()) */ - def sendRequestReplyFuture[T <: AnyRef](message: AnyRef, sender: ActorRef): Future[T] = sendRequestReplyFuture(message, timeout, sender).asInstanceOf[Future[T]] + def ask[T <: AnyRef](message: AnyRef, sender: ActorRef): Future[T] = ask(message, timeout, sender).asInstanceOf[Future[T]] /** * Akka Java API.

@@ -331,10 +294,10 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] with S * Use this method with care. In most cases it is better to use 'sendOneWay' together with the 'getContext().getSender()' to * implement request/response message exchanges. *

- * If you are sending messages using sendRequestReplyFuture then you have to use getContext().reply(..) + * If you are sending messages using ask then you have to use getContext().reply(..) * to send a reply message to the original sender. If not then the sender will block until the timeout expires. */ - def sendRequestReplyFuture[T <: AnyRef](message: AnyRef, timeout: Long, sender: ActorRef): Future[T] = !!!(message, timeout)(Option(sender)).asInstanceOf[Future[T]] + def ask[T <: AnyRef](message: AnyRef, timeout: Long, sender: ActorRef): Future[T] = !!!(message, timeout)(Option(sender)).asInstanceOf[Future[T]] /** * Akka Java API.

diff --git a/akka-docs/cluster/durable-mailbox.rst b/akka-docs/cluster/durable-mailbox.rst index 63564825a9..2d6fd2752e 100644 --- a/akka-docs/cluster/durable-mailbox.rst +++ b/akka-docs/cluster/durable-mailbox.rst @@ -19,7 +19,7 @@ in its mailbox. None of these mailboxes work with blocking message send, e.g. the message send operations that are relying on futures; ``!!``, ``!!!``, - ``sendRequestReply`` and ``sendRequestReplyFuture``. If the node has crashed + ``sendRequestReply`` and ``ask``. If the node has crashed and then restarted, the thread that was blocked waiting for the reply is gone and there is no way we can deliver the message. diff --git a/akka-docs/java/futures.rst b/akka-docs/java/futures.rst index 660dc6bd05..91fc62cd4d 100644 --- a/akka-docs/java/futures.rst +++ b/akka-docs/java/futures.rst @@ -17,11 +17,11 @@ 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: +Using the ``ActorRef``\'s ``ask`` 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); + Future[Object] future = actorRef.ask[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`_. diff --git a/akka-docs/java/untyped-actors.rst b/akka-docs/java/untyped-actors.rst index 7d6cec6013..db698b1a4f 100644 --- a/akka-docs/java/untyped-actors.rst +++ b/akka-docs/java/untyped-actors.rst @@ -109,7 +109,7 @@ Send messages Messages are sent to an Actor through one of the 'send' methods. * 'sendOneWay' means “fire-and-forget”, e.g. send a message asynchronously and return immediately. * 'sendRequestReply' means “send-and-reply-eventually”, e.g. send a message asynchronously and wait for a reply through a Future. Here you can specify a timeout. Using timeouts is very important. If no timeout is specified then the actor’s default timeout (set by the 'getContext().setTimeout(..)' method in the 'ActorRef') is used. This method throws an 'ActorTimeoutException' if the call timed out. -* 'sendRequestReplyFuture' sends a message asynchronously and returns a 'Future'. +* 'ask' sends a message asynchronously and returns a 'Future'. In all these methods you have the option of passing along your 'ActorRef' context variable. Make it a practice of doing so because it will allow the receiver actors to be able to respond to your message, since the sender reference is sent along with the message. @@ -158,11 +158,11 @@ Here are some examples: Send-And-Receive-Future ^^^^^^^^^^^^^^^^^^^^^^^ -Using 'sendRequestReplyFuture' will send a message to the receiving Actor asynchronously and will immediately return a 'Future'. +Using 'ask' will send a message to the receiving Actor asynchronously and will immediately return a 'Future'. .. code-block:: java - Future future = actorRef.sendRequestReplyFuture("Hello", getContext(), 1000); + Future future = actorRef.ask("Hello", getContext(), 1000); The 'Future' interface looks like this: @@ -182,7 +182,7 @@ So the normal way of working with futures is something like this: .. code-block:: java - Future future = actorRef.sendRequestReplyFuture("Hello", getContext(), 1000); + Future future = actorRef.ask("Hello", getContext(), 1000); future.await(); if (future.isCompleted()) { Option resultOption = future.result(); @@ -305,7 +305,7 @@ On this 'Option' you can invoke 'boolean isDefined()' or 'boolean isEmpty()' to Reply using the sender future ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If a message was sent with the 'sendRequestReply' or 'sendRequestReplyFuture' methods, which both implements request-reply semantics using Future's, then you either have the option of replying using the 'reply' method as above. This method will then resolve the Future. But you can also get a reference to the Future directly and resolve it yourself or if you would like to store it away to resolve it later, or pass it on to some other Actor to resolve it. +If a message was sent with the 'sendRequestReply' or 'ask' methods, which both implements request-reply semantics using Future's, then you either have the option of replying using the 'reply' method as above. This method will then resolve the Future. But you can also get a reference to the Future directly and resolve it yourself or if you would like to store it away to resolve it later, or pass it on to some other Actor to resolve it. The reference to the Future resides in the 'ActorRef' instance and can be retrieved using 'Option getSenderFuture()'. diff --git a/akka-spring/src/test/resources/akka-test.conf b/akka-spring/src/test/resources/akka-test.conf index bb9c0e347d..a84c60798a 100644 --- a/akka-spring/src/test/resources/akka-test.conf +++ b/akka-spring/src/test/resources/akka-test.conf @@ -26,7 +26,7 @@ akka { actor { timeout = 2000 # Default timeout for Future based invocations # - Actor: !! && !!! - # - UntypedActor: sendRequestReply && sendRequestReplyFuture + # - UntypedActor: sendRequestReply && ask # - TypedActor: methods with non-void return type serialize-messages = off # Does a deep clone of (non-primitive) messages to ensure immutability throughput = 5 # Default throughput for all ExecutorBasedEventDrivenDispatcher, set to 1 for complete fairness diff --git a/akka-stm/src/test/java/akka/transactor/example/UntypedCoordinatedExample.java b/akka-stm/src/test/java/akka/transactor/example/UntypedCoordinatedExample.java index 127a911677..f3ac18fb73 100644 --- a/akka-stm/src/test/java/akka/transactor/example/UntypedCoordinatedExample.java +++ b/akka-stm/src/test/java/akka/transactor/example/UntypedCoordinatedExample.java @@ -19,8 +19,8 @@ public class UntypedCoordinatedExample { Thread.sleep(3000); - Future future1 = counter1.sendRequestReplyFuture("GetCount"); - Future future2 = counter2.sendRequestReplyFuture("GetCount"); + Future future1 = counter1.ask("GetCount"); + Future future2 = counter2.ask("GetCount"); future1.await(); if (future1.isCompleted()) { diff --git a/akka-stm/src/test/java/akka/transactor/example/UntypedTransactorExample.java b/akka-stm/src/test/java/akka/transactor/example/UntypedTransactorExample.java index d2f83ff25e..34e2e2edec 100644 --- a/akka-stm/src/test/java/akka/transactor/example/UntypedTransactorExample.java +++ b/akka-stm/src/test/java/akka/transactor/example/UntypedTransactorExample.java @@ -18,8 +18,8 @@ public class UntypedTransactorExample { Thread.sleep(3000); - Future future1 = counter1.sendRequestReplyFuture("GetCount"); - Future future2 = counter2.sendRequestReplyFuture("GetCount"); + Future future1 = counter1.ask("GetCount"); + Future future2 = counter2.ask("GetCount"); future1.await(); if (future1.isCompleted()) { diff --git a/akka-stm/src/test/java/akka/transactor/test/UntypedCoordinatedIncrementTest.java b/akka-stm/src/test/java/akka/transactor/test/UntypedCoordinatedIncrementTest.java index a9eabe7be1..84773d7d5d 100644 --- a/akka-stm/src/test/java/akka/transactor/test/UntypedCoordinatedIncrementTest.java +++ b/akka-stm/src/test/java/akka/transactor/test/UntypedCoordinatedIncrementTest.java @@ -49,7 +49,7 @@ public class UntypedCoordinatedIncrementTest { incrementLatch.await(timeout, TimeUnit.SECONDS); } catch (InterruptedException exception) {} for (ActorRef counter : counters) { - Future future = counter.sendRequestReplyFuture("GetCount"); + Future future = counter.ask("GetCount"); future.await(); if (future.isCompleted()) { Option resultOption = future.result(); @@ -72,7 +72,7 @@ public class UntypedCoordinatedIncrementTest { incrementLatch.await(timeout, TimeUnit.SECONDS); } catch (InterruptedException exception) {} for (ActorRef counter : counters) { - Future future = counter.sendRequestReplyFuture("GetCount"); + Future future = counter.ask("GetCount"); future.await(); if (future.isCompleted()) { Option resultOption = future.result(); diff --git a/akka-stm/src/test/java/akka/transactor/test/UntypedTransactorTest.java b/akka-stm/src/test/java/akka/transactor/test/UntypedTransactorTest.java index 9610aa2ebe..97446d2cda 100644 --- a/akka-stm/src/test/java/akka/transactor/test/UntypedTransactorTest.java +++ b/akka-stm/src/test/java/akka/transactor/test/UntypedTransactorTest.java @@ -48,7 +48,7 @@ public class UntypedTransactorTest { incrementLatch.await(timeout, TimeUnit.SECONDS); } catch (InterruptedException exception) {} for (ActorRef counter : counters) { - Future future = counter.sendRequestReplyFuture("GetCount"); + Future future = counter.ask("GetCount"); future.await(); if (future.isCompleted()) { Option resultOption = future.result(); @@ -71,7 +71,7 @@ public class UntypedTransactorTest { incrementLatch.await(timeout, TimeUnit.SECONDS); } catch (InterruptedException exception) {} for (ActorRef counter : counters) { - Future future = counter.sendRequestReplyFuture("GetCount"); + Future future = counter.ask("GetCount"); future.await(); if (future.isCompleted()) { Option resultOption = future.result(); 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 5caae5e365..06b1223032 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 @@ -184,7 +184,7 @@ public class Pi { // send calculate message long timeout = 60000; - Future replyFuture = master.sendRequestReplyFuture(new Calculate(), timeout, null); + Future replyFuture = master.ask(new Calculate(), timeout, null); Option result = replyFuture.await().resultOrException(); if (result.isDefined()) { double pi = result.get(); diff --git a/config/akka-reference.conf b/config/akka-reference.conf index 0241386856..5793377029 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -29,7 +29,7 @@ akka { actor { timeout = 5 # Default timeout for Future based invocations # - Actor: !! && !!! - # - UntypedActor: sendRequestReply && sendRequestReplyFuture + # - UntypedActor: sendRequestReply && ask # - TypedActor: methods with non-void return type serialize-messages = off # Does a deep clone of (non-primitive) messages to ensure immutability throughput = 5 # Default throughput for all Dispatcher, set to 1 for complete fairness From fa0478bc3230b5ffdb80d4c9822a26d3a2979196 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 13 Jun 2011 13:43:21 +0200 Subject: [PATCH 02/13] Replacing !!! with ? --- .../scala/akka/actor/actor/ActorRefSpec.scala | 4 +- .../scala/akka/dispatch/ActorModelSpec.scala | 6 +- .../test/scala/akka/dispatch/FutureSpec.scala | 64 +++++++++---------- .../dispatch/PriorityDispatcherSpec.scala | 2 +- .../scala/akka/misc/ActorRegistrySpec.scala | 2 +- .../test/scala/akka/routing/RoutingSpec.scala | 4 +- .../scala/akka/ticket/Ticket703Spec.scala | 4 +- .../src/main/scala/akka/actor/Actor.scala | 6 +- .../src/main/scala/akka/actor/ActorRef.scala | 18 +++--- .../main/scala/akka/actor/TypedActor.scala | 6 +- .../src/main/scala/akka/dispatch/Future.scala | 20 +++--- .../src/main/scala/akka/cluster/Cluster.scala | 4 +- .../src/main/scala/akka/cluster/Routing.scala | 2 +- akka-docs/cluster/durable-mailbox.rst | 2 +- akka-docs/disabled/getting-started-first.rst | 2 +- .../intro/getting-started-first-java.rst | 2 +- .../getting-started-first-scala-eclipse.rst | 2 +- .../intro/getting-started-first-scala.rst | 2 +- akka-docs/scala/actors.rst | 12 ++-- akka-docs/scala/futures.rst | 20 +++--- .../actor/mailbox/DurableDispatcher.scala | 4 +- akka-spring/src/test/resources/akka-test.conf | 2 +- .../src/main/scala/akka/agent/Agent.scala | 6 +- .../scala/akka/testkit/TestActorRefSpec.scala | 2 +- .../src/main/scala/Pi.scala | 2 +- config/akka-reference.conf | 2 +- 26 files changed, 102 insertions(+), 100 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRefSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRefSpec.scala index 02dfb6ac9b..cd76784249 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRefSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRefSpec.scala @@ -310,8 +310,8 @@ class ActorRefSpec extends WordSpec with MustMatchers { } }).start() - val ffive: Future[String] = ref !!! 5 - val fnull: Future[String] = ref !!! null + val ffive: Future[String] = ref ? 5 + val fnull: Future[String] = ref ? null intercept[ActorKilledException] { ref !! PoisonPill 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 07414be0bc..9818b3d0e9 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala @@ -348,9 +348,9 @@ abstract class ActorModelSpec extends JUnitSuite { implicit val dispatcher = newInterceptedDispatcher val a = newTestActor.start() dispatcher.suspend(a) - val f1: Future[String] = a !!! Reply("foo") - val stopped = a !!! PoisonPill - val shouldBeCompleted = for (i ← 1 to 10) yield a !!! Reply(i) + val f1: Future[String] = a ? Reply("foo") + val stopped = a ? PoisonPill + val shouldBeCompleted = for (i ← 1 to 10) yield a ? Reply(i) dispatcher.resume(a) assert(f1.get === "foo") stopped.await 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 1d18e2c60f..99c4d52313 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -40,7 +40,7 @@ class FutureSpec extends JUnitSuite { def shouldActorReplyResultThroughExplicitFuture { val actor = actorOf[TestActor] actor.start() - val future = actor !!! "Hello" + val future = actor ? "Hello" future.await assert(future.result.isDefined) assert("World" === future.result.get) @@ -51,7 +51,7 @@ class FutureSpec extends JUnitSuite { def shouldActorReplyExceptionThroughExplicitFuture { val actor = actorOf[TestActor] actor.start() - val future = actor !!! "Failure" + val future = actor ? "Failure" future.await assert(future.exception.isDefined) assert("Expected exception; to test fault-tolerance" === future.exception.get.getMessage) @@ -62,9 +62,9 @@ class FutureSpec extends JUnitSuite { def shouldFutureCompose { val actor1 = actorOf[TestActor].start() val actor2 = actorOf(new Actor { def receive = { case s: String ⇒ self reply s.toUpperCase } }).start() - val future1 = actor1 !!! "Hello" flatMap ((s: String) ⇒ actor2 !!! s) - val future2 = actor1 !!! "Hello" flatMap (actor2 !!! (_: String)) - val future3 = actor1 !!! "Hello" flatMap (actor2 !!! (_: Int)) + 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: Any) === "WORLD") assert((future2.get: Any) === "WORLD") intercept[ClassCastException] { future3.get } @@ -76,8 +76,8 @@ class FutureSpec extends JUnitSuite { def shouldFutureComposePatternMatch { val actor1 = actorOf[TestActor].start() 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 !!! _) + 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: Any) === "WORLD") intercept[MatchError] { future2.get } actor1.stop() @@ -93,18 +93,18 @@ class FutureSpec extends JUnitSuite { } }).start() - val future0 = actor !!! "Hello" + val future0 = actor ? "Hello" val future1 = for { a: Int ← future0 // returns 5 - b: String ← actor !!! a // returns "10" - c: String ← actor !!! 7 // returns "14" + b: String ← actor ? a // returns "10" + c: String ← actor ? 7 // returns "14" } yield b + "-" + c val future2 = for { a: Int ← future0 - b: Int ← actor !!! a - c: String ← actor !!! 7 + b: Int ← actor ? a + c: String ← actor ? 7 } yield b + "-" + c assert(future1.get === "10-14") @@ -124,15 +124,15 @@ class FutureSpec extends JUnitSuite { }).start() val future1 = for { - Res(a: Int) ← actor.!!![Res[Int]](Req("Hello")) - Res(b: String) ← actor.!!![Res[String]](Req(a)) - Res(c: String) ← actor.!!![Res[String]](Req(7)) + Res(a: Int) ← actor.?[Res[Int]](Req("Hello")) + Res(b: String) ← actor.?[Res[String]](Req(a)) + Res(c: String) ← actor.?[Res[String]](Req(7)) } yield b + "-" + c val future2 = for { - Res(a: Int) ← actor.!!![Any](Req("Hello")) - Res(b: Int) ← actor.!!![Res[Int]](Req(a)) - Res(c: Int) ← actor.!!![Res[Int]](Req(7)) + Res(a: Int) ← actor.?[Any](Req("Hello")) + Res(b: Int) ← actor.?[Res[Int]](Req(a)) + Res(c: Int) ← actor.?[Res[Int]](Req(7)) } yield b + "-" + c assert(future1.get === "10-14") @@ -162,14 +162,14 @@ class FutureSpec extends JUnitSuite { val actor = actorOf[TestActor].start() - val future8 = actor !!! "Failure" - val future9 = actor !!! "Failure" recover { + val future8 = actor ? "Failure" + val future9 = actor ? "Failure" recover { case e: RuntimeException ⇒ "FAIL!" } - val future10 = actor !!! "Hello" recover { + val future10 = actor ? "Hello" recover { case e: RuntimeException ⇒ "FAIL!" } - val future11 = actor !!! "Failure" recover { case _ ⇒ "Oops!" } + val future11 = actor ? "Failure" recover { case _ ⇒ "Oops!" } assert(future1.get === 5) intercept[ArithmeticException] { future2.get } @@ -194,7 +194,7 @@ class FutureSpec extends JUnitSuite { }).start() } val timeout = 10000 - def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.!!![Int]((idx, idx * 200), timeout) } + 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) } @@ -205,7 +205,7 @@ 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), 10000) } + 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) } @@ -222,7 +222,7 @@ class FutureSpec extends JUnitSuite { }).start() } val timeout = 10000 - def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.!!![Int]((idx, idx * 100), timeout) } + 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") } @@ -239,7 +239,7 @@ class FutureSpec extends JUnitSuite { }).start() } val timeout = 10000 - def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.!!![Int]((idx, idx * 200), timeout) } + def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?[Int]((idx, idx * 200), timeout) } assert(Futures.reduce(futures, timeout)(_ + _).get === 45) } @@ -256,7 +256,7 @@ class FutureSpec extends JUnitSuite { }).start() } val timeout = 10000 - def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.!!![Int]((idx, idx * 100), timeout) } + 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") } @@ -269,7 +269,7 @@ class FutureSpec extends JUnitSuite { def receiveShouldExecuteOnComplete { val latch = new StandardLatch val actor = actorOf[TestActor].start() - actor !!! "Hello" onResult { case "World" ⇒ latch.open } + actor ? "Hello" onResult { case "World" ⇒ latch.open } assert(latch.tryAwait(5, TimeUnit.SECONDS)) actor.stop() } @@ -285,7 +285,7 @@ class FutureSpec extends JUnitSuite { } }).start() - val oddFutures: List[Future[Int]] = List.fill(100)(oddActor !!! 'GetNext) + val oddFutures: List[Future[Int]] = List.fill(100)(oddActor ? 'GetNext) assert(Future.sequence(oddFutures).get.sum === 10000) oddActor.stop() @@ -342,7 +342,7 @@ class FutureSpec extends JUnitSuite { val actor = actorOf[TestActor].start val x = Future("Hello") - val y = x flatMap (actor !!! _) + val y = x flatMap (actor ? _) val r = flow(x() + " " + y[String]() + "!") @@ -370,7 +370,7 @@ class FutureSpec extends JUnitSuite { val actor = actorOf[TestActor].start val x = Future(3) - val y = actor !!! "Hello" + val y = actor ? "Hello" val r = flow(x() + y[Int](), 100) @@ -384,7 +384,7 @@ class FutureSpec extends JUnitSuite { val actor = actorOf[TestActor].start val x = Future("Hello") - val y = actor !!! "Hello" + val y = actor ? "Hello" val r = flow(x() + y()) 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 5740edb9b8..84f98f2f75 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/PriorityDispatcherSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/PriorityDispatcherSpec.scala @@ -44,7 +44,7 @@ class PriorityDispatcherSpec extends WordSpec with MustMatchers { dispatcher.resume(actor) //Signal the actor to start treating it's message backlog - actor.!!![List[Int]]('Result).await.result.get must be === (msgs.reverse) + actor.?[List[Int]]('Result).await.result.get must be === (msgs.reverse) } } diff --git a/akka-actor-tests/src/test/scala/akka/misc/ActorRegistrySpec.scala b/akka-actor-tests/src/test/scala/akka/misc/ActorRegistrySpec.scala index df6184c292..b538b5d4ed 100644 --- a/akka-actor-tests/src/test/scala/akka/misc/ActorRegistrySpec.scala +++ b/akka-actor-tests/src/test/scala/akka/misc/ActorRegistrySpec.scala @@ -89,7 +89,7 @@ class ActorRegistrySpec extends JUnitSuite { val actor2 = actorOf[TestActor]("test-actor-2").start val results = new ConcurrentLinkedQueue[Future[String]] - Actor.registry.local.foreach(actor ⇒ results.add(actor.!!![String]("ping"))) + Actor.registry.local.foreach(actor ⇒ results.add(actor.?[String]("ping"))) assert(results.size === 2) val i = results.iterator diff --git a/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala b/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala index ddd37f760c..2375d128a6 100644 --- a/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala @@ -237,7 +237,7 @@ class RoutingSpec extends WordSpec with MustMatchers { }).start() try { - (for (count ← 1 to 500) yield pool.!!![String]("Test", 20000)) foreach { + (for (count ← 1 to 500) yield pool.?[String]("Test", 20000)) foreach { _.await.resultOrException.get must be("Response") } } finally { @@ -283,7 +283,7 @@ class RoutingSpec extends WordSpec with MustMatchers { latch = TestLatch(loops) count.set(0) for (m ← 0 until loops) { - pool !!! t + pool ? t sleepFor(50 millis) } } diff --git a/akka-actor-tests/src/test/scala/akka/ticket/Ticket703Spec.scala b/akka-actor-tests/src/test/scala/akka/ticket/Ticket703Spec.scala index ac5bea25aa..f48f7b87af 100644 --- a/akka-actor-tests/src/test/scala/akka/ticket/Ticket703Spec.scala +++ b/akka-actor-tests/src/test/scala/akka/ticket/Ticket703Spec.scala @@ -8,7 +8,7 @@ import org.scalatest.matchers.MustMatchers class Ticket703Spec extends WordSpec with MustMatchers { - "A !!! call to an actor pool" should { + "A ? call to an actor pool" should { "reuse the proper timeout" in { val actorPool = actorOf( new Actor with DefaultActorPool with BoundedCapacityStrategy with MailboxPressureCapacitor with SmallestMailboxSelector with BasicNoBackoffFilter { @@ -28,7 +28,7 @@ class Ticket703Spec extends WordSpec with MustMatchers { } }) }).start() - (actorPool.!!![String]("Ping", 7000)).await.result must be === Some("Response") + (actorPool.?[String]("Ping", 7000)).await.result must be === Some("Response") } } } \ No newline at end of file diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 67d4073a03..02b508db03 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -333,7 +333,7 @@ object Actor extends ListenerManagement { * This means that the following code is equivalent: * (actor !! "foo").as[Int] (Deprecated) * and - * (actor !!! "foo").as[Int] (Recommended) + * (actor ? "foo").as[Int] (Recommended) */ implicit def futureToAnyOptionAsTypedOption(anyFuture: Future[_]) = new AnyOptionAsTypedOption({ try { anyFuture.await } catch { case t: FutureTimeoutException ⇒ } @@ -475,7 +475,7 @@ object Actor extends ListenerManagement { * *

* Here you find functions like: - * - !, !!, !!! and forward + * - !, !!, ? and forward * - link, unlink, startLink etc * - start, stop * - etc. @@ -543,7 +543,7 @@ trait Actor { * Option[ActorRef] representation of the 'self' ActorRef reference. *

* Mainly for internal use, functions as the implicit sender references when invoking - * one of the message send functions ('!', '!!' and '!!!'). + * one of the message send functions ('!', '!!' and '?'). */ implicit def optionSelf: Option[ActorRef] = someSelf diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index 1f2a6e7725..30814566bc 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -101,8 +101,8 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] with S /** * User overridable callback/setting. *

- * Defines the default timeout for '!!' and '!!!' invocations, - * e.g. the timeout for the future returned by the call to '!!' and '!!!'. + * 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", "1.1") @BeanProperty @@ -210,7 +210,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] with S /** * Akka Java API.

* The reference sender future of the last received message. - * Is defined if the message was sent with sent with '!!' or '!!!', else None. + * Is defined if the message was sent with sent with '!!' or '?', else None. */ def getSenderFuture: Option[Promise[Any]] = senderFuture @@ -297,7 +297,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] with S * If you are sending messages using ask then you have to use getContext().reply(..) * to send a reply message to the original sender. If not then the sender will block until the timeout expires. */ - def ask[T <: AnyRef](message: AnyRef, timeout: Long, sender: ActorRef): Future[T] = !!!(message, timeout)(Option(sender)).asInstanceOf[Future[T]] + def ask[T <: AnyRef](message: AnyRef, timeout: Long, sender: ActorRef): Future[T] = ?(message, timeout)(Option(sender)).asInstanceOf[Future[T]] /** * Akka Java API.

@@ -1148,7 +1148,7 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ /** * The reference sender future of the last received message. - * Is defined if the message was sent with sent with '!!' or '!!!', else None. + * Is defined if the message was sent with sent with '!!' or '?', else None. */ def senderFuture(): Option[Promise[Any]] = { val msg = currentMessage @@ -1199,14 +1199,16 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ /** * Sends a message asynchronously returns a future holding the eventual reply message. + * Synonymous to: ask + * Pronounced: "ask" *

* NOTE: * Use this method with care. In most cases it is better to use '!' together with the 'sender' member field to * implement request/response message exchanges. - * If you are sending messages using !!! then you have to use self.reply(..) + * If you are sending messages using ? then you have to use self.reply(..) * to send a reply message to the original sender. If not then the sender will block until the timeout expires. */ - def !!![T](message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Future[T] = { + def ?[T](message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Future[T] = { if (isRunning) postMessageToMailboxAndCreateFutureResultWithTimeout[T](message, timeout, sender, None) else throw new ActorInitializationException( "Actor has not been started, you need to invoke 'actor.start()' before using it") @@ -1215,7 +1217,7 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ /** * Forwards the message and passes the original sender actor as the sender. *

- * Works with '!', '!!' and '!!!'. + * Works with '!', '!!' and '?'. */ def forward(message: Any)(implicit sender: Some[ActorRef]) = { if (isRunning) { diff --git a/akka-actor/src/main/scala/akka/actor/TypedActor.scala b/akka-actor/src/main/scala/akka/actor/TypedActor.scala index 85a388e24e..96324f3f4b 100644 --- a/akka-actor/src/main/scala/akka/actor/TypedActor.scala +++ b/akka-actor/src/main/scala/akka/actor/TypedActor.scala @@ -46,14 +46,14 @@ object TypedActor { actor ! m null case m if m.returnsFuture_? ⇒ - actor !!! m + actor ? m case m if m.returnsJOption_? || m.returnsOption_? ⇒ - (actor !!! m).as[AnyRef] match { + (actor ? m).as[AnyRef] match { case Some(null) | None ⇒ if (m.returnsJOption_?) JOption.none[Any] else None case Some(joption) ⇒ joption } case m ⇒ - (actor !!! m).get + (actor ? m).get } } } diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index e760f97bb7..9d12105f1d 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -278,7 +278,7 @@ sealed trait Future[+T] { * 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 + * 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 (_: A ⇒ Future[Any])) @@ -403,9 +403,9 @@ sealed trait Future[+T] { * Example: *

    * 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 }
+   *   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 }
    * } yield b + "-" + c
    * 
*/ @@ -468,9 +468,9 @@ sealed trait Future[+T] { * Example: *
    * val future1 = for {
-   *   a: Int    <- actor !!! "Hello" // returns 5
-   *   b: String <- actor !!! a       // returns "10"
-   *   c: String <- actor !!! 7       // returns "14"
+   *   a: Int    <- actor ? "Hello" // returns 5
+   *   b: String <- actor ? a       // returns "10"
+   *   c: String <- actor ? 7       // returns "14"
    * } yield b + "-" + c
    * 
*/ @@ -504,9 +504,9 @@ sealed trait Future[+T] { * Example: *
    * val future1 = for {
-   *   a: Int    <- actor !!! "Hello" // returns 5
-   *   b: String <- actor !!! a       // returns "10"
-   *   c: String <- actor !!! 7       // returns "14"
+   *   a: Int    <- actor ? "Hello" // returns 5
+   *   b: String <- actor ? a       // returns "10"
+   *   c: String <- actor ? 7       // returns "14"
    * } yield b + "-" + c
    * 
*/ diff --git a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala index a5b76b646c..5648aebb34 100644 --- a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala @@ -1056,7 +1056,7 @@ class DefaultClusterNode private[akka] ( .setMessageType(FUNCTION_FUN0_ANY) .setPayload(ByteString.copyFrom(Serializers.Java.toBinary(f))) .build - val results = replicaConnectionsForReplicationFactor(replicationFactor) map (_ !!! message) + val results = replicaConnectionsForReplicationFactor(replicationFactor) map (_ ? message) results.toList.asInstanceOf[List[Future[Any]]] } @@ -1082,7 +1082,7 @@ class DefaultClusterNode private[akka] ( .setMessageType(FUNCTION_FUN1_ARG_ANY) .setPayload(ByteString.copyFrom(Serializers.Java.toBinary((f, arg)))) .build - val results = replicaConnectionsForReplicationFactor(replicationFactor) map (_ !!! message) + val results = replicaConnectionsForReplicationFactor(replicationFactor) map (_ ? message) results.toList.asInstanceOf[List[Future[Any]]] } diff --git a/akka-cluster/src/main/scala/akka/cluster/Routing.scala b/akka-cluster/src/main/scala/akka/cluster/Routing.scala index d3bc4904f7..4ac6b0f4ab 100644 --- a/akka-cluster/src/main/scala/akka/cluster/Routing.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Routing.scala @@ -56,7 +56,7 @@ object Router { } def route[T](message: Any, timeout: Long)(implicit sender: Option[ActorRef]): Future[T] = next match { - case Some(actor) ⇒ actor.!!!(message, timeout)(sender) + case Some(actor) ⇒ actor.?(message, timeout)(sender) case _ ⇒ throwNoConnectionsError() } diff --git a/akka-docs/cluster/durable-mailbox.rst b/akka-docs/cluster/durable-mailbox.rst index 2d6fd2752e..b24e1fea30 100644 --- a/akka-docs/cluster/durable-mailbox.rst +++ b/akka-docs/cluster/durable-mailbox.rst @@ -18,7 +18,7 @@ in its mailbox. .. sidebar:: **IMPORTANT** None of these mailboxes work with blocking message send, e.g. the message - send operations that are relying on futures; ``!!``, ``!!!``, + send operations that are relying on futures; ``!!``, ``?``, ``sendRequestReply`` and ``ask``. If the node has crashed and then restarted, the thread that was blocked waiting for the reply is gone and there is no way we can deliver the message. diff --git a/akka-docs/disabled/getting-started-first.rst b/akka-docs/disabled/getting-started-first.rst index 6c955dc7f2..b0817fa22f 100644 --- a/akka-docs/disabled/getting-started-first.rst +++ b/akka-docs/disabled/getting-started-first.rst @@ -67,7 +67,7 @@ Here is the master actor: A couple of things are worth explaining further. -First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``!!!`` to achive the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now. +First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``?`` to achive the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now. Second, we are adding a couple of life-cycle callback methods; ``preStart`` and ``postStop``. In the ``preStart`` callback we are recording the time when the actor is started and in the ``postStop`` callback we are printing out the result (the approximation of Pi) and the time it took to calculate it. In this call we also invoke ``latch.countDown`` to tell the outside world that we are done. diff --git a/akka-docs/intro/getting-started-first-java.rst b/akka-docs/intro/getting-started-first-java.rst index 07c74dde63..57bfd01db9 100644 --- a/akka-docs/intro/getting-started-first-java.rst +++ b/akka-docs/intro/getting-started-first-java.rst @@ -435,7 +435,7 @@ Here is the master actor:: A couple of things are worth explaining further. -First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``!!!`` to achieve the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now. +First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``?`` to achieve the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now. Second, we are adding a couple of life-cycle callback methods; ``preStart`` and ``postStop``. In the ``preStart`` callback we are recording the time when the actor is started and in the ``postStop`` callback we are printing out the result (the approximation of Pi) and the time it took to calculate it. In this call we also invoke ``latch.countDown()`` to tell the outside world that we are done. diff --git a/akka-docs/intro/getting-started-first-scala-eclipse.rst b/akka-docs/intro/getting-started-first-scala-eclipse.rst index 3e4da8f619..d9c7356471 100644 --- a/akka-docs/intro/getting-started-first-scala-eclipse.rst +++ b/akka-docs/intro/getting-started-first-scala-eclipse.rst @@ -335,7 +335,7 @@ Here is the master actor:: A couple of things are worth explaining further. -First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``!!!`` to achieve the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now. +First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``?`` to achieve the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now. Second, we are adding a couple of life-cycle callback methods; ``preStart`` and ``postStop``. In the ``preStart`` callback we are recording the time when the actor is started and in the ``postStop`` callback we are printing out the result (the approximation of Pi) and the time it took to calculate it. In this call we also invoke ``latch.countDown`` to tell the outside world that we are done. diff --git a/akka-docs/intro/getting-started-first-scala.rst b/akka-docs/intro/getting-started-first-scala.rst index 2873531dfe..05ce8a15ce 100644 --- a/akka-docs/intro/getting-started-first-scala.rst +++ b/akka-docs/intro/getting-started-first-scala.rst @@ -329,7 +329,7 @@ Here is the master actor:: A couple of things are worth explaining further. -First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``!!!`` to achieve the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now. +First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``?`` to achieve the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now. Second, we are adding a couple of life-cycle callback methods; ``preStart`` and ``postStop``. In the ``preStart`` callback we are recording the time when the actor is started and in the ``postStop`` callback we are printing out the result (the approximation of Pi) and the time it took to calculate it. In this call we also invoke ``latch.countDown`` to tell the outside world that we are done. diff --git a/akka-docs/scala/actors.rst b/akka-docs/scala/actors.rst index 38076d7b58..2d0f105e0e 100644 --- a/akka-docs/scala/actors.rst +++ b/akka-docs/scala/actors.rst @@ -126,7 +126,7 @@ Messages are sent to an Actor through one of the “bang” methods. * ! means “fire-and-forget”, e.g. send a message asynchronously and return immediately. * !! means “send-and-reply-eventually”, e.g. send a message asynchronously and wait for a reply through aFuture. Here you can specify a timeout. Using timeouts is very important. If no timeout is specified then the actor’s default timeout (set by the this.timeout variable in the actor) is used. This method returns an ``Option[Any]`` which will be either ``Some(result)`` if returning successfully or None if the call timed out. -* !!! sends a message asynchronously and returns a ``Future``. +* ? sends a message asynchronously and returns a ``Future``. You can check if an Actor can handle a specific message by invoking the ``isDefinedAt`` method: @@ -180,11 +180,11 @@ Here are some examples: Send-And-Receive-Future ^^^^^^^^^^^^^^^^^^^^^^^ -Using ``!!!`` will send a message to the receiving Actor asynchronously and will return a 'Future': +Using ``?`` will send a message to the receiving Actor asynchronously and will return a 'Future': .. code-block:: scala - val future = actor !!! "Hello" + val future = actor ? "Hello" See :ref:`futures-scala` for more information. @@ -329,7 +329,7 @@ The same pattern holds for using the ``senderFuture`` in the section below. Reply using the sender future ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If a message was sent with the ``!!`` or ``!!!`` methods, which both implements request-reply semantics using Future's, then you either have the option of replying using the ``reply`` method as above. This method will then resolve the Future. But you can also get a reference to the Future directly and resolve it yourself or if you would like to store it away to resolve it later, or pass it on to some other Actor to resolve it. +If a message was sent with the ``!!`` or ``?`` methods, which both implements request-reply semantics using Future's, then you either have the option of replying using the ``reply`` method as above. This method will then resolve the Future. But you can also get a reference to the Future directly and resolve it yourself or if you would like to store it away to resolve it later, or pass it on to some other Actor to resolve it. The reference to the Future resides in the ``senderFuture: Option[Promise[_]]`` member field in the ``ActorRef`` class. @@ -427,7 +427,7 @@ PoisonPill You can also send an actor the ``akka.actor.PoisonPill`` message, which will stop the actor when the message is processed. -If the sender is a ``Future`` (e.g. the message is sent with ``!!`` or ``!!!``), the ``Future`` will be completed with an ``akka.actor.ActorKilledException("PoisonPill")``. +If the sender is a ``Future`` (e.g. the message is sent with ``!!`` or ``?``), the ``Future`` will be completed with an ``akka.actor.ActorKilledException("PoisonPill")``. HotSwap ------- @@ -457,7 +457,7 @@ To hotswap the Actor using ``become``: .. code-block:: scala def angry: Receive = { - case "foo" => self reply "I am already angry!!!" + case "foo" => self reply "I am already angry?" case "bar" => become(happy) } diff --git a/akka-docs/scala/futures.rst b/akka-docs/scala/futures.rst index de58118a4f..a9955d9a50 100644 --- a/akka-docs/scala/futures.rst +++ b/akka-docs/scala/futures.rst @@ -17,11 +17,11 @@ Use with Actors There are generally two ways of getting a reply from an ``Actor``: the first is by a sent message (``actor ! msg``), which only works if the original sender was an ``Actor``) and the second is through a ``Future``. -Using an ``Actor``\'s ``!!!`` method to send a message will return a Future. To wait for and retrieve the actual result the simplest method is: +Using an ``Actor``\'s ``?`` method to send a message will return a Future. To wait for and retrieve the actual result the simplest method is: .. code-block:: scala - val future = actor !!! msg + val future = actor ? msg val result: Any = future.get() 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`_. @@ -141,13 +141,13 @@ The example for comprehension above is an example of composing ``Future``\s. A c .. code-block:: scala - val f1 = actor1 !!! msg1 - val f2 = actor2 !!! msg2 + val f1 = actor1 ? msg1 + val f2 = actor2 ? msg2 val a: Int = f1.get() val b: Int = f2.get() - val f3 = actor3 !!! (a + b) + val f3 = actor3 ? (a + b) val result: String = f3.get() @@ -155,13 +155,13 @@ Here we wait for the results from the first 2 ``Actor``\s before sending that re .. code-block:: scala - val f1 = actor1 !!! msg1 - val f2 = actor2 !!! msg2 + val f1 = actor1 ? msg1 + val f2 = actor2 ? msg2 val f3 = for { a: Int <- f1 b: Int <- f2 - c: String <- actor3 !!! (a + b) + c: String <- actor3 ? (a + b) } yield c val result = f3.get() @@ -173,7 +173,7 @@ This is fine when dealing with a known amount of Actors, but can grow unwieldy i .. code-block:: scala // oddActor returns odd numbers sequentially from 1 - val listOfFutures: List[Future[Int]] = List.fill(100)(oddActor !!! GetNext) + val listOfFutures: List[Future[Int]] = List.fill(100)(oddActor ? GetNext) // now we have a Future[List[Int]] val futureList = Future.sequence(listOfFutures) @@ -242,7 +242,7 @@ It is also possible to handle an ``Exception`` by returning a different result. .. code-block:: scala - val future = actor !!! msg1 recover { + val future = actor ? msg1 recover { case e: ArithmeticException => 0 } diff --git a/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableDispatcher.scala b/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableDispatcher.scala index 142fbea84f..599cc21f5c 100644 --- a/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableDispatcher.scala +++ b/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableDispatcher.scala @@ -95,7 +95,7 @@ case class DurableDispatcher( private[akka] override def dispatch(invocation: MessageInvocation): Unit = { if (invocation.senderFuture.isDefined) - throw new IllegalArgumentException("Durable mailboxes do not support Future-based messages from !! and !!!") + throw new IllegalArgumentException("Durable mailboxes do not support Future-based messages from !! and ?") super.dispatch(invocation) } @@ -132,7 +132,7 @@ case class DurablePinnedDispatcher( private[akka] override def dispatch(invocation: MessageInvocation): Unit = { if (invocation.senderFuture.isDefined) - throw new IllegalArgumentException("Actor has a durable mailbox that does not support !! or !!!") + throw new IllegalArgumentException("Actor has a durable mailbox that does not support !! or ?") super.dispatch(invocation) } } diff --git a/akka-spring/src/test/resources/akka-test.conf b/akka-spring/src/test/resources/akka-test.conf index a84c60798a..a32dc0f338 100644 --- a/akka-spring/src/test/resources/akka-test.conf +++ b/akka-spring/src/test/resources/akka-test.conf @@ -25,7 +25,7 @@ akka { actor { timeout = 2000 # Default timeout for Future based invocations - # - Actor: !! && !!! + # - Actor: !! && ? # - UntypedActor: sendRequestReply && ask # - TypedActor: methods with non-void return type serialize-messages = off # Does a deep clone of (non-primitive) messages to ensure immutability diff --git a/akka-stm/src/main/scala/akka/agent/Agent.scala b/akka-stm/src/main/scala/akka/agent/Agent.scala index dfa3cfd6b8..300a038020 100644 --- a/akka-stm/src/main/scala/akka/agent/Agent.scala +++ b/akka-stm/src/main/scala/akka/agent/Agent.scala @@ -120,7 +120,7 @@ class Agent[T](initialValue: T) { * within the given timeout */ def alter(f: T ⇒ T)(timeout: Long): Future[T] = { - def dispatch = updater.!!!(Update(f), timeout) + def dispatch = updater.?(Update(f), timeout) if (Stm.activeTransaction) { val result = new DefaultPromise[T](timeout) get //Join xa @@ -168,7 +168,7 @@ class Agent[T](initialValue: T) { send((value: T) ⇒ { suspend val threadBased = Actor.actorOf(new ThreadBasedAgentUpdater(this)).start() - result completeWith threadBased.!!!(Update(f), timeout) + result completeWith threadBased.?(Update(f), timeout) value }) result @@ -178,7 +178,7 @@ class Agent[T](initialValue: T) { * A future to the current value that will be completed after any currently * queued updates. */ - def future(): Future[T] = (updater !!! Get).asInstanceOf[Future[T]] + def future(): Future[T] = (updater ? Get).asInstanceOf[Future[T]] /** * Gets this agent's value after all currently queued updates have completed. diff --git a/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala b/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala index 5d74db9b34..c952697fe7 100644 --- a/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala +++ b/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala @@ -190,7 +190,7 @@ class TestActorRefSpec extends WordSpec with MustMatchers with BeforeAndAfterEac "support futures" in { val a = TestActorRef[WorkerActor].start() - val f: Future[String] = a !!! "work" + val f: Future[String] = a ? "work" f must be('completed) f.get must equal("workDone") } 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 ff4b8d9c4a..e36f0a03d9 100644 --- a/akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala +++ b/akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala @@ -104,7 +104,7 @@ object Pi extends App { val start = now //send calculate message - master.!!![Double](Calculate, timeout = 60000). + master.?[Double](Calculate, timeout = 60000). await.resultOrException match {//wait for the result, with a 60 seconds timeout case Some(pi) => EventHandler.info(this, "\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis".format(pi, (now - start))) diff --git a/config/akka-reference.conf b/config/akka-reference.conf index 5793377029..9b43aa97b1 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -28,7 +28,7 @@ akka { actor { timeout = 5 # Default timeout for Future based invocations - # - Actor: !! && !!! + # - Actor: !! && ? # - UntypedActor: sendRequestReply && ask # - TypedActor: methods with non-void return type serialize-messages = off # Does a deep clone of (non-primitive) messages to ensure immutability From 6ddee70e0bb4fb08d4f1c4d4c8b12ad2005b5169 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 13 Jun 2011 14:59:22 +0200 Subject: [PATCH 03/13] Creating a package object for the akka-package to put the .as-conversions there, also switching over at places from !! to ? --- .../scala/akka/actor/actor/ActorRefSpec.scala | 12 +++--- .../akka/actor/actor/ForwardActorSpec.scala | 5 +-- ...ec.scala => BalancingDispatcherSpec.scala} | 0 ...orSpec.scala => DispatcherActorSpec.scala} | 2 +- ...sSpec.scala => DispatcherActorsSpec.scala} | 0 ...dActorSpec.scala => PinnedActorSpec.scala} | 12 +++--- .../test/scala/akka/routing/RoutingSpec.scala | 8 ++-- .../src/main/scala/akka/actor/Actor.scala | 41 ++----------------- .../src/main/scala/akka/actor/ActorRef.scala | 13 +++--- .../main/scala/akka/actor/TypedActor.scala | 2 +- akka-actor/src/main/scala/akka/package.scala | 39 ++++++++++++++++++ .../TypedConsumerPublishRequestorTest.scala | 16 ++++---- .../RoundRobin1ReplicaMultiJvmSpec.scala | 4 +- .../RoundRobin2ReplicasMultiJvmSpec.scala | 18 ++++---- 14 files changed, 87 insertions(+), 85 deletions(-) rename akka-actor-tests/src/test/scala/akka/dispatch/{ExecutorBasedEventDrivenWorkStealingDispatcherSpec.scala => BalancingDispatcherSpec.scala} (100%) rename akka-actor-tests/src/test/scala/akka/dispatch/{ExecutorBasedEventDrivenDispatcherActorSpec.scala => DispatcherActorSpec.scala} (98%) rename akka-actor-tests/src/test/scala/akka/dispatch/{ExecutorBasedEventDrivenDispatcherActorsSpec.scala => DispatcherActorsSpec.scala} (100%) rename akka-actor-tests/src/test/scala/akka/dispatch/{ThreadBasedActorSpec.scala => PinnedActorSpec.scala} (85%) create mode 100644 akka-actor/src/main/scala/akka/package.scala diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRefSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRefSpec.scala index cd76784249..913f5e510d 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRefSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRefSpec.scala @@ -260,7 +260,7 @@ class ActorRefSpec extends WordSpec with MustMatchers { def receive = { case _ ⇒ self reply nested } }).start() - val nested = (a !! "any").get.asInstanceOf[ActorRef] + val nested: ActorRef = (a ? "any").get a must not be null nested must not be null (a ne nested) must be === true @@ -268,13 +268,13 @@ class ActorRefSpec extends WordSpec with MustMatchers { "support advanced nested actorOfs" in { val a = Actor.actorOf(new OuterActor(Actor.actorOf(new InnerActor).start)).start - val inner = (a !! "innerself").get + val inner = (a ? "innerself").as[Any].get - (a !! a).get must be(a) - (a !! "self").get must be(a) + (a ? a).as[ActorRef].get must be(a) + (a ? "self").as[ActorRef].get must be(a) inner must not be a - (a !! "msg").get must be === "msg" + (a ? "msg").as[String] must be === Some("msg") } "support reply via channel" in { @@ -314,7 +314,7 @@ class ActorRefSpec extends WordSpec with MustMatchers { val fnull: Future[String] = ref ? null intercept[ActorKilledException] { - ref !! PoisonPill + (ref ? PoisonPill).get fail("shouldn't get here") } diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/ForwardActorSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/ForwardActorSpec.scala index 2c6773bc1c..eea3355b74 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/ForwardActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/ForwardActorSpec.scala @@ -50,10 +50,7 @@ object ForwardActorSpec { val latch = TestLatch() val forwardActor = actorOf[ForwardActor] forwardActor.start() - (forwardActor !! "SendBangBang") match { - case Some(_) ⇒ latch.countDown() - case None ⇒ {} - } + forwardActor ? "SendBangBang" onComplete { _ ⇒ latch.countDown() } def receive = { case _ ⇒ {} } diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcherSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/BalancingDispatcherSpec.scala similarity index 100% rename from akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcherSpec.scala rename to akka-actor-tests/src/test/scala/akka/dispatch/BalancingDispatcherSpec.scala diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/DispatcherActorSpec.scala similarity index 98% rename from akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorSpec.scala rename to akka-actor-tests/src/test/scala/akka/dispatch/DispatcherActorSpec.scala index 69fc9ea635..49bcc5b224 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/DispatcherActorSpec.scala @@ -45,7 +45,7 @@ class DispatcherActorSpec extends JUnitSuite { @Test def shouldSendReplySync = { val actor = actorOf[TestActor].start() - val result = (actor !! ("Hello", 10000)).as[String] + val result = (actor ? ("Hello", 10000)).as[String] assert("World" === result.get) actor.stop() } diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorsSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/DispatcherActorsSpec.scala similarity index 100% rename from akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorsSpec.scala rename to akka-actor-tests/src/test/scala/akka/dispatch/DispatcherActorsSpec.scala diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/ThreadBasedActorSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/PinnedActorSpec.scala similarity index 85% rename from akka-actor-tests/src/test/scala/akka/dispatch/ThreadBasedActorSpec.scala rename to akka-actor-tests/src/test/scala/akka/dispatch/PinnedActorSpec.scala index 722c48fbdc..71881ce465 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/ThreadBasedActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/PinnedActorSpec.scala @@ -8,7 +8,7 @@ import akka.dispatch.Dispatchers import akka.actor.Actor import Actor._ -object ThreadBasedActorSpec { +object PinnedActorSpec { class TestActor extends Actor { self.dispatcher = Dispatchers.newPinnedDispatcher(self) @@ -21,8 +21,8 @@ object ThreadBasedActorSpec { } } -class ThreadBasedActorSpec extends JUnitSuite { - import ThreadBasedActorSpec._ +class PinnedActorSpec extends JUnitSuite { + import PinnedActorSpec._ private val unit = TimeUnit.MILLISECONDS @@ -43,7 +43,7 @@ class ThreadBasedActorSpec extends JUnitSuite { @Test def shouldSendReplySync = { val actor = actorOf[TestActor].start() - val result = (actor !! ("Hello", 10000)).as[String] + val result = (actor ? ("Hello", 10000)).as[String] assert("World" === result.get) actor.stop() } @@ -51,8 +51,8 @@ class ThreadBasedActorSpec extends JUnitSuite { @Test def shouldSendReplyAsync = { val actor = actorOf[TestActor].start() - val result = actor !! "Hello" - assert("World" === result.get.asInstanceOf[String]) + val result = (actor ? "Hello").as[String] + assert("World" === result.get) actor.stop() } diff --git a/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala b/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala index 2375d128a6..5544f11fd0 100644 --- a/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala @@ -7,12 +7,12 @@ import akka.testing._ import akka.testing.Testing.{ sleepFor, testMillis } import akka.util.duration._ +import akka.actor._ import akka.actor.Actor._ import akka.routing._ import java.util.concurrent.atomic.AtomicInteger import akka.dispatch.{ KeptPromise, Future } -import akka.actor.{ TypedActor, Actor } object RoutingSpec { trait Foo { @@ -56,9 +56,9 @@ class RoutingSpec extends WordSpec with MustMatchers { }.start() val result = for { - a ← (d !! (Test1, testMillis(5 seconds))).as[Int] - b ← (d !! (Test2, testMillis(5 seconds))).as[Int] - c ← (d !! (Test3, testMillis(5 seconds))).as[Int] + a ← (d ? (Test1, testMillis(5 seconds))).as[Int] + b ← (d ? (Test2, testMillis(5 seconds))).as[Int] + c ← (d ? (Test3, testMillis(5 seconds))).as[Int] } yield a + b + c result.isDefined must be(true) diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 02b508db03..4f8a77cec6 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -10,7 +10,6 @@ import akka.config._ import Config._ import akka.util.{ ListenerManagement, ReflectiveAccess, Duration, Helpers } import ReflectiveAccess._ -import Helpers.{ narrow, narrowSilently } import akka.remoteinterface.RemoteSupport import akka.japi.{ Creator, Procedure } import akka.AkkaException @@ -321,25 +320,6 @@ 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 - }) - private[akka] def createActor(address: String, actorFactory: () ⇒ ActorRef): ActorRef = { Address.validate(address) registry.actorFor(address) match { // check if the actor for the address is already in the registry @@ -475,7 +455,7 @@ object Actor extends ListenerManagement { * *

* Here you find functions like: - * - !, !!, ? and forward + * - !, ? and forward * - link, unlink, startLink etc * - start, stop * - etc. @@ -543,7 +523,7 @@ trait Actor { * Option[ActorRef] representation of the 'self' ActorRef reference. *

* Mainly for internal use, functions as the implicit sender references when invoking - * one of the message send functions ('!', '!!' and '?'). + * one of the message send functions ('!' and '?'). */ implicit def optionSelf: Option[ActorRef] = someSelf @@ -692,19 +672,4 @@ trait Actor { } private lazy val processingBehavior = receive //ProcessingBehavior is the original behavior -} - -private[actor] class AnyOptionAsTypedOption(anyOption: Option[Any]) { - - /** - * Convenience helper to cast the given Option of Any to an Option of the given type. Will throw a ClassCastException - * if the actual type is not assignable from the given one. - */ - def as[T]: Option[T] = narrow[T](anyOption) - - /** - * Convenience helper to cast the given Option of Any to an Option of the given type. Will swallow a possible - * ClassCastException and return None in that case. - */ - def asSilently[T: Manifest]: Option[T] = narrowSilently[T](anyOption) -} +} \ No newline at end of file diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index 30814566bc..14def91a64 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -101,8 +101,8 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] with S /** * User overridable callback/setting. *

- * Defines the default timeout for '!!' and '?' invocations, - * e.g. the timeout for the future returned by the call to '!!' and '?'. + * Defines the default timeout for '?'/'ask' invocations, + * e.g. the timeout for the future returned by the call to '?'/'ask'. */ @deprecated("Will be replaced by implicit-scoped timeout on all methods that needs it, will default to timeout specified in config", "1.1") @BeanProperty @@ -210,7 +210,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] with S /** * Akka Java API.

* The reference sender future of the last received message. - * Is defined if the message was sent with sent with '!!' or '?', else None. + * Is defined if the message was sent with sent with '?'/'ask', else None. */ def getSenderFuture: Option[Promise[Any]] = senderFuture @@ -1148,7 +1148,7 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ /** * The reference sender future of the last received message. - * Is defined if the message was sent with sent with '!!' or '?', else None. + * Is defined if the message was sent with sent with '?'/'ask', else None. */ def senderFuture(): Option[Promise[Any]] = { val msg = currentMessage @@ -1188,6 +1188,7 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ * If you are sending messages using !! then you have to use self.reply(..) * to send a reply message to the original sender. If not then the sender will block until the timeout expires. */ + @deprecated("DELETE ME AND UPDATE DOCS!") def !!(message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Option[Any] = { if (isRunning) { val future = postMessageToMailboxAndCreateFutureResultWithTimeout[Any](message, timeout, sender, None) @@ -1217,9 +1218,9 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ /** * Forwards the message and passes the original sender actor as the sender. *

- * Works with '!', '!!' and '?'. + * Works with '!' and '?'/'ask'. */ - def forward(message: Any)(implicit sender: Some[ActorRef]) = { + def forward(message: Any)(implicit sender: Some[ActorRef]) { if (isRunning) { if (sender.get.senderFuture.isDefined) postMessageToMailboxAndCreateFutureResultWithTimeout(message, timeout, sender.get.sender, sender.get.senderFuture) diff --git a/akka-actor/src/main/scala/akka/actor/TypedActor.scala b/akka-actor/src/main/scala/akka/actor/TypedActor.scala index 96324f3f4b..9c103a285d 100644 --- a/akka-actor/src/main/scala/akka/actor/TypedActor.scala +++ b/akka-actor/src/main/scala/akka/actor/TypedActor.scala @@ -5,7 +5,7 @@ package akka.actor */ import akka.japi.{ Creator, Option ⇒ JOption } -import akka.actor.Actor.{ actorOf, futureToAnyOptionAsTypedOption } +import akka.actor.Actor._ import akka.dispatch.{ MessageDispatcher, Dispatchers, Future } import java.lang.reflect.{ InvocationTargetException, Method, InvocationHandler, Proxy } import akka.util.{ Duration } diff --git a/akka-actor/src/main/scala/akka/package.scala b/akka-actor/src/main/scala/akka/package.scala new file mode 100644 index 0000000000..269e3e068e --- /dev/null +++ b/akka-actor/src/main/scala/akka/package.scala @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2009-2011 Scalable Solutions AB + */ + +import akka.dispatch.{ FutureTimeoutException, Future } +import akka.util.Helpers.{ narrow, narrowSilently } + +package object akka { + /** + * 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] (Recommended) + */ + implicit def futureToAnyOptionAsTypedOption(anyFuture: Future[_]) = new AnyOptionAsTypedOption({ + try { anyFuture.await } catch { case t: FutureTimeoutException ⇒ } + anyFuture.resultOrException + }) + + private[akka] class AnyOptionAsTypedOption(anyOption: Option[Any]) { + /** + * Convenience helper to cast the given Option of Any to an Option of the given type. Will throw a ClassCastException + * if the actual type is not assignable from the given one. + */ + def as[T]: Option[T] = narrow[T](anyOption) + + /** + * Convenience helper to cast the given Option of Any to an Option of the given type. Will swallow a possible + * ClassCastException and return None in that case. + */ + def asSilently[T: Manifest]: Option[T] = narrowSilently[T](anyOption) + } +} diff --git a/akka-camel-typed/src/test/scala/akka/camel/TypedConsumerPublishRequestorTest.scala b/akka-camel-typed/src/test/scala/akka/camel/TypedConsumerPublishRequestorTest.scala index cc4ff0f0cf..03c727834b 100644 --- a/akka-camel-typed/src/test/scala/akka/camel/TypedConsumerPublishRequestorTest.scala +++ b/akka-camel-typed/src/test/scala/akka/camel/TypedConsumerPublishRequestorTest.scala @@ -40,10 +40,10 @@ class TypedConsumerPublishRequestorTest extends JUnitSuite { @Test def shouldReceiveOneConsumerMethodRegisteredEvent = { Actor.registry.addListener(requestor) - val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get + val latch = (publisher ? SetExpectedTestMessageCount(1)).as[CountDownLatch].get val obj = TypedActor.typedActorOf(classOf[SampleTypedSingleConsumer], classOf[SampleTypedSingleConsumerImpl], defaultConfiguration) assert(latch.await(5000, TimeUnit.MILLISECONDS)) - val event = (publisher !! GetRetainedMessage).as[ConsumerMethodRegistered].get + val event = (publisher ? GetRetainedMessage).as[ConsumerMethodRegistered].get assert(event.endpointUri === "direct:foo") assert(event.typedActor === obj) assert(event.methodName === "foo") @@ -52,11 +52,11 @@ class TypedConsumerPublishRequestorTest extends JUnitSuite { @Test def shouldReceiveOneConsumerMethodUnregisteredEvent = { val obj = TypedActor.typedActorOf(classOf[SampleTypedSingleConsumer], classOf[SampleTypedSingleConsumerImpl], defaultConfiguration) - val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get + val latch = (publisher ? SetExpectedTestMessageCount(1)).as[CountDownLatch].get Actor.registry.addListener(requestor) TypedActor.stop(obj) assert(latch.await(5000, TimeUnit.MILLISECONDS)) - val event = (publisher !! GetRetainedMessage).as[ConsumerMethodUnregistered].get + val event = (publisher ? GetRetainedMessage).as[ConsumerMethodUnregistered].get assert(event.endpointUri === "direct:foo") assert(event.typedActor === obj) assert(event.methodName === "foo") @@ -65,23 +65,23 @@ class TypedConsumerPublishRequestorTest extends JUnitSuite { @Test def shouldReceiveThreeConsumerMethodRegisteredEvents = { Actor.registry.addListener(requestor) - val latch = (publisher !! SetExpectedTestMessageCount(3)).as[CountDownLatch].get + val latch = (publisher ? SetExpectedTestMessageCount(3)).as[CountDownLatch].get val obj = TypedActor.typedActorOf(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl], defaultConfiguration) assert(latch.await(5000, TimeUnit.MILLISECONDS)) val request = GetRetainedMessages(_.isInstanceOf[ConsumerMethodRegistered]) - val events = (publisher !! request).as[List[ConsumerMethodRegistered]].get + val events = (publisher ? request).as[List[ConsumerMethodRegistered]].get assert(events.map(_.method.getName).sortWith(_ < _) === List("m2", "m3", "m4")) } @Test def shouldReceiveThreeConsumerMethodUnregisteredEvents = { val obj = TypedActor.typedActorOf(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl], defaultConfiguration) - val latch = (publisher !! SetExpectedTestMessageCount(3)).as[CountDownLatch].get + val latch = (publisher ? SetExpectedTestMessageCount(3)).as[CountDownLatch].get Actor.registry.addListener(requestor) TypedActor.stop(obj) assert(latch.await(5000, TimeUnit.MILLISECONDS)) val request = GetRetainedMessages(_.isInstanceOf[ConsumerMethodUnregistered]) - val events = (publisher !! request).as[List[ConsumerMethodUnregistered]].get + val events = (publisher ? request).as[List[ConsumerMethodUnregistered]].get assert(events.map(_.method.getName).sortWith(_ < _) === List("m2", "m3", "m4")) } } diff --git a/akka-cluster/src/test/scala/akka/cluster/routing/roundrobin_1_replica/RoundRobin1ReplicaMultiJvmSpec.scala b/akka-cluster/src/test/scala/akka/cluster/routing/roundrobin_1_replica/RoundRobin1ReplicaMultiJvmSpec.scala index 1c4841ee2e..52f85fead1 100644 --- a/akka-cluster/src/test/scala/akka/cluster/routing/roundrobin_1_replica/RoundRobin1ReplicaMultiJvmSpec.scala +++ b/akka-cluster/src/test/scala/akka/cluster/routing/roundrobin_1_replica/RoundRobin1ReplicaMultiJvmSpec.scala @@ -13,7 +13,7 @@ import BKException._ import akka.cluster._ import akka.actor._ -import Actor._ +import akka.actor.Actor._ import akka.config.Config object RoundRobin1ReplicaMultiJvmSpec { @@ -90,7 +90,7 @@ class RoundRobin1ReplicaMultiJvmNode2 extends WordSpec with MustMatchers { Cluster.barrier("send-message-from-node2-to-node1", NrOfNodes) { hello must not equal (null) - val reply = (hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node1")) + val reply = (hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node1")) reply must equal("World from node [node1]") } diff --git a/akka-cluster/src/test/scala/akka/cluster/routing/roundrobin_2_replicas/RoundRobin2ReplicasMultiJvmSpec.scala b/akka-cluster/src/test/scala/akka/cluster/routing/roundrobin_2_replicas/RoundRobin2ReplicasMultiJvmSpec.scala index c9c864fc27..a65abd2b1c 100644 --- a/akka-cluster/src/test/scala/akka/cluster/routing/roundrobin_2_replicas/RoundRobin2ReplicasMultiJvmSpec.scala +++ b/akka-cluster/src/test/scala/akka/cluster/routing/roundrobin_2_replicas/RoundRobin2ReplicasMultiJvmSpec.scala @@ -13,7 +13,7 @@ import BKException._ import akka.cluster._ import akka.actor._ -import Actor._ +import akka.actor.Actor._ import akka.config.Config object RoundRobin2ReplicasMultiJvmSpec { @@ -102,14 +102,14 @@ class RoundRobin2ReplicasMultiJvmNode2 extends WordSpec with MustMatchers { else replies.put(reply, replies(reply) + 1) } - count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node1"))) - count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node3"))) - count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node1"))) - count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node3"))) - count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node1"))) - count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node3"))) - count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node1"))) - count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node3"))) + count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node1"))) + count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node3"))) + count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node1"))) + count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node3"))) + count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node1"))) + count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node3"))) + count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node1"))) + count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node3"))) replies("World from node [node1]") must equal(4) replies("World from node [node3]") must equal(4) From 9c1a50ba2c672cd263ece0ff85c57441e4e9f22a Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 13 Jun 2011 15:29:35 +0200 Subject: [PATCH 04/13] Refactoring the use of !! to ?+.get for Akka internal code --- .../akka/actor/supervisor/SupervisorSpec.scala | 16 ++++++++-------- .../akka/dispatch/DispatcherActorSpec.scala | 6 +++--- .../scala/akka/dispatch/PinnedActorSpec.scala | 2 +- .../test/scala/akka/routing/RoutingSpec.scala | 16 ++++++++-------- .../src/main/scala/akka/cluster/Cluster.scala | 2 +- .../akka/cluster/ClusteredPingPongSample.scala | 2 +- .../src/main/scala/OsgiExample.scala | 6 +++--- .../transactor/CoordinatedIncrementSpec.scala | 4 ++-- .../scala/transactor/FickleFriendsSpec.scala | 4 ++-- .../test/scala/transactor/TransactorSpec.scala | 4 ++-- .../scala/akka/testkit/TestActorRefSpec.scala | 6 +++--- 11 files changed, 34 insertions(+), 34 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 e2aa812e34..53bec09d4e 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 @@ -66,7 +66,7 @@ object SupervisorSpec { } override def receive = { - case Die ⇒ temp !! (Die, TimeoutMillis) + case Die ⇒ (temp ? (Die, TimeoutMillis)).get } } @@ -200,13 +200,13 @@ class SupervisorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach } def ping(pingPongActor: ActorRef) = { - (pingPongActor !! (Ping, TimeoutMillis)).getOrElse("nil") must be(PongMessage) - messageLogPoll must be(PingMessage) + (pingPongActor ? (Ping, TimeoutMillis)).as[String].getOrElse("nil") must be === PongMessage + messageLogPoll must be === PingMessage } def kill(pingPongActor: ActorRef) = { intercept[RuntimeException] { pingPongActor !! (Die, TimeoutMillis) } - messageLogPoll must be(ExceptionMessage) + messageLogPoll must be === ExceptionMessage } "A supervisor" must { @@ -215,7 +215,7 @@ class SupervisorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach val master = actorOf[Master].start() intercept[RuntimeException] { - master !! (Die, TimeoutMillis) + (master ? (Die, TimeoutMillis)).get } sleepFor(1 second) @@ -226,7 +226,7 @@ class SupervisorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach val (temporaryActor, supervisor) = temporaryActorAllForOne intercept[RuntimeException] { - temporaryActor !! (Die, TimeoutMillis) + (temporaryActor ? (Die, TimeoutMillis)).get } sleepFor(1 second) @@ -374,13 +374,13 @@ class SupervisorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach Supervise(dyingActor, Permanent) :: Nil)) intercept[Exception] { - dyingActor !! (Die, TimeoutMillis) + (dyingActor ? (Die, TimeoutMillis)).get } // give time for restart sleepFor(3 seconds) - (dyingActor !! (Ping, TimeoutMillis)).getOrElse("nil") must be(PongMessage) + (dyingActor ? (Ping, TimeoutMillis)).as[String].getOrElse("nil") must be === PongMessage inits.get must be(3) diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/DispatcherActorSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/DispatcherActorSpec.scala index 49bcc5b224..6d7fd5e571 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/DispatcherActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/DispatcherActorSpec.scala @@ -53,8 +53,8 @@ class DispatcherActorSpec extends JUnitSuite { @Test def shouldSendReplyAsync = { val actor = actorOf[TestActor].start() - val result = actor !! "Hello" - assert("World" === result.get.asInstanceOf[String]) + val result = (actor ? "Hello").as[String] + assert("World" === result.get) actor.stop() } @@ -62,7 +62,7 @@ class DispatcherActorSpec extends JUnitSuite { def shouldSendReceiveException = { val actor = actorOf[TestActor].start() try { - actor !! "Failure" + (actor ? "Failure").get fail("Should have thrown an exception") } catch { case e ⇒ diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/PinnedActorSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/PinnedActorSpec.scala index 71881ce465..b45f514cf6 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/PinnedActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/PinnedActorSpec.scala @@ -60,7 +60,7 @@ class PinnedActorSpec extends JUnitSuite { def shouldSendReceiveException = { val actor = actorOf[TestActor].start() try { - actor !! "Failure" + (actor ? "Failure").get fail("Should have thrown an exception") } catch { case e ⇒ diff --git a/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala b/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala index 5544f11fd0..357695b69e 100644 --- a/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala @@ -208,7 +208,7 @@ class RoutingSpec extends WordSpec with MustMatchers { count.get must be(2) - (pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size must be(2) + (pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2) pool.stop() } @@ -276,7 +276,7 @@ class RoutingSpec extends WordSpec with MustMatchers { pool ! 1 - (pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size must be(2) + (pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2) var loops = 0 def loop(t: Int) = { @@ -296,7 +296,7 @@ class RoutingSpec extends WordSpec with MustMatchers { latch.await count.get must be(loops) - (pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size must be(2) + (pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2) // a whole bunch should max it out @@ -305,7 +305,7 @@ class RoutingSpec extends WordSpec with MustMatchers { latch.await count.get must be(loops) - (pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size must be(4) + (pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(4) pool.stop() } @@ -353,7 +353,7 @@ class RoutingSpec extends WordSpec with MustMatchers { latch.await count.get must be(loops) - (pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size must be(2) + (pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2) // send a bunch over the theshold and observe an increment loops = 15 @@ -362,7 +362,7 @@ class RoutingSpec extends WordSpec with MustMatchers { latch.await(10 seconds) count.get must be(loops) - (pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size must be >= (3) + (pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be >= (3) pool.stop() } @@ -458,7 +458,7 @@ class RoutingSpec extends WordSpec with MustMatchers { sleepFor(5 millis) - val z = (pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size + val z = (pool ? ActorPool.Stat).as[ActorPool.Stats].get.size z must be >= (2) @@ -469,7 +469,7 @@ class RoutingSpec extends WordSpec with MustMatchers { sleepFor(500 millis) } - (pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size must be <= (z) + (pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be <= (z) pool.stop() } diff --git a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala index 5648aebb34..cc9fbfaf2f 100644 --- a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala @@ -665,7 +665,7 @@ class DefaultClusterNode private[akka] ( .build replicaConnectionsForReplicationFactor(replicationFactor) foreach { connection ⇒ - (connection !! (command, remoteDaemonAckTimeout)) match { + (connection ? (command, remoteDaemonAckTimeout)).as[Status] match { case Some(Success) ⇒ EventHandler.debug(this, "Replica for [%s] successfully created".format(actorRef.address)) diff --git a/akka-cluster/src/test/scala/akka/cluster/ClusteredPingPongSample.scala b/akka-cluster/src/test/scala/akka/cluster/ClusteredPingPongSample.scala index 4e1b358d89..43ef424aa8 100644 --- a/akka-cluster/src/test/scala/akka/cluster/ClusteredPingPongSample.scala +++ b/akka-cluster/src/test/scala/akka/cluster/ClusteredPingPongSample.scala @@ -42,7 +42,7 @@ object PingPong { count += 1 self reply Ball } else { - self.sender.foreach(_ !! Stop) + self.sender.foreach(s ⇒ (s ? Stop).await) gameOverLatch.countDown self.stop } diff --git a/akka-samples/akka-sample-osgi/src/main/scala/OsgiExample.scala b/akka-samples/akka-sample-osgi/src/main/scala/OsgiExample.scala index 092fcf9d36..5ee3f016a9 100644 --- a/akka-samples/akka-sample-osgi/src/main/scala/OsgiExample.scala +++ b/akka-samples/akka-sample-osgi/src/main/scala/OsgiExample.scala @@ -3,8 +3,8 @@ */ package sample.osgi -import akka.actor.{ Actor, ActorRegistry } -import Actor._ +import akka.actor.Actor +import akka.actor.Actor._ import org.osgi.framework.{ BundleActivator, BundleContext } @@ -13,7 +13,7 @@ class Activator extends BundleActivator { def start(context: BundleContext) { println("Starting the OSGi example ...") val echo = actorOf[EchoActor].start() - val answer = (echo !! "OSGi example") + val answer = (echo ? "OSGi example").as[String] println(answer getOrElse "No answer!") } diff --git a/akka-stm/src/test/scala/transactor/CoordinatedIncrementSpec.scala b/akka-stm/src/test/scala/transactor/CoordinatedIncrementSpec.scala index 5615c97322..8e589c6ff8 100644 --- a/akka-stm/src/test/scala/transactor/CoordinatedIncrementSpec.scala +++ b/akka-stm/src/test/scala/transactor/CoordinatedIncrementSpec.scala @@ -64,7 +64,7 @@ class CoordinatedIncrementSpec extends WordSpec with MustMatchers { counters(0) ! coordinated(Increment(counters.tail)) coordinated.await for (counter ← counters) { - (counter !! GetCount).get must be === 1 + (counter ? GetCount).as[Int].get must be === 1 } counters foreach (_.stop()) failer.stop() @@ -76,7 +76,7 @@ class CoordinatedIncrementSpec extends WordSpec with MustMatchers { counters(0) ! Coordinated(Increment(counters.tail :+ failer)) coordinated.await for (counter ← counters) { - (counter !! GetCount).get must be === 0 + (counter ? GetCount).as[Int].get must be === 0 } counters foreach (_.stop()) failer.stop() diff --git a/akka-stm/src/test/scala/transactor/FickleFriendsSpec.scala b/akka-stm/src/test/scala/transactor/FickleFriendsSpec.scala index b275c1a071..f7b0277656 100644 --- a/akka-stm/src/test/scala/transactor/FickleFriendsSpec.scala +++ b/akka-stm/src/test/scala/transactor/FickleFriendsSpec.scala @@ -109,9 +109,9 @@ class FickleFriendsSpec extends WordSpec with MustMatchers { val latch = new CountDownLatch(1) coordinator ! FriendlyIncrement(counters, latch) latch.await // this could take a while - (coordinator !! GetCount).get must be === 1 + (coordinator ? GetCount).as[Int].get must be === 1 for (counter ← counters) { - (counter !! GetCount).get must be === 1 + (counter ? GetCount).as[Int].get must be === 1 } counters foreach (_.stop()) coordinator.stop() diff --git a/akka-stm/src/test/scala/transactor/TransactorSpec.scala b/akka-stm/src/test/scala/transactor/TransactorSpec.scala index 33ca14402f..8cccdf430c 100644 --- a/akka-stm/src/test/scala/transactor/TransactorSpec.scala +++ b/akka-stm/src/test/scala/transactor/TransactorSpec.scala @@ -92,7 +92,7 @@ class TransactorSpec extends WordSpec with MustMatchers { counters(0) ! Increment(counters.tail, incrementLatch) incrementLatch.await(timeout.length, timeout.unit) for (counter ← counters) { - (counter !! GetCount).get must be === 1 + (counter ? GetCount).as[Int].get must be === 1 } counters foreach (_.stop()) failer.stop() @@ -104,7 +104,7 @@ class TransactorSpec extends WordSpec with MustMatchers { counters(0) ! Increment(counters.tail :+ failer, failLatch) failLatch.await(timeout.length, timeout.unit) for (counter ← counters) { - (counter !! GetCount).get must be === 0 + (counter ? GetCount).as[Int].get must be === 0 } counters foreach (_.stop()) failer.stop() diff --git a/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala b/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala index c952697fe7..038aafcb0e 100644 --- a/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala +++ b/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala @@ -114,7 +114,7 @@ class TestActorRefSpec extends WordSpec with MustMatchers with BeforeAndAfterEac def receive = { case _ ⇒ self reply nested } }).start() a must not be (null) - val nested = (a !! "any").get.asInstanceOf[ActorRef] + val nested = (a ? "any").as[ActorRef].get nested must not be (null) a must not be theSameInstanceAs(nested) } @@ -125,7 +125,7 @@ class TestActorRefSpec extends WordSpec with MustMatchers with BeforeAndAfterEac def receive = { case _ ⇒ self reply nested } }).start() a must not be (null) - val nested = (a !! "any").get.asInstanceOf[ActorRef] + val nested = (a ? "any").as[ActorRef].get nested must not be (null) a must not be theSameInstanceAs(nested) } @@ -160,7 +160,7 @@ class TestActorRefSpec extends WordSpec with MustMatchers with BeforeAndAfterEac "stop when sent a poison pill" in { val a = TestActorRef[WorkerActor].start() intercept[ActorKilledException] { - a !! PoisonPill + (a ? PoisonPill).get } a must not be ('running) a must be('shutdown) From 2bbbeba82e163b1cd3b5996f3144e83a93cc7699 Mon Sep 17 00:00:00 2001 From: Peter Veentjer Date: Mon, 13 Jun 2011 16:48:03 +0300 Subject: [PATCH 05/13] - more work on the storage functionality --- .../src/main/scala/akka/cluster/Storage.scala | 182 ++++++++----- .../akka/cluster/InMemoryStorageSpec.scala | 251 ++++++++++++++++++ .../test/scala/akka/cluster/StorageSpec.scala | 165 ------------ .../scala/akka/cluster/StorageTestUtils.scala | 15 ++ .../akka/cluster/ZooKeeperStorageSpec.scala | 58 ++++ 5 files changed, 444 insertions(+), 227 deletions(-) mode change 100644 => 100755 akka-cluster/src/main/scala/akka/cluster/Storage.scala create mode 100755 akka-cluster/src/test/scala/akka/cluster/InMemoryStorageSpec.scala delete mode 100644 akka-cluster/src/test/scala/akka/cluster/StorageSpec.scala create mode 100644 akka-cluster/src/test/scala/akka/cluster/StorageTestUtils.scala create mode 100644 akka-cluster/src/test/scala/akka/cluster/ZooKeeperStorageSpec.scala diff --git a/akka-cluster/src/main/scala/akka/cluster/Storage.scala b/akka-cluster/src/main/scala/akka/cluster/Storage.scala old mode 100644 new mode 100755 index 7c39863f56..32519e56d7 --- a/akka-cluster/src/main/scala/akka/cluster/Storage.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Storage.scala @@ -5,9 +5,8 @@ import akka.AkkaException import org.apache.zookeeper.{ KeeperException, CreateMode } import org.apache.zookeeper.data.Stat import java.util.concurrent.ConcurrentHashMap -import org.apache.zookeeper.KeeperException.NoNodeException -import java.lang.UnsupportedOperationException import annotation.tailrec +import java.lang.{ UnsupportedOperationException, RuntimeException } /** * Simple abstraction to store an Array of bytes based on some String key. @@ -15,36 +14,37 @@ import annotation.tailrec * Nothing is being said about ACID, transactions etc. It depends on the implementation * of this Storage interface of what is and isn't done on the lowest level. * - * TODO: Perhaps add a version to the store to prevent lost updates using optimistic locking. - * (This is supported by ZooKeeper). + * The amount of data that is allowed to be insert/updated is implementation specific. The InMemoryStorage + * has no limits, but the ZooKeeperStorage has a maximum size of 1 mb. + * * TODO: Class is up for better names. * TODO: Instead of a String as key, perhaps also a byte-array. */ trait Storage { /** - * Loads the given entry. + * Loads the VersionedData for the given key. * - * @param key: the key of the data to load. - * @return the VersionedData for the given key. - * @throws NoNodeExistsException if the data with the given key doesn't exist. + * @param key: the key of the VersionedData to load. + * @return the VersionedData for the given entry. + * @throws MissingDataException if the entry with the given key doesn't exist. * @throws StorageException if anything goes wrong while accessing the storage */ def load(key: String): VersionedData /** - * Loads the data for the given key and version. + * Loads the VersionedData for the given key and version. * - * @param key: the key of the data to load - * @param version the version of the data to load - * @throws NoNodeExistsException if the data with the given key doesn't exist. - * @throws VersioningMismatchStorageException if the version of the data is not the same as the given data. + * @param key: the key of the VersionedData to load + * @param version the version of the VersionedData to load + * @throws MissingDataException if the data with the given key doesn't exist. + * @throws VersioningException if the version of the data is not the same as the given data. * @throws StorageException if anything goes wrong while accessing the storage */ def load(key: String, version: Long): VersionedData /** - * Checks if a value with the given key exists. + * Checks if a VersionedData with the given key exists. * * @param key the key to check the existence for. * @return true if exists, false if not. @@ -57,19 +57,34 @@ trait Storage { * * @param key the key of the Data to insert. * @param bytes the data to insert. - * @return the version of the inserted data - * @throws NodeExistsException when a Node with the given Key already exists. + * @return the VersionedData + * @throws DataExistsException when VersionedData with the given Key already exists. * @throws StorageException if anything goes wrong while accessing the storage */ def insert(key: String, bytes: Array[Byte]): VersionedData /** - * Stores a array of bytes based on some key. + * Inserts the data if there is no data for that key, or overwrites it if it is there. * - * @throws MissingNodeException when the Node with the given key doesn't exist. + * This is the method you want to call if you just want to save something and don't + * care about any lost update issues. + * + * @param key the key of the data + * @param bytes the data to insert + * @return the VersionedData that was stored. * @throws StorageException if anything goes wrong while accessing the storage */ - def update(key: String, bytes: Array[Byte]): VersionedData + def insertOrOverwrite(key: String, bytes: Array[Byte]): VersionedData + + /** + * Overwrites the current data for the given key. + * + * @param key the key of the data to overwrite + * @param bytes the data to insert. + * @throws ` when the entry with the given key doesn't exist. + * @throws StorageException if anything goes wrong while accessing the storage + */ + def overwrite(key: String, bytes: Array[Byte]): VersionedData /** * @throws StorageException if anything goes wrong while accessing the storage @@ -98,17 +113,17 @@ class StorageException(msg: String = null, cause: java.lang.Throwable = null) ex * * * A StorageException thrown when an operation is done on a non existing node. */ -class MissingNodeException(msg: String = null, cause: java.lang.Throwable = null) extends StorageException(msg, cause) +class MissingDataException(msg: String = null, cause: java.lang.Throwable = null) extends StorageException(msg, cause) /** * A StorageException thrown when an operation is done on an existing node, but no node was expected. */ -class NodeExistsException(msg: String = null, cause: java.lang.Throwable = null) extends StorageException(msg, cause) +class DataExistsException(msg: String = null, cause: java.lang.Throwable = null) extends StorageException(msg, cause) /** * A StorageException thrown when an operation causes an optimistic locking failure. */ -class VersioningMismatchStorageException(msg: String = null, cause: java.lang.Throwable = null) extends StorageException(msg, cause) +class VersioningException(msg: String = null, cause: java.lang.Throwable = null) extends StorageException(msg, cause) /** * A Storage implementation based on ZooKeeper. @@ -117,59 +132,89 @@ class VersioningMismatchStorageException(msg: String = null, cause: java.lang.Th * - so everything is written or nothing is written * - is isolated, so threadsafe, * but it will not participate in any transactions. - * //todo: unclear, is only a single connection used in the JVM?? * */ class ZooKeeperStorage(zkClient: AkkaZkClient) extends Storage { def load(key: String) = try { - val arrayOfBytes: Array[Byte] = zkClient.connection.readData(key, new Stat, false) - //Some(arrayOfBytes) - throw new UnsupportedOperationException() + val stat = new Stat + val arrayOfBytes = zkClient.connection.readData(key, stat, false) + new VersionedData(arrayOfBytes, stat.getVersion) } catch { - //todo: improved error messaged - case e: KeeperException.NoNodeException ⇒ throw new MissingNodeException("Failed to load key", e) - case e: KeeperException ⇒ throw new StorageException("failed to load key " + key, e) + case e: KeeperException.NoNodeException ⇒ throw new MissingDataException( + String.format("Failed to load key [%s]: no data was found", key), e) + case e: KeeperException ⇒ throw new StorageException( + String.format("Failed to load key [%s]", key), e) } - def load(key: String, version: Long) = { - throw new UnsupportedOperationException() + def load(key: String, expectedVersion: Long) = try { + val stat = new Stat + val arrayOfBytes = zkClient.connection.readData(key, stat, false) + + if (stat.getVersion != expectedVersion) throw new VersioningException( + "Failed to update key [" + key + "]: version mismatch, expected [" + expectedVersion + "]" + + " but found [" + stat.getVersion + "]") + + new VersionedData(arrayOfBytes, stat.getVersion) + } catch { + case e: KeeperException.NoNodeException ⇒ throw new MissingDataException( + String.format("Failed to load key [%s]: no data was found", key), e) + case e: KeeperException ⇒ throw new StorageException( + String.format("Failed to load key [%s]", key), e) + } + + def insertOrOverwrite(key: String, bytes: Array[Byte]) = { + try { + throw new UnsupportedOperationException() + } catch { + case e: KeeperException.NodeExistsException ⇒ throw new DataExistsException( + String.format("Failed to insert key [%s]: an entry already exists with the same key", key), e) + case e: KeeperException ⇒ throw new StorageException( + String.format("Failed to insert key [%s]", key), e) + } } def insert(key: String, bytes: Array[Byte]): VersionedData = { try { - zkClient.connection.create(key, bytes, CreateMode.PERSISTENT); - throw new UnsupportedOperationException() + zkClient.connection.create(key, bytes, CreateMode.PERSISTENT) + //todo: how to get hold of the reference. + val version: Long = 0 + new VersionedData(bytes, version) } catch { - //todo: improved error messaged - case e: KeeperException.NodeExistsException ⇒ throw new NodeExistsException("failed to insert key " + key, e) - case e: KeeperException ⇒ throw new StorageException("failed to insert key " + key, e) + case e: KeeperException.NodeExistsException ⇒ throw new DataExistsException( + String.format("Failed to insert key [%s]: an entry already exists with the same key", key), e) + case e: KeeperException ⇒ throw new StorageException( + String.format("Failed to insert key [%s]", key), e) } } def exists(key: String) = try { zkClient.connection.exists(key, false) } catch { - //todo: improved error messaged - case e: KeeperException ⇒ throw new StorageException("failed to check for existance on key " + key, e) + case e: KeeperException ⇒ throw new StorageException( + String.format("Failed to check existance for key [%s]", key), e) } - def update(key: String, versionedData: VersionedData) = try { - zkClient.connection.writeData(key, versionedData.data, versionedData.version.asInstanceOf[Int]) - } catch { - //todo: improved error messaged - case e: KeeperException.BadVersionException ⇒ throw new VersioningMismatchStorageException() - case e: KeeperException ⇒ throw new StorageException("failed to check for existance on key " + key, e) + def update(key: String, versionedData: VersionedData) { + try { + zkClient.connection.writeData(key, versionedData.data, versionedData.version.asInstanceOf[Int]) + } catch { + case e: KeeperException.BadVersionException ⇒ throw new VersioningException( + String.format("Failed to update key [%s]: version mismatch", key), e) + case e: KeeperException ⇒ throw new StorageException( + String.format("Failed to update key [%s]", key), e) + } } - def update(key: String, bytes: Array[Byte]): VersionedData = { + def overwrite(key: String, bytes: Array[Byte]): VersionedData = { try { zkClient.connection.writeData(key, bytes) throw new RuntimeException() } catch { - //todo: improved error messaged - case e: KeeperException.NoNodeException ⇒ throw new MissingNodeException("failed to update key ", e) - case e: KeeperException ⇒ throw new StorageException("failed to update key ", e) + case e: KeeperException.NoNodeException ⇒ throw new MissingDataException( + String.format("Failed to overwrite key [%s]: a previous entry already exists", key), e) + case e: KeeperException ⇒ throw new StorageException( + String.format("Failed to overwrite key [%s]", key), e) } } } @@ -188,8 +233,8 @@ final class InMemoryStorage extends Storage { def load(key: String) = { val result = map.get(key) - if (result == null) throw new MissingNodeException( - String.format("Failed to load data for key [%s]: no data was found", key)) + if (result == null) throw new MissingDataException( + String.format("Failed to load key [%s]: no data was found", key)) result } @@ -197,8 +242,8 @@ final class InMemoryStorage extends Storage { def load(key: String, expectedVersion: Long) = { val result = load(key) - if (result.version != expectedVersion) throw new VersioningMismatchStorageException( - "Failed to load data for key [" + key + "]: version mismatch, expected [" + result.version + "] " + + if (result.version != expectedVersion) throw new VersioningException( + "Failed to load key [" + key + "]: version mismatch, expected [" + result.version + "] " + "but found [" + expectedVersion + "]") result @@ -211,7 +256,7 @@ final class InMemoryStorage extends Storage { val result = new VersionedData(bytes, version) val previous = map.putIfAbsent(key, result) - if (previous != null) throw new NodeExistsException( + if (previous != null) throw new DataExistsException( String.format("Failed to insert key [%s]: the key already has been inserted previously", key)) result @@ -221,23 +266,36 @@ final class InMemoryStorage extends Storage { def update(key: String, updatedData: VersionedData) { val currentData = map.get(key) - if (currentData == null) throw new MissingNodeException( - String.format("Failed to update data for key [%s], no previous entry exist", key)) + if (currentData == null) throw new MissingDataException( + String.format("Failed to update key [%s], no previous entry exist", key)) val expectedVersion = currentData.version + 1 - if (expectedVersion != updatedData.version) throw new VersioningMismatchStorageException( - "Failed to update data for key [" + key + "]: version mismatch, expected [" + expectedVersion + "]" + + if (expectedVersion != updatedData.version) throw new VersioningException( + "Failed to update key [" + key + "]: version mismatch, expected [" + expectedVersion + "]" + " but found [" + updatedData.version + "]") if (!map.replace(key, currentData, updatedData)) update(key, updatedData) } - def update(key: String, bytes: Array[Byte]): VersionedData = { - if (map.get(key) == null) throw new NoNodeException( - String.format("Failed to update key [%s]: no previous insert of this key exists", key)) + @tailrec + def overwrite(key: String, bytes: Array[Byte]): VersionedData = { + val currentData = map.get(key) - //smap.put(key, bytes) - throw new UnsupportedOperationException() + if (currentData == null) throw new MissingDataException( + String.format("Failed to overwrite key [%s], no previous entry exist", key)) + + val newData = currentData.createUpdate(bytes) + if (map.replace(key, currentData, newData)) newData else overwrite(key, bytes) + } + + def insertOrOverwrite(key: String, bytes: Array[Byte]): VersionedData = { + val version = InMemoryStorage.InitialVersion + val result = new VersionedData(bytes, version) + + val previous = map.putIfAbsent(key, result) + + if (previous == null) result + else overwrite(key, bytes) } } diff --git a/akka-cluster/src/test/scala/akka/cluster/InMemoryStorageSpec.scala b/akka-cluster/src/test/scala/akka/cluster/InMemoryStorageSpec.scala new file mode 100755 index 0000000000..7f3c6a26cd --- /dev/null +++ b/akka-cluster/src/test/scala/akka/cluster/InMemoryStorageSpec.scala @@ -0,0 +1,251 @@ +package akka.cluster + +import org.scalatest.matchers.MustMatchers +import org.scalatest.WordSpec +import akka.cluster.StorageTestUtils._ + +class InMemoryStorageSpec extends WordSpec with MustMatchers { + + "unversioned load" must { + "throw MissingDataException if non existing key" in { + val store = new InMemoryStorage() + + try { + store.load("foo") + fail() + } catch { + case e: MissingDataException ⇒ + } + } + + "return VersionedData if key existing" in { + val storage = new InMemoryStorage() + val key = "somekey" + val value = "somevalue".getBytes + storage.insert(key, value) + + val result = storage.load(key) + //todo: strange that the implicit store is not found + assertContent(key, value, result.version)(storage) + } + } + + "exist" must { + "return true if value exists" in { + val store = new InMemoryStorage() + val key = "somekey" + store.insert(key, "somevalue".getBytes) + store.exists(key) must be(true) + } + + "return false if value not exists" in { + val store = new InMemoryStorage() + store.exists("somekey") must be(false) + } + } + + "versioned load" must { + "throw MissingDataException if non existing key" in { + val store = new InMemoryStorage() + + try { + store.load("foo", 1) + fail() + } catch { + case e: MissingDataException ⇒ + } + } + + "return VersionedData if key existing and exact version match" in { + val storage = new InMemoryStorage() + val key = "somekey" + val value = "somevalue".getBytes + val stored = storage.insert(key, value) + + val result = storage.load(key, stored.version) + assert(result.version == stored.version) + assert(result.data == stored.data) + } + + "throw VersioningException is version too new" in { + val storage = new InMemoryStorage() + val key = "somekey" + val value = "somevalue".getBytes + val stored = storage.insert(key, value) + + try { + storage.load(key, stored.version + 1) + fail() + } catch { + case e: VersioningException ⇒ + } + } + + "throw VersioningException is version too old" in { + val storage = new InMemoryStorage() + val key = "somekey" + val value = "somevalue".getBytes + val stored = storage.insert(key, value) + + try { + storage.load(key, stored.version - 1) + fail() + } catch { + case e: VersioningException ⇒ + } + } + } + + "insert" must { + + "place a new value when non previously existed" in { + val storage = new InMemoryStorage() + val key = "somekey" + val oldValue = "oldvalue".getBytes + storage.insert(key, oldValue) + + val result = storage.load(key) + assertContent(key, oldValue)(storage) + assert(InMemoryStorage.InitialVersion == result.version) + } + + "throw MissingDataException when there already exists an entry with the same key" in { + val storage = new InMemoryStorage() + val key = "somekey" + val oldValue = "oldvalue".getBytes + + val oldVersionedData = storage.insert(key, oldValue) + + val newValue = "newValue".getBytes + + try { + storage.insert(key, newValue) + fail() + } catch { + case e: DataExistsException ⇒ + } + + //make sure that the old value was not changed + assert(oldVersionedData == storage.load(key)) + } + } + + "update" must { + + "throw MissingDataException when no node exists" in { + val storage = new InMemoryStorage() + + val key = "somekey" + + try { + storage.update(key, new VersionedData("somevalue".getBytes, 1)) + fail() + } catch { + case e: MissingDataException ⇒ + } + } + + "replace if previous value exists and no other updates have been done" in { + val storage = new InMemoryStorage() + + //do the initial insert + val key = "foo" + val oldValue = "insert".getBytes + val insert = storage.insert(key, oldValue) + + //do the update the will be the cause of the conflict. + val updateValue = "update".getBytes + val update = insert.createUpdate(updateValue) + storage.update(key, update) + + assertContent(key, update.data, update.version)(storage) + } + + "throw VersioningException when already overwritten" in { + val storage = new InMemoryStorage() + + //do the initial insert + val key = "foo" + val oldValue = "insert".getBytes + val insert = storage.insert(key, oldValue) + + //do the update the will be the cause of the conflict. + val otherUpdateValue = "otherupdate".getBytes + val otherUpdate = insert.createUpdate(otherUpdateValue) + storage.update(key, otherUpdate) + + val update = insert.createUpdate("update".getBytes) + + try { + storage.update(key, update) + fail() + } catch { + case e: VersioningException ⇒ + } + + assertContent(key, otherUpdate.data, otherUpdate.version)(storage) + } + } + + "overwrite" must { + + "throw MissingDataException when no node exists" in { + val storage = new InMemoryStorage() + val key = "somekey" + + try { + storage.overwrite(key, "somevalue".getBytes) + fail() + } catch { + case e: MissingDataException ⇒ + } + + storage.exists(key) must be(false) + } + + "succeed if previous value exist" in { + val storage = new InMemoryStorage() + val key = "somekey" + val oldValue = "oldvalue".getBytes + val newValue: Array[Byte] = "somevalue".getBytes + + val initialInsert: VersionedData = storage.insert(key, oldValue) + + val result: VersionedData = storage.overwrite(key, newValue) + + assert(result.version == initialInsert.version + 1) + assert(result.data == newValue) + storage.load(key) must be eq (result) + } + } + + "insertOrOverwrite" must { + "insert if nothing was inserted before" in { + val storage = new InMemoryStorage() + val key = "somekey" + val value = "somevalue".getBytes + + val result = storage.insertOrOverwrite(key, value) + + assert(result.version == InMemoryStorage.InitialVersion) + assert(result.data == value) + storage.load(key) must be eq (result) + } + + "overwrite of something existed before" in { + val storage = new InMemoryStorage() + val key = "somekey" + val oldValue = "oldvalue".getBytes + val newValue = "somevalue".getBytes + + val initialInsert = storage.insert(key, oldValue) + + val result = storage.insertOrOverwrite(key, newValue) + + assert(result.version == initialInsert.version + 1) + assert(result.data == newValue) + storage.load(key) must be eq (result) + } + } + +} \ No newline at end of file diff --git a/akka-cluster/src/test/scala/akka/cluster/StorageSpec.scala b/akka-cluster/src/test/scala/akka/cluster/StorageSpec.scala deleted file mode 100644 index 535313c3a5..0000000000 --- a/akka-cluster/src/test/scala/akka/cluster/StorageSpec.scala +++ /dev/null @@ -1,165 +0,0 @@ -package akka.cluster - -import org.scalatest.matchers.MustMatchers -import org.scalatest.WordSpec - -class InMemoryStorageSpec extends WordSpec with MustMatchers { - - "unversioned load" must { - "throw MissingNodeException if non existing key" in { - val store = new InMemoryStorage() - - try { - store.load("foo") - fail() - } catch { - case e: MissingNodeException ⇒ - } - } - - "return VersionedData if key existing" in { - val storage = new InMemoryStorage() - val key = "somekey" - val value = "somevalue".getBytes - storage.insert(key, value) - - val result = storage.load(key) - //todo: strange that the implicit store is not found - assertContent(key, value, result.version)(storage) - } - } - - "exist" must { - "return true if value exists" in { - val store = new InMemoryStorage() - val key = "somekey" - store.insert(key, "somevalue".getBytes) - store.exists(key) must be(true) - } - - "return false if value not exists" in { - val store = new InMemoryStorage() - store.exists("somekey") must be(false) - } - } - - "versioned load" must { - "throw MissingNodeException if non existing key" in { - val store = new InMemoryStorage() - - try { - store.load("foo", 1) - fail() - } catch { - case e: MissingNodeException ⇒ - } - } - - "return VersionedData if key existing and exact version match" in { - val storage = new InMemoryStorage() - val key = "somekey" - val value = "somevalue".getBytes - val stored = storage.insert(key, value) - - val result = storage.load(key, stored.version) - assert(result.version == stored.version) - assert(result.data == stored.data) - } - - "throw VersioningMismatchStorageException is version too new" in { - val storage = new InMemoryStorage() - val key = "somekey" - val value = "somevalue".getBytes - val stored = storage.insert(key, value) - - try { - storage.load(key, stored.version + 1) - fail() - } catch { - case e: VersioningMismatchStorageException ⇒ - } - } - - "throw VersioningMismatchStorageException is version too old" in { - val storage = new InMemoryStorage() - val key = "somekey" - val value = "somevalue".getBytes - val stored = storage.insert(key, value) - - try { - storage.load(key, stored.version - 1) - fail() - } catch { - case e: VersioningMismatchStorageException ⇒ - } - } - } - - "insert" must { - - "place a new value when non previously existed" in { - val storage = new InMemoryStorage() - val key = "somekey" - val oldValue = "oldvalue".getBytes - storage.insert(key, oldValue) - - val result = storage.load(key) - assertContent(key, oldValue)(storage) - assert(InMemoryStorage.InitialVersion == result.version) - } - - "throw MissingNodeException when there already exists an entry with the same key" in { - val storage = new InMemoryStorage() - val key = "somekey" - val oldValue = "oldvalue".getBytes - - val oldVersionedData = storage.insert(key, oldValue) - - val newValue = "newValue".getBytes - - try { - storage.insert(key, newValue) - fail() - } catch { - case e: NodeExistsException ⇒ - } - - //make sure that the old value was not changed - assert(oldVersionedData == storage.load(key)) - } - } - - "update with versioning" must { - - "throw NoNodeException when no node exists" in { - val storage = new InMemoryStorage() - - val key = "somekey" - - try { - storage.update(key, new VersionedData("somevalue".getBytes, 1)) - fail() - } catch { - case e: MissingNodeException ⇒ - } - } - - "throw OptimisticLockException when ..." in { - - } - - "replace" in { - } - } - - def assertContent(key: String, expectedData: Array[Byte], expectedVersion: Long)(implicit storage: InMemoryStorage) { - val found = storage.load(key) - assert(found.version == expectedVersion) - assert(expectedData == found.data) //todo: structural equals - } - - def assertContent(key: String, expectedData: Array[Byte])(implicit storage: InMemoryStorage) { - val found = storage.load(key) - assert(expectedData == found.data) //todo: structural equals - } -} \ No newline at end of file diff --git a/akka-cluster/src/test/scala/akka/cluster/StorageTestUtils.scala b/akka-cluster/src/test/scala/akka/cluster/StorageTestUtils.scala new file mode 100644 index 0000000000..99ceaf0070 --- /dev/null +++ b/akka-cluster/src/test/scala/akka/cluster/StorageTestUtils.scala @@ -0,0 +1,15 @@ +package akka.cluster + +object StorageTestUtils { + + def assertContent(key: String, expectedData: Array[Byte], expectedVersion: Long)(implicit storage: InMemoryStorage) { + val found = storage.load(key) + assert(found.version == expectedVersion) + assert(expectedData == found.data) //todo: structural equals + } + + def assertContent(key: String, expectedData: Array[Byte])(implicit storage: InMemoryStorage) { + val found = storage.load(key) + assert(expectedData == found.data) //todo: structural equals + } +} \ No newline at end of file diff --git a/akka-cluster/src/test/scala/akka/cluster/ZooKeeperStorageSpec.scala b/akka-cluster/src/test/scala/akka/cluster/ZooKeeperStorageSpec.scala new file mode 100644 index 0000000000..a4bda26c77 --- /dev/null +++ b/akka-cluster/src/test/scala/akka/cluster/ZooKeeperStorageSpec.scala @@ -0,0 +1,58 @@ +package akka.cluster + +import org.scalatest.matchers.MustMatchers +import akka.actor.Actor +import org.scalatest.{ BeforeAndAfterEach, BeforeAndAfterAll, WordSpec } +import org.I0Itec.zkclient.ZkServer +import zookeeper.AkkaZkClient + +class ZooKeeperStorageSpec extends WordSpec with MustMatchers with BeforeAndAfterAll with BeforeAndAfterEach { + val dataPath = "_akka_cluster/data" + val logPath = "_akka_cluster/log" + var zkServer: ZkServer = _ + var zkClient: AkkaZkClient = _ + + override def beforeAll() { + try { + zkServer = Cluster.startLocalCluster(dataPath, logPath) + Thread.sleep(5000) + Actor.cluster.start() + zkClient = Cluster.newZkClient() + } catch { + case e ⇒ e.printStackTrace() + } + } + + override def afterAll() { + zkClient.close() + Actor.cluster.shutdown() + ClusterDeployer.shutdown() + Cluster.shutdownLocalCluster() + Actor.registry.local.shutdownAll() + } + + "unversioned load" must { + "throw MissingDataException if non existing key" in { + val store = new ZooKeeperStorage(zkClient) + + //try { + // store.load("foo") + // fail() + //} catch { + // case e: MissingDataException ⇒ + //} + } + + /* + "return VersionedData if key existing" in { + val storage = new InMemoryStorage() + val key = "somekey" + val value = "somevalue".getBytes + storage.insert(key, value) + + val result = storage.load(key) + //todo: strange that the implicit store is not found + assertContent(key, value, result.version)(storage) + } */ + } +} \ No newline at end of file From e3e389e14ffb8c4069cd16088e06c2de97865835 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 13 Jun 2011 16:47:18 +0200 Subject: [PATCH 06/13] Fixing spelling errors in docs --- akka-docs/java/dispatchers.rst | 3 ++- akka-docs/java/typed-actors.rst | 1 + akka-docs/project/release-notes.rst | 2 +- akka-docs/scala/dispatchers.rst | 2 +- akka-docs/scala/tutorial-chat-server.rst | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/akka-docs/java/dispatchers.rst b/akka-docs/java/dispatchers.rst index b67cb1c02e..7f8998c18e 100644 --- a/akka-docs/java/dispatchers.rst +++ b/akka-docs/java/dispatchers.rst @@ -13,7 +13,7 @@ The Dispatcher is an important piece that allows you to configure the right sema Akka supports dispatchers for both event-driven lightweight threads, allowing creation of millions threads on a single workstation, and thread-based Actors, where each dispatcher is bound to a dedicated OS thread. -The event-based Actors currently consume ~600 bytes per Actor which means that you can create more than 6.5 million Actors on 4 G RAM. +The event-based Actors currently consume ~600 bytes per Actor which means that you can create more than 6.5 million Actors on 4 GB RAM. Default dispatcher ------------------ @@ -84,6 +84,7 @@ The 'Dispatcher' binds a set of Actors to a thread pool backed up by a 'Blocking The event-driven dispatchers **must be shared** between multiple Typed Actors and/or Actors. One best practice is to let each top-level Actor, e.g. the Actors you define in the declarative supervisor config, to get their own dispatcher but reuse the dispatcher for each new Actor that the top-level Actor creates. But you can also share dispatcher between multiple top-level Actors. This is very use-case specific and needs to be tried out on a case by case basis. The important thing is that Akka tries to provide you with the freedom you need to design and implement your system in the most efficient way in regards to performance, throughput and latency. It comes with many different predefined BlockingQueue configurations: + * Bounded LinkedBlockingQueue * Unbounded LinkedBlockingQueue * Bounded ArrayBlockingQueue diff --git a/akka-docs/java/typed-actors.rst b/akka-docs/java/typed-actors.rst index daed909727..2eb02f6ebc 100644 --- a/akka-docs/java/typed-actors.rst +++ b/akka-docs/java/typed-actors.rst @@ -115,6 +115,7 @@ Methods that return something (e.g. non-void methods) are turned into ‘send-an User user = service.getUser(username); Generally it is preferred to use fire-forget messages as much as possible since they will never block, e.g. consume a resource by waiting. But sometimes they are neat to use since they: + # Simulates standard Java method dispatch, which is more intuitive for most Java developers # Are a neat to model request-reply # Are useful when you need to do things in a defined order diff --git a/akka-docs/project/release-notes.rst b/akka-docs/project/release-notes.rst index 5e6be44ae7..20553a15a8 100644 --- a/akka-docs/project/release-notes.rst +++ b/akka-docs/project/release-notes.rst @@ -524,7 +524,7 @@ Release 0.6 - January 5th 2010 - **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** - Lowered actor memory footprint; now an actor consumes ~600 bytes, which mean that you can create 6.5 million on 4 GB 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) diff --git a/akka-docs/scala/dispatchers.rst b/akka-docs/scala/dispatchers.rst index cc75b5c099..afc7d204ec 100644 --- a/akka-docs/scala/dispatchers.rst +++ b/akka-docs/scala/dispatchers.rst @@ -13,7 +13,7 @@ The Dispatcher is an important piece that allows you to configure the right sema Akka supports dispatchers for both event-driven lightweight threads, allowing creation of millions of threads on a single workstation, and thread-based Actors, where each dispatcher is bound to a dedicated OS thread. -The event-based Actors currently consume ~600 bytes per Actor which means that you can create more than 6.5 million Actors on 4 G RAM. +The event-based Actors currently consume ~600 bytes per Actor which means that you can create more than 6.5 million Actors on 4 GB RAM. Default dispatcher ------------------ diff --git a/akka-docs/scala/tutorial-chat-server.rst b/akka-docs/scala/tutorial-chat-server.rst index 17c99a6c35..1319ee9432 100644 --- a/akka-docs/scala/tutorial-chat-server.rst +++ b/akka-docs/scala/tutorial-chat-server.rst @@ -72,7 +72,7 @@ The 'actorOf' factory method can be imported like this: From now on we will assume that it is imported like this and can use it directly. -Akka Actors are extremely lightweight. Each Actor consume ~600 bytes, which means that you can create 6.5 million on 4 G RAM. +Akka Actors are extremely lightweight. Each Actor consume ~600 bytes, which means that you can create 6.5 million on 4 GB RAM. Messages are sent using the '!' operator: From 7712c206205b85d8c90621315a161d292de09cd2 Mon Sep 17 00:00:00 2001 From: Roland Date: Mon, 13 Jun 2011 22:36:46 +0200 Subject: [PATCH 07/13] unify sender/senderFuture into channel (++) (squashed merge from the various bits and pieces already part of release-1.2, everything related to Channel & Future) --- .../ActorFireForgetRequestReplySpec.scala | 2 +- .../akka/actor/actor/ActorTimeoutSpec.scala | 55 +++++ .../test/scala/akka/actor/actor/Bench.scala | 2 +- .../akka/dispatch/MailboxConfigSpec.scala | 6 +- .../test/scala/akka/routing/RoutingSpec.scala | 4 +- .../src/main/scala/akka/actor/Actor.scala | 35 ++- .../src/main/scala/akka/actor/ActorRef.scala | 214 ++++++++---------- .../src/main/scala/akka/actor/Channel.scala | 173 ++++++++++++++ .../src/main/scala/akka/actor/FSM.scala | 5 +- .../main/scala/akka/actor/TypedActor.scala | 14 +- .../akka/dispatch/BalancingDispatcher.scala | 5 +- .../main/scala/akka/dispatch/Dispatcher.scala | 5 +- .../src/main/scala/akka/dispatch/Future.scala | 101 +++++++-- .../scala/akka/dispatch/MessageHandling.scala | 7 +- .../src/main/scala/akka/routing/Pool.scala | 3 +- .../src/main/scala/akka/util/BoxedType.scala | 25 ++ .../src/main/scala/akka/camel/Producer.scala | 18 +- .../akka/camel/component/ActorComponent.scala | 4 +- .../scala/akka/cluster/ClusterActorRef.scala | 22 +- .../akka/cluster/ReplicatedClusterRef.scala | 9 +- .../actor/mailbox/DurableDispatcher.scala | 4 +- .../akka/actor/mailbox/DurableMailbox.scala | 15 +- .../remote/netty/NettyRemoteSupport.scala | 7 +- .../testkit/CallingThreadDispatcher.scala | 20 +- .../src/main/scala/akka/testkit/TestKit.scala | 118 ++++++++-- .../scala/akka/testkit/TestActorRefSpec.scala | 7 +- .../scala/akka/testkit/TestProbeSpec.scala | 45 ++++ .../java/akka/tutorial/java/second/Pi.java | 6 +- 28 files changed, 679 insertions(+), 252 deletions(-) create mode 100644 akka-actor-tests/src/test/scala/akka/actor/actor/ActorTimeoutSpec.scala create mode 100644 akka-actor/src/main/scala/akka/actor/Channel.scala create mode 100644 akka-actor/src/main/scala/akka/util/BoxedType.scala create mode 100644 akka-testkit/src/test/scala/akka/testkit/TestProbeSpec.scala diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorFireForgetRequestReplySpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorFireForgetRequestReplySpec.scala index dd0cb87990..3f2f8e57db 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorFireForgetRequestReplySpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorFireForgetRequestReplySpec.scala @@ -23,7 +23,7 @@ object ActorFireForgetRequestReplySpec { case "Send" ⇒ self.reply("Reply") case "SendImplicit" ⇒ - self.sender.get ! "ReplyImplicit" + self.channel ! "ReplyImplicit" } } diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorTimeoutSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorTimeoutSpec.scala new file mode 100644 index 0000000000..b42ac75bcb --- /dev/null +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorTimeoutSpec.scala @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2009-2011 Scalable Solutions AB + */ +package akka.actor + +import org.scalatest.{ WordSpec, BeforeAndAfterAll } +import org.scalatest.matchers.MustMatchers +import akka.testkit.TestKit +import akka.dispatch.FutureTimeoutException +import akka.util.duration._ + +class ActorTimeoutSpec + extends WordSpec + with BeforeAndAfterAll + with MustMatchers + with TestKit { + + val echo = Actor.actorOf(new Actor { + def receive = { + case x ⇒ + } + }).start() + + val testTimeout = if (Actor.defaultTimeout.duration < 400.millis) 500 millis else 100 millis + + override def afterAll { echo.stop() } + + "An Actor-based Future" must { + + "use the global default timeout if no implicit in scope" in { + echo.timeout = 12 + within((Actor.TIMEOUT - 100).millis, (Actor.TIMEOUT + 300).millis) { + val f = echo ? "hallo" + intercept[FutureTimeoutException] { f.await } + } + } + + "use implicitly supplied timeout" in { + implicit val timeout = Actor.Timeout(testTimeout) + within(testTimeout - 100.millis, testTimeout + 300.millis) { + val f = (echo ? "hallo").mapTo[String] + intercept[FutureTimeoutException] { f.await } + f.value must be(None) + } + } + + "use explicitly supplied timeout" in { + within(testTimeout - 100.millis, testTimeout + 300.millis) { + (echo.?("hallo")(timeout = testTimeout)).as[String] must be(None) + } + } + + } + +} 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 258809c5b6..315798cc19 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 @@ -102,7 +102,7 @@ object Chameneos { } } else { waitingChameneo.foreach(_ ! Exit) - self.sender.get ! Exit + self.channel ! Exit } } } 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 e71ca14721..678cbd2e86 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/MailboxConfigSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/MailboxConfigSpec.scala @@ -4,7 +4,7 @@ import org.scalatest.matchers.MustMatchers import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach } import org.scalatest.junit.JUnitRunner import org.junit.runner.RunWith -import akka.actor.{ Actor, ActorRegistry } +import akka.actor.{ Actor, ActorRegistry, NullChannel } import akka.actor.Actor.{ actorOf } import java.util.concurrent.{ TimeUnit, CountDownLatch, BlockingQueue } import java.util.{ Queue } @@ -84,7 +84,7 @@ abstract class MailboxSpec extends WordSpec with MustMatchers with BeforeAndAfte new MessageInvocation( actorOf(new Actor { //Dummy actor def receive = { case _ ⇒ } - }), msg, None, None) + }), msg, NullChannel) } def ensureInitialMailboxState(config: MailboxType, q: MessageQueue) { @@ -158,4 +158,4 @@ class PriorityMailboxSpec extends MailboxSpec { 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/routing/RoutingSpec.scala b/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala index ddd37f760c..de8cbae96d 100644 --- a/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala @@ -193,11 +193,11 @@ class RoutingSpec extends WordSpec with MustMatchers { }).start() val successes = TestLatch(2) - val successCounter = Some(actorOf(new Actor { + val successCounter = actorOf(new Actor { def receive = { case "success" ⇒ successes.countDown() } - }).start()) + }).start() implicit val replyTo = successCounter pool ! "a" diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index dd0642893b..50e6be2011 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -24,6 +24,7 @@ import scala.reflect.BeanProperty import com.eaio.uuid.UUID import java.lang.reflect.InvocationTargetException +import java.util.concurrent.TimeUnit /** * Life-cycle messages for the Actors @@ -110,9 +111,6 @@ object Status { */ object Actor extends ListenerManagement { - private[akka] val TIMEOUT = Duration(config.getInt("akka.actor.timeout", 5), TIME_UNIT).toMillis - private[akka] val SERIALIZE_MESSAGES = config.getBool("akka.actor.serialize-messages", false) - /** * A Receive is a convenience type that defines actor message behavior currently modeled as * a PartialFunction[Any, Unit]. @@ -140,6 +138,20 @@ object Actor extends ListenerManagement { override def initialValue = Stack[ActorRef]() } + case class Timeout(duration: Duration) { + def this(timeout: Long) = this(Duration(timeout, TimeUnit.MILLISECONDS)) + def this(length: Long, unit: TimeUnit) = this(Duration(length, unit)) + } + object Timeout { + def apply(timeout: Long) = new Timeout(timeout) + def apply(length: Long, unit: TimeUnit) = new Timeout(length, unit) + implicit def durationToTimeout(duration: Duration) = new Timeout(duration) + } + + private[akka] val TIMEOUT = Duration(config.getInt("akka.actor.timeout", 5), TIME_UNIT).toMillis + val defaultTimeout = Timeout(TIMEOUT) + private[akka] val SERIALIZE_MESSAGES = config.getBool("akka.actor.serialize-messages", false) + /** * Handle to the ActorRegistry. */ @@ -495,14 +507,14 @@ trait Actor { */ type Receive = Actor.Receive - /* + /** * Some[ActorRef] representation of the 'self' ActorRef reference. *

* Mainly for internal use, functions as the implicit sender references when invoking * the 'forward' function. */ @transient - implicit val someSelf: Some[ActorRef] = { + val someSelf: Some[ActorRef] = { val refStack = Actor.actorRefInCreation.get if (refStack.isEmpty) throw new ActorInitializationException( "ActorRef for instance of actor [" + getClass.getName + "] is not in scope." + @@ -528,7 +540,7 @@ trait Actor { * Mainly for internal use, functions as the implicit sender references when invoking * one of the message send functions ('!', '!!' and '!!!'). */ - implicit def optionSelf: Option[ActorRef] = someSelf + def optionSelf: Option[ActorRef] = someSelf /** * The 'self' field holds the ActorRef for this actor. @@ -558,7 +570,7 @@ trait Actor { * */ @transient - val self: ScalaActorRef = someSelf.get + implicit val self: ScalaActorRef = someSelf.get /** * User overridable callback/setting. @@ -645,8 +657,7 @@ trait Actor { private[akka] final def apply(msg: Any) = { if (msg.isInstanceOf[AnyRef] && (msg.asInstanceOf[AnyRef] eq null)) - throw new InvalidMessageException("Message from [" + self.sender + "] to [" + self.toString + "] is null") - + throw new InvalidMessageException("Message from [" + self.channel + "] to [" + self.toString + "] is null") val behaviorStack = self.hotswap msg match { @@ -675,9 +686,9 @@ trait Actor { case Restart(reason) ⇒ throw reason case Kill ⇒ throw new ActorKilledException("Kill") case PoisonPill ⇒ - val f = self.senderFuture() + val ch = self.channel self.stop() - if (f.isDefined) f.get.completeWithException(new ActorKilledException("PoisonPill")) + ch.sendException(new ActorKilledException("PoisonPill")) } } @@ -697,4 +708,4 @@ private[actor] class AnyOptionAsTypedOption(anyOption: Option[Any]) { * ClassCastException and return None in that case. */ def asSilently[T: Manifest]: Option[T] = narrowSilently[T](anyOption) -} \ No newline at end of file +} diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index 9cb2c14513..f05fc7fa6b 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -33,27 +33,6 @@ private[akka] object ActorRefInternals { object SHUTDOWN extends StatusType } -/** - * Abstraction for unification of sender and senderFuture for later reply. - * Can be stored away and used at a later point in time. - */ -abstract class Channel[T] { - - /** - * Scala API.

- * Sends the specified message to the channel. - */ - def !(msg: T) - - /** - * Java API.

- * Sends the specified message to the channel. - */ - def sendOneWay(msg: T) { - this.!(msg) - } -} - /** * ActorRef is an immutable and serializable handle to an Actor. *

@@ -86,7 +65,7 @@ abstract class Channel[T] { * * @author Jonas Bonér */ -trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] with Serializable { scalaRef: ScalaActorRef ⇒ +trait ActorRef extends ActorRefShared with ForwardableChannel with java.lang.Comparable[ActorRef] with Serializable { scalaRef: ScalaActorRef ⇒ // Only mutable for RemoteServer in order to maintain identity across nodes @volatile protected[akka] var _uuid = newUuid @@ -241,40 +220,18 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] with S _uuid = uid } - /** - * Akka Java API.

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

- *

-   * actor.sendOneWay(message);
-   * 
- *

- */ - def sendOneWay(message: AnyRef): Unit = { - sendOneWay(message, null) - } - - /** - * Akka Java API.

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

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

- *

-   * actor.sendOneWay(message, context);
-   * 
- *

- */ - def sendOneWay(message: AnyRef, sender: ActorRef) { - this.!(message)(Option(sender)) - } - /** * Akka Java API.

* @see sendRequestReply(message: AnyRef, timeout: Long, sender: ActorRef) * Uses the default timeout of the Actor (setTimeout()) and omits the sender reference */ - def sendRequestReply(message: AnyRef): AnyRef = sendRequestReply(message, timeout, null) + def sendRequestReply(message: AnyRef): AnyRef = { + !!(message, timeout).getOrElse(throw new ActorTimeoutException( + "Message [" + message + + "]\n\tfrom [nowhere]\n\twith timeout [" + timeout + + "]\n\ttimed out.")) + .asInstanceOf[AnyRef] + } /** * Akka Java API.

@@ -298,7 +255,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] with S * to send a reply message to the original sender. If not then the sender will block until the timeout expires. */ def sendRequestReply(message: AnyRef, timeout: Long, sender: ActorRef): AnyRef = { - !!(message, timeout)(Option(sender)).getOrElse(throw new ActorTimeoutException( + ?(message)(sender, Actor.Timeout(timeout)).as[AnyRef].getOrElse(throw new ActorTimeoutException( "Message [" + message + "]\n\tfrom [" + (if (sender ne null) sender.address else "nowhere") + "]\n\twith timeout [" + timeout + @@ -311,14 +268,14 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] with S * @see sendRequestReplyFuture(message: AnyRef, sender: ActorRef): Future[_] * Uses the Actors default timeout (setTimeout()) and omits the sender */ - def sendRequestReplyFuture[T <: AnyRef](message: AnyRef): Future[T] = sendRequestReplyFuture(message, timeout, null).asInstanceOf[Future[T]] + def sendRequestReplyFuture(message: AnyRef): Future[Any] = ?(message) /** * Akka Java API.

* @see sendRequestReplyFuture(message: AnyRef, sender: ActorRef): Future[_] * Uses the Actors default timeout (setTimeout()) */ - def sendRequestReplyFuture[T <: AnyRef](message: AnyRef, sender: ActorRef): Future[T] = sendRequestReplyFuture(message, timeout, sender).asInstanceOf[Future[T]] + def sendRequestReplyFuture(message: AnyRef, sender: ActorRef): Future[Any] = ?(message)(sender) /** * Akka Java API.

@@ -331,7 +288,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] with S * If you are sending messages using sendRequestReplyFuture then you have to use getContext().reply(..) * to send a reply message to the original sender. If not then the sender will block until the timeout expires. */ - def sendRequestReplyFuture[T <: AnyRef](message: AnyRef, timeout: Long, sender: ActorRef): Future[T] = !!!(message, timeout)(Option(sender)).asInstanceOf[Future[T]] + def sendRequestReplyFuture(message: AnyRef, timeout: Long, sender: ActorRef): Future[Any] = ?(message)(sender, Actor.Timeout(timeout)) /** * Akka Java API.

@@ -339,7 +296,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] with S */ def forward(message: AnyRef, sender: ActorRef) { if (sender eq null) throw new IllegalArgumentException("The 'sender' argument to 'forward' can't be null") - else forward(message)(Some(sender)) + else forward(message)(sender) } /** @@ -448,36 +405,36 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] with S /** * Abstraction for unification of sender and senderFuture for later reply */ - def channel: Channel[Any] = { - if (senderFuture.isDefined) { - new Channel[Any] { - val future = senderFuture.get - def !(msg: Any) = future completeWithResult msg - } - } else if (sender.isDefined) { - val someSelf = Some(this) - new Channel[Any] { - val client = sender.get - def !(msg: Any) = client.!(msg)(someSelf) - } - } else throw new IllegalActorStateException("No channel available") + def channel: UntypedChannel = { + val msg = currentMessage + if (msg ne null) msg.channel + else NullChannel } + /* + * Implementation of ForwardableChannel + */ + + def sendException(ex: Throwable) {} + def isUsableOnlyOnce = false + def isUsable = true + def isReplyable = true + def canSendException = false + /** * Java API.

* Abstraction for unification of sender and senderFuture for later reply */ - def getChannel: Channel[Any] = channel + def getChannel: UntypedChannel = channel protected[akka] def invoke(messageHandle: MessageInvocation) - protected[akka] def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]) + protected[akka] def postMessageToMailbox(message: Any, channel: UntypedChannel): Unit - protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout[T]( + protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout( message: Any, timeout: Long, - senderOption: Option[ActorRef], - senderFuture: Option[Promise[T]]): Promise[T] + channel: UntypedChannel): Future[Any] protected[akka] def actorInstance: AtomicReference[Actor] @@ -692,18 +649,19 @@ class LocalActorRef private[akka] (private[this] val actorFactory: () ⇒ Actor, _supervisor = sup } - protected[akka] def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]) { - dispatcher dispatchMessage MessageInvocation(this, message, senderOption, None) - } + protected[akka] def postMessageToMailbox(message: Any, channel: UntypedChannel): Unit = + dispatcher dispatchMessage new MessageInvocation(this, message, channel) - protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout[T]( + protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout( message: Any, timeout: Long, - senderOption: Option[ActorRef], - senderFuture: Option[Promise[T]]): Promise[T] = { - val future = if (senderFuture.isDefined) senderFuture else Some(new DefaultPromise[T](timeout)) - dispatcher dispatchMessage MessageInvocation(this, message, senderOption, future.asInstanceOf[Some[Promise[Any]]]) - future.get + channel: UntypedChannel): Future[Any] = { + val future = channel match { + case f: ActorPromise ⇒ f + case _ ⇒ new ActorPromise(timeout) + } + dispatcher dispatchMessage new MessageInvocation(this, message, future) + future } /** @@ -888,7 +846,7 @@ class LocalActorRef private[akka] (private[this] val actorFactory: () ⇒ Actor, //Prevent any further messages to be processed until the actor has been restarted dispatcher.suspend(this) - senderFuture.foreach(_.completeWithException(reason)) + channel.sendException(reason) if (supervisor.isDefined) notifySupervisorWithMessage(Exit(this, reason)) else { @@ -995,19 +953,28 @@ private[akka] case class RemoteActorRef private[akka] ( start() - def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]) { - Actor.remote.send[Any](message, senderOption, None, remoteAddress, timeout, true, this, loader) + def postMessageToMailbox(message: Any, channel: UntypedChannel): Unit = { + val chSender = channel match { + case ref: ActorRef ⇒ Some(ref) + case _ ⇒ None + } + Actor.remote.send[Any](message, chSender, None, remoteAddress, timeout, true, this, loader) } - def postMessageToMailboxAndCreateFutureResultWithTimeout[T]( + def postMessageToMailboxAndCreateFutureResultWithTimeout( message: Any, timeout: Long, - senderOption: Option[ActorRef], - senderFuture: Option[Promise[T]]): Promise[T] = { - val future = Actor.remote.send[T]( - message, senderOption, senderFuture, - remoteAddress, timeout, false, this, loader) - if (future.isDefined) future.get + channel: UntypedChannel): Future[Any] = { + val chSender = channel match { + case ref: ActorRef ⇒ Some(ref) + case _ ⇒ None + } + val chFuture = channel match { + case f: Promise[Any] ⇒ Some(f) + case _ ⇒ None + } + val future = Actor.remote.send[Any](message, chSender, chFuture, remoteAddress, timeout, false, this, loader) + if (future.isDefined) ActorPromise(future.get) else throw new IllegalActorStateException("Expected a future from remote call to actor " + toString) } @@ -1096,7 +1063,7 @@ trait ActorRefShared { * There are implicit conversions in ../actor/Implicits.scala * from ActorRef -> ScalaActorRef and back */ -trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ +trait ScalaActorRef extends ActorRefShared with ForwardableChannel { ref: ActorRef ⇒ /** * Address for actor, must be a unique one. @@ -1134,20 +1101,28 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ * The reference sender Actor of the last received message. * Is defined if the message was sent from another Actor, else None. */ + @deprecated("will be removed in 2.0, use channel instead", "1.2") def sender: Option[ActorRef] = { val msg = currentMessage if (msg eq null) None - else msg.sender + else msg.channel match { + case ref: ActorRef ⇒ Some(ref) + case _ ⇒ None + } } /** * The reference sender future of the last received message. * Is defined if the message was sent with sent with '!!' or '!!!', else None. */ + @deprecated("will be removed in 2.0, use channel instead", "1.2") def senderFuture(): Option[Promise[Any]] = { val msg = currentMessage if (msg eq null) None - else msg.senderFuture + else msg.channel match { + case f: ActorPromise ⇒ Some(f) + case _ ⇒ None + } } /** @@ -1164,8 +1139,8 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ * *

*/ - def !(message: Any)(implicit sender: Option[ActorRef] = None) { - if (isRunning) postMessageToMailbox(message, sender) + def !(message: Any)(implicit channel: UntypedChannel = NullChannel): Unit = { + if (isRunning) postMessageToMailbox(message, channel) else throw new ActorInitializationException( "Actor has not been started, you need to invoke 'actor.start()' before using it") } @@ -1182,9 +1157,10 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ * If you are sending messages using !! then you have to use self.reply(..) * to send a reply message to the original sender. If not then the sender will block until the timeout expires. */ - def !!(message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Option[Any] = { + @deprecated("use `(actor ? msg).as[T]` instead", "1.2") + def !!(message: Any, timeout: Long = this.timeout)(implicit channel: UntypedChannel = NullChannel): Option[Any] = { if (isRunning) { - val future = postMessageToMailboxAndCreateFutureResultWithTimeout[Any](message, timeout, sender, None) + val future = postMessageToMailboxAndCreateFutureResultWithTimeout(message, timeout, channel) try { future.await.resultOrException } catch { case e: FutureTimeoutException ⇒ None } } else throw new ActorInitializationException( @@ -1200,8 +1176,15 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ * If you are sending messages using !!! then you have to use self.reply(..) * to send a reply message to the original sender. If not then the sender will block until the timeout expires. */ - def !!![T](message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Future[T] = { - if (isRunning) postMessageToMailboxAndCreateFutureResultWithTimeout[T](message, timeout, sender, None) + @deprecated("return type is an illusion, use the more honest ? method", "1.2") + def !!![T](message: Any, timeout: Long = this.timeout)(implicit channel: UntypedChannel = NullChannel): Future[T] = + this.?(message)(channel, Actor.Timeout(timeout)).asInstanceOf[Future[T]] + + /** + * Sends a message asynchronously, returning a future which may eventually hold the reply. + */ + def ?(message: Any)(implicit channel: UntypedChannel = NullChannel, timeout: Actor.Timeout = Actor.defaultTimeout): Future[Any] = { + if (isRunning) postMessageToMailboxAndCreateFutureResultWithTimeout(message, timeout.duration.toMillis, channel) else throw new ActorInitializationException( "Actor has not been started, you need to invoke 'actor.start()' before using it") } @@ -1211,12 +1194,9 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ *

* Works with '!', '!!' and '!!!'. */ - def forward(message: Any)(implicit sender: Some[ActorRef]) = { + def forward(message: Any)(implicit channel: ForwardableChannel) = { if (isRunning) { - if (sender.get.senderFuture.isDefined) - postMessageToMailboxAndCreateFutureResultWithTimeout(message, timeout, sender.get.sender, sender.get.senderFuture) - else - postMessageToMailbox(message, sender.get.sender) + postMessageToMailbox(message, channel.channel) } else throw new ActorInitializationException("Actor has not been started, you need to invoke 'actor.start()' before using it") } @@ -1226,14 +1206,7 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ *

* Throws an IllegalStateException if unable to determine what to reply to. */ - def reply(message: Any) { - if (!reply_?(message)) throw new IllegalActorStateException( - "\n\tNo sender in scope, can't reply. " + - "\n\tYou have probably: " + - "\n\t\t1. Sent a message to an Actor from an instance that is NOT an Actor." + - "\n\t\t2. Invoked a method on an TypedActor from an instance NOT an TypedActor." + - "\n\tElse you might want to use 'reply_?' which returns Boolean(true) if succes and Boolean(false) if no sender in scope") - } + def reply(message: Any) = channel.!(message)(this) /** * Use reply_?(..) to reply with a message to the original sender of the message currently @@ -1241,16 +1214,7 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ *

* Returns true if reply was sent, and false if unable to determine what to reply to. */ - def reply_?(message: Any): Boolean = { - if (senderFuture.isDefined) { - senderFuture.get completeWithResult message - true - } else if (sender.isDefined) { - //TODO: optimize away this allocation, perhaps by having implicit self: Option[ActorRef] in signature - sender.get.!(message)(Some(this)) - true - } else false - } + def reply_?(message: Any): Boolean = channel.safe_!(message)(this) } case class SerializedActorRef(val uuid: Uuid, diff --git a/akka-actor/src/main/scala/akka/actor/Channel.scala b/akka-actor/src/main/scala/akka/actor/Channel.scala new file mode 100644 index 0000000000..80134f2df8 --- /dev/null +++ b/akka-actor/src/main/scala/akka/actor/Channel.scala @@ -0,0 +1,173 @@ +/** + * Copyright (C) 2009-2011 Scalable Solutions AB + */ + +package akka.actor + +/** + * Abstraction for unification of sender and senderFuture for later reply. + * Can be stored away and used at a later point in time. + * + * Channel cannot be contravariant because of Future providing its value in + * covariant position. + * + * The possible reply channel which can be passed into ! and safe_! is always + * untyped, as there is no way to utilize its real static type without + * requiring runtime-costly manifests. + */ +trait Channel[T] { + + /** + * Scala API.

+ * Sends the specified message to the channel. + */ + def !(msg: T)(implicit channel: UntypedChannel = NullChannel): Unit + + /** + * Try to send an exception. Not all channel types support this, one notable + * positive example is Future. Failure to send is silent. + */ + def sendException(ex: Throwable): Unit + + /** + * Scala API.

+ * Try to send message to the channel, return whether successful. + */ + def safe_!(msg: T)(implicit channel: UntypedChannel = NullChannel): Boolean + + /** + * Indicates whether this channel may be used only once, e.g. a Future. + */ + def isUsableOnlyOnce: Boolean + + /** + * Indicates whether this channel may still be used (only useful if + * isUsableOnlyOnce returns true). + */ + def isUsable: Boolean + + /** + * Indicates whether this channel carries reply information, e.g. an + * ActorRef. + */ + def isReplyable: Boolean + + /** + * Indicates whether this channel is capable of sending exceptions to its + * recipient. + */ + def canSendException: Boolean + + /** + * Java API.

+ * Sends the specified message to the channel, i.e. fire-and-forget semantics.

+ *

+   * actor.sendOneWay(message);
+   * 
+ */ + def sendOneWay(msg: T): Unit = this.!(msg) + + /** + * Java API.

+ * Sends the specified message to the channel, i.e. fire-and-forget + * semantics, including the sender reference if possible (not supported on + * all channels).

+ *

+   * actor.sendOneWay(message, context);
+   * 
+ */ + def sendOneWay(msg: T, sender: UntypedChannel): Unit = this.!(msg)(sender) + + /** + * Java API.

+ * Try to send the specified message to the channel, i.e. fire-and-forget semantics.

+ *

+   * actor.sendOneWay(message);
+   * 
+ */ + def sendOneWaySafe(msg: T): Boolean = this.safe_!(msg) + + /** + * Java API.

+ * Try to send the specified message to the channel, i.e. fire-and-forget + * semantics, including the sender reference if possible (not supported on + * all channels).

+ *

+   * actor.sendOneWay(message, context);
+   * 
+ */ + def sendOneWaySafe(msg: T, sender: UntypedChannel): Boolean = this.safe_!(msg)(sender) + +} + +/** + * This trait represents a channel that a priori does have sending capability, + * i.e. ! is not guaranteed to fail (e.g. NullChannel would be a + * counter-example). + */ +trait AvailableChannel[T] { self: Channel[T] ⇒ + def safe_!(msg: T)(implicit channel: UntypedChannel = NullChannel): Boolean = { + if (isUsable) { + try { + this ! msg + true + } catch { + case _ ⇒ false + } + } else false + } +} + +/** + * All channels used in conjunction with MessageInvocation are untyped by + * design, so make this explicit. + */ +trait UntypedChannel extends Channel[Any] + +object UntypedChannel { + implicit def senderOption2Channel(sender: Option[ActorRef]): UntypedChannel = + sender match { + case Some(actor) ⇒ actor + case None ⇒ NullChannel + } +} + +/** + * Default channel when none available. + */ +case object NullChannel extends UntypedChannel { + def !(msg: Any)(implicit channel: UntypedChannel = NullChannel) { + throw new IllegalActorStateException(""" + No sender in scope, can't reply. + You have probably: + 1. Sent a message to an Actor from an instance that is NOT an Actor. + 2. Invoked a method on an TypedActor from an instance NOT an TypedActor. + You may want to have a look at safe_! for a variant returning a Boolean""") + } + def safe_!(msg: Any)(implicit channel: UntypedChannel = NullChannel): Boolean = false + def sendException(ex: Throwable) {} + def isUsableOnlyOnce = false + def isUsable = false + def isReplyable = false + def canSendException = false +} + +/** + * A channel which may be forwarded: a message received with such a reply + * channel attached can be passed on transparently such that a reply from a + * later processing stage is sent directly back to the origin. Keep in mind + * that not all channels can be used multiple times. + */ +trait ForwardableChannel extends UntypedChannel with AvailableChannel[Any] { + /** + * Get channel by which this channel would reply (ActorRef.forward takes an + * implicit ForwardableChannel and uses its .channel as message origin) + */ + def channel: UntypedChannel +} + +object ForwardableChannel { + implicit def someS2FC(sender: Some[ActorRef]): ForwardableChannel = sender.get + implicit def someIS2FC(implicit sender: Some[ActorRef]): ForwardableChannel = sender.get +} + diff --git a/akka-actor/src/main/scala/akka/actor/FSM.scala b/akka-actor/src/main/scala/akka/actor/FSM.scala index 60e2b10c67..8317fc5fe3 100644 --- a/akka-actor/src/main/scala/akka/actor/FSM.scala +++ b/akka-actor/src/main/scala/akka/actor/FSM.scala @@ -494,10 +494,7 @@ trait FSM[S, D] extends ListenerManagement { * @return this state transition descriptor */ def replying(replyValue: Any): State = { - self.sender match { - case Some(sender) ⇒ sender ! replyValue - case None ⇒ - } + self.channel safe_! replyValue this } diff --git a/akka-actor/src/main/scala/akka/actor/TypedActor.scala b/akka-actor/src/main/scala/akka/actor/TypedActor.scala index 85a388e24e..4e97855026 100644 --- a/akka-actor/src/main/scala/akka/actor/TypedActor.scala +++ b/akka-actor/src/main/scala/akka/actor/TypedActor.scala @@ -6,7 +6,7 @@ package akka.actor import akka.japi.{ Creator, Option ⇒ JOption } import akka.actor.Actor.{ actorOf, futureToAnyOptionAsTypedOption } -import akka.dispatch.{ MessageDispatcher, Dispatchers, Future } +import akka.dispatch.{ MessageDispatcher, Dispatchers, Future, FutureTimeoutException } import java.lang.reflect.{ InvocationTargetException, Method, InvocationHandler, Proxy } import akka.util.{ Duration } import java.util.concurrent.atomic.{ AtomicReference ⇒ AtomVar } @@ -41,6 +41,7 @@ object TypedActor { case "equals" ⇒ (args.length == 1 && (proxy eq args(0)) || actor == getActorRefFor(args(0))).asInstanceOf[AnyRef] //Force boxing of the boolean case "hashCode" ⇒ actor.hashCode.asInstanceOf[AnyRef] case _ ⇒ + implicit val timeout = Actor.Timeout(actor.timeout) MethodCall(method, args) match { case m if m.isOneWay ⇒ actor ! m @@ -48,9 +49,12 @@ object TypedActor { case m if m.returnsFuture_? ⇒ actor !!! m case m if m.returnsJOption_? || m.returnsOption_? ⇒ - (actor !!! m).as[AnyRef] match { - case Some(null) | None ⇒ if (m.returnsJOption_?) JOption.none[Any] else None - case Some(joption) ⇒ joption + val f = actor ? m + try { f.await } catch { case _: FutureTimeoutException ⇒ } + f.value match { + case None | Some(Right(null)) ⇒ if (m.returnsJOption_?) JOption.none[Any] else None + case Some(Right(joption: AnyRef)) ⇒ joption + case Some(Left(ex)) ⇒ throw ex } case m ⇒ (actor !!! m).get @@ -164,4 +168,4 @@ object TypedActor { } private[akka] def extractInterfaces(clazz: Class[_]): Array[Class[_]] = if (clazz.isInterface) Array[Class[_]](clazz) else clazz.getInterfaces -} \ No newline at end of file +} diff --git a/akka-actor/src/main/scala/akka/dispatch/BalancingDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/BalancingDispatcher.scala index 0460720a73..98b7465e5a 100644 --- a/akka-actor/src/main/scala/akka/dispatch/BalancingDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/BalancingDispatcher.scala @@ -129,10 +129,7 @@ class BalancingDispatcher( */ protected def donate(organ: MessageInvocation, recipient: ActorRef): Boolean = { if (organ ne null) { - if (organ.senderFuture.isDefined) recipient.postMessageToMailboxAndCreateFutureResultWithTimeout[Any]( - organ.message, recipient.timeout, organ.sender, organ.senderFuture) - else if (organ.sender.isDefined) recipient.postMessageToMailbox(organ.message, organ.sender) - else recipient.postMessageToMailbox(organ.message, None) + recipient.postMessageToMailbox(organ.message, organ.channel) true } else false } diff --git a/akka-actor/src/main/scala/akka/dispatch/Dispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/Dispatcher.scala index bf02af5997..9e7cb8e754 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Dispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Dispatcher.scala @@ -164,10 +164,7 @@ class Dispatcher( var invocation = m.dequeue lazy val exception = new ActorKilledException("Actor has been stopped") while (invocation ne null) { - val f = invocation.senderFuture - if (f.isDefined) - f.get.completeWithException(exception) - + invocation.channel.sendException(exception) invocation = m.dequeue } } diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index e760f97bb7..4e8e908049 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -6,8 +6,8 @@ package akka.dispatch import akka.AkkaException import akka.event.EventHandler -import akka.actor.{ Actor, Channel } -import akka.util.Duration +import akka.actor.{ Actor, Channel, ForwardableChannel, NullChannel, UntypedChannel, ActorRef } +import akka.util.{ Duration, BoxedType } import akka.japi.{ Procedure, Function ⇒ JFunc } import scala.util.continuations._ @@ -308,6 +308,22 @@ sealed trait Future[+T] { */ def await(atMost: Duration): Future[T] + /** + * Await completion of this Future (as `await`) and return its value if it + * conforms to A's erased type. + */ + def as[A](implicit m: Manifest[A]): Option[A] = + try { + await + value match { + case None ⇒ None + case Some(_: Left[_, _]) ⇒ None + case Some(Right(v)) ⇒ Some(BoxedType(m.erasure).cast(v).asInstanceOf[A]) + } + } catch { + case _: Exception ⇒ None + } + /** * Tests whether this Future has been completed. */ @@ -357,7 +373,7 @@ sealed trait Future[+T] { * Future. If the Future has already been completed, this will apply * immediately. */ - def onComplete(func: Future[T] ⇒ Unit): Future[T] + def onComplete(func: Future[T] ⇒ Unit): this.type /** * When the future is completed with a valid result, apply the provided @@ -369,7 +385,7 @@ sealed trait Future[+T] { * } * */ - final def onResult(pf: PartialFunction[Any, Unit]): Future[T] = onComplete { f ⇒ + final def onResult(pf: PartialFunction[Any, Unit]): this.type = onComplete { f ⇒ val optr = f.result if (optr.isDefined) { val r = optr.get @@ -496,6 +512,26 @@ sealed trait Future[+T] { fa } + /** + * Creates a new Future[A] which is completed with this Future's result if + * that conforms to A's erased type or a ClassCastException otherwise. + */ + final def mapTo[A](implicit m: Manifest[A]): Future[A] = { + val fa = new DefaultPromise[A](timeoutInNanos, NANOS) + onComplete { ft ⇒ + fa complete (ft.value.get match { + case l: Left[_, _] ⇒ l.asInstanceOf[Either[Throwable, A]] + case Right(t) ⇒ + try { + Right(BoxedType(m.erasure).cast(t).asInstanceOf[A]) + } catch { + case e: ClassCastException ⇒ Left(e) + } + }) + } + fa + } + /** * Creates a new Future by applying a function to the successful result of * this Future, and returns the result of the function as the new Future. @@ -586,7 +622,7 @@ sealed trait Future[+T] { } /* Java API */ - final def onComplete[A >: T](proc: Procedure[Future[A]]): Future[T] = onComplete(proc(_)) + final def onComplete[A >: T](proc: Procedure[Future[A]]): this.type = onComplete(proc(_)) final def map[A >: T, B](f: JFunc[A, B]): Future[B] = map(f(_)) @@ -607,10 +643,7 @@ object Promise { /** * Construct a completable channel */ - def channel(timeout: Long = Actor.TIMEOUT) = new Channel[Any] { - val promise = Promise[Any](timeout) - def !(msg: Any) = promise completeWithResult msg - } + def channel(timeout: Long = Actor.TIMEOUT): ActorPromise = new ActorPromise(timeout) private[akka] val callbacksPendingExecution = new ThreadLocal[Option[Stack[() ⇒ Unit]]]() { override def initialValue = None @@ -625,26 +658,26 @@ trait Promise[T] extends Future[T] { * Completes this Future with the specified result, if not already completed. * @return this */ - def complete(value: Either[Throwable, T]): Future[T] + def complete(value: Either[Throwable, T]): this.type /** * Completes this Future with the specified result, if not already completed. * @return this */ - final def completeWithResult(result: T): Future[T] = complete(Right(result)) + final def completeWithResult(result: T): this.type = complete(Right(result)) /** * Completes this Future with the specified exception, if not already completed. * @return this */ - final def completeWithException(exception: Throwable): Future[T] = complete(Left(exception)) + final def completeWithException(exception: Throwable): this.type = complete(Left(exception)) /** * Completes this Future with the specified other Future, when that Future is completed, * unless this Future has already been completed. * @return this. */ - final def completeWith(other: Future[T]): Future[T] = { + final def completeWith(other: Future[T]): this.type = { other onComplete { f ⇒ complete(f.value.get) } this } @@ -725,7 +758,7 @@ class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] { } } - def complete(value: Either[Throwable, T]): DefaultPromise[T] = { + def complete(value: Either[Throwable, T]): this.type = { _lock.lock val notifyTheseListeners = try { if (_value.isEmpty) { //Only complete if we aren't expired @@ -772,7 +805,7 @@ class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] { this } - def onComplete(func: Future[T] ⇒ Unit): Promise[T] = { + def onComplete(func: Future[T] ⇒ Unit): this.type = { _lock.lock val notifyNow = try { if (_value.isEmpty) { @@ -804,6 +837,36 @@ class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] { private def timeLeft(): Long = timeoutInNanos - (currentTimeInNanos - _startTimeInNanos) } +class ActorPromise(timeout: Long, timeunit: TimeUnit) + extends DefaultPromise[Any](timeout, timeunit) + with ForwardableChannel { + def this() = this(0, MILLIS) + def this(timeout: Long) = this(timeout, MILLIS) + + def !(message: Any)(implicit channel: UntypedChannel = NullChannel) = completeWithResult(message) + + def sendException(ex: Throwable) = completeWithException(ex) + + def channel: UntypedChannel = this + + def isUsableOnlyOnce = true + def isUsable = !isCompleted + def isReplyable = false + def canSendException = true + + @deprecated("ActorPromise merged with Channel[Any], just use 'this'", "1.2") + def future = this +} + +object ActorPromise { + def apply(f: Promise[Any]): ActorPromise = + new ActorPromise(f.timeoutInNanos, NANOS) { + completeWith(f) + override def !(message: Any)(implicit channel: UntypedChannel) = f completeWithResult message + override def sendException(ex: Throwable) = f completeWithException ex + } +} + /** * An already completed Future is seeded with it's result at creation, is useful for when you are participating in * a Future-composition but you already have a value to contribute. @@ -811,10 +874,10 @@ class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] { sealed class KeptPromise[T](suppliedValue: Either[Throwable, T]) extends Promise[T] { val value = Some(suppliedValue) - def complete(value: Either[Throwable, T]): Promise[T] = this - def onComplete(func: Future[T] ⇒ Unit): Future[T] = { func(this); this } - def await(atMost: Duration): Future[T] = this - def await: Future[T] = this + def complete(value: Either[Throwable, T]): this.type = this + def onComplete(func: Future[T] ⇒ Unit): this.type = { func(this); this } + def await(atMost: Duration): this.type = this + def await: this.type = this def isExpired: Boolean = true def timeoutInNanos: Long = 0 } diff --git a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala index 0c87581e5e..d109d274d4 100644 --- a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala +++ b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala @@ -16,10 +16,9 @@ import akka.actor._ /** * @author Jonas Bonér */ -final case class MessageInvocation(receiver: ActorRef, - message: Any, - sender: Option[ActorRef], - senderFuture: Option[Promise[Any]]) { +final case class MessageInvocation(val receiver: ActorRef, + val message: Any, + val channel: UntypedChannel) { if (receiver eq null) throw new IllegalArgumentException("Receiver can't be null") final def invoke() { diff --git a/akka-actor/src/main/scala/akka/routing/Pool.scala b/akka-actor/src/main/scala/akka/routing/Pool.scala index c036616521..097f2d720a 100644 --- a/akka-actor/src/main/scala/akka/routing/Pool.scala +++ b/akka-actor/src/main/scala/akka/routing/Pool.scala @@ -5,6 +5,7 @@ package akka.routing import akka.actor.{ Actor, ActorRef, PoisonPill } +import akka.dispatch.{ Promise } /** * Actor pooling @@ -195,7 +196,7 @@ trait MailboxPressureCapacitor { */ trait ActiveFuturesPressureCapacitor { def pressure(delegates: Seq[ActorRef]): Int = - delegates count { _.senderFuture.isDefined } + delegates count { _.channel.isInstanceOf[Promise[Any]] } } /** diff --git a/akka-actor/src/main/scala/akka/util/BoxedType.scala b/akka-actor/src/main/scala/akka/util/BoxedType.scala new file mode 100644 index 0000000000..7bcacaa5f9 --- /dev/null +++ b/akka-actor/src/main/scala/akka/util/BoxedType.scala @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2009-2011 Scalable Solutions AB + */ +package akka.util + +import java.{ lang ⇒ jl } + +object BoxedType { + + private val toBoxed = Map[Class[_], Class[_]]( + classOf[Boolean] -> classOf[jl.Boolean], + classOf[Byte] -> classOf[jl.Byte], + classOf[Char] -> classOf[jl.Character], + classOf[Short] -> classOf[jl.Short], + classOf[Int] -> classOf[jl.Integer], + classOf[Long] -> classOf[jl.Long], + classOf[Float] -> classOf[jl.Float], + classOf[Double] -> classOf[jl.Double], + classOf[Unit] -> classOf[scala.runtime.BoxedUnit]) + + def apply(c: Class[_]): Class[_] = { + if (c.isPrimitive) toBoxed(c) else c + } + +} diff --git a/akka-camel/src/main/scala/akka/camel/Producer.scala b/akka-camel/src/main/scala/akka/camel/Producer.scala index f4f745a294..041f3397ff 100644 --- a/akka-camel/src/main/scala/akka/camel/Producer.scala +++ b/akka-camel/src/main/scala/akka/camel/Producer.scala @@ -10,6 +10,7 @@ import org.apache.camel._ import org.apache.camel.processor.SendProcessor import akka.actor.{ Actor, ActorRef, UntypedActor } +import akka.dispatch.ActorPromise /** * Support trait for producing messages to Camel endpoints. @@ -96,10 +97,9 @@ trait ProducerSupport { this: Actor ⇒ val exchange = createExchange(pattern).fromRequestMessage(cmsg) processor.process(exchange, new AsyncCallback { val producer = self - // Need copies of sender and senderFuture references here - // since the callback could be done later by another thread. - val sender = self.sender - val senderFuture = self.senderFuture + // Need copies of channel reference here since the callback could be done + // later by another thread. + val channel = self.channel def done(doneSync: Boolean): Unit = { (doneSync, exchange.isFailed) match { @@ -114,10 +114,12 @@ trait ProducerSupport { this: Actor ⇒ receiveAfterProduce(result) private def dispatchAsync(result: Any) = { - if (senderFuture.isDefined) - producer.postMessageToMailboxAndCreateFutureResultWithTimeout(result, producer.timeout, sender, senderFuture) - else - producer.postMessageToMailbox(result, sender) + channel match { + case _: ActorPromise ⇒ + producer.postMessageToMailboxAndCreateFutureResultWithTimeout(result, producer.timeout, channel) + case _ ⇒ + producer.postMessageToMailbox(result, channel) + } } }) } diff --git a/akka-camel/src/main/scala/akka/camel/component/ActorComponent.scala b/akka-camel/src/main/scala/akka/camel/component/ActorComponent.scala index d06bdcb70e..0000a74503 100644 --- a/akka-camel/src/main/scala/akka/camel/component/ActorComponent.scala +++ b/akka-camel/src/main/scala/akka/camel/component/ActorComponent.scala @@ -285,7 +285,7 @@ private[akka] class AsyncCallbackAdapter(exchange: Exchange, callback: AsyncCall * @param message reply message * @param sender ignored */ - protected[akka] def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]) = { + protected[akka] def postMessageToMailbox(message: Any, channel: UntypedChannel) = { message match { case Ack ⇒ { /* no response message to set */ } case msg: Failure ⇒ exchange.fromFailureMessage(msg) @@ -312,7 +312,7 @@ private[akka] class AsyncCallbackAdapter(exchange: Exchange, callback: AsyncCall def shutdownLinkedActors: Unit = unsupported def supervisor: Option[ActorRef] = unsupported def homeAddress: Option[InetSocketAddress] = None - protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout[T](message: Any, timeout: Long, senderOption: Option[ActorRef], senderFuture: Option[Promise[T]]) = unsupported + protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout(message: Any, timeout: Long, channel: UntypedChannel) = unsupported protected[akka] def mailbox: AnyRef = unsupported protected[akka] def mailbox_=(msg: AnyRef): AnyRef = unsupported protected[akka] def restart(reason: Throwable, maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]): Unit = unsupported diff --git a/akka-cluster/src/main/scala/akka/cluster/ClusterActorRef.scala b/akka-cluster/src/main/scala/akka/cluster/ClusterActorRef.scala index f107904892..5698ac7be0 100644 --- a/akka-cluster/src/main/scala/akka/cluster/ClusterActorRef.scala +++ b/akka-cluster/src/main/scala/akka/cluster/ClusterActorRef.scala @@ -8,7 +8,7 @@ import Cluster._ import akka.actor._ import akka.actor.Actor._ import akka.event.EventHandler -import akka.dispatch.Promise +import akka.dispatch.Future import java.net.InetSocketAddress import java.util.concurrent.atomic.AtomicReference @@ -37,15 +37,23 @@ class ClusterActorRef private[akka] ( def connections: Map[InetSocketAddress, ActorRef] = inetSocketAddressToActorRefMap.get - override def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]): Unit = - route(message)(senderOption) + override def postMessageToMailbox(message: Any, channel: UntypedChannel): Unit = { + val sender = channel match { + case ref: ActorRef ⇒ Some(ref) + case _ ⇒ None + } + route(message)(sender) + } - override def postMessageToMailboxAndCreateFutureResultWithTimeout[T]( + override def postMessageToMailboxAndCreateFutureResultWithTimeout( message: Any, timeout: Long, - senderOption: Option[ActorRef], - senderFuture: Option[Promise[T]]): Promise[T] = { - route[T](message, timeout)(senderOption).asInstanceOf[Promise[T]] + channel: UntypedChannel): Future[Any] = { + val sender = channel match { + case ref: ActorRef ⇒ Some(ref) + case _ ⇒ None + } + route[Any](message, timeout)(sender) } private[akka] def failOver(fromInetSocketAddress: InetSocketAddress, toInetSocketAddress: InetSocketAddress) { diff --git a/akka-cluster/src/main/scala/akka/cluster/ReplicatedClusterRef.scala b/akka-cluster/src/main/scala/akka/cluster/ReplicatedClusterRef.scala index 4b075c7f91..b9ea77043c 100644 --- a/akka-cluster/src/main/scala/akka/cluster/ReplicatedClusterRef.scala +++ b/akka-cluster/src/main/scala/akka/cluster/ReplicatedClusterRef.scala @@ -79,14 +79,13 @@ class ReplicatedActorRef private[akka] (actorRef: ActorRef, val address: String) def startLink(actorRef: ActorRef): ActorRef = actorRef.startLink(actorRef) def supervisor: Option[ActorRef] = actorRef.supervisor def linkedActors: JMap[Uuid, ActorRef] = actorRef.linkedActors - protected[akka] def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]) { - actorRef.postMessageToMailbox(message, senderOption) + protected[akka] def postMessageToMailbox(message: Any, channel: UntypedChannel) { + actorRef.postMessageToMailbox(message, channel) } - protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout[T]( + protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout( message: Any, timeout: Long, - senderOption: Option[ActorRef], - senderFuture: Option[Promise[T]]): Promise[T] = actorRef.postMessageToMailboxAndCreateFutureResultWithTimeout(message, timeout, senderOption, senderFuture) + channel: UntypedChannel): Future[Any] = actorRef.postMessageToMailboxAndCreateFutureResultWithTimeout(message, timeout, channel) protected[akka] def actorInstance: AtomicReference[Actor] = actorRef.actorInstance protected[akka] def supervisor_=(sup: Option[ActorRef]) { actorRef.supervisor_=(sup) diff --git a/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableDispatcher.scala b/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableDispatcher.scala index 142fbea84f..adc3a12beb 100644 --- a/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableDispatcher.scala +++ b/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableDispatcher.scala @@ -94,7 +94,7 @@ case class DurableDispatcher( override def createMailbox(actorRef: ActorRef): AnyRef = _storage.createFor(actorRef) private[akka] override def dispatch(invocation: MessageInvocation): Unit = { - if (invocation.senderFuture.isDefined) + if (invocation.channel.isInstanceOf[ActorPromise]) throw new IllegalArgumentException("Durable mailboxes do not support Future-based messages from !! and !!!") super.dispatch(invocation) } @@ -131,7 +131,7 @@ case class DurablePinnedDispatcher( override def createMailbox(actorRef: ActorRef): AnyRef = _storage.createFor(actorRef) private[akka] override def dispatch(invocation: MessageInvocation): Unit = { - if (invocation.senderFuture.isDefined) + if (invocation.channel.isInstanceOf[ActorPromise]) throw new IllegalArgumentException("Actor has a durable mailbox that does not support !! or !!!") super.dispatch(invocation) } diff --git a/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableMailbox.scala b/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableMailbox.scala index 5b83f6a8c4..ec1a3a6e62 100644 --- a/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableMailbox.scala +++ b/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableMailbox.scala @@ -5,7 +5,7 @@ package akka.actor.mailbox import MailboxProtocol._ -import akka.actor.{Actor, ActorRef} +import akka.actor.{Actor, ActorRef, NullChannel} import akka.dispatch._ import akka.event.EventHandler import akka.remote.MessageSerializer @@ -48,7 +48,10 @@ abstract class DurableExecutableMailbox(owner: ActorRef) extends MessageQueue wi val builder = DurableMailboxMessageProtocol.newBuilder .setOwnerAddress(ownerAddress) .setMessage(message.toByteString) - if (durableMessage.sender.isDefined) builder.setSenderAddress(durableMessage.sender.get.address) + durableMessage.channel match { + case a : ActorRef => builder.setSenderAddress(a.address) + case _ => + } builder.build.toByteArray } @@ -62,10 +65,14 @@ abstract class DurableExecutableMailbox(owner: ActorRef) extends MessageQueue wi throw new DurableMailboxException("No actor could be found for address [" + ownerAddress + "], could not deserialize message.")) - val sender = if (durableMessage.hasSenderAddress) { + val senderOption = if (durableMessage.hasSenderAddress) { Actor.registry.actorFor(durableMessage.getSenderAddress) } else None + val sender = senderOption match { + case Some(ref) => ref + case None => NullChannel + } - new MessageInvocation(owner, message, sender, None) + new MessageInvocation(owner, message, sender) } } 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 5ffc4b16f3..e107f45d49 100644 --- a/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala +++ b/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala @@ -4,7 +4,7 @@ package akka.remote.netty -import akka.dispatch.{ DefaultPromise, Promise, Future } +import akka.dispatch.{ ActorPromise, DefaultPromise, Promise, Future } import akka.remote.{ MessageSerializer, RemoteClientSettings, RemoteServerSettings } import akka.remote.protocol.RemoteProtocol._ import akka.serialization.RemoteActorSerialization @@ -913,8 +913,7 @@ class RemoteServerHandler( else actorRef.postMessageToMailboxAndCreateFutureResultWithTimeout( message, request.getActorInfo.getTimeout, - None, - Some(new DefaultPromise[Any](request.getActorInfo.getTimeout). + new ActorPromise(request.getActorInfo.getTimeout). onComplete(_.value.get match { case l: Left[Throwable, Any] ⇒ write(channel, createErrorReplyMessage(l.a, request)) case r: Right[Throwable, Any] ⇒ @@ -931,7 +930,7 @@ class RemoteServerHandler( if (request.hasSupervisorUuid) messageBuilder.setSupervisorUuid(request.getSupervisorUuid) write(channel, RemoteEncoder.encode(messageBuilder.build)) - }))) + })) } } diff --git a/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala b/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala index 90d9bfda83..cd8a778c93 100644 --- a/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala +++ b/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala @@ -5,7 +5,7 @@ package akka.testkit import akka.event.EventHandler import akka.actor.ActorRef -import akka.dispatch.{ MessageDispatcher, MessageInvocation, FutureInvocation } +import akka.dispatch.{ MessageDispatcher, MessageInvocation, FutureInvocation, Promise, ActorPromise } import java.util.concurrent.locks.ReentrantLock import java.util.LinkedList import java.util.concurrent.RejectedExecutionException @@ -140,14 +140,14 @@ class CallingThreadDispatcher(val warnings: Boolean = true) extends MessageDispa val queue = mbox.queue val execute = mbox.suspended.ifElseYield { queue.push(handle) - if (warnings && handle.senderFuture.isDefined) { + if (warnings && handle.channel.isInstanceOf[Promise[_]]) { EventHandler.warning(this, "suspended, creating Future could deadlock; target: %s" format handle.receiver) } false } { queue.push(handle) if (queue.isActive) { - if (warnings && handle.senderFuture.isDefined) { + if (warnings && handle.channel.isInstanceOf[Promise[_]]) { EventHandler.warning(this, "blocked on this thread, creating Future could deadlock; target: %s" format handle.receiver) } false @@ -186,14 +186,18 @@ class CallingThreadDispatcher(val warnings: Boolean = true) extends MessageDispa if (handle ne null) { try { handle.invoke - val f = handle.senderFuture - if (warnings && f.isDefined && !f.get.isCompleted) { - EventHandler.warning(this, "calling %s with message %s did not reply as expected, might deadlock" format (handle.receiver, handle.message)) + if (warnings) handle.channel match { + case f: ActorPromise if !f.isCompleted ⇒ + EventHandler.warning(this, "calling %s with message %s did not reply as expected, might deadlock" format (handle.receiver, handle.message)) + case _ ⇒ } + true } catch { - case _ ⇒ queue.leave + case e ⇒ + EventHandler.error(this, e) + queue.leave + false } - true } else if (queue.isActive) { queue.leave false diff --git a/akka-testkit/src/main/scala/akka/testkit/TestKit.scala b/akka-testkit/src/main/scala/akka/testkit/TestKit.scala index c13787fb0e..4598749ae2 100644 --- a/akka-testkit/src/main/scala/akka/testkit/TestKit.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestKit.scala @@ -3,7 +3,7 @@ */ package akka.testkit -import akka.actor.{ Actor, FSM } +import akka.actor._ import Actor._ import akka.util.Duration import akka.util.duration._ @@ -17,9 +17,19 @@ object TestActor { case class SetTimeout(d: Duration) case class SetIgnore(i: Ignore) + + trait Message { + def msg: AnyRef + def channel: UntypedChannel + } + case class RealMessage(msg: AnyRef, channel: UntypedChannel) extends Message + case object NullMessage extends Message { + override def msg: AnyRef = throw new IllegalActorStateException("last receive did not dequeue a message") + override def channel: UntypedChannel = throw new IllegalActorStateException("last receive did not dequeue a message") + } } -class TestActor(queue: BlockingDeque[AnyRef]) extends Actor with FSM[Int, TestActor.Ignore] { +class TestActor(queue: BlockingDeque[TestActor.Message]) extends Actor with FSM[Int, TestActor.Ignore] { import FSM._ import TestActor._ @@ -36,7 +46,7 @@ class TestActor(queue: BlockingDeque[AnyRef]) extends Actor with FSM[Int, TestAc case Event(x: AnyRef, ign) ⇒ val ignore = ign map (z ⇒ if (z isDefinedAt x) z(x) else false) getOrElse false if (!ignore) { - queue.offerLast(x) + queue.offerLast(RealMessage(x, self.channel)) } stay } @@ -76,19 +86,23 @@ class TestActor(queue: BlockingDeque[AnyRef]) extends Actor with FSM[Int, TestAc */ trait TestKit { - private val queue = new LinkedBlockingDeque[AnyRef]() + import TestActor.{ Message, RealMessage, NullMessage } + + private val queue = new LinkedBlockingDeque[Message]() + private[akka] var lastMessage: Message = NullMessage /** * ActorRef of the test actor. Access is provided to enable e.g. * registration as message target. */ - protected val testActor = actorOf(new TestActor(queue)).start() + implicit val testActor = actorOf(new TestActor(queue)).start() /** * Implicit sender reference so that replies are possible for messages sent * from the test class. */ - protected implicit val senderOption = Some(testActor) + @deprecated("will be removed after 1.2, replaced by implicit testActor", "1.2") + val senderOption = Some(testActor) private var end: Duration = Duration.Inf /* @@ -183,6 +197,14 @@ trait TestKit { */ def within[T](max: Duration)(f: ⇒ T): T = within(0 seconds, max)(f) + /** + * Send reply to the last dequeued message. Will throw + * IllegalActorStateException if no message has been dequeued, yet. Dequeuing + * means reception of the message as part of an expect... or receive... call, + * not reception by the testActor. + */ + def reply(msg: AnyRef) { lastMessage.channel ! msg } + /** * Same as `expectMsg`, but takes the maximum wait time from the innermost * enclosing `within` block. @@ -396,16 +418,21 @@ trait TestKit { */ def receiveWhile[T](max: Duration)(f: PartialFunction[AnyRef, T]): Seq[T] = { val stop = now + max + var msg: Message = NullMessage @tailrec def doit(acc: List[T]): List[T] = { - receiveOne(stop - now) match { - case null ⇒ + receiveOne(stop - now) + lastMessage match { + case NullMessage ⇒ + lastMessage = msg acc.reverse - case o if (f isDefinedAt o) ⇒ + case RealMessage(o, _) if (f isDefinedAt o) ⇒ + msg = lastMessage doit(f(o) :: acc) - case o ⇒ - queue.offerFirst(o) + case RealMessage(o, _) ⇒ + queue.offerFirst(lastMessage) + lastMessage = msg acc.reverse } } @@ -415,7 +442,10 @@ trait TestKit { ret } - private def receiveN(n: Int, stop: Duration): Seq[AnyRef] = { + /** + * Receive N messages in a row before the given deadline. + */ + def receiveN(n: Int, stop: Duration): Seq[AnyRef] = { for { x ← 1 to n } yield { val timeout = stop - now val o = receiveOne(timeout) @@ -424,17 +454,65 @@ trait TestKit { } } - private def receiveOne(max: Duration): AnyRef = { - if (max == 0.seconds) { - queue.pollFirst - } else if (max.finite_?) { - queue.pollFirst(max.length, max.unit) - } else { - queue.takeFirst + /** + * Receive one message from the internal queue of the TestActor. If the given + * duration is zero, the queue is polled (non-blocking). + */ + def receiveOne(max: Duration): AnyRef = { + val message = + if (max == 0.seconds) { + queue.pollFirst + } else if (max.finite_?) { + queue.pollFirst(max.length, max.unit) + } else { + queue.takeFirst + } + message match { + case null ⇒ + lastMessage = NullMessage + null + case RealMessage(msg, _) ⇒ + lastMessage = message + msg } } private def format(u: TimeUnit, d: Duration) = "%.3f %s".format(d.toUnit(u), u.toString.toLowerCase) } -// vim: set ts=2 sw=2 et: +/** + * TestKit-based probe which allows sending, reception and reply. + */ +class TestProbe extends TestKit { + + /** + * Shorthand to get the testActor. + */ + def ref = testActor + + /** + * Send message to an actor while using the probe's TestActor as the sender. + * Replies will be available for inspection with all of TestKit's assertion + * methods. + */ + def send(actor: ActorRef, msg: AnyRef) = { + actor ! msg + } + + /** + * Forward this message as if in the TestActor's receive method with self.forward. + */ + def forward(actor: ActorRef, msg: AnyRef = lastMessage.msg) { + actor.!(msg)(lastMessage.channel) + } + + /** + * Get channel of last received message. + */ + def channel = lastMessage.channel + +} + +object TestProbe { + def apply() = new TestProbe +} diff --git a/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala b/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala index 5d74db9b34..27e8a5fbd7 100644 --- a/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala +++ b/akka-testkit/src/test/scala/akka/testkit/TestActorRefSpec.scala @@ -190,7 +190,7 @@ class TestActorRefSpec extends WordSpec with MustMatchers with BeforeAndAfterEac "support futures" in { val a = TestActorRef[WorkerActor].start() - val f: Future[String] = a !!! "work" + val f = a ? "work" mapTo manifest[String] f must be('completed) f.get must equal("workDone") } @@ -239,9 +239,8 @@ class TestActorRefSpec extends WordSpec with MustMatchers with BeforeAndAfterEac intercept[IllegalActorStateException] { ref("work") } val ch = Promise.channel() ref ! ch - val f = ch.promise - f must be('completed) - f.get must be("complexReply") + ch must be('completed) + ch.get must be("complexReply") } } diff --git a/akka-testkit/src/test/scala/akka/testkit/TestProbeSpec.scala b/akka-testkit/src/test/scala/akka/testkit/TestProbeSpec.scala new file mode 100644 index 0000000000..f12d8a2e19 --- /dev/null +++ b/akka-testkit/src/test/scala/akka/testkit/TestProbeSpec.scala @@ -0,0 +1,45 @@ +package akka.testkit + +import org.scalatest.WordSpec +import org.scalatest.matchers.MustMatchers +import org.scalatest.{ BeforeAndAfterEach, WordSpec } +import akka.actor._ +import akka.config.Supervision.OneForOneStrategy +import akka.event.EventHandler +import akka.dispatch.Future +import akka.util.duration._ + +class TestProbeSpec extends WordSpec with MustMatchers { + + "A TestProbe" must { + + "reply to futures" in { + val tk = TestProbe() + val future = tk.ref ? "hello" + tk.expectMsg(0 millis, "hello") // TestActor runs on CallingThreadDispatcher + tk.reply("world") + future must be('completed) + future.get must equal("world") + } + + "reply to messages" in { + val tk1 = TestProbe() + val tk2 = TestProbe() + tk1.ref.!("hello")(tk2.ref) + tk1.expectMsg(0 millis, "hello") + tk1.reply("world") + tk2.expectMsg(0 millis, "world") + } + + "properly send and reply to messages" in { + val probe1 = TestProbe() + val probe2 = TestProbe() + probe1.send(probe2.ref, "hello") + probe2.expectMsg(0 millis, "hello") + probe2.reply("world") + probe1.expectMsg(0 millis, "world") + } + + } + +} 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 5caae5e365..12c16d0142 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 @@ -184,10 +184,10 @@ public class Pi { // send calculate message long timeout = 60000; - Future replyFuture = master.sendRequestReplyFuture(new Calculate(), timeout, null); - Option result = replyFuture.await().resultOrException(); + Future replyFuture = master.sendRequestReplyFuture(new Calculate(), timeout, null); + Option result = replyFuture.await().resultOrException(); if (result.isDefined()) { - double pi = result.get(); + double pi = (Double) result.get(); // TODO java api for EventHandler? // EventHandler.info(this, String.format("\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis", pi, (currentTimeMillis() - start))); System.out.println(String.format("\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis", pi, (currentTimeMillis() - start))); From 73694af5a5911cc0cabbfc43203b8f51072f3fec Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Tue, 14 Jun 2011 09:49:07 +1200 Subject: [PATCH 08/13] Manually fix remote protocol for scaladoc --- .../akka/remote/protocol/RemoteProtocol.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/akka-remote/src/main/java/akka/remote/protocol/RemoteProtocol.java b/akka-remote/src/main/java/akka/remote/protocol/RemoteProtocol.java index b643a9a750..7bc78d53da 100644 --- a/akka-remote/src/main/java/akka/remote/protocol/RemoteProtocol.java +++ b/akka-remote/src/main/java/akka/remote/protocol/RemoteProtocol.java @@ -594,7 +594,7 @@ public final class RemoteProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -1388,7 +1388,7 @@ public final class RemoteProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -2760,7 +2760,7 @@ public final class RemoteProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -3235,7 +3235,7 @@ public final class RemoteProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -4062,7 +4062,7 @@ public final class RemoteProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -5428,7 +5428,7 @@ public final class RemoteProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -5962,7 +5962,7 @@ public final class RemoteProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -6475,7 +6475,7 @@ public final class RemoteProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -7022,7 +7022,7 @@ public final class RemoteProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -7455,7 +7455,7 @@ public final class RemoteProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -7858,7 +7858,7 @@ public final class RemoteProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -8261,7 +8261,7 @@ public final class RemoteProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -8731,7 +8731,7 @@ public final class RemoteProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } From d917f999cf01f39e45cc344b7b6699859d0c49ec Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Tue, 14 Jun 2011 10:26:38 +1200 Subject: [PATCH 09/13] Adjust all protocols to work with scaladoc --- .../src/main/java/akka/cluster/ClusterProtocol.java | 6 +++--- .../src/main/java/akka/actor/mailbox/MailboxProtocol.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/akka-cluster/src/main/java/akka/cluster/ClusterProtocol.java b/akka-cluster/src/main/java/akka/cluster/ClusterProtocol.java index 840e89c015..54ca02a15f 100644 --- a/akka-cluster/src/main/java/akka/cluster/ClusterProtocol.java +++ b/akka-cluster/src/main/java/akka/cluster/ClusterProtocol.java @@ -402,7 +402,7 @@ public final class ClusterProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -1092,7 +1092,7 @@ public final class ClusterProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -1694,7 +1694,7 @@ public final class ClusterProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } diff --git a/akka-durable-mailboxes/akka-mailboxes-common/src/main/java/akka/actor/mailbox/MailboxProtocol.java b/akka-durable-mailboxes/akka-mailboxes-common/src/main/java/akka/actor/mailbox/MailboxProtocol.java index bc128610cf..94c8cfde3e 100644 --- a/akka-durable-mailboxes/akka-mailboxes-common/src/main/java/akka/actor/mailbox/MailboxProtocol.java +++ b/akka-durable-mailboxes/akka-mailboxes-common/src/main/java/akka/actor/mailbox/MailboxProtocol.java @@ -323,7 +323,7 @@ public final class MailboxProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } @@ -925,7 +925,7 @@ public final class MailboxProtocol { maybeForceBuilderInitialization(); } - private Builder(BuilderParent parent) { + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } From cbdfd0fe237fde8ed1fd86f25caca8ba36170072 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Mon, 13 Jun 2011 20:25:31 -0600 Subject: [PATCH 10/13] Fix Future type issues --- .../test/scala/akka/dispatch/FutureSpec.scala | 30 +++++-------------- .../src/main/scala/akka/dispatch/Future.scala | 16 ++-------- .../src/main/scala/akka/japi/JavaAPI.scala | 8 ++--- 3 files changed, 14 insertions(+), 40 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 d54bd1c12f..4bb58852cb 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -62,22 +62,8 @@ class FutureSpec extends JUnitSuite { def shouldFutureCompose { val actor1 = actorOf[TestActor].start() val actor2 = actorOf(new Actor { def receive = { case s: String ⇒ self reply s.toUpperCase } }).start() - val future1 = actor1 ? "Hello" mapTo manifest[String] flatMap ((s: String) ⇒ actor2 ? s) - val future2 = actor1 ? "Hello" mapTo manifest[String] flatMap (actor2 ? (_: String)) - val future3 = actor1 ? "Hello" mapTo manifest[Int] flatMap (actor2 ? (_: Int)) - assert((future1.get: Any) === "WORLD") - assert((future2.get: Any) === "WORLD") - intercept[ClassCastException] { future3.get } - actor1.stop() - actor2.stop() - } - - @Test - def shouldFutureComposePatternMatch { - val actor1 = actorOf[TestActor].start() - 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 ? _) + val future1 = actor1 ? "Hello" flatMap { case s: String ⇒ actor2 ? s } + val future2 = actor1 ? "Hello" flatMap { case i: Int ⇒ actor2 ? i } assert((future1.get: Any) === "WORLD") intercept[MatchError] { future2.get } actor1.stop() @@ -124,15 +110,15 @@ class FutureSpec extends JUnitSuite { }).start() val future1 = for { - Res(a: Int) ← actor.?(Req("Hello")).mapTo[Res[Int]] - Res(b: String) ← actor.?(Req(a)).mapTo[Res[String]] - Res(c: String) ← actor.?(Req(7)).mapTo[Res[String]] + Res(a: Int) ← actor ? Req("Hello") + Res(b: String) ← actor ? Req(a) + Res(c: String) ← actor ? Req(7) } yield b + "-" + c val future2 = for { - Res(a: Int) ← actor.?(Req("Hello")) - Res(b: Int) ← actor.?(Req(a)).mapTo[Res[Int]] - Res(c: Int) ← actor.?(Req(7)).mapTo[Res[Int]] + Res(a: Int) ← actor ? Req("Hello") + Res(b: Int) ← actor ? Req(a) + Res(c: Int) ← actor ? Req(7) } yield b + "-" + c assert(future1.get === "10-14") diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 3e8bd0515b..b089c7debc 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -575,7 +575,7 @@ sealed trait Future[+T] { } } - /*final def withFilter(p: T ⇒ Boolean) = new FutureWithFilter[T](this, p) + final def withFilter(p: T ⇒ Boolean) = new FutureWithFilter[T](this, p) final class FutureWithFilter[+A](self: Future[A], p: A ⇒ Boolean) { def foreach(f: A ⇒ Unit): Unit = self filter p foreach f @@ -584,8 +584,7 @@ sealed trait Future[+T] { def withFilter(q: A ⇒ Boolean): FutureWithFilter[A] = new FutureWithFilter[A](self, x ⇒ p(x) && q(x)) } - final def filter(p: T ⇒ Boolean): Future[T] = { */ - final def filter(p: Any ⇒ Boolean): Future[T] = { + final def filter(p: T ⇒ Boolean): Future[T] = { val f = new DefaultPromise[T](timeoutInNanos, NANOS) onComplete { ft ⇒ val optv = ft.value @@ -621,17 +620,6 @@ sealed trait Future[+T] { } else None } - /* Java API */ - final def onComplete[A >: T](proc: Procedure[Future[A]]): this.type = onComplete(proc(_)) - - final def map[A >: T, B](f: JFunc[A, B]): Future[B] = map(f(_)) - - final def flatMap[A >: T, B](f: JFunc[A, Future[B]]): Future[B] = flatMap(f(_)) - - final def foreach[A >: T](proc: Procedure[A]): Unit = foreach(proc(_)) - - final def filter(p: JFunc[Any, Boolean]): Future[Any] = filter(p(_)) - } object Promise { diff --git a/akka-actor/src/main/scala/akka/japi/JavaAPI.scala b/akka-actor/src/main/scala/akka/japi/JavaAPI.scala index f5c4ccdcaa..05d52d2fac 100644 --- a/akka-actor/src/main/scala/akka/japi/JavaAPI.scala +++ b/akka-actor/src/main/scala/akka/japi/JavaAPI.scala @@ -3,28 +3,28 @@ package akka.japi /** * A Function interface. Used to create first-class-functions is Java (sort of). */ -trait Function[T, R] { +abstract class Function[-T, +R] extends (T) => R { def apply(param: T): R } /** * A Function interface. Used to create 2-arg first-class-functions is Java (sort of). */ -trait Function2[T1, T2, R] { +abstract class Function2[-T1, -T2, +R] extends (T1, T2) => R { def apply(arg1: T1, arg2: T2): R } /** * A Procedure is like a Function, but it doesn't produce a return value */ -trait Procedure[T] { +abstract class Procedure[-T] extends (T) => Unit { def apply(param: T): Unit } /** * A Procedure is like a Function, but it doesn't produce a return value */ -trait Procedure2[T1, T2] { +abstract class Procedure2[-T1, -T2] extends (T1, T2) => Unit { def apply(param: T1, param2: T2): Unit } From c62e6096d3b4f223434bf3b4c53bfc9aa62d58f2 Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Mon, 13 Jun 2011 20:39:31 -0600 Subject: [PATCH 11/13] Didn't test that change, reverted to my original --- akka-actor/src/main/scala/akka/japi/JavaAPI.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/akka-actor/src/main/scala/akka/japi/JavaAPI.scala b/akka-actor/src/main/scala/akka/japi/JavaAPI.scala index 05d52d2fac..76cac73fe2 100644 --- a/akka-actor/src/main/scala/akka/japi/JavaAPI.scala +++ b/akka-actor/src/main/scala/akka/japi/JavaAPI.scala @@ -3,28 +3,28 @@ package akka.japi /** * A Function interface. Used to create first-class-functions is Java (sort of). */ -abstract class Function[-T, +R] extends (T) => R { +abstract class Function[-T, +R] extends scala.Function1[T, R] { def apply(param: T): R } /** * A Function interface. Used to create 2-arg first-class-functions is Java (sort of). */ -abstract class Function2[-T1, -T2, +R] extends (T1, T2) => R { +abstract class Function2[-T1, -T2, +R] extends scala.Function2[T1, T2, R] { def apply(arg1: T1, arg2: T2): R } /** * A Procedure is like a Function, but it doesn't produce a return value */ -abstract class Procedure[-T] extends (T) => Unit { +abstract class Procedure[-T] extends scala.Function1[T, Unit] { def apply(param: T): Unit } /** * A Procedure is like a Function, but it doesn't produce a return value */ -abstract class Procedure2[-T1, -T2] extends (T1, T2) => Unit { +abstract class Procedure2[-T1, -T2] extends scala.Function2[T1, T2, Unit] { def apply(param: T1, param2: T2): Unit } From e18cc7bac6ca8734b75485acd616cd08be507f6d Mon Sep 17 00:00:00 2001 From: Derek Williams Date: Mon, 13 Jun 2011 22:31:06 -0600 Subject: [PATCH 12/13] revert changes to java api --- .../src/test/scala/akka/dispatch/FutureSpec.scala | 4 ++-- akka-actor/src/main/scala/akka/dispatch/Future.scala | 11 +++++++++++ akka-actor/src/main/scala/akka/japi/JavaAPI.scala | 8 ++++---- 3 files changed, 17 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 4bb58852cb..df829177b9 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 { def shouldFutureCompose { val actor1 = actorOf[TestActor].start() val actor2 = actorOf(new Actor { def receive = { case s: String ⇒ self reply s.toUpperCase } }).start() - val future1 = actor1 ? "Hello" flatMap { case s: String ⇒ actor2 ? s } - val future2 = actor1 ? "Hello" flatMap { case i: Int ⇒ actor2 ? i } + val future1 = actor1 ? "Hello" flatMap { _ match { case s: String ⇒ actor2 ? s } } + val future2 = actor1 ? "Hello" flatMap { _ match { case i: Int ⇒ actor2 ? i } } assert((future1.get: Any) === "WORLD") intercept[MatchError] { future2.get } actor1.stop() diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index b089c7debc..02cd02f1df 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -620,6 +620,17 @@ sealed trait Future[+T] { } else None } + /* Java API */ + final def onComplete[A >: T](proc: Procedure[Future[A]]): this.type = onComplete(proc(_)) + + final def map[A >: T, B](f: JFunc[A, B]): Future[B] = map(f(_)) + + final def flatMap[A >: T, B](f: JFunc[A, Future[B]]): Future[B] = flatMap(f(_)) + + final def foreach[A >: T](proc: Procedure[A]): Unit = foreach(proc(_)) + + final def filter(p: JFunc[Any, Boolean]): Future[Any] = filter(p(_)) + } object Promise { diff --git a/akka-actor/src/main/scala/akka/japi/JavaAPI.scala b/akka-actor/src/main/scala/akka/japi/JavaAPI.scala index 76cac73fe2..f5c4ccdcaa 100644 --- a/akka-actor/src/main/scala/akka/japi/JavaAPI.scala +++ b/akka-actor/src/main/scala/akka/japi/JavaAPI.scala @@ -3,28 +3,28 @@ package akka.japi /** * A Function interface. Used to create first-class-functions is Java (sort of). */ -abstract class Function[-T, +R] extends scala.Function1[T, R] { +trait Function[T, R] { def apply(param: T): R } /** * A Function interface. Used to create 2-arg first-class-functions is Java (sort of). */ -abstract class Function2[-T1, -T2, +R] extends scala.Function2[T1, T2, R] { +trait Function2[T1, T2, R] { def apply(arg1: T1, arg2: T2): R } /** * A Procedure is like a Function, but it doesn't produce a return value */ -abstract class Procedure[-T] extends scala.Function1[T, Unit] { +trait Procedure[T] { def apply(param: T): Unit } /** * A Procedure is like a Function, but it doesn't produce a return value */ -abstract class Procedure2[-T1, -T2] extends scala.Function2[T1, T2, Unit] { +trait Procedure2[T1, T2] { def apply(param: T1, param2: T2): Unit } From 26500bed096d7709373b443e695e9283058634ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Tue, 14 Jun 2011 14:26:13 +0200 Subject: [PATCH 13/13] 1. Removed implicit scoped timeout in ? method. Reason: 'pingPongActor.?(Ping)(timeout = TimeoutMillis)' breaks old user code and is so ugly. It is not worth it. Now user write (as he used to): 'pingPongActor ? (Ping, TimeoutMillis)' 2. Fixed broken Cluster communication 3. Added constructor with only String arg for UnhandledMessageException to allow client instantiation of remote exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonas Bonér --- .../akka/actor/actor/ActorTimeoutSpec.scala | 13 +---------- .../actor/supervisor/SupervisorSpec.scala | 12 +++++----- .../akka/dispatch/DispatcherActorSpec.scala | 2 +- .../test/scala/akka/dispatch/FutureSpec.scala | 10 ++++---- .../scala/akka/dispatch/PinnedActorSpec.scala | 2 +- .../test/scala/akka/routing/RoutingSpec.scala | 2 +- .../scala/akka/ticket/Ticket703Spec.scala | 2 +- .../src/main/scala/akka/actor/Actor.scala | 7 ++++-- .../src/main/scala/akka/actor/ActorRef.scala | 11 +++++---- .../main/scala/akka/actor/TypedActor.scala | 8 +++---- .../src/main/scala/akka/dispatch/Future.scala | 23 ++++++++++--------- .../src/main/scala/akka/cluster/Cluster.scala | 2 +- .../akka/cluster/ZooKeeperStorageSpec.scala | 6 ++--- .../RoundRobin1ReplicaMultiJvmSpec.scala | 8 +++---- .../src/main/scala/akka/agent/Agent.scala | 4 ++-- .../src/main/scala/Pi.scala | 2 +- 16 files changed, 55 insertions(+), 59 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorTimeoutSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorTimeoutSpec.scala index b42ac75bcb..a10100497a 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorTimeoutSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorTimeoutSpec.scala @@ -35,21 +35,10 @@ class ActorTimeoutSpec } } - "use implicitly supplied timeout" in { - implicit val timeout = Actor.Timeout(testTimeout) - within(testTimeout - 100.millis, testTimeout + 300.millis) { - val f = (echo ? "hallo").mapTo[String] - intercept[FutureTimeoutException] { f.await } - f.value must be(None) - } - } - "use explicitly supplied timeout" in { within(testTimeout - 100.millis, testTimeout + 300.millis) { - (echo.?("hallo")(timeout = testTimeout)).as[String] must be(None) + (echo.?("hallo", testTimeout)).as[String] must be(None) } } - } - } 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 3fb0908402..8c5f397387 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 @@ -66,7 +66,7 @@ object SupervisorSpec { } override def receive = { - case Die ⇒ (temp.?(Die)(timeout = TimeoutMillis)).get + case Die ⇒ (temp.?(Die, TimeoutMillis)).get } } @@ -200,7 +200,7 @@ class SupervisorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach } def ping(pingPongActor: ActorRef) = { - (pingPongActor.?(Ping)(timeout = TimeoutMillis)).as[String].getOrElse("nil") must be === PongMessage + (pingPongActor.?(Ping, TimeoutMillis)).as[String].getOrElse("nil") must be === PongMessage messageLogPoll must be === PingMessage } @@ -215,7 +215,7 @@ class SupervisorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach val master = actorOf[Master].start() intercept[RuntimeException] { - (master.?(Die)(timeout = TimeoutMillis)).get + (master.?(Die, TimeoutMillis)).get } sleepFor(1 second) @@ -226,7 +226,7 @@ class SupervisorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach val (temporaryActor, supervisor) = temporaryActorAllForOne intercept[RuntimeException] { - (temporaryActor.?(Die)(timeout = TimeoutMillis)).get + (temporaryActor.?(Die, TimeoutMillis)).get } sleepFor(1 second) @@ -374,13 +374,13 @@ class SupervisorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach Supervise(dyingActor, Permanent) :: Nil)) intercept[Exception] { - (dyingActor.?(Die)(timeout = TimeoutMillis)).get + (dyingActor.?(Die, TimeoutMillis)).get } // give time for restart sleepFor(3 seconds) - (dyingActor.?(Ping)(timeout = TimeoutMillis)).as[String].getOrElse("nil") must be === PongMessage + (dyingActor.?(Ping, TimeoutMillis)).as[String].getOrElse("nil") must be === PongMessage inits.get must be(3) diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/DispatcherActorSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/DispatcherActorSpec.scala index 83d6bc21eb..75f984065c 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/DispatcherActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/DispatcherActorSpec.scala @@ -45,7 +45,7 @@ class DispatcherActorSpec extends JUnitSuite { @Test def shouldSendReplySync = { val actor = actorOf[TestActor].start() - val result = (actor.?("Hello")(timeout = 10000)).as[String] + val result = (actor.?("Hello", 10000)).as[String] assert("World" === result.get) actor.stop() } 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 df829177b9..de81074303 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/FutureSpec.scala @@ -180,7 +180,7 @@ class FutureSpec extends JUnitSuite { }).start() } val timeout = 10000 - def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 200))(timeout = timeout).mapTo[Int] } + def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 200), timeout).mapTo[Int] } assert(Futures.fold(0, timeout)(futures)(_ + _).await.result.get === 45) } @@ -191,7 +191,7 @@ 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.?((idx, idx * 200))(timeout = 10000).mapTo[Int] } + def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 200), 10000).mapTo[Int] } assert(futures.foldLeft(Future(0))((fr, fa) ⇒ for (r ← fr; a ← fa) yield (r + a)).get === 45) } @@ -208,7 +208,7 @@ class FutureSpec extends JUnitSuite { }).start() } val timeout = 10000 - def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 100))(timeout = timeout).mapTo[Int] } + def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 100), timeout).mapTo[Int] } assert(Futures.fold(0, timeout)(futures)(_ + _).await.exception.get.getMessage === "shouldFoldResultsWithException: expected") } @@ -225,7 +225,7 @@ class FutureSpec extends JUnitSuite { }).start() } val timeout = 10000 - def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 200))(timeout = timeout).mapTo[Int] } + def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 200), timeout).mapTo[Int] } assert(Futures.reduce(futures, timeout)(_ + _).get === 45) } @@ -242,7 +242,7 @@ class FutureSpec extends JUnitSuite { }).start() } val timeout = 10000 - def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 100))(timeout = timeout).mapTo[Int] } + def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 100), timeout).mapTo[Int] } assert(Futures.reduce(futures, timeout)(_ + _).await.exception.get.getMessage === "shouldFoldResultsWithException: expected") } diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/PinnedActorSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/PinnedActorSpec.scala index 4744e8f451..a3d647fcf3 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/PinnedActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/PinnedActorSpec.scala @@ -43,7 +43,7 @@ class PinnedActorSpec extends JUnitSuite { @Test def shouldSendReplySync = { val actor = actorOf[TestActor].start() - val result = (actor.?("Hello")(timeout = 10000)).as[String] + val result = (actor.?("Hello", 10000)).as[String] assert("World" === result.get) actor.stop() } diff --git a/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala b/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala index 126eb3ba45..9c1704b7f5 100644 --- a/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala @@ -238,7 +238,7 @@ class RoutingSpec extends WordSpec with MustMatchers { }).start() try { - (for (count ← 1 to 500) yield pool.?("Test")(timeout = 20000)) foreach { + (for (count ← 1 to 500) yield pool.?("Test", 20000)) foreach { _.await.resultOrException.get must be("Response") } } finally { diff --git a/akka-actor-tests/src/test/scala/akka/ticket/Ticket703Spec.scala b/akka-actor-tests/src/test/scala/akka/ticket/Ticket703Spec.scala index ba2a84196d..b838f33efc 100644 --- a/akka-actor-tests/src/test/scala/akka/ticket/Ticket703Spec.scala +++ b/akka-actor-tests/src/test/scala/akka/ticket/Ticket703Spec.scala @@ -28,7 +28,7 @@ class Ticket703Spec extends WordSpec with MustMatchers { } }) }).start() - (actorPool.?("Ping")(timeout = 7000)).await.result must be === Some("Response") + (actorPool.?("Ping", 7000)).await.result must be === Some("Response") } } } diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 429b686d5b..e4031962c9 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -89,8 +89,11 @@ class InvalidMessageException private[akka] (message: String, cause: Throwable = /** * This message is thrown by default when an Actors behavior doesn't match a message */ -case class UnhandledMessageException(msg: Any, ref: ActorRef) extends Exception { - override def getMessage = "Actor %s does not handle [%s]".format(ref, msg) +case class UnhandledMessageException(msg: Any, ref: ActorRef = null) extends Exception { + // constructor with 'null' ActorRef needed to work with client instantiation of remote exception + override def getMessage = + if (ref ne null) "Actor %s does not handle [%s]".format(ref, msg) + else "Actor does not handle [%s]".format(msg) override def fillInStackTrace() = this //Don't waste cycles generating stack trace } diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index dea6f39018..7694663c95 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -248,7 +248,8 @@ trait ActorRef extends ActorRefShared with ForwardableChannel with java.lang.Com * If you are sending messages using ask then you have to use getContext().reply(..) * to send a reply message to the original sender. If not then the sender will block until the timeout expires. */ - def ask(message: AnyRef, timeout: Long, sender: ActorRef): Future[AnyRef] = ?(message)(sender, Actor.Timeout(timeout)).asInstanceOf[Future[AnyRef]] + def ask(message: AnyRef, timeout: Long, sender: ActorRef): Future[AnyRef] = + ?(message, Actor.Timeout(timeout))(sender).asInstanceOf[Future[AnyRef]] /** * Akka Java API.

@@ -758,7 +759,8 @@ class LocalActorRef private[akka] ( } def tooManyRestarts() { - notifySupervisorWithMessage(MaximumNumberOfRestartsWithinTimeRangeReached(this, maxNrOfRetries, withinTimeRange, reason)) + notifySupervisorWithMessage( + MaximumNumberOfRestartsWithinTimeRangeReached(this, maxNrOfRetries, withinTimeRange, reason)) stop() } @@ -1170,7 +1172,7 @@ trait ScalaActorRef extends ActorRefShared with ForwardableChannel { ref: ActorR /** * Sends a message asynchronously, returning a future which may eventually hold the reply. */ - def ?(message: Any)(implicit channel: UntypedChannel = NullChannel, timeout: Actor.Timeout = Actor.defaultTimeout): Future[Any] = { + def ?(message: Any, timeout: Actor.Timeout = Actor.defaultTimeout)(implicit channel: UntypedChannel = NullChannel): Future[Any] = { if (isRunning) postMessageToMailboxAndCreateFutureResultWithTimeout(message, timeout.duration.toMillis, channel) else throw new ActorInitializationException( "Actor has not been started, you need to invoke 'actor.start()' before using it") @@ -1184,7 +1186,8 @@ trait ScalaActorRef extends ActorRefShared with ForwardableChannel { ref: ActorR def forward(message: Any)(implicit channel: ForwardableChannel) = { if (isRunning) { postMessageToMailbox(message, channel.channel) - } else throw new ActorInitializationException("Actor has not been started, you need to invoke 'actor.start()' before using it") + } else throw new ActorInitializationException( + "Actor has not been started, you need to invoke 'actor.start()' before using it") } /** diff --git a/akka-actor/src/main/scala/akka/actor/TypedActor.scala b/akka-actor/src/main/scala/akka/actor/TypedActor.scala index 0ba0e44d5c..1e7c095f39 100644 --- a/akka-actor/src/main/scala/akka/actor/TypedActor.scala +++ b/akka-actor/src/main/scala/akka/actor/TypedActor.scala @@ -41,15 +41,15 @@ object TypedActor { case "equals" ⇒ (args.length == 1 && (proxy eq args(0)) || actor == getActorRefFor(args(0))).asInstanceOf[AnyRef] //Force boxing of the boolean case "hashCode" ⇒ actor.hashCode.asInstanceOf[AnyRef] case _ ⇒ - implicit val timeout = Actor.Timeout(actor.timeout) + val timeout = Actor.Timeout(actor.timeout) MethodCall(method, args) match { case m if m.isOneWay ⇒ actor ! m null case m if m.returnsFuture_? ⇒ - actor ? m + actor ? (m, timeout) case m if m.returnsJOption_? || m.returnsOption_? ⇒ - val f = actor ? m + val f = actor ? (m, timeout) try { f.await } catch { case _: FutureTimeoutException ⇒ } f.value match { case None | Some(Right(null)) ⇒ if (m.returnsJOption_?) JOption.none[Any] else None @@ -57,7 +57,7 @@ object TypedActor { case Some(Left(ex)) ⇒ throw ex } case m ⇒ - (actor ? m).get.asInstanceOf[AnyRef] + (actor ? (m, timeout)).get.asInstanceOf[AnyRef] } } } diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 02cd02f1df..aa96e58845 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -311,18 +311,19 @@ sealed trait Future[+T] { /** * Await completion of this Future (as `await`) and return its value if it * conforms to A's erased type. + * + * def as[A](implicit m: Manifest[A]): Option[A] = + * try { + * await + * value match { + * case None ⇒ None + * case Some(_: Left[_, _]) ⇒ None + * case Some(Right(v)) ⇒ Some(BoxedType(m.erasure).cast(v).asInstanceOf[A]) + * } + * } catch { + * case _: Exception ⇒ None + * } */ - def as[A](implicit m: Manifest[A]): Option[A] = - try { - await - value match { - case None ⇒ None - case Some(_: Left[_, _]) ⇒ None - case Some(Right(v)) ⇒ Some(BoxedType(m.erasure).cast(v).asInstanceOf[A]) - } - } catch { - case _: Exception ⇒ None - } /** * Tests whether this Future has been completed. diff --git a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala index cc9fbfaf2f..73eb117f0b 100644 --- a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala @@ -676,7 +676,7 @@ class DefaultClusterNode private[akka] ( case None ⇒ val error = new ClusterException( - "Operation to instantiate replicas throughout the cluster timed out, cause of error unknow") + "Operation to instantiate replicas throughout the cluster timed out") EventHandler.error(error, this, error.toString) throw error } diff --git a/akka-cluster/src/test/scala/akka/cluster/ZooKeeperStorageSpec.scala b/akka-cluster/src/test/scala/akka/cluster/ZooKeeperStorageSpec.scala index a4bda26c77..84f91a0453 100644 --- a/akka-cluster/src/test/scala/akka/cluster/ZooKeeperStorageSpec.scala +++ b/akka-cluster/src/test/scala/akka/cluster/ZooKeeperStorageSpec.scala @@ -11,7 +11,7 @@ class ZooKeeperStorageSpec extends WordSpec with MustMatchers with BeforeAndAfte val logPath = "_akka_cluster/log" var zkServer: ZkServer = _ var zkClient: AkkaZkClient = _ - + /* override def beforeAll() { try { zkServer = Cluster.startLocalCluster(dataPath, logPath) @@ -30,10 +30,10 @@ class ZooKeeperStorageSpec extends WordSpec with MustMatchers with BeforeAndAfte Cluster.shutdownLocalCluster() Actor.registry.local.shutdownAll() } - +*/ "unversioned load" must { "throw MissingDataException if non existing key" in { - val store = new ZooKeeperStorage(zkClient) + // val store = new ZooKeeperStorage(zkClient) //try { // store.load("foo") diff --git a/akka-cluster/src/test/scala/akka/cluster/routing/roundrobin_1_replica/RoundRobin1ReplicaMultiJvmSpec.scala b/akka-cluster/src/test/scala/akka/cluster/routing/roundrobin_1_replica/RoundRobin1ReplicaMultiJvmSpec.scala index 52f85fead1..668acb3376 100644 --- a/akka-cluster/src/test/scala/akka/cluster/routing/roundrobin_1_replica/RoundRobin1ReplicaMultiJvmSpec.scala +++ b/akka-cluster/src/test/scala/akka/cluster/routing/roundrobin_1_replica/RoundRobin1ReplicaMultiJvmSpec.scala @@ -31,7 +31,7 @@ class RoundRobin1ReplicaMultiJvmNode1 extends WordSpec with MustMatchers with Be import RoundRobin1ReplicaMultiJvmSpec._ private var bookKeeper: BookKeeper = _ - private var localBookKeeper: LocalBookKeeper = _ + // private var localBookKeeper: LocalBookKeeper = _ "A cluster" must { @@ -55,13 +55,13 @@ class RoundRobin1ReplicaMultiJvmNode1 extends WordSpec with MustMatchers with Be override def beforeAll() = { Cluster.startLocalCluster() - LocalBookKeeperEnsemble.start() + // LocalBookKeeperEnsemble.start() } override def afterAll() = { Cluster.shutdownLocalCluster() - TransactionLog.shutdown() - LocalBookKeeperEnsemble.shutdown() + // TransactionLog.shutdown() + // LocalBookKeeperEnsemble.shutdown() } } diff --git a/akka-stm/src/main/scala/akka/agent/Agent.scala b/akka-stm/src/main/scala/akka/agent/Agent.scala index 7e11fc3670..be30615b85 100644 --- a/akka-stm/src/main/scala/akka/agent/Agent.scala +++ b/akka-stm/src/main/scala/akka/agent/Agent.scala @@ -120,7 +120,7 @@ class Agent[T](initialValue: T) { * within the given timeout */ def alter(f: T ⇒ T)(timeout: Long): Future[T] = { - def dispatch = updater.?(Update(f))(timeout = timeout).asInstanceOf[Future[T]] + def dispatch = updater.?(Update(f), timeout).asInstanceOf[Future[T]] if (Stm.activeTransaction) { val result = new DefaultPromise[T](timeout) get //Join xa @@ -168,7 +168,7 @@ class Agent[T](initialValue: T) { send((value: T) ⇒ { suspend val threadBased = Actor.actorOf(new ThreadBasedAgentUpdater(this)).start() - result completeWith threadBased.?(Update(f))(timeout = timeout).asInstanceOf[Future[T]] + result completeWith threadBased.?(Update(f), timeout).asInstanceOf[Future[T]] value }) result 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 7fcabf0990..72ee614752 100644 --- a/akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala +++ b/akka-tutorials/akka-tutorial-second/src/main/scala/Pi.scala @@ -104,7 +104,7 @@ object Pi extends App { val start = now //send calculate message - master.?(Calculate)(timeout = Actor.Timeout(60000)). + master.?(Calculate, Actor.Timeout(60000)). await.resultOrException match {//wait for the result, with a 60 seconds timeout case Some(pi) => EventHandler.info(this, "\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis".format(pi, (now - start)))