diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 4c8877463c..72c4ecabc3 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -156,15 +156,44 @@ object Actor { /** * Actor base trait that should be extended by or mixed to create an Actor with the semantics of the 'Actor Model': * http://en.wikipedia.org/wiki/Actor_model - *

- * An actor has a well-defined (non-cyclic) life-cycle. - *

- * => RUNNING (created and started actor) - can receive messages
- * => SHUTDOWN (when 'stop' or 'exit' is invoked) - can't do anything
- * 
* - *

- * The Actor's own ActorRef is available in the 'self' member variable. + * An actor has a well-defined (non-cyclic) life-cycle. + * - ''RUNNING'' (created and started actor) - can receive messages + * - ''SHUTDOWN'' (when 'stop' or 'exit' is invoked) - can't do anything + * + * The Actor's own [[akka.actor.ActorRef]] is available as `self`, the current + * message’s sender as `sender` and the [[akka.actor.ActorContext]] as + * `context`. The only abstract method is `receive` which shall return the + * initial behavior of the actor as a partial function (behavior can be changed + * using `context.become` and `context.unbecome`). + * + * {{{ + * class ExampleActor extends Actor { + * def receive = { + * // directly calculated reply + * case Request(r) => sender ! calculate(r) + * + * // just to demonstrate how to stop yourself + * case Shutdown => context.stop(self) + * + * // error kernel with child replying directly to “customer” + * case Dangerous(r) => context.actorOf(Props[ReplyToOriginWorker]).tell(PerformWork(r), sender) + * + * // error kernel with reply going through us + * case OtherJob(r) => context.actorOf(Props[ReplyToMeWorker]) ! JobRequest(r, sender) + * case JobReply(result, orig_s) => orig_s ! result + * } + * } + * }}} + * + * The last line demonstrates the essence of the error kernel design: spawn + * one-off actors which terminate after doing their job, pass on `sender` to + * allow direct reply if that is what makes sense, or round-trip the sender + * as shown with the fictitious JobRequest/JobReply message pair. + * + * If you don’t like writing `context` you can always `import context._` to get + * direct access to `actorOf`, `stop` etc. This is not default in order to keep + * the name-space clean. */ trait Actor { @@ -218,25 +247,8 @@ trait Actor { final def sender: ActorRef = context.sender /** - * User overridable callback/setting. - *

- * Partial function implementing the actor logic. - * To be implemented by concrete actor class. - *

- * Example code: - *

-   *   def receive = {
-   *     case Ping =>
-   *       println("got a 'Ping' message")
-   *       sender ! "pong"
-   *
-   *     case OneWay =>
-   *       println("got a 'OneWay' message")
-   *
-   *     case unknown =>
-   *       println("unknown message: " + unknown)
-   * }
-   * 
+ * This defines the initial actor behavior, it must return a partial function + * with the actor logic. */ protected def receive: Receive @@ -258,11 +270,10 @@ trait Actor { def postStop() {} /** - * User overridable callback. + * User overridable callback: '''By default it disposes of all children and then calls `postStop()`.''' *

* Is called on a crashed Actor right BEFORE it is restarted to allow clean * up of resources before Actor is terminated. - * By default it disposes of all children calls postStop(). */ def preRestart(reason: Throwable, message: Option[Any]) { context.children foreach (context.stop(_)) @@ -270,10 +281,9 @@ trait Actor { } /** - * User overridable callback. + * User overridable callback: By default it calls `preStart()`. *

* Is called right AFTER restart on the newly created Actor to allow reinitialization after an Actor crash. - * By default it calls preStart() */ def postRestart(reason: Throwable) { preStart() } @@ -281,7 +291,9 @@ trait Actor { * User overridable callback. *

* Is called when a message isn't handled by the current behavior of the actor - * by default it does: EventHandler.warning(self, message) + * by default it fails with either a [[akka.actor.DeathPactException]] (in + * case of an unhandled [[akka.actor.Terminated]] message) or a + * [[akka.actor.UnhandledMessageException]]. */ def unhandled(message: Any) { message match { diff --git a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala index 26a81d5eba..f801f7120d 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala @@ -365,18 +365,12 @@ class ActorSystemImpl(val name: String, applicationConfig: Config) extends Actor def stop(actor: ActorRef): Unit = { implicit val timeout = settings.CreationTimeout val path = actor.path - if (path.parent == guardian.path) { - (guardian ? StopChild(actor)).get match { - case ex: Exception ⇒ throw ex - case _ ⇒ - } - } else if (path.parent == systemGuardian.path) { - (systemGuardian ? StopChild(actor)).get match { - case ex: Exception ⇒ throw ex - case _ ⇒ - } - } else { - actor.asInstanceOf[InternalActorRef].stop() + val guard = guardian.path + val sys = systemGuardian.path + path.parent match { + case `guard` ⇒ (guardian ? StopChild(actor)).get + case `sys` ⇒ (systemGuardian ? StopChild(actor)).get + case _ ⇒ actor.asInstanceOf[InternalActorRef].stop() } } diff --git a/akka-actor/src/main/scala/akka/actor/UntypedActor.scala b/akka-actor/src/main/scala/akka/actor/UntypedActor.scala index 1692396a8f..13677f84cc 100644 --- a/akka-actor/src/main/scala/akka/actor/UntypedActor.scala +++ b/akka-actor/src/main/scala/akka/actor/UntypedActor.scala @@ -8,46 +8,67 @@ import akka.japi.{ Creator, Procedure } import akka.dispatch.{ MessageDispatcher, Promise } /** + * Actor base trait that should be extended by or mixed to create an Actor with the semantics of the 'Actor Model': + * http://en.wikipedia.org/wiki/Actor_model + * + * This class is the Java cousin to the [[akka.actor.Actor]] Scala interface. * Subclass this abstract class to create a MDB-style untyped actor. - *

- * This class is meant to be used from Java. - *

+ * + * An actor has a well-defined (non-cyclic) life-cycle. + * - ''RUNNING'' (created and started actor) - can receive messages + * - ''SHUTDOWN'' (when 'stop' or 'exit' is invoked) - can't do anything + * + * The Actor's own [[akka.actor.ActorRef]] is available as `getSelf()`, the current + * message’s sender as `getSender()` and the [[akka.actor.UntypedActorContext]] as + * `getContext()`. The only abstract method is `onReceive()` which is invoked for + * each processed message unless dynamically overridden using `getContext().become()`. + * * Here is an example on how to create and use an UntypedActor: - *

+ *
+ * {{{
  *  public class SampleUntypedActor extends UntypedActor {
+ *  
+ *    public class Reply {
+ *      final public ActorRef sender;
+ *      final public Result result;
+ *      Reply(ActorRef sender, Result result) {
+ *        this.sender = sender;
+ *        this.result = result;
+ *      }
+ *    }
+ *    
  *    public void onReceive(Object message) throws Exception {
  *      if (message instanceof String) {
  *        String msg = (String)message;
  *
- *        if (msg.equals("UseReply")) {
- *          // Reply to original sender of message using the 'reply' method
- *          getContext().getSender().tell(msg + ":" + getSelf().getAddress());
- *
- *        } else if (msg.equals("UseSender") && getSender().isDefined()) {
- *          // Reply to original sender of message using the sender reference
- *          // also passing along my own reference (the self)
- *          getSender().get().tell(msg, getSelf());
+ *        if (msg.equals("UseSender")) {
+ *          // Reply to original sender of message
+ *          getSender().tell(msg + ":" + getSelf());
  *
  *        } else if (msg.equals("SendToSelf")) {
  *          // Send message to the actor itself recursively
- *          getSelf().tell(msg)
+ *          getSelf().tell("SomeOtherMessage");
  *
- *        } else if (msg.equals("ForwardMessage")) {
- *          // Retreive an actor from the ActorRegistry by ID and get an ActorRef back
- *          ActorRef actorRef = Actor.registry.local.actorsFor("some-actor-id").head();
+ *        } else if (msg.equals("ErrorKernelWithDirectReply")) {
+ *          // Send work to one-off child which will reply directly to original sender
+ *          getContext().actorOf(new Props(Worker.class)).tell("DoSomeDangerousWork", getSender());
+ *
+ *        } else if (msg.equals("ErrorKernelWithReplyHere")) {
+ *          // Send work to one-off child and collect the answer, reply handled further down
+ *          getContext().actorOf(new Props(Worker.class)).tell("DoWorkAndReplyToMe");
  *
  *        } else throw new IllegalArgumentException("Unknown message: " + message);
+ *
+ *      } else if (message instanceof Reply) {
+ *
+ *        final Reply reply = (Reply) message;
+ *        // might want to do some processing/book-keeping here
+ *        reply.sender.tell(reply.result);
+ *
  *      } else throw new IllegalArgumentException("Unknown message: " + message);
  *    }
- *
- *    public static void main(String[] args) {
- *      ActorSystem system = ActorSystem.create("Sample");
- *      ActorRef actor = system.actorOf(SampleUntypedActor.class);
- *      actor.tell("SendToSelf");
- *      actor.stop();
- *    }
  *  }
- * 
+ * }}} */ abstract class UntypedActor extends Actor { @@ -65,8 +86,9 @@ abstract class UntypedActor extends Actor { def getSelf(): ActorRef = self /** - * The reference sender Actor of the last received message. - * Is defined if the message was sent from another Actor, else None. + * The reference sender Actor of the currently processed message. This is + * always a legal destination to send to, even if there is no logical recipient + * for the reply, in which case it will be sent to the dead letter mailbox. */ def getSender(): ActorRef = sender @@ -77,7 +99,7 @@ abstract class UntypedActor extends Actor { * Actor are automatically started asynchronously when created. * Empty default implementation. */ - override def preStart() {} + override def preStart(): Unit = super.preStart() /** * User overridable callback. @@ -85,24 +107,22 @@ abstract class UntypedActor extends Actor { * Is called asynchronously after 'actor.stop()' is invoked. * Empty default implementation. */ - override def postStop() {} + override def postStop(): Unit = super.postStop() /** - * User overridable callback. + * User overridable callback: '''By default it disposes of all children and then calls `postStop()`.''' *

* Is called on a crashed Actor right BEFORE it is restarted to allow clean * up of resources before Actor is terminated. - * By default it calls postStop() */ - override def preRestart(reason: Throwable, message: Option[Any]) { postStop() } + override def preRestart(reason: Throwable, message: Option[Any]): Unit = super.preRestart(reason, message) /** - * User overridable callback. + * User overridable callback: By default it calls `preStart()`. *

* Is called right AFTER restart on the newly created Actor to allow reinitialization after an Actor crash. - * By default it calls preStart() */ - override def postRestart(reason: Throwable) { preStart() } + override def postRestart(reason: Throwable): Unit = super.postRestart(reason) /** * User overridable callback.