Add empty actor context (just wrapping self). See #1202
This commit is contained in:
parent
b96f3d9260
commit
27bb7b4d52
5 changed files with 68 additions and 53 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
* <p/>
|
||||
* 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 {
|
|||
* </pre>
|
||||
*/
|
||||
@transient
|
||||
implicit val self = someSelf.get
|
||||
implicit def self = someSelf.get
|
||||
|
||||
/**
|
||||
* User overridable callback/setting.
|
||||
|
|
|
|||
13
akka-actor/src/main/scala/akka/actor/ActorContext.scala
Normal file
13
akka-actor/src/main/scala/akka/actor/ActorContext.scala
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.actor
|
||||
|
||||
/**
|
||||
* Everything that gets injected into the actor.
|
||||
* Just a wrapper on self for now.
|
||||
*/
|
||||
private[akka] class ActorContext(val self: LocalActorRef) {
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue