From 27bb7b4d52648352dd2a67f04015649ee0dd6a86 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 15 Sep 2011 18:00:19 +0200 Subject: [PATCH] Add empty actor context (just wrapping self). See #1202 --- .../test/scala/akka/actor/ActorRefSpec.scala | 26 +++++------ .../src/main/scala/akka/actor/Actor.scala | 45 ++++++++++--------- .../main/scala/akka/actor/ActorContext.scala | 13 ++++++ .../main/scala/akka/actor/ActorInstance.scala | 35 +++++++-------- .../src/main/scala/akka/actor/ActorRef.scala | 2 +- 5 files changed, 68 insertions(+), 53 deletions(-) create mode 100644 akka-actor/src/main/scala/akka/actor/ActorContext.scala diff --git a/akka-actor-tests/src/test/scala/akka/actor/ActorRefSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/ActorRefSpec.scala index 8a0d665d4d..ba177698e2 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/ActorRefSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/ActorRefSpec.scala @@ -129,45 +129,45 @@ class ActorRefSpec extends WordSpec with MustMatchers { }) } - def refStackMustBeEmpty = ActorInstance.refStack.get.headOption must be === None + def contextStackMustBeEmpty = ActorInstance.contextStack.get.headOption must be === None - refStackMustBeEmpty + contextStackMustBeEmpty intercept[akka.actor.ActorInitializationException] { actorOf(new FailingOuterActor(actorOf(new InnerActor))) } - refStackMustBeEmpty + contextStackMustBeEmpty intercept[akka.actor.ActorInitializationException] { actorOf(new OuterActor(actorOf(new FailingInnerActor))) } - refStackMustBeEmpty + contextStackMustBeEmpty intercept[akka.actor.ActorInitializationException] { actorOf(new FailingInheritingOuterActor(actorOf(new InnerActor))) } - refStackMustBeEmpty + contextStackMustBeEmpty intercept[akka.actor.ActorInitializationException] { actorOf(new FailingOuterActor(actorOf(new FailingInheritingInnerActor))) } - refStackMustBeEmpty + contextStackMustBeEmpty intercept[akka.actor.ActorInitializationException] { actorOf(new FailingInheritingOuterActor(actorOf(new FailingInheritingInnerActor))) } - refStackMustBeEmpty + contextStackMustBeEmpty intercept[akka.actor.ActorInitializationException] { actorOf(new FailingInheritingOuterActor(actorOf(new FailingInnerActor))) } - refStackMustBeEmpty + contextStackMustBeEmpty intercept[akka.actor.ActorInitializationException] { actorOf(new OuterActor(actorOf(new InnerActor { @@ -175,31 +175,31 @@ class ActorRefSpec extends WordSpec with MustMatchers { }))) } - refStackMustBeEmpty + contextStackMustBeEmpty intercept[akka.actor.ActorInitializationException] { actorOf(new FailingOuterActor(actorOf(new FailingInheritingInnerActor))) } - refStackMustBeEmpty + contextStackMustBeEmpty intercept[akka.actor.ActorInitializationException] { actorOf(new OuterActor(actorOf(new FailingInheritingInnerActor))) } - refStackMustBeEmpty + contextStackMustBeEmpty intercept[akka.actor.ActorInitializationException] { actorOf(new OuterActor(actorOf({ new InnerActor; new InnerActor }))) } - refStackMustBeEmpty + contextStackMustBeEmpty (intercept[java.lang.IllegalStateException] { actorOf(new OuterActor(actorOf({ throw new IllegalStateException("Ur state be b0rked"); new InnerActor }))) }).getMessage must be === "Ur state be b0rked" - refStackMustBeEmpty + contextStackMustBeEmpty } "be serializable using Java Serialization on local node" in { diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index bc197fecb5..9f39106f3a 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -454,32 +454,35 @@ trait Actor { */ type Receive = Actor.Receive + /** + * Stores the context for this actor, including self, sender, and hotswap. + */ + @transient + private[akka] val actorContext: ActorContext = { + val contextStack = ActorInstance.contextStack.get + + def noContextError = { + throw new 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 = Actor.actorOf[MyActor]', or" + + "\n\t\t'val actor = Actor.actorOf(new MyActor(..))'") + } + + if (contextStack.isEmpty) noContextError + val context = contextStack.head + if (context eq null) noContextError + ActorInstance.contextStack.set(contextStack.push(null)) + context + } + /** * Some[ActorRef] representation of the 'self' ActorRef reference. *

