diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index c1ae9c57bf..ea78bb705c 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -99,7 +99,7 @@ private[akka] case class SelectParent(next: Any) extends SelectionPath * IllegalActorStateException is thrown when a core invariant in the Actor implementation has been violated. * For instance, if you try to create an Actor that doesn't extend Actor. */ -class IllegalActorStateException private[akka] (message: String, cause: Throwable = null) +case class IllegalActorStateException private[akka] (message: String, cause: Throwable = null) extends AkkaException(message, cause) { def this(msg: String) = this(msg, null) } @@ -107,7 +107,7 @@ class IllegalActorStateException private[akka] (message: String, cause: Throwabl /** * ActorKilledException is thrown when an Actor receives the akka.actor.Kill message */ -class ActorKilledException private[akka] (message: String, cause: Throwable) +case class ActorKilledException private[akka] (message: String, cause: Throwable) extends AkkaException(message, cause) with NoStackTrace { def this(msg: String) = this(msg, null) @@ -117,28 +117,43 @@ class ActorKilledException private[akka] (message: String, cause: Throwable) * An InvalidActorNameException is thrown when you try to convert something, usually a String, to an Actor name * which doesn't validate. */ -class InvalidActorNameException(message: String) extends AkkaException(message) +case class InvalidActorNameException(message: String) extends AkkaException(message) /** * An ActorInitializationException is thrown when the the initialization logic for an Actor fails. + * + * There is an extractor which works for ActorInitializationException and its subtypes: + * + * {{{ + * ex match { + * case ActorInitializationException(actor, message, cause) => ... + * } + * }}} */ -class ActorInitializationException private[akka] (val actor: ActorRef, message: String, cause: Throwable) +class ActorInitializationException protected (actor: ActorRef, message: String, cause: Throwable) extends AkkaException(message, cause) { - def this(msg: String) = this(null, msg, null) - def this(actor: ActorRef, msg: String) = this(actor, msg, null) + def getActor: ActorRef = actor +} +object ActorInitializationException { + private[akka] def apply(actor: ActorRef, message: String, cause: Throwable = null): ActorInitializationException = + new ActorInitializationException(actor, message, cause) + private[akka] def apply(message: String): ActorInitializationException = new ActorInitializationException(null, message, null) + def unapply(ex: ActorInitializationException): Option[(ActorRef, String, Throwable)] = Some((ex.getActor, ex.getMessage, ex.getCause)) } /** - * A PreRestartException is thrown when the preRestart() method failed. + * A PreRestartException is thrown when the preRestart() method failed; this + * exception is not propagated to the supervisor, as it originates from the + * already failed instance, hence it is only visible as log entry on the event + * stream. * * @param actor is the actor whose preRestart() hook failed * @param cause is the exception thrown by that actor within preRestart() * @param origCause is the exception which caused the restart in the first place * @param msg is the message which was optionally passed into preRestart() */ -class PreRestartException private[akka] (actor: ActorRef, cause: Throwable, val origCause: Throwable, val msg: Option[Any]) - extends ActorInitializationException(actor, "exception in preRestart(" + origCause.getClass + ", " + msg.map(_.getClass) + ")", cause) { -} +case class PreRestartException private[akka] (actor: ActorRef, cause: Throwable, origCause: Throwable, msg: Option[Any]) + extends ActorInitializationException(actor, "exception in preRestart(" + origCause.getClass + ", " + msg.map(_.getClass) + ")", cause) /** * A PostRestartException is thrown when constructor or postRestart() method @@ -148,16 +163,30 @@ class PreRestartException private[akka] (actor: ActorRef, cause: Throwable, val * @param cause is the exception thrown by that actor within preRestart() * @param origCause is the exception which caused the restart in the first place */ -class PostRestartException private[akka] (actor: ActorRef, cause: Throwable, val origCause: Throwable) - extends ActorInitializationException(actor, "exception post restart (" + origCause.getClass + ")", cause) { +case class PostRestartException private[akka] (actor: ActorRef, cause: Throwable, origCause: Throwable) + extends ActorInitializationException(actor, "exception post restart (" + origCause.getClass + ")", cause) + +/** + * This is an extractor for retrieving the original cause (i.e. the first + * failure) from a [[akka.actor.PostRestartException]]. In the face of multiple + * “nested” restarts it will walk the origCause-links until it arrives at a + * non-PostRestartException type. + */ +object OriginalRestartException { + def unapply(ex: PostRestartException): Option[Throwable] = { + def rec(ex: PostRestartException): Option[Throwable] = ex match { + case PostRestartException(_, _, e: PostRestartException) ⇒ rec(e) + case PostRestartException(_, _, e) ⇒ Some(e) + } + rec(ex) + } } /** - * InvalidMessageException is thrown when an invalid message is sent to an Actor. - * Technically it's only "null" which is an InvalidMessageException but who knows, - * there might be more of them in the future, or not. + * InvalidMessageException is thrown when an invalid message is sent to an Actor; + * Currently only `null` is an invalid message. */ -class InvalidMessageException private[akka] (message: String, cause: Throwable = null) +case class InvalidMessageException private[akka] (message: String, cause: Throwable = null) extends AkkaException(message, cause) { def this(msg: String) = this(msg, null) } @@ -302,7 +331,7 @@ trait Actor { protected[akka] implicit val context: ActorContext = { val contextStack = ActorCell.contextStack.get if ((contextStack.isEmpty) || (contextStack.head eq null)) - throw new ActorInitializationException( + throw ActorInitializationException( "\n\tYou cannot create an instance of [" + getClass.getName + "] explicitly using the constructor (new)." + "\n\tYou have to use one of the factory methods to create a new actor. Either use:" + "\n\t\t'val actor = context.actorOf(Props[MyActor])' (to create a supervised child actor from within an actor), or" + diff --git a/akka-actor/src/main/scala/akka/actor/ActorCell.scala b/akka-actor/src/main/scala/akka/actor/ActorCell.scala index 6650fffa94..020ea211fd 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorCell.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorCell.scala @@ -386,7 +386,7 @@ private[akka] class ActorCell( val instance = props.creator.apply() if (instance eq null) - throw new ActorInitializationException(self, "Actor instance passed to actorOf can't be 'null'") + throw ActorInitializationException(self, "Actor instance passed to actorOf can't be 'null'") // If no becomes were issued, the actors behavior is its receive method behaviorStack = if (behaviorStack.isEmpty) instance.receive :: behaviorStack else behaviorStack @@ -407,12 +407,12 @@ private[akka] class ActorCell( if (system.settings.DebugLifecycle) publish(Debug(self.path.toString, clazz(created), "started (" + created + ")")) } catch { case NonFatal(i: InstantiationException) ⇒ - throw new ActorInitializationException(self, + throw ActorInitializationException(self, """exception during creation, this problem is likely to occur because the class of the Actor you tried to create is either, a non-static inner class (in which case make it a static inner class or use Props(new ...) or Props( new UntypedActorFactory ... ) or is missing an appropriate, reachable no-args constructor. """, i.getCause) - case NonFatal(e) ⇒ throw new ActorInitializationException(self, "exception during creation", e) + case NonFatal(e) ⇒ throw ActorInitializationException(self, "exception during creation", e) } } diff --git a/akka-actor/src/main/scala/akka/actor/Stash.scala b/akka-actor/src/main/scala/akka/actor/Stash.scala index 2415b38618..7076a996df 100644 --- a/akka-actor/src/main/scala/akka/actor/Stash.scala +++ b/akka-actor/src/main/scala/akka/actor/Stash.scala @@ -69,7 +69,7 @@ trait Stash { private val mailbox: DequeBasedMessageQueue = { context.asInstanceOf[ActorCell].mailbox.messageQueue match { case queue: DequeBasedMessageQueue ⇒ queue - case other ⇒ throw new ActorInitializationException(self, "DequeBasedMailbox required, got: " + other.getClass() + """ + case other ⇒ throw ActorInitializationException(self, "DequeBasedMailbox required, got: " + other.getClass() + """ An (unbounded) deque-based mailbox can be configured as follows: my-custom-dispatcher { mailbox-type = "akka.dispatch.UnboundedDequeBasedMailbox" diff --git a/akka-actor/src/main/scala/akka/routing/Routing.scala b/akka-actor/src/main/scala/akka/routing/Routing.scala index 09f1cf9bd8..7fd5470e4c 100644 --- a/akka-actor/src/main/scala/akka/routing/Routing.scala +++ b/akka-actor/src/main/scala/akka/routing/Routing.scala @@ -71,7 +71,7 @@ private[akka] class RoutedActorCell(_system: ActorSystemImpl, _ref: InternalActo } if (routerConfig.resizer.isEmpty && _routees.isEmpty) - throw new ActorInitializationException("router " + routerConfig + " did not register routees!") + throw ActorInitializationException("router " + routerConfig + " did not register routees!") start(sendSupervise = false) @@ -284,7 +284,7 @@ trait Router extends Actor { val ref = context match { case x: RoutedActorCell ⇒ x - case _ ⇒ throw new ActorInitializationException("Router actor can only be used in RoutedActorRef, not in " + context.getClass) + case _ ⇒ throw ActorInitializationException("Router actor can only be used in RoutedActorRef, not in " + context.getClass) } final def receive = ({ diff --git a/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala b/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala index 26efaef84d..d25a156917 100644 --- a/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala +++ b/akka-testkit/src/main/scala/akka/testkit/CallingThreadDispatcher.scala @@ -148,7 +148,7 @@ class CallingThreadDispatcher( val queue = mbox.queue queue.enter runQueue(mbox, queue) - case x ⇒ throw new ActorInitializationException("expected CallingThreadMailbox, got " + x.getClass) + case x ⇒ throw ActorInitializationException("expected CallingThreadMailbox, got " + x.getClass) } } diff --git a/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala b/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala index ba830b3855..d961a75fff 100644 --- a/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala @@ -127,7 +127,7 @@ object TestActorRef { def apply[T <: Actor](name: String)(implicit t: ClassTag[T], system: ActorSystem): TestActorRef[T] = apply[T](Props({ system.asInstanceOf[ExtendedActorSystem].dynamicAccess.createInstanceFor[T](t.runtimeClass, Seq()) match { case Right(value) ⇒ value - case Left(exception) ⇒ throw new ActorInitializationException(null, + case Left(exception) ⇒ throw ActorInitializationException(null, "Could not instantiate Actor" + "\nMake sure Actor is NOT defined inside a class/trait," + "\nif so put it outside the class/trait, f.e. in a companion object," +