+act #3513 Clarify gracefulStop stopMessage, and add Java API

This commit is contained in:
Patrik Nordwall 2014-01-21 17:15:09 +01:00
parent 1c0e799370
commit d506f6df4f
6 changed files with 105 additions and 7 deletions

View file

@ -37,7 +37,7 @@ trait GracefulStopSupport {
* If the target actor isn't terminated within the timeout the [[scala.concurrent.Future]]
* is completed with failure [[akka.pattern.AskTimeoutException]].
*
* If you want to invoke specalized stopping logic on your target actor instead of PoisonPill, you can pass your
* If you want to invoke specialized stopping logic on your target actor instead of PoisonPill, you can pass your
* stop command as a parameter:
* {{{
* gracefulStop(someChild, timeout, MyStopGracefullyMessage).onComplete {

View file

@ -174,6 +174,22 @@ object Patterns {
def gracefulStop(target: ActorRef, timeout: FiniteDuration): Future[java.lang.Boolean] =
scalaGracefulStop(target, timeout).asInstanceOf[Future[java.lang.Boolean]]
/**
* Returns a [[scala.concurrent.Future]] that will be completed with success (value `true`) when
* existing messages of the target actor has been processed and the actor has been
* terminated.
*
* Useful when you need to wait for termination or compose ordered termination of several actors.
*
* If you want to invoke specialized stopping logic on your target actor instead of PoisonPill, you can pass your
* stop command as `stopMessage` parameter
*
* If the target actor isn't terminated within the timeout the [[scala.concurrent.Future]]
* is completed with failure [[akka.pattern.AskTimeoutException]].
*/
def gracefulStop(target: ActorRef, timeout: FiniteDuration, stopMessage: Any): Future[java.lang.Boolean] =
scalaGracefulStop(target, timeout, stopMessage).asInstanceOf[Future[java.lang.Boolean]]
/**
* Returns a [[scala.concurrent.Future]] that will be completed with the success or failure of the provided Callable
* after the specified duration.

View file

@ -11,7 +11,7 @@ import static akka.pattern.Patterns.pipe;
import static akka.pattern.Patterns.gracefulStop;
//#import-gracefulStop
import akka.actor.PoisonPill;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
@ -403,11 +403,11 @@ public class UntypedActorDocTest {
@Test
public void usePatternsGracefulStop() throws Exception {
ActorRef actorRef = system.actorOf(Props.create(MyUntypedActor.class));
ActorRef actorRef = system.actorOf(Props.create(Manager.class));
//#gracefulStop
try {
Future<Boolean> stopped =
gracefulStop(actorRef, Duration.create(5, TimeUnit.SECONDS));
gracefulStop(actorRef, Duration.create(5, TimeUnit.SECONDS), Manager.SHUTDOWN);
Await.result(stopped, Duration.create(6, TimeUnit.SECONDS));
// the actor has been stopped
} catch (AskTimeoutException e) {
@ -592,6 +592,43 @@ public class UntypedActorDocTest {
return "Hi";
}
}
static
//#gracefulStop-actor
public class Manager extends UntypedActor {
public static final String SHUTDOWN = "shutdown";
ActorRef worker = getContext().watch(getContext().actorOf(
Props.create(Cruncher.class), "worker"));
public void onReceive(Object message) {
if (message.equals("job")) {
worker.tell("crunch", getSelf());
} else if (message.equals(SHUTDOWN)) {
worker.tell(PoisonPill.getInstance(), getSelf());
getContext().become(shuttingDown);
}
}
Procedure<Object> shuttingDown = new Procedure<Object>() {
@Override
public void apply(Object message) {
if (message.equals("job")) {
getSender().tell("service unavailable, shutting down", getSelf());
} else if (message instanceof Terminated) {
getContext().stop(getSelf());
}
}
};
}
//#gracefulStop-actor
static class Cruncher extends UntypedActor {
public void onReceive(Object message) {
// crunch...
}
}
static
//#hot-swap-actor

View file

@ -664,10 +664,18 @@ termination of several actors:
.. includecode:: code/docs/actor/UntypedActorDocTest.java
:include: gracefulStop
.. includecode:: code/docs/actor/UntypedActorDocTest.java
:include: gracefulStop-actor
When ``gracefulStop()`` returns successfully, the actors ``postStop()`` hook
will have been executed: there exists a happens-before edge between the end of
``postStop()`` and the return of ``gracefulStop()``.
In the above example a custom ``Manager.SHUTDOWN`` message is sent to the target
actor to initiate the process of stopping the actor. You can use ``PoisonPill`` for
this, but then you have limited possibilities to perform interactions with other actors
before stopping the target actor. Simple cleanup tasks can be handled in ``postStop``.
.. warning::
Keep in mind that an actor stopping and its name being deregistered are

View file

@ -706,10 +706,17 @@ termination of several actors:
.. includecode:: code/docs/actor/ActorDocSpec.scala#gracefulStop
.. includecode:: code/docs/actor/ActorDocSpec.scala#gracefulStop-actor
When ``gracefulStop()`` returns successfully, the actors ``postStop()`` hook
will have been executed: there exists a happens-before edge between the end of
``postStop()`` and the return of ``gracefulStop()``.
In the above example a custom ``Manager.Shutdown`` message is sent to the target
actor to initiate the process of stopping the actor. You can use ``PoisonPill`` for
this, but then you have limited possibilities to perform interactions with other actors
before stopping the target actor. Simple cleanup tasks can be handled in ``postStop``.
.. warning::
Keep in mind that an actor stopping and its name being deregistered are

View file

@ -13,7 +13,7 @@ import akka.event.Logging
//#imports1
import scala.concurrent.Future
import akka.actor.{ ActorRef, ActorSystem }
import akka.actor.{ ActorRef, ActorSystem, PoisonPill, Terminated }
import org.scalatest.{ BeforeAndAfterAll, WordSpec }
import org.scalatest.Matchers
import akka.testkit._
@ -136,6 +136,36 @@ class ReplyException extends Actor {
}
//#gracefulStop-actor
object Manager {
case object Shutdown
}
class Manager extends Actor {
import Manager._
val worker = context.watch(context.actorOf(Props[Cruncher], "worker"))
def receive = {
case "job" => worker ! "crunch"
case Shutdown =>
worker ! PoisonPill
context become shuttingDown
}
def shuttingDown: Receive = {
case "job" => sender() ! "service unavailable, shutting down"
case Terminated(`worker`) =>
context stop self
}
}
//#gracefulStop-actor
class Cruncher extends Actor {
def receive = {
case "crunch" => // crunch...
}
}
//#swapper
case object Swap
class Swapper extends Actor {
@ -480,13 +510,13 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
}
"using pattern gracefulStop" in {
val actorRef = system.actorOf(Props[MyActor])
val actorRef = system.actorOf(Props[Manager])
//#gracefulStop
import akka.pattern.gracefulStop
import scala.concurrent.Await
try {
val stopped: Future[Boolean] = gracefulStop(actorRef, 5 seconds)
val stopped: Future[Boolean] = gracefulStop(actorRef, 5 seconds, Manager.Shutdown)
Await.result(stopped, 6 seconds)
// the actor has been stopped
} catch {