* Mainly for internal use, functions as the implicit sender references when invoking * the 'forward' function. */ - @transient - val someSelf: Some[ScalaActorRef with SelfActorRef] = { - val refStack = ActorInstance.refStack.get - if (refStack.isEmpty) throw new ActorInitializationException( - "\n\tYou can not create an instance of an " + getClass.getName + " explicitly using 'new MyActor'." + - "\n\tYou have to use one of the factory methods in the 'Actor' object to create a new actor." + - "\n\tEither use:" + - "\n\t\t'val actor = Actor.actorOf[MyActor]', or" + - "\n\t\t'val actor = Actor.actorOf(new MyActor(..))'") - - val ref = refStack.head - - if (ref eq null) - throw new ActorInitializationException("Trying to create an instance of " + getClass.getName + " outside of a wrapping 'actorOf'") - else { - // Push a null marker so any subsequent calls to new Actor doesn't reuse this actor ref - ActorInstance.refStack.set(refStack.push(null)) - Some(ref) - } - } + def someSelf: Some[ScalaActorRef with SelfActorRef] = Some(actorContext.self) /* * Option[ActorRef] representation of the 'self' ActorRef reference. @@ -515,7 +518,7 @@ trait Actor { * */ @transient - implicit val self = someSelf.get + implicit def self = someSelf.get /** * User overridable callback/setting. diff --git a/akka-actor/src/main/scala/akka/actor/ActorContext.scala b/akka-actor/src/main/scala/akka/actor/ActorContext.scala new file mode 100644 index 0000000000..f24f644484 --- /dev/null +++ b/akka-actor/src/main/scala/akka/actor/ActorContext.scala @@ -0,0 +1,13 @@ +/** + * Copyright (C) 2009-2011 Typesafe Inc. + */ + +package akka.actor + +/** + * Everything that gets injected into the actor. + * Just a wrapper on self for now. + */ +private[akka] class ActorContext(val self: LocalActorRef) { + +} \ No newline at end of file diff --git a/akka-actor/src/main/scala/akka/actor/ActorInstance.scala b/akka-actor/src/main/scala/akka/actor/ActorInstance.scala index 6864486989..e2cb3fd67f 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorInstance.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorInstance.scala @@ -23,8 +23,8 @@ private[akka] object ActorInstance { object Shutdown extends Status } - val refStack = new ThreadLocal[Stack[ScalaActorRef with SelfActorRef]] { - override def initialValue = Stack[ScalaActorRef with SelfActorRef]() + val contextStack = new ThreadLocal[Stack[ActorContext]] { + override def initialValue = Stack[ActorContext]() } } @@ -93,8 +93,8 @@ private[akka] class ActorInstance(props: Props, self: LocalActorRef) { } def newActor: Actor = { - val stackBefore = refStack.get - refStack.set(stackBefore.push(self)) + val stackBefore = contextStack.get + contextStack.set(stackBefore.push(new ActorContext(self))) try { if (status == Status.BeingRestarted) { val a = actor.get() @@ -111,9 +111,9 @@ private[akka] class ActorInstance(props: Props, self: LocalActorRef) { props.creator() } } finally { - val stackAfter = refStack.get + val stackAfter = contextStack.get if (stackAfter.nonEmpty) - refStack.set(if (stackAfter.head eq null) stackAfter.pop.pop else stackAfter.pop) //pop null marker plus self + contextStack.set(if (stackAfter.head eq null) stackAfter.pop.pop else stackAfter.pop) // pop null marker plus our context } } match { case null ⇒ throw new ActorInitializationException("Actor instance passed to actorOf can't be 'null'") @@ -138,7 +138,7 @@ private[akka] class ActorInstance(props: Props, self: LocalActorRef) { stopSupervisedActors() } finally { self.currentMessage = null - setActorSelf(null) + clearActorContext() } } } @@ -283,7 +283,7 @@ private[akka] class ActorInstance(props: Props, self: LocalActorRef) { val message = if (self.currentMessage ne null) Some(self.currentMessage.message) else None failedActor.preRestart(reason, message) val freshActor = newActor - setActorSelf(null) // only null out the references if we could instantiate the new actor + clearActorContext() actor.set(freshActor) // assign it here so if preStart fails, we can null out the sef-refs next call freshActor.postRestart(reason) if (Actor.debugLifecycle) EventHandler.debug(freshActor, "restarted") @@ -404,16 +404,15 @@ private[akka] class ActorInstance(props: Props, self: LocalActorRef) { } } - def setActorSelf(value: ActorRef): Unit = { + def clearActorContext(): Unit = setActorContext(null) + + def setActorContext(newContext: ActorContext): Unit = { @tailrec - def lookupAndSetSelfFields(clazz: Class[_], actor: Actor, value: ActorRef): Boolean = { + def lookupAndSetSelfFields(clazz: Class[_], actor: Actor, newContext: ActorContext): Boolean = { val success = try { - val selfField = clazz.getDeclaredField("self") - val someSelfField = clazz.getDeclaredField("someSelf") - selfField.setAccessible(true) - someSelfField.setAccessible(true) - selfField.set(actor, value) - someSelfField.set(actor, if (value ne null) Some(value) else null) + val contextField = clazz.getDeclaredField("actorContext") + contextField.setAccessible(true) + contextField.set(actor, newContext) true } catch { case e: NoSuchFieldException ⇒ false @@ -424,10 +423,10 @@ private[akka] class ActorInstance(props: Props, self: LocalActorRef) { val parent = clazz.getSuperclass if (parent eq null) throw new IllegalActorStateException(toString + " is not an Actor since it have not mixed in the 'Actor' trait") - lookupAndSetSelfFields(parent, actor, value) + lookupAndSetSelfFields(parent, actor, newContext) } } - lookupAndSetSelfFields(actor.get.getClass, actor.get, value) + lookupAndSetSelfFields(actor.get.getClass, actor.get, newContext) } } diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index 063394c4ee..ba1cc02103 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -341,7 +341,7 @@ class LocalActorRef private[akka] ( hotswap = __hotswap receiveTimeout = __receiveTimeout - actorInstance.setActorSelf(this) // TODO: why is this needed? + actorInstance.setActorContext(new ActorContext(this)) // this is needed for deserialization - why? } private[this] val actorInstance = new ActorInstance(props, this)