Merge branch 'wip-ActorPath-rk'

This commit is contained in:
Roland 2011-12-06 15:12:09 +01:00
commit 66c1d62a51
75 changed files with 1904 additions and 962 deletions

View file

@ -37,6 +37,15 @@ public class JavaExtension {
system = i;
}
}
static class OtherExtension implements Extension {
static final ExtensionKey<OtherExtension> key = new ExtensionKey<OtherExtension>(OtherExtension.class) {};
public final ActorSystemImpl system;
public OtherExtension(ActorSystemImpl i) {
system = i;
}
}
private static ActorSystem system;
@ -58,5 +67,10 @@ public class JavaExtension {
assertSame(system.extension(defaultInstance).system, system);
assertSame(defaultInstance.apply(system).system, system);
}
@Test
public void mustBeAdHoc() {
assertSame(OtherExtension.key.apply(system).system, system);
}
}

View file

@ -0,0 +1,259 @@
/**
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
import akka.testkit._
import akka.util.duration._
object ActorLookupSpec {
case class Create(child: String)
trait Query
case class LookupElems(path: Iterable[String]) extends Query
case class LookupString(path: String) extends Query
case class LookupPath(path: ActorPath) extends Query
case class GetSender(to: ActorRef) extends Query
val p = Props[Node]
class Node extends Actor {
def receive = {
case Create(name) sender ! context.actorOf(p, name)
case LookupElems(path) sender ! context.actorFor(path)
case LookupString(path) sender ! context.actorFor(path)
case LookupPath(path) sender ! context.actorFor(path)
case GetSender(ref) ref ! sender
}
}
}
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class ActorLookupSpec extends AkkaSpec {
import ActorLookupSpec._
val c1 = system.actorOf(p, "c1")
val c2 = system.actorOf(p, "c2")
val c21 = (c2 ? Create("c21")).as[ActorRef].get
val user = system.asInstanceOf[ActorSystemImpl].guardian
val syst = system.asInstanceOf[ActorSystemImpl].systemGuardian
val root = system.asInstanceOf[ActorSystemImpl].lookupRoot
"An ActorSystem" must {
"find actors by looking up their path" in {
system.actorFor(c1.path) must be === c1
system.actorFor(c2.path) must be === c2
system.actorFor(c21.path) must be === c21
system.actorFor(system / "c1") must be === c1
system.actorFor(system / "c2") must be === c2
system.actorFor(system / "c2" / "c21") must be === c21
system.actorFor(system / Seq("c2", "c21")) must be === c21
}
"find actors by looking up their string representation" in {
system.actorFor(c1.path.toString) must be === c1
system.actorFor(c2.path.toString) must be === c2
system.actorFor(c21.path.toString) must be === c21
}
"find actors by looking up their root-anchored relative path" in {
system.actorFor(c1.path.elements.mkString("/", "/", "")) must be === c1
system.actorFor(c2.path.elements.mkString("/", "/", "")) must be === c2
system.actorFor(c21.path.elements.mkString("/", "/", "")) must be === c21
}
"find actors by looking up their relative path" in {
system.actorFor(c1.path.elements.mkString("/")) must be === c1
system.actorFor(c2.path.elements.mkString("/")) must be === c2
system.actorFor(c21.path.elements.mkString("/")) must be === c21
}
"find actors by looking up their path elements" in {
system.actorFor(c1.path.elements) must be === c1
system.actorFor(c2.path.elements) must be === c2
system.actorFor(c21.path.elements) must be === c21
}
"find system-generated actors" in {
system.actorFor("/user") must be === user
system.actorFor("/null") must be === system.deadLetters
system.actorFor("/system") must be === syst
system.actorFor(syst.path) must be === syst
system.actorFor(syst.path.toString) must be === syst
system.actorFor("/") must be === root
system.actorFor("..") must be === root
system.actorFor(root.path) must be === root
system.actorFor(root.path.toString) must be === root
system.actorFor("user") must be === user
system.actorFor("null") must be === system.deadLetters
system.actorFor("system") must be === syst
system.actorFor("user/") must be === user
system.actorFor("null/") must be === system.deadLetters
system.actorFor("system/") must be === syst
}
"return deadLetters for non-existing paths" in {
system.actorFor("a/b/c") must be === system.deadLetters
system.actorFor("") must be === system.deadLetters
system.actorFor("akka://all-systems/Nobody") must be === system.deadLetters
system.actorFor("akka://all-systems/user") must be === system.deadLetters
system.actorFor(system / "hallo") must be === system.deadLetters
system.actorFor(Seq()) must be === system.deadLetters
system.actorFor(Seq("a")) must be === system.deadLetters
}
"find temporary actors" in {
val f = c1 ? GetSender(testActor)
val a = expectMsgType[ActorRef]
a.path.elements.head must be === "temp"
system.actorFor(a.path) must be === a
system.actorFor(a.path.toString) must be === a
system.actorFor(a.path.elements) must be === a
system.actorFor(a.path.toString + "/") must be === a
system.actorFor(a.path.toString + "/hallo") must be === system.deadLetters
f.isCompleted must be === false
a ! 42
f.isCompleted must be === true
f.get must be === 42
system.actorFor(a.path) must be === system.deadLetters
}
}
"An ActorContext" must {
val all = Seq(c1, c2, c21)
"find actors by looking up their path" in {
def check(looker: ActorRef, pathOf: ActorRef, result: ActorRef) {
(looker ? LookupPath(pathOf.path)).get must be === result
}
for {
looker all
target all
} check(looker, target, target)
}
"find actors by looking up their string representation" in {
def check(looker: ActorRef, pathOf: ActorRef, result: ActorRef) {
(looker ? LookupString(pathOf.path.toString)).get must be === result
(looker ? LookupString(pathOf.path.toString + "/")).get must be === result
}
for {
looker all
target all
} check(looker, target, target)
}
"find actors by looking up their root-anchored relative path" in {
def check(looker: ActorRef, pathOf: ActorRef, result: ActorRef) {
(looker ? LookupString(pathOf.path.elements.mkString("/", "/", ""))).get must be === result
(looker ? LookupString(pathOf.path.elements.mkString("/", "/", "/"))).get must be === result
}
for {
looker all
target all
} check(looker, target, target)
}
"find actors by looking up their relative path" in {
def check(looker: ActorRef, result: ActorRef, elems: String*) {
(looker ? LookupElems(elems)).get must be === result
(looker ? LookupString(elems mkString "/")).get must be === result
(looker ? LookupString(elems mkString ("", "/", "/"))).get must be === result
}
check(c1, user, "..")
for {
looker Seq(c1, c2)
target all
} check(looker, target, Seq("..") ++ target.path.elements.drop(1): _*)
check(c21, user, "..", "..")
check(c21, root, "..", "..", "..")
check(c21, root, "..", "..", "..", "..")
}
"find system-generated actors" in {
def check(target: ActorRef) {
for (looker all) {
(looker ? LookupPath(target.path)).get must be === target
(looker ? LookupString(target.path.toString)).get must be === target
(looker ? LookupString(target.path.toString + "/")).get must be === target
(looker ? LookupString(target.path.elements.mkString("/", "/", ""))).get must be === target
if (target != root) (looker ? LookupString(target.path.elements.mkString("/", "/", "/"))).get must be === target
}
}
for (target Seq(root, syst, user, system.deadLetters)) check(target)
}
"return deadLetters for non-existing paths" in {
def checkOne(looker: ActorRef, query: Query) {
(looker ? query).get must be === system.deadLetters
}
def check(looker: ActorRef) {
Seq(LookupString("a/b/c"),
LookupString(""),
LookupString("akka://all-systems/Nobody"),
LookupPath(system / "hallo"),
LookupElems(Seq()),
LookupElems(Seq("a"))) foreach (checkOne(looker, _))
}
for (looker all) check(looker)
}
"find temporary actors" in {
val f = c1 ? GetSender(testActor)
val a = expectMsgType[ActorRef]
a.path.elements.head must be === "temp"
(c2 ? LookupPath(a.path)).get must be === a
(c2 ? LookupString(a.path.toString)).get must be === a
(c2 ? LookupString(a.path.elements.mkString("/", "/", ""))).get must be === a
(c2 ? LookupString("../../" + a.path.elements.mkString("/"))).get must be === a
(c2 ? LookupString(a.path.toString + "/")).get must be === a
(c2 ? LookupString(a.path.elements.mkString("/", "/", "") + "/")).get must be === a
(c2 ? LookupString("../../" + a.path.elements.mkString("/") + "/")).get must be === a
(c2 ? LookupElems(Seq("..", "..") ++ a.path.elements)).get must be === a
(c2 ? LookupElems(Seq("..", "..") ++ a.path.elements :+ "")).get must be === a
f.isCompleted must be === false
a ! 42
f.isCompleted must be === true
f.get must be === 42
(c2 ? LookupPath(a.path)).get must be === system.deadLetters
}
}
"An ActorSelection" must {
"send messages directly" in {
ActorSelection(c1, "") ! GetSender(testActor)
expectMsg(system.deadLetters)
lastSender must be === c1
}
"send messages with correct sender" in {
implicit val sender = c1
ActorSelection(c21, "../../*") ! GetSender(testActor)
val actors = receiveWhile(messages = 2) {
case `c1` lastSender
}
actors must be === Seq(c1, c2)
expectNoMsg(1 second)
}
"drop messages which cannot be delivered" in {
implicit val sender = c2
ActorSelection(c21, "../../*/c21") ! GetSender(testActor)
val actors = receiveWhile(messages = 2) {
case `c2` lastSender
}
actors must be === Seq(c21)
expectNoMsg(1 second)
}
}
}

View file

@ -278,14 +278,14 @@ class ActorRefSpec extends AkkaSpec {
" Use akka.serialization.Serialization.currentSystem.withValue(system) { ... }"
}
"must throw exception on deserialize if not present in actor hierarchy (and remoting is not enabled)" in {
"must return deadLetters on deserialize if not present in actor hierarchy (and remoting is not enabled)" in {
import java.io._
val baos = new ByteArrayOutputStream(8192 * 32)
val out = new ObjectOutputStream(baos)
val addr = system.asInstanceOf[ActorSystemImpl].provider.rootPath.remoteAddress
val serialized = SerializedActorRef(addr.hostname, addr.port, "/this/path/does/not/exist")
val addr = system.asInstanceOf[ActorSystemImpl].provider.rootPath.address
val serialized = SerializedActorRef(addr + "/non-existing")
out.writeObject(serialized)
@ -294,9 +294,7 @@ class ActorRefSpec extends AkkaSpec {
Serialization.currentSystem.withValue(system.asInstanceOf[ActorSystemImpl]) {
val in = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray))
(intercept[java.lang.IllegalStateException] {
in.readObject
}).getMessage must be === "Could not deserialize ActorRef"
in.readObject must be === system.deadLetters
}
}

View file

@ -14,59 +14,59 @@ import com.typesafe.config.ConfigParseOptions
object DeployerSpec {
val deployerConf = ConfigFactory.parseString("""
akka.actor.deployment {
/app/service1 {
/user/service1 {
}
/app/service2 {
/user/service2 {
router = round-robin
nr-of-instances = 3
remote {
nodes = ["wallace:2552", "gromit:2552"]
}
}
/app/service3 {
/user/service3 {
create-as {
class = "akka.actor.DeployerSpec$RecipeActor"
}
}
/app/service-auto {
/user/service-auto {
router = round-robin
nr-of-instances = auto
}
/app/service-direct {
/user/service-direct {
router = direct
}
/app/service-direct2 {
/user/service-direct2 {
router = direct
# nr-of-instances ignored when router = direct
nr-of-instances = 2
}
/app/service-round-robin {
/user/service-round-robin {
router = round-robin
}
/app/service-random {
/user/service-random {
router = random
}
/app/service-scatter-gather {
/user/service-scatter-gather {
router = scatter-gather
}
/app/service-least-cpu {
/user/service-least-cpu {
router = least-cpu
}
/app/service-least-ram {
/user/service-least-ram {
router = least-ram
}
/app/service-least-messages {
/user/service-least-messages {
router = least-messages
}
/app/service-custom {
/user/service-custom {
router = org.my.Custom
}
/app/service-cluster1 {
/user/service-cluster1 {
cluster {
preferred-nodes = ["node:wallace", "node:gromit"]
}
}
/app/service-cluster2 {
/user/service-cluster2 {
cluster {
preferred-nodes = ["node:wallace", "node:gromit"]
replication {
@ -89,7 +89,7 @@ class DeployerSpec extends AkkaSpec(DeployerSpec.deployerConf) {
"A Deployer" must {
"be able to parse 'akka.actor.deployment._' with all default values" in {
val service = "/app/service1"
val service = "/user/service1"
val deployment = system.asInstanceOf[ActorSystemImpl].provider.deployer.lookupDeployment(service)
deployment must be('defined)
@ -103,13 +103,13 @@ class DeployerSpec extends AkkaSpec(DeployerSpec.deployerConf) {
}
"use None deployment for undefined service" in {
val service = "/app/undefined"
val service = "/user/undefined"
val deployment = system.asInstanceOf[ActorSystemImpl].provider.deployer.lookupDeployment(service)
deployment must be(None)
}
"be able to parse 'akka.actor.deployment._' with specified remote nodes" in {
val service = "/app/service2"
val service = "/user/service2"
val deployment = system.asInstanceOf[ActorSystemImpl].provider.deployer.lookupDeployment(service)
deployment must be('defined)
@ -120,11 +120,11 @@ class DeployerSpec extends AkkaSpec(DeployerSpec.deployerConf) {
RoundRobin,
NrOfInstances(3),
RemoteScope(Seq(
RemoteAddress("wallace", 2552), RemoteAddress("gromit", 2552))))))
RemoteAddress(system.name, "wallace", 2552), RemoteAddress(system.name, "gromit", 2552))))))
}
"be able to parse 'akka.actor.deployment._' with recipe" in {
val service = "/app/service3"
val service = "/user/service3"
val deployment = system.asInstanceOf[ActorSystemImpl].provider.deployer.lookupDeployment(service)
deployment must be('defined)
@ -138,7 +138,7 @@ class DeployerSpec extends AkkaSpec(DeployerSpec.deployerConf) {
}
"be able to parse 'akka.actor.deployment._' with number-of-instances=auto" in {
val service = "/app/service-auto"
val service = "/user/service-auto"
val deployment = system.asInstanceOf[ActorSystemImpl].provider.deployer.lookupDeployment(service)
deployment must be('defined)
@ -155,7 +155,7 @@ class DeployerSpec extends AkkaSpec(DeployerSpec.deployerConf) {
intercept[akka.config.ConfigurationException] {
val invalidDeployerConf = ConfigFactory.parseString("""
akka.actor.deployment {
/app/service-invalid-number-of-instances {
/user/service-invalid-number-of-instances {
router = round-robin
nr-of-instances = boom
}
@ -167,38 +167,38 @@ class DeployerSpec extends AkkaSpec(DeployerSpec.deployerConf) {
}
"be able to parse 'akka.actor.deployment._' with direct router" in {
assertRouting(Direct, "/app/service-direct")
assertRouting(Direct, "/user/service-direct")
}
"ignore nr-of-instances with direct router" in {
assertRouting(Direct, "/app/service-direct2")
assertRouting(Direct, "/user/service-direct2")
}
"be able to parse 'akka.actor.deployment._' with round-robin router" in {
assertRouting(RoundRobin, "/app/service-round-robin")
assertRouting(RoundRobin, "/user/service-round-robin")
}
"be able to parse 'akka.actor.deployment._' with random router" in {
assertRouting(Random, "/app/service-random")
assertRouting(Random, "/user/service-random")
}
"be able to parse 'akka.actor.deployment._' with scatter-gather router" in {
assertRouting(ScatterGather, "/app/service-scatter-gather")
assertRouting(ScatterGather, "/user/service-scatter-gather")
}
"be able to parse 'akka.actor.deployment._' with least-cpu router" in {
assertRouting(LeastCPU, "/app/service-least-cpu")
assertRouting(LeastCPU, "/user/service-least-cpu")
}
"be able to parse 'akka.actor.deployment._' with least-ram router" in {
assertRouting(LeastRAM, "/app/service-least-ram")
assertRouting(LeastRAM, "/user/service-least-ram")
}
"be able to parse 'akka.actor.deployment._' with least-messages router" in {
assertRouting(LeastMessages, "/app/service-least-messages")
assertRouting(LeastMessages, "/user/service-least-messages")
}
"be able to parse 'akka.actor.deployment._' with custom router" in {
assertRouting(CustomRouter("org.my.Custom"), "/app/service-custom")
assertRouting(CustomRouter("org.my.Custom"), "/user/service-custom")
}
def assertRouting(expected: Routing, service: String) {
@ -216,7 +216,7 @@ class DeployerSpec extends AkkaSpec(DeployerSpec.deployerConf) {
}
"be able to parse 'akka.actor.deployment._' with specified cluster nodes" in {
val service = "/app/service-cluster1"
val service = "/user/service-cluster1"
val deploymentConfig = system.asInstanceOf[ActorSystemImpl].provider.deployer.deploymentConfig
val deployment = system.asInstanceOf[ActorSystemImpl].provider.deployer.lookupDeployment(service)
deployment must be('defined)
@ -230,7 +230,7 @@ class DeployerSpec extends AkkaSpec(DeployerSpec.deployerConf) {
}
"be able to parse 'akka.actor.deployment._' with specified cluster replication" in {
val service = "/app/service-cluster2"
val service = "/user/service-cluster2"
val deploymentConfig = system.asInstanceOf[ActorSystemImpl].provider.deployer.deploymentConfig
val deployment = system.asInstanceOf[ActorSystemImpl].provider.deployer.lookupDeployment(service)
deployment must be('defined)

View file

@ -164,8 +164,8 @@ class FSMActorSpec extends AkkaSpec(Map("akka.actor.debug.fsm" -> true)) with Im
case Ev("go") goto(2)
}
})
val name = fsm.toString
filterException[Logging.EventHandlerException] {
val name = fsm.path.toString
EventFilter.error("Next state 2 does not exist", occurrences = 1) intercept {
system.eventStream.subscribe(testActor, classOf[Logging.Error])
fsm ! "go"
expectMsgPF(1 second, hint = "Next state 2 does not exist") {
@ -194,11 +194,11 @@ class FSMActorSpec extends AkkaSpec(Map("akka.actor.debug.fsm" -> true)) with Im
"log events and transitions if asked to do so" in {
import scala.collection.JavaConverters._
val config = ConfigFactory.parseMap(Map("akka.loglevel" -> "DEBUG",
"akka.actor.debug.fsm" -> true).asJava).withFallback(AkkaSpec.testConf)
val fsmEventSystem = ActorSystem("fsm event", config)
"akka.actor.debug.fsm" -> true).asJava).withFallback(system.settings.config)
val fsmEventSystem = ActorSystem("fsmEvent", config)
try {
new TestKit(fsmEventSystem) {
EventFilter.debug() intercept {
EventFilter.debug(occurrences = 5) intercept {
val fsm = TestActorRef(new Actor with LoggingFSM[Int, Null] {
startWith(1, null)
when(1) {
@ -215,7 +215,7 @@ class FSMActorSpec extends AkkaSpec(Map("akka.actor.debug.fsm" -> true)) with Im
case StopEvent(r, _, _) testActor ! r
}
})
val name = fsm.toString
val name = fsm.path.toString
system.eventStream.subscribe(testActor, classOf[Logging.Debug])
fsm ! "go"
expectMsgPF(1 second, hint = "processing Event(go,null)") {

View file

@ -12,17 +12,46 @@ import akka.dispatch.Future
class LocalActorRefProviderSpec extends AkkaSpec {
"An LocalActorRefProvider" must {
"find actor refs using actorFor" in {
val a = actorOf(Props(ctx { case _ }))
val b = system.actorFor(a.path)
a must be === b
}
}
"An ActorRefFactory" must {
"only create one instance of an actor with a specific address in a concurrent environment" in {
val impl = system.asInstanceOf[ActorSystemImpl]
val provider = impl.provider
provider.isInstanceOf[LocalActorRefProvider] must be(true)
(0 until 100) foreach { i // 100 concurrent runs
for (i 0 until 100) {
val address = "new-actor" + i
implicit val timeout = Timeout(5 seconds)
((1 to 4) map { _ Future { provider.actorOf(impl, Props(c { case _ }), impl.guardian, address) } }).map(_.get).distinct.size must be(1)
val actors = for (j 1 to 4) yield Future(system.actorOf(Props(c { case _ }), address))
val set = Set() ++ actors.map(_.await.value match {
case Some(Right(a: ActorRef)) 1
case Some(Left(ex: InvalidActorNameException)) 2
case x x
})
set must be === Set(1, 2)
}
}
"only create one instance of an actor from within the same message invocation" in {
val supervisor = system.actorOf(new Actor {
def receive = {
case ""
val a, b = context.actorOf(Props.empty, "duplicate")
}
})
EventFilter[InvalidActorNameException](occurrences = 1) intercept {
supervisor ! ""
}
}
}
}

View file

@ -24,15 +24,15 @@ class SupervisorTreeSpec extends AkkaSpec with ImplicitSender {
def receive = {
case p: Props sender ! context.actorOf(p)
}
override def preRestart(cause: Throwable, msg: Option[Any]) { testActor ! self.address }
override def preRestart(cause: Throwable, msg: Option[Any]) { testActor ! self.path }
}).withFaultHandler(OneForOneStrategy(List(classOf[Exception]), 3, 1000))
val headActor = actorOf(p)
val middleActor = (headActor ? p).as[ActorRef].get
val lastActor = (middleActor ? p).as[ActorRef].get
middleActor ! Kill
expectMsg(middleActor.address)
expectMsg(lastActor.address)
expectMsg(middleActor.path)
expectMsg(lastActor.path)
expectNoMsg(2 seconds)
headActor.stop()
}

View file

@ -397,7 +397,9 @@ class TypedActorSpec extends AkkaSpec with BeforeAndAfterEach with BeforeAndAfte
val latch = new CountDownLatch(16)
val ta = TypedActor(system)
val t: LifeCycles = ta.typedActorOf(classOf[LifeCycles], new Creator[LifeCyclesImpl] { def create = new LifeCyclesImpl(latch) }, Props())
t.crash()
EventFilter[IllegalStateException]("Crash!", occurrences = 1) intercept {
t.crash()
}
ta.poisonPill(t)
latch.await(10, TimeUnit.SECONDS) must be === true
}

View file

@ -51,7 +51,6 @@ class EventStreamSpec extends AkkaSpec(EventStreamSpec.config) {
"manage subscriptions" in {
val bus = new EventStream(true)
bus.start(impl)
bus.subscribe(testActor, classOf[M])
bus.publish(M(42))
within(1 second) {
@ -64,7 +63,6 @@ class EventStreamSpec extends AkkaSpec(EventStreamSpec.config) {
"manage log levels" in {
val bus = new EventStream(false)
bus.start(impl)
bus.startDefaultLoggers(impl)
bus.publish(SetTarget(testActor))
expectMsg("OK")
@ -86,7 +84,6 @@ class EventStreamSpec extends AkkaSpec(EventStreamSpec.config) {
val b2 = new B2
val c = new C
val bus = new EventStream(false)
bus.start(impl)
within(2 seconds) {
bus.subscribe(testActor, classOf[B2]) === true
bus.publish(c)

View file

@ -83,7 +83,7 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
sender ! "x"
}
})
val name = actor.toString
val name = actor.path.toString
actor ! "buh"
within(1 second) {
expectMsg(Logging.Debug(name, "received handled message buh"))
@ -93,7 +93,7 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
case null
}
actor ! HotSwap(_ r, false)
filterException[UnhandledMessageException] {
EventFilter[UnhandledMessageException](pattern = "does not handle", occurrences = 1) intercept {
within(500 millis) {
actor ! "bah"
expectMsgPF() {
@ -114,7 +114,7 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
})
actor ! "buh"
within(1 second) {
expectMsg(Logging.Debug(actor.toString, "received handled message buh"))
expectMsg(Logging.Debug(actor.path.toString, "received handled message buh"))
expectMsg("x")
}
}
@ -132,7 +132,7 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
case _
}
})
val name = actor.toString
val name = actor.path.toString
actor ! PoisonPill
expectMsgPF() {
case Logging.Debug(`name`, msg: String) if msg startsWith "received AutoReceiveMessage Envelope(PoisonPill" true
@ -143,19 +143,19 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
"log LifeCycle changes if requested" in {
new TestKit(appLifecycle) {
val impl = system.asInstanceOf[ActorSystemImpl]
val sys = impl.systemGuardian.path.toString
ignoreMute(this)
ignoreMsg {
case Logging.Debug(ref, _)
val s = ref.toString
s.contains("MainBusReaper") || s.contains("Supervisor")
case Logging.Debug(s, _) s.contains("MainBusReaper") || s == sys
}
system.eventStream.subscribe(testActor, classOf[Logging.Debug])
system.eventStream.subscribe(testActor, classOf[Logging.Error])
within(3 seconds) {
val lifecycleGuardian = appLifecycle.asInstanceOf[ActorSystemImpl].guardian
val lname = lifecycleGuardian.toString
val lname = lifecycleGuardian.path.toString
val supervisor = TestActorRef[TestLogActor](Props[TestLogActor].withFaultHandler(OneForOneStrategy(List(classOf[Throwable]), 5, 5000)))
val sname = supervisor.toString
val sname = supervisor.path.toString
val supervisorSet = receiveWhile(messages = 2) {
case Logging.Debug(`lname`, msg: String) if msg startsWith "now supervising" 1
@ -165,7 +165,7 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
assert(supervisorSet == Set(1, 2), supervisorSet + " was not Set(1, 2)")
val actor = TestActorRef[TestLogActor](Props[TestLogActor], supervisor, "none")
val aname = actor.toString
val aname = actor.path.toString
val set = receiveWhile(messages = 2) {
case Logging.Debug(`sname`, msg: String) if msg startsWith "now supervising" 1
@ -186,7 +186,7 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
ref == supervisor.underlyingActor && msg.startsWith("stopped monitoring")
}
filterException[ActorKilledException] {
EventFilter[ActorKilledException](occurrences = 1) intercept {
actor ! Kill
val set = receiveWhile(messages = 3) {
case Logging.Error(_: ActorKilledException, `aname`, "Kill") 1

View file

@ -263,7 +263,7 @@ class ActorPoolSpec extends AkkaSpec {
def instance(p: Props): ActorRef = actorOf(p.withCreator(new Actor {
def receive = {
case _
delegates put (self.address, "")
delegates put (self.path.toString, "")
latch1.countDown()
}
}))
@ -291,7 +291,7 @@ class ActorPoolSpec extends AkkaSpec {
def instance(p: Props) = actorOf(p.withCreator(new Actor {
def receive = {
case _
delegates put (self.address, "")
delegates put (self.path.toString, "")
latch2.countDown()
}
}))

View file

@ -76,11 +76,15 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
* @version $Rev: 2297 $, $Date: 2010-06-07 10:50:02 +0900 (Mon, 07 Jun 2010) $
*
* The original implementation has been slightly altered to fit the specific requirements of Akka.
*
* Specifically: it is required to throw an IllegalStateException if a job
* cannot be queued. If no such exception is thrown, the job must be executed
* (or returned upon stop()).
*/
public class HashedWheelTimer implements Timer {
private final Worker worker = new Worker();
final Thread workerThread;
final AtomicBoolean shutdown = new AtomicBoolean();
boolean shutdown = false;
private final long roundDuration;
final long tickDuration;
final Set<HashedWheelTimeout>[] wheel;
@ -181,12 +185,17 @@ public class HashedWheelTimer implements Timer {
* {@linkplain #stop() stopped} already
*/
public synchronized void start() {
if (shutdown.get()) {
throw new IllegalStateException("cannot be started once stopped");
}
lock.readLock().lock();
try {
if (shutdown) {
throw new IllegalStateException("cannot be started once stopped");
}
if (!workerThread.isAlive()) {
workerThread.start();
if (!workerThread.isAlive()) {
workerThread.start();
}
} finally {
lock.readLock().unlock();
}
}
@ -198,8 +207,15 @@ public class HashedWheelTimer implements Timer {
TimerTask.class.getSimpleName());
}
if (!shutdown.compareAndSet(false, true)) {
return Collections.emptySet();
lock.writeLock().lock();
try {
if (shutdown) {
return Collections.emptySet();
} else {
shutdown = true;
}
} finally {
lock.writeLock().unlock();
}
boolean interrupted = false;
@ -224,6 +240,10 @@ public class HashedWheelTimer implements Timer {
return Collections.unmodifiableSet(unprocessedTimeouts);
}
public HashedWheelTimeout createTimeout(TimerTask task, long time) {
return new HashedWheelTimeout(task, time);
}
public Timeout newTimeout(TimerTask task, Duration delay) {
final long currentTime = System.nanoTime();
@ -239,7 +259,7 @@ public class HashedWheelTimer implements Timer {
start();
}
HashedWheelTimeout timeout = new HashedWheelTimeout(task, currentTime + delay.toNanos());
HashedWheelTimeout timeout = createTimeout(task, currentTime + delay.toNanos());
scheduleTimeout(timeout, delay.toNanos());
return timeout;
}
@ -260,6 +280,7 @@ public class HashedWheelTimer implements Timer {
// Add the timeout to the wheel.
lock.readLock().lock();
try {
if (shutdown) throw new IllegalStateException("cannot enqueue after shutdown");
int stopIndex = (int) (wheelCursor + relativeIndex & mask);
timeout.stopIndex = stopIndex;
timeout.remainingRounds = remainingRounds;
@ -277,6 +298,15 @@ public class HashedWheelTimer implements Timer {
Worker() {
super();
}
private boolean shutdown() {
lock.readLock().lock();
try {
return shutdown;
} finally {
lock.readLock().unlock();
}
}
public void run() {
List<HashedWheelTimeout> expiredTimeouts =
@ -285,7 +315,7 @@ public class HashedWheelTimer implements Timer {
startTime = System.nanoTime();
tick = 1;
while (!shutdown.get()) {
while (!shutdown()) {
final long deadline = waitForNextTick();
if (deadline > 0) {
fetchExpiredTimeouts(expiredTimeouts, deadline);
@ -372,7 +402,7 @@ public class HashedWheelTimer implements Timer {
int nanoSeconds = (int) (sleepTime - (milliSeconds * 1000000));
Thread.sleep(milliSeconds, nanoSeconds);
} catch (InterruptedException e) {
if (shutdown.get()) {
if (shutdown()) {
return -1;
}
}

View file

@ -15,15 +15,13 @@ import akka.event.Logging.Debug
import akka.event.LogSource
import akka.experimental
import akka.AkkaException
import scala.reflect.BeanProperty
import scala.util.control.NoStackTrace
import com.eaio.uuid.UUID
import java.lang.reflect.InvocationTargetException
import java.util.concurrent.TimeUnit
import java.util.{ Collection JCollection }
import java.util.regex.Pattern
/**
* Marker trait to show which Messages are automatically handled by Akka
@ -53,8 +51,6 @@ case class HotSwap(code: ActorRef ⇒ Actor.Receive, discardOld: Boolean = true)
case class Failed(cause: Throwable) extends AutoReceivedMessage with PossiblyHarmful
case object ChildTerminated extends AutoReceivedMessage with PossiblyHarmful
case object RevertHotSwap extends AutoReceivedMessage with PossiblyHarmful
case object PoisonPill extends AutoReceivedMessage with PossiblyHarmful
@ -65,6 +61,16 @@ case class Terminated(@BeanProperty actor: ActorRef) extends PossiblyHarmful
case object ReceiveTimeout extends PossiblyHarmful
/**
* ActorRefFactory.actorSelection returns a special ref which sends these
* nested path descriptions whenever using ! on them, the idea being that the
* message is delivered by active routing of the various actors involved.
*/
sealed trait SelectionPath extends AutoReceivedMessage
case class SelectChildName(name: String, next: Any) extends SelectionPath
case class SelectChildPattern(pattern: Pattern, next: Any) extends SelectionPath
case class SelectParent(next: Any) extends SelectionPath
// Exceptions for Actors
class IllegalActorStateException private[akka] (message: String, cause: Throwable = null)
extends AkkaException(message, cause) {
@ -77,6 +83,8 @@ class ActorKilledException private[akka] (message: String, cause: Throwable)
def this(msg: String) = this(msg, null);
}
case class InvalidActorNameException(message: String) extends AkkaException(message)
case class ActorInitializationException private[akka] (actor: ActorRef, message: String, cause: Throwable = null)
extends AkkaException(message, cause) with NoStackTrace {
def this(msg: String) = this(null, msg, null);

View file

@ -73,9 +73,9 @@ private[akka] object ActorCell {
//Make sure that they are not read/written outside of a message processing (systemInvoke/invoke)
private[akka] class ActorCell(
val system: ActorSystemImpl,
val self: ActorRef with ScalaActorRef,
val self: InternalActorRef,
val props: Props,
val parent: ActorRef,
val parent: InternalActorRef,
/*no member*/ _receiveTimeout: Option[Long],
var hotswap: Stack[PartialFunction[Any, Unit]]) extends ActorContext {
@ -85,6 +85,8 @@ private[akka] class ActorCell(
protected final def guardian = self
protected final def lookupRoot = self
final def provider = system.provider
override def receiveTimeout: Option[Long] = if (receiveTimeoutData._1 > 0) Some(receiveTimeoutData._1) else None
@ -99,6 +101,16 @@ private[akka] class ActorCell(
var childrenRefs: TreeMap[String, ChildRestartStats] = emptyChildrenRefs
def actorOf(props: Props, name: String): ActorRef = {
if (name == null || name == "" || name.charAt(0) == '$')
throw new InvalidActorNameException("actor name must not be null, empty or start with $")
if (childrenRefs contains name)
throw new InvalidActorNameException("actor name " + name + " is not unique!")
val actor = provider.actorOf(systemImpl, props, guardian, name, false)
childrenRefs = childrenRefs.updated(name, ChildRestartStats(actor))
actor
}
var currentMessage: Envelope = null
var actor: Actor = _
@ -154,9 +166,6 @@ private[akka] class ActorCell(
final def children: Iterable[ActorRef] = childrenRefs.values.view.map(_.child)
final def getChild(name: String): Option[ActorRef] =
if (isTerminated) None else childrenRefs.get(name).map(_.child)
final def tell(message: Any, sender: ActorRef): Unit =
dispatcher.dispatch(this, Envelope(message, if (sender eq null) system.deadLetters else sender))
@ -192,12 +201,12 @@ private[akka] class ActorCell(
actor = created
created.preStart()
checkReceiveTimeout
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.toString, "started (" + actor + ")"))
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, "started (" + actor + ")"))
} catch {
// FIXME catching all and continue isn't good for OOME, ticket #1418
case e
try {
system.eventStream.publish(Error(e, self.toString, "error while creating actor"))
system.eventStream.publish(Error(e, self.path.toString, "error while creating actor"))
// prevent any further messages to be processed until the actor has been restarted
dispatcher.suspend(this)
} finally {
@ -207,7 +216,7 @@ private[akka] class ActorCell(
def recreate(cause: Throwable): Unit = try {
val failedActor = actor
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.toString, "restarting"))
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, "restarting"))
val freshActor = newActor()
if (failedActor ne null) {
val c = currentMessage //One read only plz
@ -221,7 +230,7 @@ private[akka] class ActorCell(
}
actor = freshActor // assign it here so if preStart fails, we can null out the sef-refs next call
freshActor.postRestart(cause)
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.toString, "restarted"))
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, "restarted"))
dispatcher.resume(this) //FIXME should this be moved down?
@ -229,7 +238,7 @@ private[akka] class ActorCell(
} catch {
// FIXME catching all and continue isn't good for OOME, ticket #1418
case e try {
system.eventStream.publish(Error(e, self.toString, "error while creating actor"))
system.eventStream.publish(Error(e, self.path.toString, "error while creating actor"))
// prevent any further messages to be processed until the actor has been restarted
dispatcher.suspend(this)
} finally {
@ -248,47 +257,51 @@ private[akka] class ActorCell(
val c = children
if (c.isEmpty) doTerminate()
else {
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.toString, "stopping"))
// do not process normal messages while waiting for all children to terminate
dispatcher suspend this
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, "stopping"))
for (child c) child.stop()
stopping = true
}
}
def supervise(child: ActorRef): Unit = {
val stat = childrenRefs.get(child.name)
if (stat.isDefined) {
if (stat.get.child == child)
system.eventStream.publish(Warning(self.toString, "Already supervising " + child))
else
system.eventStream.publish(Warning(self.toString, "Already supervising other child with same name '" + child.name + "', old: " + stat.get + " new: " + child))
} else {
childrenRefs = childrenRefs.updated(child.name, ChildRestartStats(child))
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.toString, "now supervising " + child))
childrenRefs.get(child.path.name) match {
case None
childrenRefs = childrenRefs.updated(child.path.name, ChildRestartStats(child))
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, "now supervising " + child))
case Some(ChildRestartStats(`child`, _, _))
// this is the nominal case where we created the child and entered it in actorCreated() above
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, "now supervising " + child))
case Some(ChildRestartStats(c, _, _))
system.eventStream.publish(Warning(self.path.toString, "Already supervising other child with same name '" + child.path.name + "', old: " + c + " new: " + child))
}
}
try {
if (stopping) message match {
case Terminate() terminate() // to allow retry
case _
case Terminate() terminate() // to allow retry
case ChildTerminated(child) handleChildTerminated(child)
case _
}
else message match {
case Create() create()
case Recreate(cause) recreate(cause)
case Link(subject)
system.deathWatch.subscribe(self, subject)
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.toString, "now monitoring " + subject))
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, "now monitoring " + subject))
case Unlink(subject)
system.deathWatch.unsubscribe(self, subject)
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.toString, "stopped monitoring " + subject))
case Suspend() suspend()
case Resume() resume()
case Terminate() terminate()
case Supervise(child) supervise(child)
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, "stopped monitoring " + subject))
case Suspend() suspend()
case Resume() resume()
case Terminate() terminate()
case Supervise(child) supervise(child)
case ChildTerminated(child) handleChildTerminated(child)
}
} catch {
case e //Should we really catch everything here?
system.eventStream.publish(Error(e, self.toString, "error while processing " + message))
system.eventStream.publish(Error(e, self.path.toString, "error while processing " + message))
//TODO FIXME How should problems here be handled???
throw e
}
@ -303,14 +316,12 @@ private[akka] class ActorCell(
cancelReceiveTimeout() // FIXME: leave this here???
messageHandle.message match {
case msg: AutoReceivedMessage autoReceiveMessage(messageHandle)
case msg if stopping // receiving Terminated in response to stopping children is too common to generate noise
if (!msg.isInstanceOf[Terminated]) system.deadLetterMailbox.enqueue(self, messageHandle)
case msg actor(msg)
case msg actor(msg)
}
currentMessage = null // reset current message after successful invocation
} catch {
case e
system.eventStream.publish(Error(e, self.toString, e.getMessage))
system.eventStream.publish(Error(e, self.path.toString, e.getMessage))
// prevent any further messages to be processed until the actor has been restarted
dispatcher.suspend(this)
@ -330,7 +341,7 @@ private[akka] class ActorCell(
}
} catch {
case e
system.eventStream.publish(Error(e, self.toString, e.getMessage))
system.eventStream.publish(Error(e, self.path.toString, e.getMessage))
throw e
}
}
@ -347,26 +358,21 @@ private[akka] class ActorCell(
}
def autoReceiveMessage(msg: Envelope) {
if (system.settings.DebugAutoReceive) system.eventStream.publish(Debug(self.toString, "received AutoReceiveMessage " + msg))
if (system.settings.DebugAutoReceive) system.eventStream.publish(Debug(self.path.toString, "received AutoReceiveMessage " + msg))
if (stopping) msg.message match {
case ChildTerminated handleChildTerminated(sender)
case _ system.deadLetterMailbox.enqueue(self, msg)
}
else msg.message match {
msg.message match {
case HotSwap(code, discardOld) become(code(self), discardOld)
case RevertHotSwap unbecome()
case Failed(cause) handleFailure(sender, cause)
case ChildTerminated handleChildTerminated(sender)
case Kill throw new ActorKilledException("Kill")
case PoisonPill self.stop()
case SelectParent(m) parent.tell(m, msg.sender)
case SelectChildName(name, m) if (childrenRefs contains name) childrenRefs(name).child.tell(m, msg.sender)
case SelectChildPattern(p, m) for (c children if p.matcher(c.path.name).matches) c.tell(m, msg.sender)
}
}
private def doTerminate() {
if (!system.provider.evict(self.path.toString))
system.eventStream.publish(Warning(self.toString, "evict of " + self.path.toString + " failed"))
dispatcher.detach(this)
try {
@ -374,9 +380,9 @@ private[akka] class ActorCell(
if (a ne null) a.postStop()
} finally {
try {
parent.tell(ChildTerminated, self)
parent.sendSystemMessage(ChildTerminated(self))
system.deathWatch.publish(Terminated(self))
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.toString, "stopped"))
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, "stopped"))
} finally {
currentMessage = null
clearActorFields()
@ -384,14 +390,14 @@ private[akka] class ActorCell(
}
}
final def handleFailure(child: ActorRef, cause: Throwable): Unit = childrenRefs.get(child.name) match {
final def handleFailure(child: ActorRef, cause: Throwable): Unit = childrenRefs.get(child.path.name) match {
case Some(stats) if stats.child == child if (!props.faultHandler.handleFailure(child, cause, stats, childrenRefs.values)) throw cause
case Some(stats) system.eventStream.publish(Warning(self.toString, "dropping Failed(" + cause + ") from unknown child " + child + " matching names but not the same, was: " + stats.child))
case None system.eventStream.publish(Warning(self.toString, "dropping Failed(" + cause + ") from unknown child " + child))
case Some(stats) system.eventStream.publish(Warning(self.path.toString, "dropping Failed(" + cause + ") from unknown child " + child + " matching names but not the same, was: " + stats.child))
case None system.eventStream.publish(Warning(self.path.toString, "dropping Failed(" + cause + ") from unknown child " + child))
}
final def handleChildTerminated(child: ActorRef): Unit = {
childrenRefs -= child.name
childrenRefs -= child.path.name
props.faultHandler.handleChildTerminated(child, children)
if (stopping && childrenRefs.isEmpty) doTerminate()
}

View file

@ -1,68 +1,38 @@
/**
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
import akka.remote.RemoteAddress
import scala.annotation.tailrec
object ActorPath {
final val separator = "/"
val pattern = """(/[0-9a-zA-Z\-\_\$\.]+)+""".r.pattern
/**
* Create an actor path from a string.
*/
def apply(system: ActorSystem, path: String): ActorPath =
apply(system, split(path))
/**
* Create an actor path from an iterable.
*/
def apply(system: ActorSystem, path: Iterable[String]): ActorPath =
path.foldLeft(system.asInstanceOf[ActorSystemImpl].provider.rootPath)(_ / _)
/**
* Split a string path into an iterable.
*/
def split(path: String): Iterable[String] =
if (path.startsWith(separator))
path.substring(1).split(separator)
else
path.split(separator)
/**
* Join an iterable path into a string.
*/
def join(path: Iterable[String]): String =
path.mkString(separator, separator, "")
/**
* Is this string representation of a path valid?
*/
def valid(path: String): Boolean =
pattern.matcher(path).matches
/**
* Validate a path. Moved here from Address.validate.
* Throws an IllegalArgumentException if the path is invalid.
*/
def validate(path: String): Unit = {
if (!valid(path))
throw new IllegalArgumentException("Path [" + path + "] is not valid. Needs to follow this pattern: " + pattern)
def split(s: String): List[String] = {
@tailrec
def rec(pos: Int, acc: List[String]): List[String] = {
val from = s.lastIndexOf('/', pos - 1)
val sub = s.substring(from + 1, pos)
val l = sub :: acc
if (from == -1) l else rec(from, l)
}
rec(s.length, Nil)
}
}
/**
* Actor path is a unique path to an actor that shows the creation path
* up through the actor tree to the root actor.
*
* ActorPath defines a natural ordering (so that ActorRefs can be put into
* collections with this requirement); this ordering is intended to be as fast
* as possible, which owing to the bottom-up recursive nature of ActorPath
* is sorted by path elements FROM RIGHT TO LEFT, where RootActorPath >
* ChildActorPath in case the number of elements is different.
*/
trait ActorPath {
sealed trait ActorPath extends Comparable[ActorPath] {
/**
* The RemoteAddress for this path.
* The Address under which this path can be reached; walks up the tree to
* the RootActorPath.
*/
def remoteAddress: RemoteAddress
def address: Address
/**
* The name of the actor that this path refers to.
@ -85,48 +55,118 @@ trait ActorPath {
def /(child: Iterable[String]): ActorPath = (this /: child)(_ / _)
/**
* String representation of this path. Different from toString for root path.
* Sequence of names for this path. Performance implication: has to allocate a list.
*/
def string: String
def elements: Iterable[String]
/**
* Sequence of names for this path.
* Walk up the tree to obtain and return the RootActorPath.
*/
def path: Iterable[String]
def root: RootActorPath
/**
* Is this the root path?
*/
def isRoot: Boolean
}
class RootActorPath(val remoteAddress: RemoteAddress) extends ActorPath {
def name: String = "/"
/**
* Root of the hierarchy of ActorPaths. There is exactly root per ActorSystem
* and node (for remote-enabled or clustered systems).
*/
final case class RootActorPath(address: Address, name: String = "/") extends ActorPath {
def parent: ActorPath = this
def /(child: String): ActorPath = new ChildActorPath(remoteAddress, this, child)
def root: RootActorPath = this
def string: String = ""
def /(child: String): ActorPath = new ChildActorPath(this, child)
def path: Iterable[String] = Iterable.empty
val elements: Iterable[String] = List("")
def isRoot: Boolean = true
override val toString = address + name
override def toString = ActorPath.separator
def compareTo(other: ActorPath) = other match {
case r: RootActorPath toString compareTo r.toString
case c: ChildActorPath 1
}
}
class ChildActorPath(val remoteAddress: RemoteAddress, val parent: ActorPath, val name: String) extends ActorPath {
final class ChildActorPath(val parent: ActorPath, val name: String) extends ActorPath {
def /(child: String): ActorPath = new ChildActorPath(remoteAddress, this, child)
def address: Address = root.address
def string: String = parent.string + ActorPath.separator + name
def /(child: String): ActorPath = new ChildActorPath(this, child)
def path: Iterable[String] = parent.path ++ Iterable(name)
def elements: Iterable[String] = {
@tailrec
def rec(p: ActorPath, acc: List[String]): Iterable[String] = p match {
case r: RootActorPath acc
case _ rec(p.parent, p.name :: acc)
}
rec(this, Nil)
}
def isRoot: Boolean = false
def root = {
@tailrec
def rec(p: ActorPath): RootActorPath = p match {
case r: RootActorPath r
case _ rec(p.parent)
}
rec(this)
}
override def toString = string
// TODO research whether this should be cached somehow (might be fast enough, but creates GC pressure)
/*
* idea: add one field which holds the total length (because that is known)
* so that only one String needs to be allocated before traversal; this is
* cheaper than any cache
*/
override def toString = {
@tailrec
def rec(p: ActorPath, s: StringBuilder): StringBuilder = p match {
case r: RootActorPath s.insert(0, r.toString)
case _ rec(p.parent, s.insert(0, '/').insert(0, p.name))
}
rec(parent, new StringBuilder(32).append(name)).toString
}
override def equals(other: Any): Boolean = {
@tailrec
def rec(left: ActorPath, right: ActorPath): Boolean =
if (left eq right) true
else if (left.isInstanceOf[RootActorPath]) left equals right
else if (right.isInstanceOf[RootActorPath]) right equals left
else left.name == right.name && rec(left.parent, right.parent)
other match {
case p: ActorPath rec(this, p)
case _ false
}
}
// TODO RK investigate Phils hash from scala.collection.mutable.HashTable.improve
override def hashCode: Int = {
import scala.util.MurmurHash._
@tailrec
def rec(p: ActorPath, h: Int, c: Int, k: Int): Int = p match {
case r: RootActorPath extendHash(h, r.##, c, k)
case _ rec(p.parent, extendHash(h, stringHash(name), c, k), nextMagicA(c), nextMagicB(k))
}
finalizeHash(rec(this, startHash(42), startMagicA, startMagicB))
}
def compareTo(other: ActorPath) = {
@tailrec
def rec(left: ActorPath, right: ActorPath): Int =
if (left eq right) 0
else if (left.isInstanceOf[RootActorPath]) left compareTo right
else if (right.isInstanceOf[RootActorPath]) -(right compareTo left)
else {
val x = left.name compareTo right.name
if (x == 0) rec(left.parent, right.parent)
else x
}
rec(this, other)
}
}

View file

@ -14,6 +14,7 @@ import akka.remote.RemoteAddress
import java.util.concurrent.TimeUnit
import akka.event.EventStream
import akka.event.DeathWatch
import scala.annotation.tailrec
/**
* ActorRef is an immutable and serializable handle to an Actor.
@ -43,31 +44,23 @@ import akka.event.DeathWatch
* actor.stop()
* </pre>
*
* The natural ordering of ActorRef is defined in terms of its [[akka.actor.ActorPath]].
*
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
abstract class ActorRef extends java.lang.Comparable[ActorRef] with Serializable {
scalaRef: ScalaActorRef with RefInternals
scalaRef: InternalActorRef
// Only mutable for RemoteServer in order to maintain identity across nodes
/**
* Returns the name for this actor. Locally unique (across siblings).
*/
def name: String
/**
* Returns the path for this actor (from this actor up to the root actor).
*/
def path: ActorPath
/**
* Returns the absolute address for this actor in the form hostname:port/path/to/actor.
*/
def address: String
/**
* Comparison only takes address into account.
*/
def compareTo(other: ActorRef) = this.address compareTo other.address
final def compareTo(other: ActorRef) = this.path compareTo other.path
/**
* Sends the specified message to the sender, i.e. fire-and-forget semantics.<p/>
@ -118,14 +111,78 @@ abstract class ActorRef extends java.lang.Comparable[ActorRef] with Serializable
*/
def isTerminated: Boolean
override def hashCode: Int = HashCode.hash(HashCode.SEED, address)
// FIXME RK check if we should scramble the bits or whether they can stay the same
final override def hashCode: Int = path.hashCode
override def equals(that: Any): Boolean = {
that.isInstanceOf[ActorRef] &&
that.asInstanceOf[ActorRef].address == address
final override def equals(that: Any): Boolean = that match {
case other: ActorRef path == other.path
case _ false
}
override def toString = "Actor[%s]".format(address)
override def toString = "Actor[%s]".format(path)
}
/**
* This trait represents the Scala Actor API
* There are implicit conversions in ../actor/Implicits.scala
* from ActorRef -> ScalaActorRef and back
*/
trait ScalaActorRef { ref: ActorRef
/**
* Sends a one-way asynchronous message. E.g. fire-and-forget semantics.
* <p/>
*
* If invoked from within an actor then the actor reference is implicitly passed on as the implicit 'sender' argument.
* <p/>
*
* This actor 'sender' reference is then available in the receiving actor in the 'sender' member variable,
* if invoked from within an Actor. If not then no sender is available.
* <pre>
* actor ! message
* </pre>
* <p/>
*/
def !(message: Any)(implicit sender: ActorRef = null): Unit
/**
* Sends a message asynchronously, returning a future which may eventually hold the reply.
*/
def ?(message: Any)(implicit timeout: Timeout): Future[Any]
/**
* Sends a message asynchronously, returning a future which may eventually hold the reply.
* The implicit parameter with the default value is just there to disambiguate it from the version that takes the
* implicit timeout
*/
def ?(message: Any, timeout: Timeout)(implicit ignore: Int = 0): Future[Any] = ?(message)(timeout)
}
/**
* Internal trait for assembling all the functionality needed internally on
* ActorRefs. NOTE THAT THIS IS NOT A STABLE EXTERNAL INTERFACE!
*
* DO NOT USE THIS UNLESS INTERNALLY WITHIN AKKA!
*/
private[akka] abstract class InternalActorRef extends ActorRef with ScalaActorRef {
def resume(): Unit
def suspend(): Unit
def restart(cause: Throwable): Unit
def sendSystemMessage(message: SystemMessage): Unit
def getParent: InternalActorRef
/**
* Obtain ActorRef by possibly traversing the actor tree or looking it up at
* some provider-specific location. This method shall return the end result,
* i.e. not only the next step in the look-up; this will typically involve
* recursive invocation. A path element of ".." signifies the parent, a
* trailing "" element must be disregarded. If the requested path does not
* exist, return Nobody.
*/
def getChild(name: Iterable[String]): InternalActorRef
}
private[akka] case object Nobody extends MinimalActorRef {
val path = new RootActorPath(new LocalAddress("all-systems"), "/Nobody")
}
/**
@ -136,16 +193,12 @@ abstract class ActorRef extends java.lang.Comparable[ActorRef] with Serializable
class LocalActorRef private[akka] (
system: ActorSystemImpl,
_props: Props,
_supervisor: ActorRef,
_supervisor: InternalActorRef,
val path: ActorPath,
val systemService: Boolean = false,
_receiveTimeout: Option[Long] = None,
_hotswap: Stack[PartialFunction[Any, Unit]] = Props.noHotSwap)
extends ActorRef with ScalaActorRef with RefInternals {
def name = path.name
def address: String = path.toString
extends InternalActorRef {
/*
* actorCell.start() publishes actorCell & this to the dispatcher, which
@ -187,6 +240,43 @@ class LocalActorRef private[akka] (
*/
def stop(): Unit = actorCell.stop()
def getParent: InternalActorRef = actorCell.parent
/**
* Method for looking up a single child beneath this actor. Override in order
* to inject synthetic actor paths like /temp.
*/
protected def getSingleChild(name: String): InternalActorRef = {
if (actorCell.isTerminated) Nobody // read of the mailbox status ensures we get the latest childrenRefs
else {
val children = actorCell.childrenRefs
if (children contains name) children(name).child.asInstanceOf[InternalActorRef]
else Nobody
}
}
def getChild(names: Iterable[String]): InternalActorRef = {
/*
* The idea is to recursively descend as far as possible with LocalActor
* Refs and hand over to that foreign child when we encounter it.
*/
@tailrec
def rec(ref: InternalActorRef, name: Iterable[String]): InternalActorRef = ref match {
case l: LocalActorRef
val n = name.head
val rest = name.tail
val next = n match {
case ".." l.getParent
case "" l
case _ l.getSingleChild(n)
}
if (next == Nobody || rest.isEmpty) next else rec(next, rest)
case _
ref.getChild(name)
}
rec(this, names)
}
// ========= AKKA PROTECTED FUNCTIONS =========
protected[akka] def underlying: ActorCell = actorCell
@ -202,90 +292,42 @@ class LocalActorRef private[akka] (
instance
}
protected[akka] def sendSystemMessage(message: SystemMessage) { underlying.dispatcher.systemDispatch(underlying, message) }
def sendSystemMessage(message: SystemMessage) { underlying.dispatcher.systemDispatch(underlying, message) }
def !(message: Any)(implicit sender: ActorRef = null): Unit = actorCell.tell(message, sender)
def ?(message: Any)(implicit timeout: Timeout): Future[Any] = actorCell.provider.ask(message, this, timeout)
protected[akka] override def restart(cause: Throwable): Unit = actorCell.restart(cause)
def restart(cause: Throwable): Unit = actorCell.restart(cause)
@throws(classOf[java.io.ObjectStreamException])
private def writeReplace(): AnyRef = actorCell.provider.serialize(this)
}
/**
* This trait represents the Scala Actor API
* There are implicit conversions in ../actor/Implicits.scala
* from ActorRef -> ScalaActorRef and back
*/
trait ScalaActorRef { ref: ActorRef
protected[akka] def sendSystemMessage(message: SystemMessage): Unit
/**
* Sends a one-way asynchronous message. E.g. fire-and-forget semantics.
* <p/>
*
* If invoked from within an actor then the actor reference is implicitly passed on as the implicit 'sender' argument.
* <p/>
*
* This actor 'sender' reference is then available in the receiving actor in the 'sender' member variable,
* if invoked from within an Actor. If not then no sender is available.
* <pre>
* actor ! message
* </pre>
* <p/>
*/
def !(message: Any)(implicit sender: ActorRef = null): Unit
/**
* Sends a message asynchronously, returning a future which may eventually hold the reply.
*/
def ?(message: Any)(implicit timeout: Timeout): Future[Any]
/**
* Sends a message asynchronously, returning a future which may eventually hold the reply.
* The implicit parameter with the default value is just there to disambiguate it from the version that takes the
* implicit timeout
*/
def ?(message: Any, timeout: Timeout)(implicit ignore: Int = 0): Future[Any] = ?(message)(timeout)
}
private[akka] trait RefInternals {
def resume(): Unit
def suspend(): Unit
protected[akka] def restart(cause: Throwable): Unit
private def writeReplace(): AnyRef = SerializedActorRef(path.toString)
}
/**
* Memento pattern for serializing ActorRefs transparently
*/
case class SerializedActorRef(hostname: String, port: Int, path: String) {
case class SerializedActorRef(path: String) {
import akka.serialization.Serialization.currentSystem
def this(remoteAddress: RemoteAddress, path: String) = this(remoteAddress.hostname, remoteAddress.port, path)
@throws(classOf[java.io.ObjectStreamException])
def readResolve(): AnyRef = currentSystem.value match {
case null throw new IllegalStateException(
"Trying to deserialize a serialized ActorRef without an ActorSystem in scope." +
" Use akka.serialization.Serialization.currentSystem.withValue(system) { ... }")
case someSystem someSystem.provider.deserialize(this) match {
case Some(actor) actor
case None throw new IllegalStateException("Could not deserialize ActorRef")
}
case someSystem someSystem.actorFor(path)
}
}
/**
* Trait for ActorRef implementations where all methods contain default stubs.
*/
trait MinimalActorRef extends ActorRef with ScalaActorRef with RefInternals {
trait MinimalActorRef extends InternalActorRef {
private[akka] val uuid: Uuid = newUuid()
def name: String = uuid.toString
def getParent: InternalActorRef = Nobody
def getChild(name: Iterable[String]): InternalActorRef =
if (name.size == 1 && name.head.isEmpty) this
else Nobody
//FIXME REMOVE THIS, ticket #1416
//FIXME REMOVE THIS, ticket #1415
@ -301,8 +343,16 @@ trait MinimalActorRef extends ActorRef with ScalaActorRef with RefInternals {
def ?(message: Any)(implicit timeout: Timeout): Future[Any] =
throw new UnsupportedOperationException("Not supported for %s".format(getClass.getName))
protected[akka] def sendSystemMessage(message: SystemMessage): Unit = ()
protected[akka] def restart(cause: Throwable): Unit = ()
def sendSystemMessage(message: SystemMessage): Unit = ()
def restart(cause: Throwable): Unit = ()
}
object MinimalActorRef {
def apply(_path: ActorPath)(receive: PartialFunction[Any, Unit]): ActorRef = new MinimalActorRef {
def path = _path
override def !(message: Any)(implicit sender: ActorRef = null): Unit =
if (receive.isDefinedAt(message)) receive(message)
}
}
case class DeadLetter(message: Any, sender: ActorRef, recipient: ActorRef)
@ -327,14 +377,10 @@ class DeadLetterActorRef(val eventStream: EventStream) extends MinimalActorRef {
}
private[akka] def init(dispatcher: MessageDispatcher, rootPath: ActorPath) {
_path = rootPath / "nul"
_path = rootPath / "null"
brokenPromise = new KeptPromise[Any](Left(new ActorKilledException("In DeadLetterActorRef, promises are always broken.")))(dispatcher)
}
override val name: String = "dead-letter"
def address: String = path.toString
override def isTerminated(): Boolean = true
override def !(message: Any)(implicit sender: ActorRef = this): Unit = message match {
@ -353,20 +399,22 @@ class DeadLetterActorRef(val eventStream: EventStream) extends MinimalActorRef {
private def writeReplace(): AnyRef = DeadLetterActorRef.serialized
}
abstract class AskActorRef(val path: ActorPath, provider: ActorRefProvider, deathWatch: DeathWatch, timeout: Timeout, val dispatcher: MessageDispatcher) extends MinimalActorRef {
class AskActorRef(
val path: ActorPath,
override val getParent: InternalActorRef,
deathWatch: DeathWatch,
timeout: Timeout,
val dispatcher: MessageDispatcher) extends MinimalActorRef {
final val result = new DefaultPromise[Any](timeout)(dispatcher)
override def name = path.name
def address: String = path.toString
{
val callback: Future[Any] Unit = { _ deathWatch.publish(Terminated(AskActorRef.this)); whenDone() }
result onComplete callback
result onTimeout callback
}
protected def whenDone(): Unit
protected def whenDone(): Unit = ()
override def !(message: Any)(implicit sender: ActorRef = null): Unit = message match {
case Status.Success(r) result.completeWithResult(r)
@ -374,7 +422,7 @@ abstract class AskActorRef(val path: ActorPath, provider: ActorRefProvider, deat
case other result.completeWithResult(other)
}
protected[akka] override def sendSystemMessage(message: SystemMessage): Unit = message match {
override def sendSystemMessage(message: SystemMessage): Unit = message match {
case _: Terminate stop()
case _
}
@ -387,5 +435,5 @@ abstract class AskActorRef(val path: ActorPath, provider: ActorRefProvider, deat
override def stop(): Unit = if (!isTerminated) result.completeWithException(new ActorKilledException("Stopped"))
@throws(classOf[java.io.ObjectStreamException])
private def writeReplace(): AnyRef = provider.serialize(this)
private def writeReplace(): AnyRef = SerializedActorRef(path.toString)
}

View file

@ -10,13 +10,13 @@ import scala.annotation.tailrec
import org.jboss.netty.akka.util.{ TimerTask, HashedWheelTimer }
import akka.actor.Timeout.intToTimeout
import akka.config.ConfigurationException
import akka.dispatch.{ SystemMessage, Supervise, Promise, MessageDispatcher, Future, DefaultPromise, Dispatcher, Mailbox, Envelope }
import akka.routing.{ ScatterGatherFirstCompletedRouter, Routing, RouterType, Router, RoutedProps, RoutedActorRef, RoundRobinRouter, RandomRouter, LocalConnectionManager, DirectRouter, BroadcastRouter }
import akka.dispatch._
import akka.routing._
import akka.AkkaException
import com.eaio.uuid.UUID
import akka.util.{ Duration, Switch, Helpers }
import akka.remote.RemoteAddress
import akka.remote.LocalOnly
import org.jboss.netty.akka.util.internal.ConcurrentIdentityHashMap
import akka.event._
import akka.event.Logging.Error._
import akka.event.Logging.Warning
@ -27,14 +27,26 @@ import java.io.Closeable
*/
trait ActorRefProvider {
def actorOf(system: ActorSystemImpl, props: Props, supervisor: ActorRef, name: String): ActorRef = actorOf(system, props, supervisor, name, false)
/**
* Reference to the supervisor of guardian and systemGuardian; this is
* exposed so that the ActorSystemImpl can use it as lookupRoot, i.e.
* for anchoring absolute actor look-ups.
*/
def rootGuardian: InternalActorRef
def actorFor(path: Iterable[String]): Option[ActorRef]
/**
* Reference to the supervisor used for all top-level user actors.
*/
def guardian: InternalActorRef
def guardian: ActorRef
def systemGuardian: ActorRef
/**
* Reference to the supervisor used for all top-level system actors.
*/
def systemGuardian: InternalActorRef
/**
* Reference to the death watch service.
*/
def deathWatch: DeathWatch
// FIXME: remove/replace???
@ -51,32 +63,47 @@ trait ActorRefProvider {
def settings: ActorSystem.Settings
def init(system: ActorSystemImpl)
/**
* Initialization of an ActorRefProvider happens in two steps: first
* construction of the object with settings, eventStream, scheduler, etc.
* and thenwhen the ActorSystem is constructedthe second phase during
* which actors may be created (e.g. the guardians).
*/
def init(system: ActorSystemImpl): Unit
private[akka] def deployer: Deployer
private[akka] def scheduler: Scheduler
/**
* Create an Actor with the given name below the given supervisor.
* Actor factory with create-only semantics: will create an actor as
* described by props with the given supervisor and path (may be different
* in case of remote supervision). If systemService is true, deployment is
* bypassed (local-only).
*/
private[akka] def actorOf(system: ActorSystemImpl, props: Props, supervisor: ActorRef, name: String, systemService: Boolean): ActorRef
def actorOf(system: ActorSystemImpl, props: Props, supervisor: InternalActorRef, name: String, systemService: Boolean = false): InternalActorRef
/**
* Create an Actor with the given full path below the given supervisor.
*
* FIXME: Remove! this is dangerous!?
* Create actor reference for a specified local or remote path. If no such
* actor exists, it will be (equivalent to) a dead letter reference.
*/
private[akka] def actorOf(system: ActorSystemImpl, props: Props, supervisor: ActorRef, path: ActorPath, systemService: Boolean): ActorRef
def actorFor(path: ActorPath): InternalActorRef
/**
* Remove this path from the lookup map.
* Create actor reference for a specified local or remote path, which will
* be parsed using java.net.URI. If no such actor exists, it will be
* (equivalent to) a dead letter reference. If `s` is a relative URI, resolve
* it relative to the given ref.
*/
private[akka] def evict(path: String): Boolean
def actorFor(ref: InternalActorRef, s: String): InternalActorRef
private[akka] def deserialize(actor: SerializedActorRef): Option[ActorRef]
private[akka] def serialize(actor: ActorRef): SerializedActorRef
/**
* Create actor reference for the specified child path starting at the
* given starting point. This method always returns an actor which is logically local,
* i.e. it cannot be used to obtain a reference to an actor which is not
* physically or logically attached to this actor system.
*/
def actorFor(ref: InternalActorRef, p: Iterable[String]): InternalActorRef
private[akka] def createDeathWatch(): DeathWatch
@ -93,7 +120,7 @@ trait ActorRefProvider {
}
/**
* Interface implemented by ActorSystem and AkkaContext, the only two places from which you can get fresh actors
* Interface implemented by ActorSystem and AkkaContext, the only two places from which you can get fresh actors.
*/
trait ActorRefFactory {
@ -106,57 +133,186 @@ trait ActorRefFactory {
/**
* Father of all children created by this interface.
*/
protected def guardian: ActorRef
protected def guardian: InternalActorRef
protected def lookupRoot: InternalActorRef
protected def randomName(): String
/**
* Create new actor as child of this context and give it an automatically
* generated name (currently similar to base64-encoded integer count,
* reversed and with $ prepended, may change in the future).
*
* See [[akka.actor.Props]] for details on how to obtain a `Props` object.
*/
def actorOf(props: Props): ActorRef = provider.actorOf(systemImpl, props, guardian, randomName(), false)
/*
* TODO this will have to go at some point, because creating two actors with
* the same address can race on the cluster, and then you never know which
* implementation wins
/**
* Create new actor as child of this context with the given name, which must
* not be null, empty or start with $. If the given name is already in use,
* and `InvalidActorNameException` is thrown.
*
* See [[akka.actor.Props]] for details on how to obtain a `Props` object.
*/
def actorOf(props: Props, name: String): ActorRef = {
if (name == null || name == "" || name.startsWith("$"))
throw new ActorInitializationException("actor name must not be null, empty or start with $")
provider.actorOf(systemImpl, props, guardian, name, false)
}
def actorOf(props: Props, name: String): ActorRef
/**
* Create new actor of the given type as child of this context and give it an automatically
* generated name (currently similar to base64-encoded integer count,
* reversed and with $ prepended, may change in the future). The type must have
* a no-arg constructor which will be invoked using reflection.
*/
def actorOf[T <: Actor](implicit m: Manifest[T]): ActorRef = actorOf(Props(m.erasure.asInstanceOf[Class[_ <: Actor]]))
/**
* Create new actor of the given type as child of this context with the given name, which must
* not be null, empty or start with $. If the given name is already in use,
* and `InvalidActorNameException` is thrown. The type must have
* a no-arg constructor which will be invoked using reflection.
*/
def actorOf[T <: Actor](name: String)(implicit m: Manifest[T]): ActorRef =
actorOf(Props(m.erasure.asInstanceOf[Class[_ <: Actor]]), name)
/**
* Create new actor of the given class as child of this context and give it an automatically
* generated name (currently similar to base64-encoded integer count,
* reversed and with $ prepended, may change in the future). The class must have
* a no-arg constructor which will be invoked using reflection.
*/
def actorOf[T <: Actor](clazz: Class[T]): ActorRef = actorOf(Props(clazz))
/**
* Create new actor as child of this context and give it an automatically
* generated name (currently similar to base64-encoded integer count,
* reversed and with $ prepended, may change in the future). Use this
* method to pass constructor arguments to the [[akka.actor.Actor]] while using
* only default [[akka.actor.Props]]; otherwise refer to `actorOf(Props)`.
*/
def actorOf(factory: Actor): ActorRef = actorOf(Props(() factory))
/**
* ''Java API'': Create new actor as child of this context and give it an
* automatically generated name (currently similar to base64-encoded integer
* count, reversed and with $ prepended, may change in the future).
*
* Identical to `actorOf(Props(() => creator.create()))`.
*/
def actorOf(creator: UntypedActorFactory): ActorRef = actorOf(Props(() creator.create()))
def actorFor(path: ActorPath): Option[ActorRef] = actorFor(path.path)
/**
* ''Java API'': Create new actor as child of this context with the given name, which must
* not be null, empty or start with $. If the given name is already in use,
* and `InvalidActorNameException` is thrown.
*
* Identical to `actorOf(Props(() => creator.create()), name)`.
*/
def actorOf(creator: UntypedActorFactory, name: String): ActorRef = actorOf(Props(() creator.create()), name)
def actorFor(path: String): Option[ActorRef] = actorFor(ActorPath.split(path))
/**
* Look-up an actor by path; if it does not exist, returns a reference to
* the dead-letter mailbox of the [[akka.actor.ActorSystem]]. If the path
* point to an actor which is not local, no attempt is made during this
* call to verify that the actor it represents does exist or is alive; use
* `watch(ref)` to be notified of the targets termination, which is also
* signaled if the queried path cannot be resolved.
*/
def actorFor(path: ActorPath): ActorRef = provider.actorFor(path)
def actorFor(path: Iterable[String]): Option[ActorRef] = provider.actorFor(path)
/**
* Look-up an actor by path represented as string.
*
* Absolute URIs like `akka://appname/user/actorA` are looked up as described
* for look-ups by `actorOf(ActorPath)`.
*
* Relative URIs like `/service/actorA/childB` are looked up relative to the
* root path of the [[akka.actor.ActorSystem]] containing this factory and as
* described for look-ups by `actorOf(Iterable[String])`.
*
* Relative URIs like `myChild/grandChild` or `../myBrother` are looked up
* relative to the current context as described for look-ups by
* `actorOf(Iterable[String])`
*/
def actorFor(path: String): ActorRef = provider.actorFor(lookupRoot, path)
/**
* Look-up an actor by applying the given path elements, starting from the
* current context, where `".."` signifies the parent of an actor.
*
* Example:
* {{{
* class MyActor extends Actor {
* def receive = {
* case msg =>
* ...
* val target = context.actorFor(Seq("..", "myBrother", "myNephew"))
* ...
* }
* }
* }}}
*
* For maximum performance use a collection with efficient head & tail operations.
*/
def actorFor(path: Iterable[String]): ActorRef = provider.actorFor(lookupRoot, path)
/**
* Look-up an actor by applying the given path elements, starting from the
* current context, where `".."` signifies the parent of an actor.
*
* Example:
* {{{
* public class MyActor extends UntypedActor {
* public void onReceive(Object msg) throws Exception {
* ...
* final List<String> path = new ArrayList<String>();
* path.add("..");
* path.add("myBrother");
* path.add("myNephew");
* final ActorRef target = context().actorFor(path);
* ...
* }
* }
* }}}
*
* For maximum performance use a collection with efficient head & tail operations.
*/
def actorFor(path: java.util.List[String]): ActorRef = {
import scala.collection.JavaConverters._
provider.actorFor(lookupRoot, path.asScala)
}
/**
* Construct an [[akka.actor.ActorSelection]] from the given path, which is
* parsed for wildcards (these are replaced by regular expressions
* internally). No attempt is made to verify the existence of any part of
* the supplied path, it is recommended to send a message and gather the
* replies in order to resolve the matching set of actors.
*/
def actorSelection(path: String): ActorSelection = ActorSelection(lookupRoot, path)
}
class ActorRefProviderException(message: String) extends AkkaException(message)
/**
* Internal Akka use only, used in implementation of system.actorOf.
*/
private[akka] case class CreateChild(props: Props, name: String)
/**
* Local ActorRef provider.
*/
class LocalActorRefProvider(
_systemName: String,
val settings: ActorSystem.Settings,
val eventStream: EventStream,
val scheduler: Scheduler,
val rootPath: ActorPath,
val nodename: String,
val clustername: String) extends ActorRefProvider {
val deadLetters: InternalActorRef) extends ActorRefProvider {
def this(settings: ActorSystem.Settings, eventStream: EventStream, scheduler: Scheduler) {
this(settings, eventStream, scheduler, new RootActorPath(LocalOnly), "local", "local")
}
val rootPath: ActorPath = new RootActorPath(LocalAddress(_systemName))
// FIXME remove both
val nodename: String = "local"
val clustername: String = "local"
val log = Logging(eventStream, "LocalActorRefProvider")
@ -167,35 +323,27 @@ class LocalActorRefProvider(
*/
private val tempNumber = new AtomicLong
def tempName = "$_" + Helpers.base64(tempNumber.getAndIncrement())
private def tempName() = Helpers.base64(tempNumber.getAndIncrement())
private val tempNode = rootPath / "tmp"
private val tempNode = rootPath / "temp"
def tempPath = tempNode / tempName
// FIXME (actor path): this could become a cache for the new tree traversal actorFor
// currently still used for tmp actors (e.g. ask actor refs)
private val actors = new ConcurrentHashMap[String, AnyRef]
def tempPath() = tempNode / tempName()
/**
* Top-level anchor for the supervision hierarchy of this actor system. Will
* receive only Supervise/ChildTerminated system messages or Failure message.
*/
private[akka] val theOneWhoWalksTheBubblesOfSpaceTime: ActorRef = new MinimalActorRef {
private[akka] val theOneWhoWalksTheBubblesOfSpaceTime: InternalActorRef = new MinimalActorRef {
val stopped = new Switch(false)
@volatile
var causeOfTermination: Option[Throwable] = None
override val name = "bubble-walker"
// FIXME (actor path): move the root path to the new root guardian
val path = rootPath / name
val path = rootPath / "bubble-walker"
val address = path.toString
override def toString = name
override def stop() = stopped switchOn {
terminationFuture.complete(causeOfTermination.toLeft(()))
}
@ -203,22 +351,24 @@ class LocalActorRefProvider(
override def isTerminated = stopped.isOn
override def !(message: Any)(implicit sender: ActorRef = null): Unit = stopped.ifOff(message match {
case Failed(ex) causeOfTermination = Some(ex); sender.stop()
case ChildTerminated stop()
case _ log.error(this + " received unexpected message " + message)
case Failed(ex) if sender ne null causeOfTermination = Some(ex); sender.stop()
case _ log.error(this + " received unexpected message " + message)
})
protected[akka] override def sendSystemMessage(message: SystemMessage): Unit = stopped ifOff {
override def sendSystemMessage(message: SystemMessage): Unit = stopped ifOff {
message match {
case Supervise(child) // TODO register child in some map to keep track of it and enable shutdown after all dead
case _ log.error(this + " received unexpected system message " + message)
case Supervise(child) // TODO register child in some map to keep track of it and enable shutdown after all dead
case ChildTerminated(child) stop()
case _ log.error(this + " received unexpected system message " + message)
}
}
}
private class Guardian extends Actor {
def receive = {
case Terminated(_) context.self.stop()
case Terminated(_) context.self.stop()
case CreateChild(child, name) sender ! (try context.actorOf(child, name) catch { case e: Exception e })
case m deadLetters ! DeadLetter(m, sender, self)
}
}
@ -227,6 +377,8 @@ class LocalActorRefProvider(
case Terminated(_)
eventStream.stopDefaultLoggers()
context.self.stop()
case CreateChild(child, name) sender ! (try context.actorOf(child, name) catch { case e: Exception e })
case m deadLetters ! DeadLetter(m, sender, self)
}
}
@ -253,9 +405,35 @@ class LocalActorRefProvider(
def dispatcher: MessageDispatcher = system.dispatcher
lazy val terminationFuture: DefaultPromise[Unit] = new DefaultPromise[Unit](Timeout.never)(dispatcher)
lazy val rootGuardian: ActorRef = actorOf(system, guardianProps, theOneWhoWalksTheBubblesOfSpaceTime, rootPath, true)
lazy val guardian: ActorRef = actorOf(system, guardianProps, rootGuardian, "app", true)
lazy val systemGuardian: ActorRef = actorOf(system, guardianProps.withCreator(new SystemGuardian), rootGuardian, "sys", true)
lazy val rootGuardian: InternalActorRef = new LocalActorRef(system, guardianProps, theOneWhoWalksTheBubblesOfSpaceTime, rootPath, true) {
override def getParent: InternalActorRef = this
override def getSingleChild(name: String): InternalActorRef = {
name match {
case "temp" tempContainer
case _ super.getSingleChild(name)
}
}
}
lazy val guardian: InternalActorRef = actorOf(system, guardianProps, rootGuardian, "user", true)
lazy val systemGuardian: InternalActorRef = actorOf(system, guardianProps.withCreator(new SystemGuardian), rootGuardian, "system", true)
lazy val tempContainer = new MinimalActorRef {
val children = new ConcurrentHashMap[String, AskActorRef]
def path = tempNode
override def getParent = rootGuardian
override def getChild(name: Iterable[String]): InternalActorRef = {
children.get(name.head) match {
case null Nobody
case some
val t = name.tail
if (t.isEmpty) some
else some.getChild(t)
}
}
}
val deathWatch = createDeathWatch()
@ -266,96 +444,66 @@ class LocalActorRefProvider(
deathWatch.subscribe(rootGuardian, systemGuardian)
}
// FIXME (actor path): should start at the new root guardian, and not use the tail (just to avoid the expected "system" name for now)
def actorFor(path: Iterable[String]): Option[ActorRef] = findInCache(ActorPath.join(path)) orElse findInTree(Some(guardian), path.tail)
def actorFor(ref: InternalActorRef, path: String): InternalActorRef = path match {
case RelativeActorPath(elems)
if (elems.isEmpty) deadLetters
else if (elems.head.isEmpty) actorFor(rootGuardian, elems.tail)
else actorFor(ref, elems)
case LocalActorPath(address, elems) if address == rootPath.address actorFor(rootGuardian, elems)
case _ deadLetters
}
@tailrec
private def findInTree(start: Option[ActorRef], path: Iterable[String]): Option[ActorRef] = {
if (path.isEmpty) start
else {
val child = start match {
case Some(local: LocalActorRef) local.underlying.getChild(path.head)
case _ None
}
findInTree(child, path.tail)
def actorFor(path: ActorPath): InternalActorRef =
if (path.root == rootPath) actorFor(rootGuardian, path.elements)
else deadLetters
def actorFor(ref: InternalActorRef, path: Iterable[String]): InternalActorRef =
if (path.isEmpty) deadLetters
else ref.getChild(path) match {
case Nobody deadLetters
case x x
}
}
private def findInCache(path: String): Option[ActorRef] = actors.get(path) match {
case null None
case actor: ActorRef Some(actor)
case future: Future[_] Some(future.get.asInstanceOf[ActorRef])
}
def actorOf(system: ActorSystemImpl, props: Props, supervisor: InternalActorRef, name: String, systemService: Boolean): InternalActorRef = {
val path = supervisor.path / name
(if (systemService) None else deployer.lookupDeployment(path.toString)) match {
/**
* Returns true if the actor was in the provider's cache and evicted successfully, else false.
*/
private[akka] def evict(path: String): Boolean = actors.remove(path) ne null
// create a local actor
case None | Some(DeploymentConfig.Deploy(_, _, DeploymentConfig.Direct, _, DeploymentConfig.LocalScope))
new LocalActorRef(system, props, supervisor, path, systemService) // create a local actor
private[akka] def actorOf(system: ActorSystemImpl, props: Props, supervisor: ActorRef, name: String, systemService: Boolean): ActorRef =
actorOf(system, props, supervisor, supervisor.path / name, systemService)
private[akka] def actorOf(system: ActorSystemImpl, props: Props, supervisor: ActorRef, path: ActorPath, systemService: Boolean): ActorRef = {
val name = path.name
val newFuture = Promise[ActorRef](system.settings.ActorTimeout)(dispatcher)
actors.putIfAbsent(path.toString, newFuture) match {
case null
val actor: ActorRef = try {
(if (systemService) None else deployer.lookupDeployment(path.toString)) match {
// see if the deployment already exists, if so use it, if not create actor
// create a local actor
case None | Some(DeploymentConfig.Deploy(_, _, DeploymentConfig.Direct, _, DeploymentConfig.LocalScope))
new LocalActorRef(system, props, supervisor, path, systemService) // create a local actor
// create a routed actor ref
case deploy @ Some(DeploymentConfig.Deploy(_, _, routerType, nrOfInstances, DeploymentConfig.LocalScope))
implicit val dispatcher = if (props.dispatcher == Props.defaultDispatcher) system.dispatcher else props.dispatcher
implicit val timeout = system.settings.ActorTimeout
val routerFactory: () Router = DeploymentConfig.routerTypeFor(routerType) match {
case RouterType.Direct () new DirectRouter
case RouterType.Random () new RandomRouter
case RouterType.RoundRobin () new RoundRobinRouter
case RouterType.Broadcast () new BroadcastRouter
case RouterType.ScatterGather () new ScatterGatherFirstCompletedRouter
case RouterType.LeastCPU sys.error("Router LeastCPU not supported yet")
case RouterType.LeastRAM sys.error("Router LeastRAM not supported yet")
case RouterType.LeastMessages sys.error("Router LeastMessages not supported yet")
case RouterType.Custom(implClass) () Routing.createCustomRouter(implClass)
}
val connections: Iterable[ActorRef] = (1 to nrOfInstances.factor) map { i
val routedPath = path.parent / (path.name + ":" + i)
new LocalActorRef(system, props, supervisor, routedPath, systemService)
}
actorOf(system, RoutedProps(routerFactory = routerFactory, connectionManager = new LocalConnectionManager(connections)), supervisor, path.toString)
case unknown throw new Exception("Don't know how to create this actor ref! Why? Got: " + unknown)
}
} catch {
case e: Exception
newFuture completeWithException e // so the other threads gets notified of error
//TODO FIXME should we remove the mapping in "actors" here?
throw e
// create a routed actor ref
case deploy @ Some(DeploymentConfig.Deploy(_, _, routerType, nrOfInstances, DeploymentConfig.LocalScope))
implicit val dispatcher = if (props.dispatcher == Props.defaultDispatcher) system.dispatcher else props.dispatcher
implicit val timeout = system.settings.ActorTimeout
val routerFactory: () Router = DeploymentConfig.routerTypeFor(routerType) match {
case RouterType.Direct () new DirectRouter
case RouterType.Random () new RandomRouter
case RouterType.RoundRobin () new RoundRobinRouter
case RouterType.Broadcast () new BroadcastRouter
case RouterType.ScatterGather () new ScatterGatherFirstCompletedRouter()(
if (props.dispatcher == Props.defaultDispatcher) dispatcher else props.dispatcher, settings.ActorTimeout)
case RouterType.LeastCPU sys.error("Router LeastCPU not supported yet")
case RouterType.LeastRAM sys.error("Router LeastRAM not supported yet")
case RouterType.LeastMessages sys.error("Router LeastMessages not supported yet")
case RouterType.Custom(implClass) () Routing.createCustomRouter(implClass)
}
newFuture completeWithResult actor
actors.replace(path.toString, newFuture, actor)
actor
case actor: ActorRef
actor
case future: Future[_]
future.get.asInstanceOf[ActorRef]
}
val connections: Iterable[ActorRef] = (1 to nrOfInstances.factor) map { i
val routedPath = path.parent / (path.name + ":" + i)
new LocalActorRef(system, props, supervisor, routedPath, systemService)
}
actorOf(system, RoutedProps(routerFactory = routerFactory, connectionManager = new LocalConnectionManager(connections)), supervisor, path.name)
case unknown throw new Exception("Don't know how to create this actor ref! Why? Got: " + unknown)
}
}
/**
* Creates (or fetches) a routed actor reference, configured by the 'props: RoutedProps' configuration.
*/
def actorOf(system: ActorSystem, props: RoutedProps, supervisor: ActorRef, name: String): ActorRef = {
def actorOf(system: ActorSystem, props: RoutedProps, supervisor: InternalActorRef, name: String): InternalActorRef = {
// FIXME: this needs to take supervision into account!
//FIXME clustering should be implemented by cluster actor ref provider
@ -371,10 +519,6 @@ class LocalActorRefProvider(
new RoutedActorRef(system, props, supervisor, name)
}
private[akka] def deserialize(actor: SerializedActorRef): Option[ActorRef] = actorFor(ActorPath.split(actor.path))
private[akka] def serialize(actor: ActorRef): SerializedActorRef = new SerializedActorRef(rootPath.remoteAddress, actor.path.toString)
private[akka] def createDeathWatch(): DeathWatch = new LocalDeathWatch
private[akka] def ask(message: Any, recipient: ActorRef, within: Timeout): Future[Any] = {
@ -383,10 +527,14 @@ class LocalActorRefProvider(
case t if t.duration.length <= 0
new DefaultPromise[Any](0)(dispatcher) //Abort early if nonsensical timeout
case t
val a = new AskActorRef(tempPath, this, deathWatch, t, dispatcher) {
def whenDone() = actors.remove(this)
val path = tempPath()
val name = path.name
val a = new AskActorRef(path, tempContainer, deathWatch, t, dispatcher) {
override def whenDone() {
tempContainer.children.remove(name)
}
}
assert(actors.putIfAbsent(a.path.toString, a) eq null) //If this fails, we're in deep trouble
tempContainer.children.put(name, a)
recipient.tell(message, a)
a.result
}
@ -414,8 +562,13 @@ class LocalDeathWatch extends DeathWatch with ActorClassification {
* Scheduled tasks (Runnable and functions) are executed with the supplied dispatcher.
* Note that dispatcher is by-name parameter, because dispatcher might not be initialized
* when the scheduler is created.
*
* The HashedWheelTimer used by this class MUST throw an IllegalStateException
* if it does not enqueue a task. Once a task is queued, it MUST be executed or
* returned from stop().
*/
class DefaultScheduler(hashedWheelTimer: HashedWheelTimer, log: LoggingAdapter, dispatcher: MessageDispatcher) extends Scheduler with Closeable {
import org.jboss.netty.akka.util.{ Timeout HWTimeout }
def schedule(initialDelay: Duration, delay: Duration, receiver: ActorRef, message: Any): Cancellable =
new DefaultCancellable(hashedWheelTimer.newTimeout(createContinuousTask(delay, receiver, message), initialDelay))
@ -459,7 +612,9 @@ class DefaultScheduler(hashedWheelTimer: HashedWheelTimer, log: LoggingAdapter,
// Check if the receiver is still alive and kicking before sending it a message and reschedule the task
if (!receiver.isTerminated) {
receiver ! message
timeout.getTimer.newTimeout(this, delay)
try timeout.getTimer.newTimeout(this, delay) catch {
case _: IllegalStateException // stop recurring if timer is stopped
}
} else {
log.warning("Could not reschedule message to be sent because receiving actor has been terminated.")
}
@ -471,12 +626,24 @@ class DefaultScheduler(hashedWheelTimer: HashedWheelTimer, log: LoggingAdapter,
new TimerTask {
def run(timeout: org.jboss.netty.akka.util.Timeout) {
dispatcher.dispatchTask(() f)
timeout.getTimer.newTimeout(this, delay)
try timeout.getTimer.newTimeout(this, delay) catch {
case _: IllegalStateException // stop recurring if timer is stopped
}
}
}
}
def close() = hashedWheelTimer.stop()
private def execDirectly(t: HWTimeout): Unit = {
try t.getTask.run(t) catch {
case e: InterruptedException throw e
case e: Exception log.error(e, "exception while executing timer task")
}
}
def close() = {
import scala.collection.JavaConverters._
hashedWheelTimer.stop().asScala foreach execDirectly
}
}
class DefaultCancellable(val timeout: org.jboss.netty.akka.util.Timeout) extends Cancellable {

View file

@ -0,0 +1,58 @@
/**
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
import java.util.regex.Pattern
import akka.util.Helpers
abstract class ActorSelection {
this: ScalaActorSelection
protected def target: ActorRef
protected def path: Array[AnyRef]
def tell(msg: Any) { target ! toMessage(msg, path) }
def tell(msg: Any, sender: ActorRef) { target.tell(toMessage(msg, path), sender) }
// this may want to be fast ...
private def toMessage(msg: Any, path: Array[AnyRef]): Any = {
var acc = msg
var index = path.length - 1
while (index >= 0) {
acc = path(index) match {
case ".." SelectParent(acc)
case s: String SelectChildName(s, acc)
case p: Pattern SelectChildPattern(p, acc)
}
index -= 1
}
acc
}
}
object ActorSelection {
implicit def toScala(sel: ActorSelection): ScalaActorSelection = sel.asInstanceOf[ScalaActorSelection]
/**
* Construct an ActorSelection from the given string representing a path
* relative to the given target. This operation has to create all the
* matching magic, so it is preferable to cache its result if the
* intention is to send messages frequently.
*/
def apply(anchor: ActorRef, path: String): ActorSelection = {
val elems = path.split("/+").dropWhile(_.isEmpty)
val compiled: Array[AnyRef] = elems map (x if (x.contains("?") || x.contains("*")) Helpers.makePattern(x) else x)
new ActorSelection with ScalaActorSelection {
def target = anchor
def path = compiled
}
}
}
trait ScalaActorSelection {
this: ActorSelection
def !(msg: Any)(implicit sender: ActorRef = null) = tell(msg, sender)
}

View file

@ -20,8 +20,7 @@ import com.typesafe.config.ConfigException
import java.lang.reflect.InvocationTargetException
import akka.util.{ Helpers, Duration, ReflectiveAccess }
import java.util.concurrent.atomic.AtomicLong
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors
import java.util.concurrent.{ CountDownLatch, Executors, ConcurrentHashMap }
import scala.annotation.tailrec
import org.jboss.netty.akka.util.internal.ConcurrentIdentityHashMap
import java.io.Closeable
@ -57,7 +56,7 @@ object ActorSystem {
def create(): ActorSystem = apply()
def apply(): ActorSystem = apply("default")
class Settings(cfg: Config) {
class Settings(cfg: Config, val name: String) {
val config: Config = {
val config = cfg.withFallback(ConfigFactory.defaultReference)
@ -195,6 +194,11 @@ abstract class ActorSystem extends ActorRefFactory {
*/
def /(name: String): ActorPath
/**
* Construct a path below the application guardian to be used with [[ActorSystem.actorFor]].
*/
def /(name: Iterable[String]): ActorPath
/**
* Start-up time in milliseconds since the epoch.
*/
@ -285,15 +289,30 @@ abstract class ActorSystem extends ActorRefFactory {
class ActorSystemImpl(val name: String, applicationConfig: Config) extends ActorSystem {
if (!name.matches("""^\w+$"""))
throw new IllegalArgumentException("invalid ActorSystem name '" + name + "', must contain only word characters (i.e. [a-zA-Z_0-9])")
import ActorSystem._
val settings = new Settings(applicationConfig)
val settings = new Settings(applicationConfig, name)
def logConfiguration(): Unit = log.info(settings.toString)
protected def systemImpl = this
private[akka] def systemActorOf(props: Props, address: String): ActorRef = provider.actorOf(this, props, systemGuardian, address, true)
implicit def timeout = settings.ActorTimeout
private[akka] def systemActorOf(props: Props, name: String): ActorRef =
(systemGuardian ? CreateChild(props, name)).get match {
case ref: ActorRef ref
case ex: Exception throw ex
}
def actorOf(props: Props, name: String): ActorRef =
(guardian ? CreateChild(props, name)).get match {
case ref: ActorRef ref
case ex: Exception throw ex
}
import settings._
@ -304,25 +323,6 @@ class ActorSystemImpl(val name: String, applicationConfig: Config) extends Actor
val scheduler = createScheduler()
val provider: ActorRefProvider = {
val providerClass = ReflectiveAccess.getClassFor(ProviderClass) match {
case Left(e) throw e
case Right(b) b
}
val arguments = Seq(
classOf[Settings] -> settings,
classOf[EventStream] -> eventStream,
classOf[Scheduler] -> scheduler)
val types: Array[Class[_]] = arguments map (_._1) toArray
val values: Array[AnyRef] = arguments map (_._2) toArray
ReflectiveAccess.createInstance[ActorRefProvider](providerClass, types, values) match {
case Left(e: InvocationTargetException) throw e.getTargetException
case Left(e) throw e
case Right(p) p
}
}
val deadLetters = new DeadLetterActorRef(eventStream)
val deadLetterMailbox = new Mailbox(null) {
becomeClosed()
@ -335,13 +335,35 @@ class ActorSystemImpl(val name: String, applicationConfig: Config) extends Actor
override def numberOfMessages = 0
}
val provider: ActorRefProvider = {
val providerClass = ReflectiveAccess.getClassFor(ProviderClass) match {
case Left(e) throw e
case Right(b) b
}
val arguments = Seq(
classOf[String] -> name,
classOf[Settings] -> settings,
classOf[EventStream] -> eventStream,
classOf[Scheduler] -> scheduler,
classOf[InternalActorRef] -> deadLetters)
val types: Array[Class[_]] = arguments map (_._1) toArray
val values: Array[AnyRef] = arguments map (_._2) toArray
ReflectiveAccess.createInstance[ActorRefProvider](providerClass, types, values) match {
case Left(e: InvocationTargetException) throw e.getTargetException
case Left(e) throw e
case Right(p) p
}
}
val dispatcherFactory = new Dispatchers(settings, DefaultDispatcherPrerequisites(eventStream, deadLetterMailbox, scheduler))
// TODO why implicit val dispatcher?
implicit val dispatcher = dispatcherFactory.defaultGlobalDispatcher
def terminationFuture: Future[Unit] = provider.terminationFuture
def guardian: ActorRef = provider.guardian
def systemGuardian: ActorRef = provider.systemGuardian
def lookupRoot: InternalActorRef = provider.rootGuardian
def guardian: InternalActorRef = provider.guardian
def systemGuardian: InternalActorRef = provider.systemGuardian
def deathWatch: DeathWatch = provider.deathWatch
def nodename: String = provider.nodename
def clustername: String = provider.clustername
@ -350,13 +372,14 @@ class ActorSystemImpl(val name: String, applicationConfig: Config) extends Actor
override protected def randomName(): String = Helpers.base64(nextName.incrementAndGet())
def /(actorName: String): ActorPath = guardian.path / actorName
def /(path: Iterable[String]): ActorPath = guardian.path / path
private lazy val _start: this.type = {
provider.init(this)
deadLetters.init(dispatcher, provider.rootPath)
// this starts the reaper actor and the user-configured logging subscribers, which are also actors
eventStream.start(this)
eventStream.startDefaultLoggers(this)
registerOnTermination(stopScheduler())
loadExtensions()
if (LogConfigOnStart) logConfiguration()
this
@ -367,16 +390,19 @@ class ActorSystemImpl(val name: String, applicationConfig: Config) extends Actor
def registerOnTermination[T](code: T) { terminationFuture onComplete (_ code) }
def registerOnTermination(code: Runnable) { terminationFuture onComplete (_ code.run) }
// TODO shutdown all that other stuff, whatever that may be
def stop() {
guardian.stop()
try terminationFuture.await(10 seconds) catch {
case _: FutureTimeoutException log.warning("Failed to stop [{}] within 10 seconds", name)
}
// Dispatchers shutdown themselves, but requires the scheduler
terminationFuture onComplete (_ stopScheduler())
}
/**
* Create the scheduler service. This one needs one special behavior: if
* Closeable, it MUST execute all outstanding tasks upon .close() in order
* to properly shutdown all dispatchers.
*
* Furthermore, this timer service MUST throw IllegalStateException if it
* cannot schedule a task. Once scheduled, the task MUST be executed. If
* executed upon close(), the task may execute before its timeout.
*/
protected def createScheduler(): Scheduler = {
val threadFactory = new MonitorableThreadFactory("DefaultScheduler")
val hwt = new HashedWheelTimer(log, threadFactory, settings.SchedulerTickDuration, settings.SchedulerTicksPerWheel)
@ -394,15 +420,14 @@ class ActorSystemImpl(val name: String, applicationConfig: Config) extends Actor
new DefaultScheduler(hwt, log, safeDispatcher)
}
/*
* This is called after the last actor has signaled its termination, i.e.
* after the last dispatcher has had its chance to schedule its shutdown
* action.
*/
protected def stopScheduler(): Unit = scheduler match {
case x: Closeable
// Let dispatchers shutdown first.
// Dispatchers schedule shutdown and may also reschedule, therefore wait 4 times the shutdown delay.
x.scheduleOnce(settings.DispatcherDefaultShutdown * 4) {
x.close()
dispatcher.shutdown()
}
case _
case x: Closeable x.close()
case _
}
private val extensions = new ConcurrentIdentityHashMap[ExtensionId[_], AnyRef]

View file

@ -0,0 +1,45 @@
/**
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
import java.net.URI
import java.net.URISyntaxException
/**
* The address specifies the physical location under which an Actor can be
* reached. Examples are local addresses, identified by the ActorSystems
* name, and remote addresses, identified by protocol, host and port.
*/
abstract class Address {
def protocol: String
def hostPort: String
@transient
override lazy val toString = protocol + "://" + hostPort
}
case class LocalAddress(systemName: String) extends Address {
def protocol = "akka"
def hostPort = systemName
}
object RelativeActorPath {
def unapply(addr: String): Option[Iterable[String]] = {
try {
val uri = new URI(addr)
if (uri.isAbsolute) None
else Some(ActorPath.split(uri.getPath))
}
}
}
object LocalActorPath {
def unapply(addr: String): Option[(LocalAddress, Iterable[String])] = {
try {
val uri = new URI(addr)
if (uri.getScheme != "akka" || uri.getUserInfo != null || uri.getHost == null || uri.getPath == null) None
else Some(LocalAddress(uri.getHost), ActorPath.split(uri.getPath).drop(1))
} catch {
case _: URISyntaxException None
}
}
}

View file

@ -179,7 +179,7 @@ class Deployer(val settings: ActorSystem.Settings, val eventStream: EventStream,
}
if (port == 0) raiseRemoteNodeParsingError()
RemoteAddress(new InetSocketAddress(hostname, port))
RemoteAddress(settings.name, hostname, port)
}
RemoteScope(remoteAddresses)

View file

@ -3,6 +3,8 @@
*/
package akka.actor
import akka.util.ReflectiveAccess
/**
* The basic ActorSystem covers all that is needed for locally running actors,
* using futures and so on. In addition, more features can hook into it and
@ -19,7 +21,7 @@ package akka.actor
*/
/**
* Market interface to signify an Akka Extension
* Marker interface to signify an Akka Extension
*/
trait Extension
@ -64,3 +66,37 @@ trait ExtensionIdProvider {
*/
def lookup(): ExtensionId[_ <: Extension]
}
/**
* This is a one-stop-shop if all you want is an extension which is
* constructed with the ActorSystemImpl as its only constructor argument:
*
* {{{
* object MyExt extends ExtensionKey[Ext]
*
* class Ext(system: ActorSystemImpl) extends MyExt {
* ...
* }
* }}}
*
* Java API:
*
* {{{
* public class MyExt extends Extension {
* static final ExtensionKey<MyExt> key = new ExtensionKey<MyExt>(MyExt.class);
*
* public MyExt(ActorSystemImpl system) {
* ...
* }
* }}}
*/
abstract class ExtensionKey[T <: Extension](implicit m: ClassManifest[T]) extends ExtensionId[T] with ExtensionIdProvider {
def this(clazz: Class[T]) = this()(ClassManifest.fromClass(clazz))
override def lookup(): ExtensionId[T] = this
def createExtension(system: ActorSystemImpl): T =
ReflectiveAccess.createInstance[T](m.erasure, Array[Class[_]](classOf[ActorSystemImpl]), Array[AnyRef](system)) match {
case Left(ex) throw ex
case Right(r) r
}
}

View file

@ -122,12 +122,12 @@ abstract class FaultHandlingStrategy {
def handleSupervisorFailing(supervisor: ActorRef, children: Iterable[ActorRef]): Unit = {
if (children.nonEmpty)
children.foreach(_.asInstanceOf[RefInternals].suspend())
children.foreach(_.asInstanceOf[InternalActorRef].suspend())
}
def handleSupervisorRestarted(cause: Throwable, supervisor: ActorRef, children: Iterable[ActorRef]): Unit = {
if (children.nonEmpty)
children.foreach(_.asInstanceOf[RefInternals].restart(cause))
children.foreach(_.asInstanceOf[InternalActorRef].restart(cause))
}
/**
@ -136,7 +136,7 @@ abstract class FaultHandlingStrategy {
def handleFailure(child: ActorRef, cause: Throwable, stats: ChildRestartStats, children: Iterable[ChildRestartStats]): Boolean = {
val action = if (decider.isDefinedAt(cause)) decider(cause) else Escalate
action match {
case Resume child.asInstanceOf[RefInternals].resume(); true
case Resume child.asInstanceOf[InternalActorRef].resume(); true
case Restart processFailure(true, child, cause, stats, children); true
case Stop processFailure(false, child, cause, stats, children); true
case Escalate false
@ -194,7 +194,7 @@ case class AllForOneStrategy(decider: FaultHandlingStrategy.Decider,
def processFailure(restart: Boolean, child: ActorRef, cause: Throwable, stats: ChildRestartStats, children: Iterable[ChildRestartStats]): Unit = {
if (children.nonEmpty) {
if (restart && children.forall(_.requestRestartPermission(retriesWindow)))
children.foreach(_.child.asInstanceOf[RefInternals].restart(cause))
children.foreach(_.child.asInstanceOf[InternalActorRef].restart(cause))
else
children.foreach(_.child.stop())
}
@ -247,7 +247,7 @@ case class OneForOneStrategy(decider: FaultHandlingStrategy.Decider,
def processFailure(restart: Boolean, child: ActorRef, cause: Throwable, stats: ChildRestartStats, children: Iterable[ChildRestartStats]): Unit = {
if (restart && stats.requestRestartPermission(retriesWindow))
child.asInstanceOf[RefInternals].restart(cause)
child.asInstanceOf[InternalActorRef].restart(cause)
else
child.stop() //TODO optimization to drop child here already?
}

View file

@ -14,6 +14,15 @@ package akka.actor
import akka.util.Duration
/**
* An Akka scheduler service. This one needs one special behavior: if
* Closeable, it MUST execute all outstanding tasks upon .close() in order
* to properly shutdown all dispatchers.
*
* Furthermore, this timer service MUST throw IllegalStateException if it
* cannot schedule a task. Once scheduled, the task MUST be executed. If
* executed upon close(), the task may execute before its timeout.
*/
trait Scheduler {
/**
* Schedules a message to be sent repeatedly with an initial delay and frequency.

View file

@ -61,6 +61,7 @@ case class Suspend() extends SystemMessage // sent to self from ActorCell.suspen
case class Resume() extends SystemMessage // sent to self from ActorCell.resume
case class Terminate() extends SystemMessage // sent to self from ActorCell.stop
case class Supervise(child: ActorRef) extends SystemMessage // sent to supervisor ActorRef from ActorCell.start
case class ChildTerminated(child: ActorRef) extends SystemMessage // sent to supervisor from ActorCell.doTerminate
case class Link(subject: ActorRef) extends SystemMessage // sent to self from ActorCell.startsWatching
case class Unlink(subject: ActorRef) extends SystemMessage // sent to self from ActorCell.stopsWatching
@ -211,7 +212,9 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
}
case RESCHEDULED
if (shutdownScheduleUpdater.compareAndSet(MessageDispatcher.this, RESCHEDULED, SCHEDULED))
scheduler.scheduleOnce(shutdownTimeout, this)
try scheduler.scheduleOnce(shutdownTimeout, this) catch {
case _: IllegalStateException shutdown()
}
else run()
}
}

View file

@ -76,7 +76,7 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
*/
def newPinnedDispatcher(actor: LocalActorRef) = actor match {
case null new PinnedDispatcher(prerequisites, null, "anon", MailboxType, settings.DispatcherDefaultShutdown)
case some new PinnedDispatcher(prerequisites, some.underlying, some.address, MailboxType, settings.DispatcherDefaultShutdown)
case some new PinnedDispatcher(prerequisites, some.underlying, some.path.toString, MailboxType, settings.DispatcherDefaultShutdown)
}
/**
@ -87,7 +87,7 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
*/
def newPinnedDispatcher(actor: LocalActorRef, mailboxType: MailboxType) = actor match {
case null new PinnedDispatcher(prerequisites, null, "anon", mailboxType, settings.DispatcherDefaultShutdown)
case some new PinnedDispatcher(prerequisites, some.underlying, some.address, mailboxType, settings.DispatcherDefaultShutdown)
case some new PinnedDispatcher(prerequisites, some.underlying, some.path.toString, mailboxType, settings.DispatcherDefaultShutdown)
}
/**

View file

@ -956,7 +956,11 @@ class DefaultPromise[T](val timeout: Timeout)(implicit val dispatcher: MessageDi
val runnable = new Runnable {
def run() {
if (!isCompleted) {
if (!isExpired) dispatcher.prerequisites.scheduler.scheduleOnce(Duration(timeLeftNoinline(), NANOSECONDS), this)
if (!isExpired)
try dispatcher.prerequisites.scheduler.scheduleOnce(Duration(timeLeftNoinline(), TimeUnit.NANOSECONDS), this)
catch {
case _: IllegalStateException func(DefaultPromise.this)
}
else func(DefaultPromise.this)
}
}
@ -983,8 +987,17 @@ class DefaultPromise[T](val timeout: Timeout)(implicit val dispatcher: MessageDi
val runnable = new Runnable {
def run() {
if (!isCompleted) {
if (!isExpired) dispatcher.prerequisites.scheduler.scheduleOnce(Duration(timeLeftNoinline(), NANOSECONDS), this)
else promise complete (try { Right(fallback) } catch { case e Left(e) }) // FIXME catching all and continue isn't good for OOME, ticket #1418
val done =
if (!isExpired)
try {
dispatcher.prerequisites.scheduler.scheduleOnce(Duration(timeLeftNoinline(), TimeUnit.NANOSECONDS), this)
true
} catch {
case _: IllegalStateException false
}
else false
if (!done)
promise complete (try { Right(fallback) } catch { case e Left(e) }) // FIXME catching all and continue isn't good for OOME, ticket #1418
}
}
}

View file

@ -28,6 +28,9 @@ object Mailbox {
// secondary status: Scheduled bit may be added to Open/Suspended
final val Scheduled = 4
// mailbox debugging helper using println (see below)
// FIXME RK take this out before release (but please leave in until M2!)
final val debug = false
}
/**
@ -164,6 +167,7 @@ abstract class Mailbox(val actor: ActorCell) extends MessageQueue with SystemMes
var processedMessages = 0
val deadlineNs = if (dispatcher.isThroughputDeadlineTimeDefined) System.nanoTime + dispatcher.throughputDeadlineTime.toNanos else 0
do {
if (debug) println(actor.self + " processing message " + nextMessage)
actor invoke nextMessage
processAllSystemMessages() //After we're done, process all system messages
@ -186,6 +190,7 @@ abstract class Mailbox(val actor: ActorCell) extends MessageQueue with SystemMes
var nextMessage = systemDrain()
try {
while (nextMessage ne null) {
if (debug) println(actor.self + " processing system message " + nextMessage + " with children " + actor.childrenRefs)
actor systemInvoke nextMessage
nextMessage = nextMessage.next
// dont ever execute normal message when system message present!
@ -193,7 +198,7 @@ abstract class Mailbox(val actor: ActorCell) extends MessageQueue with SystemMes
}
} catch {
case e
actor.system.eventStream.publish(Error(e, actor.self.toString, "exception during processing system messages, dropping " + SystemMessage.size(nextMessage) + " messages!"))
actor.system.eventStream.publish(Error(e, actor.self.path.toString, "exception during processing system messages, dropping " + SystemMessage.size(nextMessage) + " messages!"))
throw e
}
}
@ -240,6 +245,7 @@ trait DefaultSystemMessageQueue { self: Mailbox ⇒
@tailrec
final def systemEnqueue(receiver: ActorRef, message: SystemMessage): Unit = {
assert(message.next eq null)
if (Mailbox.debug) println(actor.self + " having enqueued " + message)
val head = systemQueueGet
/*
* this write is safely published by the compareAndSet contained within

View file

@ -5,9 +5,11 @@ package akka.event
import akka.actor.{ ActorRef, Actor, Props, ActorSystemImpl, Terminated, ActorSystem, simpleName }
import akka.util.Subclassification
import java.util.concurrent.atomic.AtomicInteger
object EventStream {
implicit def fromActorSystem(system: ActorSystem) = system.eventStream
val generation = new AtomicInteger
}
class A(x: Int = 0) extends Exception("x=" + x)
@ -23,37 +25,27 @@ class EventStream(debug: Boolean = false) extends LoggingBus with SubchannelClas
def isSubclass(x: Class[_], y: Class[_]) = y isAssignableFrom x
}
@volatile
private var reaper: ActorRef = _
protected def classify(event: AnyRef): Class[_] = event.getClass
protected def publish(event: AnyRef, subscriber: ActorRef) = subscriber ! event
protected def publish(event: AnyRef, subscriber: ActorRef) = {
if (subscriber.isTerminated) unsubscribe(subscriber)
else subscriber ! event
}
override def subscribe(subscriber: ActorRef, channel: Class[_]): Boolean = {
if (debug) publish(Logging.Debug(simpleName(this), "subscribing " + subscriber + " to channel " + channel))
if (reaper ne null) reaper ! subscriber
super.subscribe(subscriber, channel)
}
override def unsubscribe(subscriber: ActorRef, channel: Class[_]): Boolean = {
val ret = super.unsubscribe(subscriber, channel)
if (debug) publish(Logging.Debug(simpleName(this), "unsubscribing " + subscriber + " from channel " + channel))
super.unsubscribe(subscriber, channel)
ret
}
override def unsubscribe(subscriber: ActorRef) {
if (debug) publish(Logging.Debug(simpleName(this), "unsubscribing " + subscriber + " from all channels"))
super.unsubscribe(subscriber)
}
def start(system: ActorSystemImpl) {
reaper = system.systemActorOf(Props(new Actor {
def receive = {
case ref: ActorRef watch(ref)
case Terminated(ref) unsubscribe(ref)
}
}), "MainBusReaper")
subscribers foreach (reaper ! _)
if (debug) publish(Logging.Debug(simpleName(this), "unsubscribing " + subscriber + " from all channels"))
}
}

View file

@ -3,7 +3,7 @@
*/
package akka.event
import akka.actor.{ Actor, ActorPath, ActorRef, MinimalActorRef, LocalActorRef, Props, ActorSystem, ActorSystemImpl, simpleName }
import akka.actor._
import akka.AkkaException
import akka.actor.ActorSystem.Settings
import akka.util.ReflectiveAccess
@ -38,7 +38,6 @@ trait LoggingBus extends ActorEventBus {
private val guard = new ReentrantGuard
private var loggers = Seq.empty[ActorRef]
private var _logLevel: LogLevel = _
private val loggerId = new AtomicInteger
/**
* Query currently set log level. See object Logging for more information.
@ -144,7 +143,7 @@ trait LoggingBus extends ActorEventBus {
}
private def addLogger(system: ActorSystemImpl, clazz: Class[_ <: Actor], level: LogLevel): ActorRef = {
val name = "log" + loggerId.incrementAndGet + "-" + simpleName(clazz)
val name = "log" + Extension(system).id() + "-" + simpleName(clazz)
val actor = system.systemActorOf(Props(clazz), name)
implicit val timeout = Timeout(3 seconds)
val response = try actor ? InitializeLogger(this) get catch {
@ -170,11 +169,11 @@ object LogSource {
}
implicit val fromActor: LogSource[Actor] = new LogSource[Actor] {
def genString(a: Actor) = a.self.toString
def genString(a: Actor) = a.self.path.toString
}
implicit val fromActorRef: LogSource[ActorRef] = new LogSource[ActorRef] {
def genString(a: ActorRef) = a.toString
def genString(a: ActorRef) = a.path.toString
}
// this one unfortunately does not work as implicit, because existential types have some weird behavior
@ -225,6 +224,13 @@ object LogSource {
*/
object Logging {
object Extension extends ExtensionKey[LogExt]
class LogExt(system: ActorSystemImpl) extends Extension {
private val loggerId = new AtomicInteger
def id() = loggerId.incrementAndGet()
}
/**
* Marker trait for annotating LogLevel, which must be Int after erasure.
*/
@ -398,12 +404,6 @@ object Logging {
event.thread.getName,
event.logSource,
event.message))
def instanceName(instance: AnyRef): String = instance match {
case null "NULL"
case a: ActorRef a.address
case _ simpleName(instance)
}
}
/**
@ -414,9 +414,7 @@ object Logging {
* <code>akka.stdout-loglevel</code> in <code>akka.conf</code>.
*/
class StandardOutLogger extends MinimalActorRef with StdOutLogger {
override val name: String = "standard-out-logger"
val path: ActorPath = null // pathless
val address: String = name
val path: ActorPath = new RootActorPath(LocalAddress("all-systems"), "/StandardOutLogger")
override val toString = "StandardOutLogger"
override def !(message: Any)(implicit sender: ActorRef = null): Unit = print(message)
}

View file

@ -6,42 +6,48 @@ package akka.remote
import akka.actor._
import akka.AkkaException
import scala.reflect.BeanProperty
import java.io.{ PrintWriter, PrintStream }
import java.net.InetSocketAddress
import java.net.URI
import java.net.URISyntaxException
import java.net.InetAddress
import java.net.UnknownHostException
object RemoteAddress {
def apply(host: String, port: Int): RemoteAddress = apply(new InetSocketAddress(host, port))
def apply(inetAddress: InetSocketAddress): RemoteAddress = inetAddress match {
case null null
case inet
val host = inet.getAddress match {
case null inet.getHostName //Fall back to given name
case other other.getHostAddress
}
val portNo = inet.getPort
RemoteAddress(portNo, host)
def apply(system: String, host: String, port: Int): RemoteAddress = {
// TODO check whether we should not rather bail out early
val ip = try InetAddress.getByName(host) catch { case _: UnknownHostException null }
new RemoteAddress(system, host, ip, port)
}
def apply(address: String): RemoteAddress = {
val index = address.indexOf(":")
if (index < 1) throw new IllegalArgumentException(
"Remote address must be a string on the format [\"hostname:port\"], was [" + address + "]")
val hostname = address.substring(0, index)
val port = address.substring(index + 1, address.length).toInt
apply(new InetSocketAddress(hostname, port)) // want the fallback in this method
val RE = """(?:(\w+)@)?(\w+):(\d+)""".r
object Int {
def unapply(s: String) = Some(Integer.parseInt(s))
}
def apply(stringRep: String, defaultSystem: String): RemoteAddress = stringRep match {
case RE(sys, host, Int(port)) apply(if (sys != null) sys else defaultSystem, host, port)
case _ throw new IllegalArgumentException(stringRep + " is not a valid remote address [system@host:port]")
}
}
case class RemoteAddress private[remote] (port: Int, hostname: String) {
case class RemoteAddress(system: String, host: String, ip: InetAddress, port: Int) extends Address {
def protocol = "akka"
@transient
override lazy val toString = "" + hostname + ":" + port
lazy val hostPort = system + "@" + host + ":" + port
}
object LocalOnly extends RemoteAddress(0, "local")
object RemoteActorPath {
def unapply(addr: String): Option[(RemoteAddress, Iterable[String])] = {
try {
val uri = new URI(addr)
if (uri.getScheme != "akka" || uri.getUserInfo == null || uri.getHost == null || uri.getPort == -1 || uri.getPath == null) None
else Some(RemoteAddress(uri.getUserInfo, uri.getHost, uri.getPort), ActorPath.split(uri.getPath).drop(1))
} catch {
case _: URISyntaxException None
}
}
}
class RemoteException(message: String) extends AkkaException(message)

View file

@ -177,13 +177,10 @@ abstract private[akka] class AbstractRoutedActorRef(val system: ActorSystem, val
* A RoutedActorRef is an ActorRef that has a set of connected ActorRef and it uses a Router to send a message to
* on (or more) of these actors.
*/
private[akka] class RoutedActorRef(system: ActorSystem, val routedProps: RoutedProps, val supervisor: ActorRef, override val name: String) extends AbstractRoutedActorRef(system, routedProps) {
private[akka] class RoutedActorRef(system: ActorSystem, val routedProps: RoutedProps, val supervisor: InternalActorRef, name: String) extends AbstractRoutedActorRef(system, routedProps) {
val path = supervisor.path / name
// FIXME (actor path): address normally has host and port, what about routed actor ref?
def address = "routed:/" + path.toString
@volatile
private var running: Boolean = true
@ -194,6 +191,7 @@ private[akka] class RoutedActorRef(system: ActorSystem, val routedProps: RoutedP
if (running) {
running = false
router.route(Routing.Broadcast(PoisonPill))(this)
supervisor.sendSystemMessage(akka.dispatch.ChildTerminated(this))
}
}
}

View file

@ -6,12 +6,15 @@ package akka.util
import java.io.{ PrintWriter, StringWriter }
import java.util.Comparator
import scala.annotation.tailrec
import java.util.regex.Pattern
/**
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
object Helpers {
def makePattern(s: String): Pattern = Pattern.compile("^\\Q" + s.replace("?", "\\E.\\Q").replace("*", "\\E.*\\Q") + "\\E$")
def compareIdentityHash(a: AnyRef, b: AnyRef): Int = {
/*
* make sure that there is no overflow or underflow in comparisons, so
@ -26,7 +29,7 @@ object Helpers {
def compare(a: AnyRef, b: AnyRef): Int = compareIdentityHash(a, b)
}
final val base64chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789*?"
final val base64chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+%"
@tailrec
def base64(l: Long, sb: StringBuilder = new StringBuilder("$")): String = {

View file

@ -0,0 +1,268 @@
Actor References, Paths and Addresses
=====================================
This chapter describes how actors are identified and located within a possibly
distributed actor system. It ties into the central idea that actor systems form
intrinsic supervision hierarchies as well as that communication between actors
is transparent with respect to their placement across multiple network nodes.
What is an Actor Reference?
---------------------------
An actor reference is a subtype of :class:`ActorRef`, whose foremost purpose is
to support sending messages to the actor it represents. Each actor has access
to its canonical (local) reference through the :meth:`self` field; this
reference is also included as sender reference by default for all messages sent
to other actors. Conversely, during message processing the actor has access to
a reference representing the sender of the current message through the
:meth:`sender` field.
There are several different types of actor references that are supported
depending on the configuration of the actor system:
- Purely local actor references are used by actor systems which are not
configured to support networking functions. These actor references cannot
ever be sent across a network connection while retaining their functionality.
- Local actor references when remoting is enabled are used by actor systems
which support networking functions for those references which represent
actors within the same JVM. In order to be recognizable also when sent to
other network nodes, these references include protocol and remote addressing
information.
- There is a subtype of local actor references which is used for routers (i.e.
actors mixing in the :class:`Router` trait). Its logical structure is the
same as for the aforementioned local references, but sending a message to
them dispatches to one of their children directly instead.
- Remote actor references represent actors which are reachable using remote
communication, i.e. sending messages to them will serialize the messages
transparently and send them to the other JVM.
- There are several special types of actor references which behave like local
actor references for all practical purposes:
- :class:`AskActorRef` is the special representation of a :meth:`Promise` for
the purpose of being completed by the response from an actor; it is created
by the :meth:`ActorRef.ask` invocation.
- :class:`DeadLetterActorRef` is the default implementation of the dead
letters service, where all messages are re-routed whose targets are shut
down or non-existent.
- And then there are some one-off internal implementations which you should
never really see:
- There is an actor reference which does not represent an actor but acts only
as a pseudo-supervisor for the root guardian, we call it “the one who walks
the bubbles of space-time”.
- The first logging service started before actually firing up actor creation
facilities is a fake actor reference which accepts log events and prints
them directly to standard output; it is :class:`Logging.StandardOutLogger`.
- **(Future Extension)** Cluster actor references represent clustered actor
services which may be replicated, migrated or load-balanced across multiple
cluster nodes. As such they are virtual names which the cluster service
translates into local or remote actor references as appropriate.
What is an Actor Path?
----------------------
Since actors are created in a strictly hierarchical fashion, there exists a
unique sequence of actor names given by recursively following the supervision
links between child and parent down towards the root of the actor system. This
sequence can be seen as enclosing folders in a file system, hence we adopted
the name “path” to refer to it. As in some real file-systems there also are
“symbolic links”, i.e. one actor may be reachable using more than one path,
where all but one involve some translation which decouples part of the path
from the actors actual supervision ancestor line; these specialities are
described in the sub-sections to follow.
Each actor path has an address component, describing the protocol and location
by which the corresponding actor is reachable, followed by the names of the
actors in the hierarchy from the root up. Examples are::
"akka://my-system/app/service-a/worker1" // purely local
"akka://my-system@serv.example.com:5678/app/service-b" // local or remote
"cluster://my-cluster/service-c" // clustered (Future Extension)
Here, ``akka`` is the default remote protocol for the 2.0 release, and others
are pluggable. The interpretation of the host & port part (i.e.
``serv.example.com:5678`` in the example) depends on the transport mechanism
used, but it should abide by the URI structural rules.
Logical Actor Paths
^^^^^^^^^^^^^^^^^^^
The unique path obtained by following the parental supervision links towards
the root guardian is called the logical actor path. This path matches exactly
the creation ancestry of an actor, so it is completely deterministic as soon as
the actor systems remoting configuration (and with it the address component of
the path) is set.
Physical Actor Paths
^^^^^^^^^^^^^^^^^^^^
While the logical actor path describes the functional location within one actor
system, configuration-based transparent remoting means that an actor may be
created on a different network host as its parent, i.e. within a different
actor system. In this case, following the actor path from the root guardian up
entails traversing the network, which is a costly operation. Therefore, each
actor also has a physical path, starting at the root guardian of the actor
system where the actual actor object resides. Using this path as sender
reference when querying other actors will let them reply directly to this
actor, minimizing delays incurred by routing.
One important aspect is that a physical actor path never spans multiple actor
systems or JVMs. This means that the logical path (supervision hierarchy) and
the physical path (actor deployment) of an actor may diverge if one of its
ancestors is remotely supervised.
Virtual Actor Paths **(Future Extension)**
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In order to be able to replicate and migrate actors across a cluster of Akka
nodes, another level of indirection has to be introduced. The cluster component
therefore provides a translation from virtual paths to physical paths which may
change in reaction to node failures, cluster rebalancing, etc.
*This area is still under active development, expect updates in this section
for the 2.1 release.*
How are Actor References obtained?
----------------------------------
There are two general categories to how actor references may be obtained: by
creating actors or by looking them up, where the latter functionality comes in
the two flavours of creating actor references from concrete actor paths and
querying the logical actor hierarchy.
*While local and remote actor references and their paths work in the same way
concerning the facilities mentioned below, the exact semantics of clustered
actor references and their paths—while certainly as similar as possible—may
differ in certain aspects, owing to the virtual nature of those paths. Expect
updates for the 2.1 release.*
Creating Actors
^^^^^^^^^^^^^^^
An actor system is typically started by creating actors above the guardian
actor using the :meth:`ActorSystem.actorOf` method and then using
:meth:`ActorContext.actorOf` from within the created actors to spawn the actor
tree. These methods return a reference to the newly created actors. Each actor
has direct access to references for its parent, itself and its children. These
references may be sent within messages to other actors, enabling those to reply
directly.
Looking up Actors by Concrete Path
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In addition, actor references may be looked up using the
:meth:`ActorSystem.actorFor` method, which returns an (unverified) local,
remote or clustered actor reference. Sending messages to such a reference or
attempting to observe its livelyhood will traverse the actor hierarchy of the
actor system from top to bottom by passing messages from parent to child until
either the target is reached or failure is certain, i.e. a name in the path
does not exist (in practice this process will be optimized using caches, but it
still has added cost compared to using the physical actor path, can for example
to obtained from the sender reference included in replies from that actor). The
messages passed are handled automatically by Akka, so this process is not
visible to client code.
Absolute vs. Relative Paths
```````````````````````````
In addition to :meth:`ActorSystem.actorFor` there is also
:meth:`ActorContext.actorFor`, which is available inside any actor as
``context.actorFor``. This yields an actor reference much like its twin on
:class:`ActorSystem`, but instead of looking up the path starting from the root
of the actor tree it starts out on the current actor. Path elements consisting
of two dots (``".."``) may be used to access the parent actor. You can for
example send a message to a specific sibling::
context.actorFor("../brother") ! msg
Querying the Logical Actor Hierarchy
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Since the actor system forms a file-system like hierarchy, matching on paths is
possible in the same was as supported by Unix shells: you may replace (parts
of) path element names with wildcards (`"*"` and `"?"`) to formulate a
selection which may match zero or more actual actors. Because the result is not
a single actor reference, it has a different type :class:`ActorSelection` and
does not support the full set of operations an :class:`ActorRef` does.
Selections may be formulated using the :meth:`ActorSystem.actorSelection` and
:meth:`ActorContext.actorSelection` methods and do support sending messages::
context.actorSelection("../*") ! msg
will send `msg` to all siblings including the current actor. As for references
obtained using `actorFor`, a traversal of the supervision hierarchy is done in
order to perform the message send. As the exact set of actors which match a
selection may change even while a message is making its way to the recipients,
it is not possible to watch a selection for liveliness changes. In order to do
that, resolve the uncertainty by sending a request and gathering all answers,
extracting the sender references, and then watch all discovered concrete
actors. This scheme of resolving a selection may be improved upon in a future
release.
The Interplay with Remote Deployment
------------------------------------
When an actor creates a child, the actor systems deployer will decide whether
the new actor resides in the same JVM or on another node. In the second case,
creation of the actor will be triggered via a network connection to happen in a
different JVM and consequently within a different actor system. The remote
system will place the new actor below a special path reserved for this purpose
and the supervisor of the new actor will be a remote actor reference
(representing that actor which triggered its creation). In this case,
:meth:`parent` (the supervisor reference) and :meth:`context.path.parent` (the
parent node in the actors path) do not represent the same actor. However,
looking up the childs name within the supervisor will find it on the remote
node, preserving logical structure e.g. when sending to an unresolved actor
reference.
The Interplay with Clustering **(Future Extension)**
----------------------------------------------------
*This section is subject to change!*
When creating a scaled-out actor subtree, a cluster name is created for a
routed actor reference, where sending to this reference will send to one (or
more) of the actual actors created in the cluster. In order for those actors to
be able to query other actors while processing their messages, their sender
reference must be unique for each of the replicas, which means that physical
paths will be used as ``self`` references for these instances. In the case
of replication for achieving fault-tolerance the opposite is required: the
``self`` reference will be a virtual (cluster) path so that in case of
migration or fail-over communication is resumed with the fresh instance.
What is the Address part used for?
----------------------------------
When sending an actor reference across the network, it is represented by its
path. Hence, the path must fully encode all information necessary to send
messages to the underlying actor. This is achieved by encoding protocol, host
and port in the address part of the path string. When an actor system receives
an actor path from a remote node, it checks whether that paths address matches
the address of this actor system, in which case it will be resolved to the
actors local reference. Otherwise, it will be represented by a remote actor
reference.
Special Paths used by Akka
--------------------------
At the root of the path hierarchy resides the root guardian above which all
other actors are found. The next level consists of the following:
- ``"/user"`` is the guardian actor for all user-created top-level actors;
actors created using :meth:`ActorSystem.actorOf` are found at the next level.
- ``"/system"`` is the guardian actor for all system-created top-level actors,
e.g. logging listeners or actors automatically deployed by configuration at
the start of the actor system.
- ``"/null"`` is the dead letter actor, which is where all messages sent to
stopped or non-existing actors are re-routed.
- ``"/temp"`` is the guardian for all short-lived system-created actors, e.g.
those which are used in the implementation of :meth:`ActorRef.ask`.
- ``"/remote"`` is an artificial path below which all actors reside whose
supervisors are remote actor references
- ``"/service"`` is an artificial path below which actors can be presented by
means of configuration, i.e. deployed at system start-up or just-in-time
(triggered by look-up) or “mounting” other actors by path—local or remote—to
give them logical names.

View file

@ -8,5 +8,6 @@ General
configuration
event-handler
slf4j
addressing
supervision
guaranteed-delivery

View file

@ -41,7 +41,7 @@ abstract class DurableMailbox(owner: ActorCell) extends Mailbox(owner) with Defa
def system = owner.system
def ownerPath = owner.self.path
val ownerPathString = ownerPath.path.mkString("/")
val ownerPathString = ownerPath.elements.mkString("/")
val name = "mailbox_" + Name.replaceAllIn(ownerPathString, "_")
}
@ -52,10 +52,7 @@ trait DurableMessageSerialization {
def serialize(durableMessage: Envelope): Array[Byte] = {
def serializeActorRef(ref: ActorRef): ActorRefProtocol = {
val serRef = owner.system.provider.serialize(ref)
ActorRefProtocol.newBuilder.setPath(serRef.path).setHost(serRef.hostname).setPort(serRef.port).build
}
def serializeActorRef(ref: ActorRef): ActorRefProtocol = ActorRefProtocol.newBuilder.setPath(ref.path.toString).build
val message = MessageSerializer.serialize(owner.system, durableMessage.message.asInstanceOf[AnyRef])
val builder = RemoteMessageProtocol.newBuilder
@ -68,10 +65,7 @@ trait DurableMessageSerialization {
def deserialize(bytes: Array[Byte]): Envelope = {
def deserializeActorRef(refProtocol: ActorRefProtocol): ActorRef = {
val serRef = SerializedActorRef(refProtocol.getHost, refProtocol.getPort, refProtocol.getPath)
owner.system.provider.deserialize(serRef).getOrElse(owner.system.deadLetters)
}
def deserializeActorRef(refProtocol: ActorRefProtocol): ActorRef = owner.system.actorFor(refProtocol.getPath)
val durableMessage = RemoteMessageProtocol.parseFrom(bytes)
val message = MessageSerializer.deserialize(owner.system, durableMessage.getMessage)

View file

@ -29,11 +29,7 @@ class BSONSerializableMailbox(system: ActorSystem) extends SerializableBSONObjec
val b = Map.newBuilder[String, Any]
b += "_id" -> msg._id
b += "ownerPath" -> msg.ownerPath
val sender = systemImpl.provider.serialize(msg.sender)
b += "senderPath" -> sender.path
b += "senderHostname" -> sender.hostname
b += "senderPort" -> sender.port
b += "senderPath" -> msg.sender.path
/**
* TODO - Figure out a way for custom serialization of the message instance
@ -75,10 +71,7 @@ class BSONSerializableMailbox(system: ActorSystem) extends SerializableBSONObjec
val msg = MessageSerializer.deserialize(system, msgData)
val ownerPath = doc.as[String]("ownerPath")
val senderPath = doc.as[String]("senderPath")
val senderHostname = doc.as[String]("senderHostname")
val senderPort = doc.as[Int]("senderPort")
val sender = systemImpl.provider.deserialize(SerializedActorRef(senderHostname, senderPort, senderPath)).
getOrElse(system.deadLetters)
val sender = systemImpl.actorOf(senderPath)
MongoDurableMessage(ownerPath, msg, sender)
}

View file

@ -2711,15 +2711,7 @@ public final class RemoteProtocol {
public interface ActorRefProtocolOrBuilder
extends com.google.protobuf.MessageOrBuilder {
// required string host = 1;
boolean hasHost();
String getHost();
// required uint32 port = 2;
boolean hasPort();
int getPort();
// required string path = 3;
// required string path = 1;
boolean hasPath();
String getPath();
}
@ -2752,53 +2744,11 @@ public final class RemoteProtocol {
}
private int bitField0_;
// required string host = 1;
public static final int HOST_FIELD_NUMBER = 1;
private java.lang.Object host_;
public boolean hasHost() {
return ((bitField0_ & 0x00000001) == 0x00000001);
}
public String getHost() {
java.lang.Object ref = host_;
if (ref instanceof String) {
return (String) ref;
} else {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
String s = bs.toStringUtf8();
if (com.google.protobuf.Internal.isValidUtf8(bs)) {
host_ = s;
}
return s;
}
}
private com.google.protobuf.ByteString getHostBytes() {
java.lang.Object ref = host_;
if (ref instanceof String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8((String) ref);
host_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
// required uint32 port = 2;
public static final int PORT_FIELD_NUMBER = 2;
private int port_;
public boolean hasPort() {
return ((bitField0_ & 0x00000002) == 0x00000002);
}
public int getPort() {
return port_;
}
// required string path = 3;
public static final int PATH_FIELD_NUMBER = 3;
// required string path = 1;
public static final int PATH_FIELD_NUMBER = 1;
private java.lang.Object path_;
public boolean hasPath() {
return ((bitField0_ & 0x00000004) == 0x00000004);
return ((bitField0_ & 0x00000001) == 0x00000001);
}
public String getPath() {
java.lang.Object ref = path_;
@ -2827,8 +2777,6 @@ public final class RemoteProtocol {
}
private void initFields() {
host_ = "";
port_ = 0;
path_ = "";
}
private byte memoizedIsInitialized = -1;
@ -2836,14 +2784,6 @@ public final class RemoteProtocol {
byte isInitialized = memoizedIsInitialized;
if (isInitialized != -1) return isInitialized == 1;
if (!hasHost()) {
memoizedIsInitialized = 0;
return false;
}
if (!hasPort()) {
memoizedIsInitialized = 0;
return false;
}
if (!hasPath()) {
memoizedIsInitialized = 0;
return false;
@ -2856,13 +2796,7 @@ public final class RemoteProtocol {
throws java.io.IOException {
getSerializedSize();
if (((bitField0_ & 0x00000001) == 0x00000001)) {
output.writeBytes(1, getHostBytes());
}
if (((bitField0_ & 0x00000002) == 0x00000002)) {
output.writeUInt32(2, port_);
}
if (((bitField0_ & 0x00000004) == 0x00000004)) {
output.writeBytes(3, getPathBytes());
output.writeBytes(1, getPathBytes());
}
getUnknownFields().writeTo(output);
}
@ -2875,15 +2809,7 @@ public final class RemoteProtocol {
size = 0;
if (((bitField0_ & 0x00000001) == 0x00000001)) {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(1, getHostBytes());
}
if (((bitField0_ & 0x00000002) == 0x00000002)) {
size += com.google.protobuf.CodedOutputStream
.computeUInt32Size(2, port_);
}
if (((bitField0_ & 0x00000004) == 0x00000004)) {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(3, getPathBytes());
.computeBytesSize(1, getPathBytes());
}
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
@ -3009,12 +2935,8 @@ public final class RemoteProtocol {
public Builder clear() {
super.clear();
host_ = "";
bitField0_ = (bitField0_ & ~0x00000001);
port_ = 0;
bitField0_ = (bitField0_ & ~0x00000002);
path_ = "";
bitField0_ = (bitField0_ & ~0x00000004);
bitField0_ = (bitField0_ & ~0x00000001);
return this;
}
@ -3056,14 +2978,6 @@ public final class RemoteProtocol {
if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
to_bitField0_ |= 0x00000001;
}
result.host_ = host_;
if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
to_bitField0_ |= 0x00000002;
}
result.port_ = port_;
if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
to_bitField0_ |= 0x00000004;
}
result.path_ = path_;
result.bitField0_ = to_bitField0_;
onBuilt();
@ -3081,12 +2995,6 @@ public final class RemoteProtocol {
public Builder mergeFrom(akka.remote.RemoteProtocol.ActorRefProtocol other) {
if (other == akka.remote.RemoteProtocol.ActorRefProtocol.getDefaultInstance()) return this;
if (other.hasHost()) {
setHost(other.getHost());
}
if (other.hasPort()) {
setPort(other.getPort());
}
if (other.hasPath()) {
setPath(other.getPath());
}
@ -3095,14 +3003,6 @@ public final class RemoteProtocol {
}
public final boolean isInitialized() {
if (!hasHost()) {
return false;
}
if (!hasPort()) {
return false;
}
if (!hasPath()) {
return false;
@ -3135,16 +3035,6 @@ public final class RemoteProtocol {
}
case 10: {
bitField0_ |= 0x00000001;
host_ = input.readBytes();
break;
}
case 16: {
bitField0_ |= 0x00000002;
port_ = input.readUInt32();
break;
}
case 26: {
bitField0_ |= 0x00000004;
path_ = input.readBytes();
break;
}
@ -3154,67 +3044,10 @@ public final class RemoteProtocol {
private int bitField0_;
// required string host = 1;
private java.lang.Object host_ = "";
public boolean hasHost() {
return ((bitField0_ & 0x00000001) == 0x00000001);
}
public String getHost() {
java.lang.Object ref = host_;
if (!(ref instanceof String)) {
String s = ((com.google.protobuf.ByteString) ref).toStringUtf8();
host_ = s;
return s;
} else {
return (String) ref;
}
}
public Builder setHost(String value) {
if (value == null) {
throw new NullPointerException();
}
bitField0_ |= 0x00000001;
host_ = value;
onChanged();
return this;
}
public Builder clearHost() {
bitField0_ = (bitField0_ & ~0x00000001);
host_ = getDefaultInstance().getHost();
onChanged();
return this;
}
void setHost(com.google.protobuf.ByteString value) {
bitField0_ |= 0x00000001;
host_ = value;
onChanged();
}
// required uint32 port = 2;
private int port_ ;
public boolean hasPort() {
return ((bitField0_ & 0x00000002) == 0x00000002);
}
public int getPort() {
return port_;
}
public Builder setPort(int value) {
bitField0_ |= 0x00000002;
port_ = value;
onChanged();
return this;
}
public Builder clearPort() {
bitField0_ = (bitField0_ & ~0x00000002);
port_ = 0;
onChanged();
return this;
}
// required string path = 3;
// required string path = 1;
private java.lang.Object path_ = "";
public boolean hasPath() {
return ((bitField0_ & 0x00000004) == 0x00000004);
return ((bitField0_ & 0x00000001) == 0x00000001);
}
public String getPath() {
java.lang.Object ref = path_;
@ -3230,19 +3063,19 @@ public final class RemoteProtocol {
if (value == null) {
throw new NullPointerException();
}
bitField0_ |= 0x00000004;
bitField0_ |= 0x00000001;
path_ = value;
onChanged();
return this;
}
public Builder clearPath() {
bitField0_ = (bitField0_ & ~0x00000004);
bitField0_ = (bitField0_ & ~0x00000001);
path_ = getDefaultInstance().getPath();
onChanged();
return this;
}
void setPath(com.google.protobuf.ByteString value) {
bitField0_ |= 0x00000004;
bitField0_ |= 0x00000001;
path_ = value;
onChanged();
}
@ -6864,35 +6697,34 @@ public final class RemoteProtocol {
"\0132\026.MetadataEntryProtocol\"l\n\025RemoteContr" +
"olProtocol\022!\n\013commandType\030\001 \002(\0162\014.Comman",
"dType\022\016\n\006cookie\030\002 \001(\t\022 \n\006origin\030\003 \001(\0132\020." +
"AddressProtocol\"<\n\020ActorRefProtocol\022\014\n\004h" +
"ost\030\001 \002(\t\022\014\n\004port\030\002 \002(\r\022\014\n\004path\030\003 \002(\t\";\n" +
"\017MessageProtocol\022\017\n\007message\030\001 \002(\014\022\027\n\017mes" +
"sageManifest\030\002 \001(\014\")\n\014UuidProtocol\022\014\n\004hi" +
"gh\030\001 \002(\004\022\013\n\003low\030\002 \002(\004\"3\n\025MetadataEntryPr" +
"otocol\022\013\n\003key\030\001 \002(\t\022\r\n\005value\030\002 \002(\014\"1\n\017Ad" +
"dressProtocol\022\020\n\010hostname\030\001 \002(\t\022\014\n\004port\030" +
"\002 \002(\r\"7\n\021ExceptionProtocol\022\021\n\tclassname\030" +
"\001 \002(\t\022\017\n\007message\030\002 \002(\t\"\253\001\n!RemoteSystemD",
"aemonMessageProtocol\0223\n\013messageType\030\001 \002(" +
"\0162\036.RemoteSystemDaemonMessageType\022\021\n\tact" +
"orPath\030\002 \001(\t\022\017\n\007payload\030\003 \001(\014\022-\n\026replica" +
"teActorFromUuid\030\004 \001(\0132\r.UuidProtocol\"y\n\035" +
"DurableMailboxMessageProtocol\022$\n\trecipie" +
"nt\030\001 \002(\0132\021.ActorRefProtocol\022!\n\006sender\030\002 " +
"\001(\0132\021.ActorRefProtocol\022\017\n\007message\030\003 \002(\014*" +
"(\n\013CommandType\022\013\n\007CONNECT\020\001\022\014\n\010SHUTDOWN\020" +
"\002*K\n\026ReplicationStorageType\022\r\n\tTRANSIENT" +
"\020\001\022\023\n\017TRANSACTION_LOG\020\002\022\r\n\tDATA_GRID\020\003*>",
"\n\027ReplicationStrategyType\022\021\n\rWRITE_THROU" +
"GH\020\001\022\020\n\014WRITE_BEHIND\020\002*\241\002\n\035RemoteSystemD" +
"aemonMessageType\022\010\n\004STOP\020\001\022\007\n\003USE\020\002\022\013\n\007R" +
"ELEASE\020\003\022\022\n\016MAKE_AVAILABLE\020\004\022\024\n\020MAKE_UNA" +
"VAILABLE\020\005\022\016\n\nDISCONNECT\020\006\022\r\n\tRECONNECT\020" +
"\007\022\n\n\006RESIGN\020\010\022\n\n\006GOSSIP\020\t\022\031\n\025FAIL_OVER_C" +
"ONNECTIONS\020\024\022\026\n\022FUNCTION_FUN0_UNIT\020\025\022\025\n\021" +
"FUNCTION_FUN0_ANY\020\026\022\032\n\026FUNCTION_FUN1_ARG" +
"_UNIT\020\027\022\031\n\025FUNCTION_FUN1_ARG_ANY\020\030B\017\n\013ak" +
"ka.remoteH\001"
"AddressProtocol\" \n\020ActorRefProtocol\022\014\n\004p" +
"ath\030\001 \002(\t\";\n\017MessageProtocol\022\017\n\007message\030" +
"\001 \002(\014\022\027\n\017messageManifest\030\002 \001(\014\")\n\014UuidPr" +
"otocol\022\014\n\004high\030\001 \002(\004\022\013\n\003low\030\002 \002(\004\"3\n\025Met" +
"adataEntryProtocol\022\013\n\003key\030\001 \002(\t\022\r\n\005value" +
"\030\002 \002(\014\"1\n\017AddressProtocol\022\020\n\010hostname\030\001 " +
"\002(\t\022\014\n\004port\030\002 \002(\r\"7\n\021ExceptionProtocol\022\021" +
"\n\tclassname\030\001 \002(\t\022\017\n\007message\030\002 \002(\t\"\253\001\n!R" +
"emoteSystemDaemonMessageProtocol\0223\n\013mess",
"ageType\030\001 \002(\0162\036.RemoteSystemDaemonMessag" +
"eType\022\021\n\tactorPath\030\002 \001(\t\022\017\n\007payload\030\003 \001(" +
"\014\022-\n\026replicateActorFromUuid\030\004 \001(\0132\r.Uuid" +
"Protocol\"y\n\035DurableMailboxMessageProtoco" +
"l\022$\n\trecipient\030\001 \002(\0132\021.ActorRefProtocol\022" +
"!\n\006sender\030\002 \001(\0132\021.ActorRefProtocol\022\017\n\007me" +
"ssage\030\003 \002(\014*(\n\013CommandType\022\013\n\007CONNECT\020\001\022" +
"\014\n\010SHUTDOWN\020\002*K\n\026ReplicationStorageType\022" +
"\r\n\tTRANSIENT\020\001\022\023\n\017TRANSACTION_LOG\020\002\022\r\n\tD" +
"ATA_GRID\020\003*>\n\027ReplicationStrategyType\022\021\n",
"\rWRITE_THROUGH\020\001\022\020\n\014WRITE_BEHIND\020\002*\241\002\n\035R" +
"emoteSystemDaemonMessageType\022\010\n\004STOP\020\001\022\007" +
"\n\003USE\020\002\022\013\n\007RELEASE\020\003\022\022\n\016MAKE_AVAILABLE\020\004" +
"\022\024\n\020MAKE_UNAVAILABLE\020\005\022\016\n\nDISCONNECT\020\006\022\r" +
"\n\tRECONNECT\020\007\022\n\n\006RESIGN\020\010\022\n\n\006GOSSIP\020\t\022\031\n" +
"\025FAIL_OVER_CONNECTIONS\020\024\022\026\n\022FUNCTION_FUN" +
"0_UNIT\020\025\022\025\n\021FUNCTION_FUN0_ANY\020\026\022\032\n\026FUNCT" +
"ION_FUN1_ARG_UNIT\020\027\022\031\n\025FUNCTION_FUN1_ARG" +
"_ANY\020\030B\017\n\013akka.remoteH\001"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@ -6928,7 +6760,7 @@ public final class RemoteProtocol {
internal_static_ActorRefProtocol_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_ActorRefProtocol_descriptor,
new java.lang.String[] { "Host", "Port", "Path", },
new java.lang.String[] { "Path", },
akka.remote.RemoteProtocol.ActorRefProtocol.class,
akka.remote.RemoteProtocol.ActorRefProtocol.Builder.class);
internal_static_MessageProtocol_descriptor =

View file

@ -66,9 +66,7 @@ enum ReplicationStrategyType {
* on the original node.
*/
message ActorRefProtocol {
required string host = 1;
required uint32 port = 2;
required string path = 3;
required string path = 1;
}
/**

View file

@ -119,7 +119,7 @@ class Gossiper(remote: Remote) {
else seeds
}
private val address = system.asInstanceOf[ActorSystemImpl].provider.rootPath.remoteAddress
private val address = remote.remoteAddress
private val nodeFingerprint = address.##
private val random = SecureRandom.getInstance("SHA1PRNG")
@ -253,12 +253,12 @@ class Gossiper(remote: Remote) {
log.error(cause, cause.toString)
case None
val error = new RemoteException("Gossip to [%s] timed out".format(connection.address))
val error = new RemoteException("Gossip to [%s] timed out".format(connection.path))
log.error(error, error.toString)
}
} catch {
case e: Exception
log.error(e, "Could not gossip to [{}] due to: {}", connection.address, e.toString)
log.error(e, "Could not gossip to [{}] due to: {}", connection.path, e.toString)
}
seeds exists (peer == _)

View file

@ -63,9 +63,9 @@ class NetworkEventStream(system: ActorSystemImpl) {
import NetworkEventStream._
// FIXME: check that this supervision is correct, ticket #1408
private[akka] val sender = system.provider.actorOf(system,
Props[Channel].copy(dispatcher = system.dispatcherFactory.newPinnedDispatcher("NetworkEventStream")),
system.systemGuardian, "network-event-sender", systemService = true)
private[akka] val sender =
system.systemActorOf(Props[Channel].copy(dispatcher = system.dispatcherFactory.newPinnedDispatcher("NetworkEventStream")),
"network-event-sender")
/**
* Registers a network event stream listener (asyncronously).

View file

@ -38,7 +38,7 @@ class Remote(val system: ActorSystemImpl, val nodename: String) {
private[remote] val remoteExtension = RemoteExtension(system)
private[remote] val serialization = SerializationExtension(system)
private[remote] val remoteAddress = {
RemoteAddress(remoteExtension.serverSettings.Hostname, remoteExtension.serverSettings.Port)
RemoteAddress(system.name, remoteExtension.serverSettings.Hostname, remoteExtension.serverSettings.Port)
}
val failureDetector = new AccrualFailureDetector(system)
@ -56,7 +56,7 @@ class Remote(val system: ActorSystemImpl, val nodename: String) {
private[remote] lazy val remoteDaemon =
system.provider.actorOf(system,
Props(new RemoteSystemDaemon(this)).withDispatcher(dispatcherFactory.newPinnedDispatcher(remoteDaemonServiceName)),
remoteDaemonSupervisor,
remoteDaemonSupervisor.asInstanceOf[InternalActorRef],
remoteDaemonServiceName,
systemService = true)
@ -71,8 +71,13 @@ class Remote(val system: ActorSystemImpl, val nodename: String) {
lazy val eventStream = new NetworkEventStream(system)
lazy val server: RemoteSupport = {
//new akka.remote.netty.NettyRemoteSupport(system)
ReflectiveAccess.createInstance[RemoteSupport](remoteExtension.RemoteTransport, Array[Class[_]](classOf[ActorSystem]), Array[AnyRef](system)) match {
val arguments = Seq(
classOf[ActorSystem] -> system,
classOf[Remote] -> this)
val types: Array[Class[_]] = arguments map (_._1) toArray
val values: Array[AnyRef] = arguments map (_._2) toArray
ReflectiveAccess.createInstance[RemoteSupport](remoteExtension.RemoteTransport, types, values) match {
case Left(problem)
log.error(problem, "Could not load remote transport layer")
throw problem
@ -86,10 +91,9 @@ class Remote(val system: ActorSystemImpl, val nodename: String) {
}
}
def start(): Unit = {
val serverAddress = server.system.asInstanceOf[ActorSystemImpl].provider.rootPath.remoteAddress //Force init of server
val daemonAddress = remoteDaemon.address //Force init of daemon
log.info("Starting remote server on [{}] and starting remoteDaemon with address [{}]", serverAddress, daemonAddress)
def start() {
val daemonPath = remoteDaemon.path //Force init of daemon
log.info("Starting remote server on [{}] and starting remoteDaemon with path [{}]", remoteAddress, daemonPath)
}
}
@ -145,13 +149,17 @@ class RemoteSystemDaemon(remote: Remote) extends Actor {
case Right(instance) instance.asInstanceOf[() Actor]
}
val actorPath = ActorPath(systemImpl, message.getActorPath)
val parent = system.actorFor(actorPath.parent)
if (parent.isDefined) {
systemImpl.provider.actorOf(systemImpl, Props(creator = actorFactory), parent.get, actorPath.name)
} else {
log.error("Parent actor does not exist, ignoring remote system daemon command [{}]", message)
message.getActorPath match {
case RemoteActorPath(`remoteAddress`, elems) if elems.size > 0
val name = elems.last
systemImpl.provider.actorFor(systemImpl.lookupRoot, elems.dropRight(1)) match {
case x if x eq system.deadLetters
log.error("Parent actor does not exist, ignoring remote system daemon command [{}]", message)
case parent
systemImpl.provider.actorOf(systemImpl, Props(creator = actorFactory), parent, name)
}
case _
log.error("remote path does not match path from message [{}]", message)
}
} else {
@ -249,13 +257,10 @@ class RemoteMessage(input: RemoteMessageProtocol, remote: RemoteSupport, classLo
val provider = remote.system.asInstanceOf[ActorSystemImpl].provider
lazy val sender: ActorRef =
if (input.hasSender)
provider.deserialize(
SerializedActorRef(input.getSender.getHost, input.getSender.getPort, input.getSender.getPath)).getOrElse(throw new IllegalStateException("OHNOES"))
else
remote.system.deadLetters
if (input.hasSender) provider.actorFor(provider.rootGuardian, input.getSender.getPath)
else remote.system.deadLetters
lazy val recipient: ActorRef = remote.system.actorFor(input.getRecipient.getPath).getOrElse(remote.system.deadLetters)
lazy val recipient: ActorRef = remote.system.actorFor(input.getRecipient.getPath)
lazy val payload: Either[Throwable, AnyRef] =
if (input.hasException) Left(parseException())
@ -302,8 +307,7 @@ trait RemoteMarshallingOps {
* Serializes the ActorRef instance into a Protocol Buffers (protobuf) Message.
*/
def toRemoteActorRefProtocol(actor: ActorRef): ActorRefProtocol = {
val rep = system.asInstanceOf[ActorSystemImpl].provider.serialize(actor)
ActorRefProtocol.newBuilder.setHost(rep.hostname).setPort(rep.port).setPath(rep.path).build
ActorRefProtocol.newBuilder.setPath(actor.path.toString).build
}
def createRemoteMessageProtocolBuilder(

View file

@ -30,18 +30,20 @@ import akka.serialization.SerializationExtension
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
class RemoteActorRefProvider(
val systemName: String,
val settings: ActorSystem.Settings,
val eventStream: EventStream,
val scheduler: Scheduler) extends ActorRefProvider {
val scheduler: Scheduler,
_deadLetters: InternalActorRef) extends ActorRefProvider {
val log = Logging(eventStream, "RemoteActorRefProvider")
def deathWatch = local.deathWatch
def rootGuardian = local.rootGuardian
def guardian = local.guardian
def systemGuardian = local.systemGuardian
def nodename = local.nodename
def clustername = local.clustername
def tempName = local.tempName
def nodename = remoteExtension.NodeName
def clustername = remoteExtension.ClusterName
private val actors = new ConcurrentHashMap[String, AnyRef]
@ -57,11 +59,10 @@ class RemoteActorRefProvider(
private lazy val remoteExtension = RemoteExtension(system)
private lazy val serialization = SerializationExtension(system)
lazy val rootPath: ActorPath = {
val remoteAddress = RemoteAddress(remoteExtension.serverSettings.Hostname, remoteExtension.serverSettings.Port)
val remoteAddress = RemoteAddress(system.name, remoteExtension.serverSettings.Hostname, remoteExtension.serverSettings.Port)
new RootActorPath(remoteAddress)
}
private lazy val local = new LocalActorRefProvider(settings, eventStream, scheduler, rootPath,
remoteExtension.NodeName, remoteExtension.ClusterName)
private lazy val local = new LocalActorRefProvider(systemName, settings, eventStream, scheduler, _deadLetters)
private[akka] lazy val remote = new Remote(system, nodename)
private lazy val remoteDaemonConnectionManager = new RemoteConnectionManager(system, remote)
@ -79,22 +80,19 @@ class RemoteActorRefProvider(
def dispatcher = local.dispatcher
def defaultTimeout = settings.ActorTimeout
private[akka] def actorOf(system: ActorSystemImpl, props: Props, supervisor: ActorRef, name: String, systemService: Boolean): ActorRef =
actorOf(system, props, supervisor, supervisor.path / name, systemService)
private[akka] def actorOf(system: ActorSystemImpl, props: Props, supervisor: ActorRef, path: ActorPath, systemService: Boolean): ActorRef =
if (systemService) local.actorOf(system, props, supervisor, path, systemService)
def actorOf(system: ActorSystemImpl, props: Props, supervisor: InternalActorRef, name: String, systemService: Boolean): InternalActorRef =
if (systemService) local.actorOf(system, props, supervisor, name, systemService)
else {
val name = path.name
val path = supervisor.path / name
val newFuture = Promise[ActorRef](system.settings.ActorTimeout)(dispatcher)
actors.putIfAbsent(path.toString, newFuture) match { // we won the race -- create the actor and resolve the future
case null
val actor: ActorRef = try {
val actor: InternalActorRef = try {
deployer.lookupDeploymentFor(path.toString) match {
case Some(DeploymentConfig.Deploy(_, _, routerType, nrOfInstances, DeploymentConfig.RemoteScope(remoteAddresses)))
def isReplicaNode: Boolean = remoteAddresses exists { _ == rootPath.remoteAddress }
def isReplicaNode: Boolean = remoteAddresses exists { _ == remote.remoteAddress }
//system.eventHandler.debug(this, "%s: Deploy Remote Actor with address [%s] connected to [%s]: isReplica(%s)".format(system.defaultAddress, address, remoteAddresses.mkString, isReplicaNode))
@ -145,7 +143,7 @@ class RemoteActorRefProvider(
}
val connections = (Map.empty[RemoteAddress, ActorRef] /: remoteAddresses) { (conns, a)
val remoteAddress = RemoteAddress(a.hostname, a.port)
val remoteAddress = RemoteAddress(system.name, a.host, a.port)
conns + (remoteAddress -> RemoteActorRef(remote.system.provider, remote.server, remoteAddress, path, None))
}
@ -169,8 +167,8 @@ class RemoteActorRefProvider(
newFuture completeWithResult actor
actors.replace(path.toString, newFuture, actor)
actor
case actor: ActorRef actor
case future: Future[_] future.get.asInstanceOf[ActorRef]
case actor: InternalActorRef actor
case future: Future[_] future.get.asInstanceOf[InternalActorRef]
}
}
@ -178,16 +176,14 @@ class RemoteActorRefProvider(
* Copied from LocalActorRefProvider...
*/
// FIXME: implement supervision, ticket #1408
def actorOf(system: ActorSystem, props: RoutedProps, supervisor: ActorRef, name: String): ActorRef = {
def actorOf(system: ActorSystem, props: RoutedProps, supervisor: InternalActorRef, name: String): InternalActorRef = {
if (props.connectionManager.isEmpty) throw new ConfigurationException("RoutedProps used for creating actor [" + name + "] has zero connections configured; can't create a router")
new RoutedActorRef(system, props, supervisor, name)
}
def actorFor(path: Iterable[String]): Option[ActorRef] = actors.get(ActorPath.join(path)) match {
case null local.actorFor(path)
case actor: ActorRef Some(actor)
case future: Future[_] Some(future.get.asInstanceOf[ActorRef])
}
def actorFor(path: ActorPath): InternalActorRef = local.actorFor(path)
def actorFor(ref: InternalActorRef, path: String): InternalActorRef = local.actorFor(ref, path)
def actorFor(ref: InternalActorRef, path: Iterable[String]): InternalActorRef = local.actorFor(ref, path)
// TODO remove me
val optimizeLocal = new AtomicBoolean(true)
@ -196,22 +192,7 @@ class RemoteActorRefProvider(
/**
* Returns true if the actor was in the provider's cache and evicted successfully, else false.
*/
private[akka] def evict(path: String): Boolean = actors.remove(path) ne null
private[akka] def serialize(actor: ActorRef): SerializedActorRef = actor match {
case r: RemoteActorRef new SerializedActorRef(r.remoteAddress, actor.path.toString)
case other local.serialize(actor)
}
private[akka] def deserialize(actor: SerializedActorRef): Option[ActorRef] = {
val remoteAddress = RemoteAddress(actor.hostname, actor.port)
if (optimizeLocalScoped_? && remoteAddress == rootPath.remoteAddress) {
local.actorFor(ActorPath.split(actor.path))
} else {
log.debug("{}: Creating RemoteActorRef with address [{}] connected to [{}]", rootPath.remoteAddress, actor.path, remoteAddress)
Some(RemoteActorRef(remote.system.provider, remote.server, remoteAddress, rootPath / ActorPath.split(actor.path), None)) //Should it be None here
}
}
private[akka] def evict(path: ActorPath): Boolean = actors.remove(path) ne null
/**
* Using (checking out) actor on a specific node.
@ -231,7 +212,7 @@ class RemoteActorRefProvider(
.setPayload(ByteString.copyFrom(actorFactoryBytes))
.build()
val connectionFactory = () deserialize(new SerializedActorRef(remoteAddress, remote.remoteDaemon.path.toString)).get
val connectionFactory = () actorFor(RootActorPath(remoteAddress) / remote.remoteDaemon.path.elements)
// try to get the connection for the remote address, if not already there then create it
val connection = remoteDaemonConnectionManager.putIfAbsent(remoteAddress, connectionFactory)
@ -252,13 +233,13 @@ class RemoteActorRefProvider(
throw cause
case None
val error = new RemoteException("Remote system command to [%s] timed out".format(connection.address))
val error = new RemoteException("Remote system command to [%s] timed out".format(connection.path))
log.error(error, error.toString)
throw error
}
} catch {
case e: Exception
log.error(e, "Could not send remote system command to [{}] due to: {}", connection.address, e.toString)
log.error(e, "Could not send remote system command to [{}] due to: {}", connection.path, e.toString)
throw e
}
} else {
@ -285,18 +266,18 @@ private[akka] case class RemoteActorRef private[akka] (
remoteAddress: RemoteAddress,
path: ActorPath,
loader: Option[ClassLoader])
extends ActorRef with ScalaActorRef with RefInternals {
extends InternalActorRef {
// FIXME RK
def getParent = Nobody
def getChild(name: Iterable[String]) = Nobody
@volatile
private var running: Boolean = true
def name = path.name
def address = remoteAddress + path.toString
def isTerminated: Boolean = !running
protected[akka] def sendSystemMessage(message: SystemMessage): Unit = throw new UnsupportedOperationException("Not supported for RemoteActorRef")
def sendSystemMessage(message: SystemMessage): Unit = throw new UnsupportedOperationException("Not supported for RemoteActorRef")
override def !(message: Any)(implicit sender: ActorRef = null): Unit = remote.send(message, Option(sender), remoteAddress, this, loader)
@ -316,7 +297,7 @@ private[akka] case class RemoteActorRef private[akka] (
}
@throws(classOf[java.io.ObjectStreamException])
private def writeReplace(): AnyRef = provider.serialize(this)
private def writeReplace(): AnyRef = SerializedActorRef(path.toString)
protected[akka] def restart(cause: Throwable): Unit = ()
def restart(cause: Throwable): Unit = ()
}

View file

@ -14,10 +14,10 @@ import scala.collection.JavaConverters._
object RemoteExtension extends ExtensionId[RemoteExtensionSettings] with ExtensionIdProvider {
def lookup() = this
def createExtension(system: ActorSystemImpl) = new RemoteExtensionSettings(system.settings.config)
def createExtension(system: ActorSystemImpl) = new RemoteExtensionSettings(system.settings.config, system.name)
}
class RemoteExtensionSettings(val config: Config) extends Extension {
class RemoteExtensionSettings(val config: Config, val systemName: String) extends Extension {
import config._
@ -31,7 +31,7 @@ class RemoteExtensionSettings(val config: Config) extends Extension {
// TODO cluster config will go into akka-cluster-reference.conf when we enable that module
val ClusterName = getString("akka.cluster.name")
val SeedNodes = Set.empty[RemoteAddress] ++ getStringList("akka.cluster.seed-nodes").asScala.toSeq.map(RemoteAddress(_))
val SeedNodes = Set.empty[RemoteAddress] ++ getStringList("akka.cluster.seed-nodes").asScala.toSeq.map(RemoteAddress(_, systemName))
val NodeName: String = config.getString("akka.cluster.nodename") match {
case "" throw new ConfigurationException("akka.cluster.nodename configuration property must be defined")

View file

@ -139,7 +139,7 @@ class ActiveRemoteClient private[akka] (
def currentChannel = connection.getChannel
private val senderRemoteAddress = remoteSupport.system.asInstanceOf[ActorSystemImpl].provider.rootPath.remoteAddress
private val senderRemoteAddress = remoteSupport.remote.remoteAddress
/**
* Connect to remote server.
@ -150,7 +150,7 @@ class ActiveRemoteClient private[akka] (
val handshake = RemoteControlProtocol.newBuilder.setCommandType(CommandType.CONNECT)
if (SecureCookie.nonEmpty) handshake.setCookie(SecureCookie.get)
handshake.setOrigin(RemoteProtocol.AddressProtocol.newBuilder
.setHostname(senderRemoteAddress.hostname)
.setHostname(senderRemoteAddress.host)
.setPort(senderRemoteAddress.port)
.build)
connection.getChannel.write(remoteSupport.createControlEnvelope(handshake.build))
@ -164,7 +164,7 @@ class ActiveRemoteClient private[akka] (
def attemptReconnect(): Boolean = {
log.debug("Remote client reconnecting to [{}]", remoteAddress)
val connection = bootstrap.connect(new InetSocketAddress(remoteAddress.hostname, remoteAddress.port))
val connection = bootstrap.connect(new InetSocketAddress(remoteAddress.ip, remoteAddress.port))
openChannels.add(connection.awaitUninterruptibly.getChannel) // Wait until the connection attempt succeeds or fails.
if (!connection.isSuccess) {
@ -186,7 +186,7 @@ class ActiveRemoteClient private[akka] (
log.debug("Starting remote client connection to [{}]", remoteAddress)
connection = bootstrap.connect(new InetSocketAddress(remoteAddress.hostname, remoteAddress.port))
connection = bootstrap.connect(new InetSocketAddress(remoteAddress.ip, remoteAddress.port))
val channel = connection.awaitUninterruptibly.getChannel
openChannels.add(channel)
@ -349,7 +349,7 @@ class ActiveRemoteClientHandler(
/**
* Provides the implementation of the Netty remote support
*/
class NettyRemoteSupport(_system: ActorSystem) extends RemoteSupport(_system) with RemoteMarshallingOps {
class NettyRemoteSupport(_system: ActorSystem, val remote: Remote) extends RemoteSupport(_system) with RemoteMarshallingOps {
val log = Logging(system, "NettyRemoteSupport")
val serverSettings = RemoteExtension(system).serverSettings
@ -456,7 +456,7 @@ class NettyRemoteSupport(_system: ActorSystem) extends RemoteSupport(_system) wi
def name = currentServer.get match {
case Some(server) server.name
case None "Non-running NettyRemoteServer@" + system.asInstanceOf[ActorSystemImpl].provider.rootPath.remoteAddress
case None "Non-running NettyRemoteServer@" + remote.remoteAddress
}
private val _isRunning = new Switch(false)
@ -491,7 +491,7 @@ class NettyRemoteServer(val remoteSupport: NettyRemoteSupport, val loader: Optio
val log = Logging(remoteSupport.system, "NettyRemoteServer")
import remoteSupport.serverSettings._
val address = remoteSupport.system.asInstanceOf[ActorSystemImpl].provider.rootPath.remoteAddress
val address = remoteSupport.remote.remoteAddress
val name = "NettyRemoteServer@" + address
@ -510,7 +510,7 @@ class NettyRemoteServer(val remoteSupport: NettyRemoteSupport, val loader: Optio
bootstrap.setOption("child.reuseAddress", true)
bootstrap.setOption("child.connectTimeoutMillis", ConnectionTimeout.toMillis)
openChannels.add(bootstrap.bind(new InetSocketAddress(address.hostname, address.port)))
openChannels.add(bootstrap.bind(new InetSocketAddress(address.ip, address.port)))
remoteSupport.notifyListeners(RemoteServerStarted(remoteSupport))
def shutdown() {
@ -518,7 +518,7 @@ class NettyRemoteServer(val remoteSupport: NettyRemoteSupport, val loader: Optio
val shutdownSignal = {
val b = RemoteControlProtocol.newBuilder.setCommandType(CommandType.SHUTDOWN)
b.setOrigin(RemoteProtocol.AddressProtocol.newBuilder
.setHostname(address.hostname)
.setHostname(address.host)
.setPort(address.port)
.build)
if (SecureCookie.nonEmpty)
@ -647,7 +647,8 @@ class RemoteServerHandler(
instruction.getCommandType match {
case CommandType.CONNECT if UsePassiveConnections
val origin = instruction.getOrigin
val inbound = RemoteAddress(origin.getHostname, origin.getPort)
// FIXME RK need to include system-name in remote protocol
val inbound = RemoteAddress("BORKED", origin.getHostname, origin.getPort)
val client = new PassiveRemoteClient(event.getChannel, remoteSupport, inbound)
remoteSupport.bindClient(inbound, client)
case CommandType.SHUTDOWN //FIXME Dispose passive connection here, ticket #1410
@ -666,7 +667,7 @@ class RemoteServerHandler(
private def getClientAddress(c: Channel): Option[RemoteAddress] =
c.getRemoteAddress match {
case inet: InetSocketAddress Some(RemoteAddress(inet))
case inet: InetSocketAddress Some(RemoteAddress("BORKED", inet.getHostName, inet.getPort)) // FIXME RK Broken!
case _ None
}
}

View file

@ -3,9 +3,9 @@ akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.router = "direct"
/app/service-hello.nr-of-instances = 1
/app/service-hello.remote.nodes = ["localhost:9991"]
/user/service-hello.router = "direct"
/user/service-hello.nr-of-instances = 1
/user/service-hello.remote.nodes = ["localhost:9991"]
}
}
}

View file

@ -1,11 +1,11 @@
akka {
qakka {
loglevel = "WARNING"
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.router = "direct"
/app/service-hello.nr-of-instances = 1
/app/service-hello.remote.nodes = ["localhost:9991"]
/user/service-hello.router = "direct"
/user/service-hello.nr-of-instances = 1
/user/service-hello.remote.nodes = ["localhost:9991"]
}
}
}

View file

@ -48,7 +48,7 @@ class DirectRoutedRemoteActorMultiJvmNode2 extends AkkaRemoteSpec {
barrier("start")
val actor = system.actorOf[SomeActor]("service-hello")
actor.isInstanceOf[RoutedActorRef] must be(true)
//actor.isInstanceOf[RoutedActorRef] must be(true)
val result = (actor ? "identify").get
result must equal("node1")

View file

@ -3,7 +3,7 @@ akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.remote.nodes = ["localhost:9991"]
/user/service-hello.remote.nodes = ["localhost:9991"]
}
}
}

View file

@ -3,7 +3,7 @@ akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.remote.nodes = ["localhost:9991"]
/user/service-hello.remote.nodes = ["localhost:9991"]
}
}
}

View file

@ -3,9 +3,9 @@ akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.router = "random"
/app/service-hello.nr-of-instances = 3
/app/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
/user/service-hello.router = "random"
/user/service-hello.nr-of-instances = 3
/user/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
}
}
}

View file

@ -2,8 +2,8 @@ akka {
loglevel = "WARNING"
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment./app/service-hello.router = "random"
deployment./app/service-hello.nr-of-instances = 3
deployment./app/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
deployment./user/service-hello.router = "random"
deployment./user/service-hello.nr-of-instances = 3
deployment./user/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
}
}

View file

@ -3,9 +3,9 @@ akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.router = "random"
/app/service-hello.nr-of-instances = 3
/app/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
/user/service-hello.router = "random"
/user/service-hello.nr-of-instances = 3
/user/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
}
}
}

View file

@ -3,9 +3,9 @@ akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.router = "random"
/app/service-hello.nr-of-instances = 3
/app/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
/user/service-hello.router = "random"
/user/service-hello.nr-of-instances = 3
/user/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
}
}
}

View file

@ -3,9 +3,9 @@ akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.router = "round-robin"
/app/service-hello.nr-of-instances = 3
/app/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
/user/service-hello.router = "round-robin"
/user/service-hello.nr-of-instances = 3
/user/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
}
}
}

View file

@ -3,9 +3,9 @@ akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.router = "round-robin"
/app/service-hello.nr-of-instances = 3
/app/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
/user/service-hello.router = "round-robin"
/user/service-hello.nr-of-instances = 3
/user/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
}
}
}

View file

@ -3,9 +3,9 @@ akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.router = "round-robin"
/app/service-hello.nr-of-instances = 3
/app/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
/user/service-hello.router = "round-robin"
/user/service-hello.nr-of-instances = 3
/user/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
}
}
}

View file

@ -3,9 +3,9 @@ akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.router = "round-robin"
/app/service-hello.nr-of-instances = 3
/app/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
/user/service-hello.router = "round-robin"
/user/service-hello.nr-of-instances = 3
/user/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
}
}
}

View file

@ -3,9 +3,9 @@ akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.router = "scatter-gather"
/app/service-hello.nr-of-instances = 3
/app/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
/user/service-hello.router = "scatter-gather"
/user/service-hello.nr-of-instances = 3
/user/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
}
}
}

View file

@ -3,9 +3,9 @@ akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.router = "scatter-gather"
/app/service-hello.nr-of-instances = 3
/app/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
/user/service-hello.router = "scatter-gather"
/user/service-hello.nr-of-instances = 3
/user/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
}
}
}

View file

@ -3,9 +3,9 @@ akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.router = "scatter-gather"
/app/service-hello.nr-of-instances = 3
/app/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
/user/service-hello.router = "scatter-gather"
/user/service-hello.nr-of-instances = 3
/user/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
}
}
}

View file

@ -3,9 +3,9 @@ akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/app/service-hello.router = "scatter-gather"
/app/service-hello.nr-of-instances = 3
/app/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
/user/service-hello.router = "scatter-gather"
/user/service-hello.nr-of-instances = 3
/user/service-hello.remote.nodes = ["localhost:9991","localhost:9992","localhost:9993"]
}
}
}

View file

@ -6,7 +6,7 @@ import akka.testkit.AkkaSpec
class AccrualFailureDetectorSpec extends AkkaSpec {
"An AccrualFailureDetector" must {
val conn = RemoteAddress(new InetSocketAddress("localhost", 2552))
val conn = RemoteAddress("tester", "localhost", 2552)
"mark node as available after a series of successful heartbeats" in {
val fd = new AccrualFailureDetector

View file

@ -78,7 +78,7 @@ class Hakker(name: String, left: ActorRef, right: ActorRef) extends Actor {
//back to think about how he should obtain his chopsticks :-)
def waiting_for(chopstickToWaitFor: ActorRef, otherChopstick: ActorRef): Receive = {
case Taken(`chopstickToWaitFor`)
println("%s has picked up %s and %s and starts to eat".format(name, left.name, right.name))
println("%s has picked up %s and %s and starts to eat".format(name, left.path.name, right.path.name))
become(eating)
system.scheduler.scheduleOnce(5 seconds, self, Think)

View file

@ -128,7 +128,7 @@ class FSMHakker(name: String, left: ActorRef, right: ActorRef) extends Actor wit
}
private def startEating(left: ActorRef, right: ActorRef): State = {
println("%s has picked up %s and %s and starts to eat".format(name, left.name, right.name))
println("%s has picked up %s and %s and starts to eat".format(name, left.path.name, right.path.name))
goto(Eating) using TakenChopsticks(Some(left), Some(right)) forMax (5 seconds)
}

View file

@ -224,6 +224,7 @@ class CallingThreadDispatcher(
}
if (handle ne null) {
try {
if (Mailbox.debug) println(mbox.actor.self + " processing message " + handle)
mbox.actor.invoke(handle)
true
} catch {

View file

@ -25,7 +25,7 @@ class TestActorRef[T <: Actor](
_system: ActorSystemImpl,
_prerequisites: DispatcherPrerequisites,
_props: Props,
_supervisor: ActorRef,
_supervisor: InternalActorRef,
name: String)
extends LocalActorRef(_system, _props.withDispatcher(new CallingThreadDispatcher(_prerequisites)), _supervisor, _supervisor.path / name, false) {
/**
@ -60,9 +60,8 @@ class TestActorRef[T <: Actor](
*/
def stopsWatching(subject: ActorRef): ActorRef = underlying.stopsWatching(subject)
override def toString = "TestActor[" + address + "]"
override def toString = "TestActor[" + path + "]"
override def equals(other: Any) = other.isInstanceOf[TestActorRef[_]] && other.asInstanceOf[TestActorRef[_]].address == address
}
object TestActorRef {
@ -83,7 +82,7 @@ object TestActorRef {
apply[T](props, system.asInstanceOf[ActorSystemImpl].guardian, name)
def apply[T <: Actor](props: Props, supervisor: ActorRef, name: String)(implicit system: ActorSystem): TestActorRef[T] =
new TestActorRef(system.asInstanceOf[ActorSystemImpl], system.dispatcherFactory.prerequisites, props, supervisor, name)
new TestActorRef(system.asInstanceOf[ActorSystemImpl], system.dispatcherFactory.prerequisites, props, supervisor.asInstanceOf[InternalActorRef], name)
def apply[T <: Actor](implicit m: Manifest[T], system: ActorSystem): TestActorRef[T] = apply[T](randomName)

View file

@ -151,6 +151,15 @@ object EventFilter {
if (message ne null) Left(message) else Option(pattern) map (new Regex(_)) toRight start,
message ne null)(occurrences)
/**
* Create a filter for Error events which do not have a cause set (i.e. use
* implicitly supplied Logging.Error.NoCause). See apply() for more details.
*/
def error(message: String = null, source: String = null, start: String = "", pattern: String = null, occurrences: Int = Int.MaxValue): EventFilter =
ErrorFilter(Logging.Error.NoCause.getClass, Option(source),
if (message ne null) Left(message) else Option(pattern) map (new Regex(_)) toRight start,
message ne null)(occurrences)
/**
* Create a filter for Warning events. Give up to one of <code>start</code> and <code>pattern</code>:
*
@ -447,12 +456,12 @@ class TestEventListener extends Logging.DefaultLogger {
case event: LogEvent if (!filter(event)) print(event)
case DeadLetter(msg: SystemMessage, _, rcp)
if (!msg.isInstanceOf[Terminate]) {
val event = Warning(rcp.toString, "received dead system message: " + msg)
val event = Warning(rcp.path.toString, "received dead system message: " + msg)
if (!filter(event)) print(event)
}
case DeadLetter(msg, snd, rcp)
if (!msg.isInstanceOf[Terminated]) {
val event = Warning(rcp.toString, "received dead letter from " + snd + ": " + msg)
val event = Warning(rcp.path.toString, "received dead letter from " + snd + ": " + msg)
if (!filter(event)) print(event)
}
}

View file

@ -40,7 +40,7 @@ class TestFSMRef[S, D, T <: Actor](
system: ActorSystemImpl,
_prerequisites: DispatcherPrerequisites,
props: Props,
supervisor: ActorRef,
supervisor: InternalActorRef,
name: String)(implicit ev: T <:< FSM[S, D])
extends TestActorRef(system, _prerequisites, props, supervisor, name) {
@ -89,11 +89,11 @@ object TestFSMRef {
def apply[S, D, T <: Actor](factory: T)(implicit ev: T <:< FSM[S, D], system: ActorSystem): TestFSMRef[S, D, T] = {
val impl = system.asInstanceOf[ActorSystemImpl]
new TestFSMRef(impl, system.dispatcherFactory.prerequisites, Props(creator = () factory), impl.guardian, TestActorRef.randomName)
new TestFSMRef(impl, system.dispatcherFactory.prerequisites, Props(creator = () factory), impl.guardian.asInstanceOf[InternalActorRef], TestActorRef.randomName)
}
def apply[S, D, T <: Actor](factory: T, name: String)(implicit ev: T <:< FSM[S, D], system: ActorSystem): TestFSMRef[S, D, T] = {
val impl = system.asInstanceOf[ActorSystemImpl]
new TestFSMRef(impl, system.dispatcherFactory.prerequisites, Props(creator = () factory), impl.guardian, name)
new TestFSMRef(impl, system.dispatcherFactory.prerequisites, Props(creator = () factory), impl.guardian.asInstanceOf[InternalActorRef], name)
}
}

View file

@ -39,6 +39,11 @@ class TestActor(queue: BlockingDeque[TestActor.Message]) extends Actor {
val observe = ignore map (ignoreFunc if (ignoreFunc isDefinedAt x) !ignoreFunc(x) else true) getOrElse true
if (observe) queue.offerLast(RealMessage(x, sender))
}
override def postStop() = {
import scala.collection.JavaConverters._
queue.asScala foreach { m system.deadLetters ! DeadLetter(m.msg, m.sender, self) }
}
}
/**

View file

@ -13,6 +13,10 @@ import akka.util.duration._
import akka.dispatch.FutureTimeoutException
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import akka.actor.PoisonPill
import java.util.concurrent.LinkedBlockingQueue
import akka.actor.CreateChild
import akka.actor.DeadLetter
object TimingTest extends Tag("timing")
@ -35,11 +39,24 @@ object AkkaSpec {
ConfigFactory.parseMap(map.asJava)
}
def getCallerName: String = {
val s = Thread.currentThread.getStackTrace map (_.getClassName) drop 1 dropWhile (_ matches ".*AkkaSpec.?$")
s.head.replaceFirst(""".*\.""", "").replaceAll("[^a-zA-Z_0-9]", "_")
}
}
abstract class AkkaSpec(_system: ActorSystem = ActorSystem(getClass.getSimpleName, AkkaSpec.testConf))
abstract class AkkaSpec(_system: ActorSystem)
extends TestKit(_system) with WordSpec with MustMatchers with BeforeAndAfterAll {
def this(config: Config) = this(ActorSystem(AkkaSpec.getCallerName, config.withFallback(AkkaSpec.testConf)))
def this(s: String) = this(ConfigFactory.parseString(s))
def this(configMap: Map[String, _]) = this(AkkaSpec.mapToConfig(configMap))
def this() = this(ActorSystem(AkkaSpec.getCallerName, AkkaSpec.testConf))
val log: LoggingAdapter = Logging(system, this.getClass)
final override def beforeAll {
@ -58,14 +75,6 @@ abstract class AkkaSpec(_system: ActorSystem = ActorSystem(getClass.getSimpleNam
protected def atTermination() {}
def this(config: Config) = this(ActorSystem(getClass.getSimpleName, ConfigFactory.load(config.withFallback(AkkaSpec.testConf))))
def this(s: String) = this(ConfigFactory.parseString(s))
def this(configMap: Map[String, _]) = {
this(AkkaSpec.mapToConfig(configMap))
}
def actorOf(props: Props): ActorRef = system.actorOf(props)
def actorOf[T <: Actor](clazz: Class[T]): ActorRef = actorOf(Props(clazz))
@ -81,13 +90,16 @@ abstract class AkkaSpec(_system: ActorSystem = ActorSystem(getClass.getSimpleNam
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class AkkaSpecSpec extends WordSpec with MustMatchers {
"An AkkaSpec" must {
"terminate all actors" in {
// verbose config just for demonstration purposes, please leave in in case of debugging
import scala.collection.JavaConverters._
val conf = Map(
"akka.actor.debug.lifecycle" -> true, "akka.actor.debug.event-stream" -> true,
"akka.loglevel" -> "DEBUG", "akka.stdout-loglevel" -> "DEBUG")
val system = ActorSystem("test", ConfigFactory.parseMap(conf.asJava).withFallback(AkkaSpec.testConf))
val system = ActorSystem("AkkaSpec1", ConfigFactory.parseMap(conf.asJava).withFallback(AkkaSpec.testConf))
val spec = new AkkaSpec(system) {
val ref = Seq(testActor, system.actorOf(Props.empty, "name"))
}
@ -95,6 +107,56 @@ class AkkaSpecSpec extends WordSpec with MustMatchers {
system.stop()
spec.awaitCond(spec.ref forall (_.isTerminated), 2 seconds)
}
"must stop correctly when sending PoisonPill to rootGuardian" in {
val system = ActorSystem("AkkaSpec2", AkkaSpec.testConf)
val spec = new AkkaSpec(system) {}
val latch = new TestLatch(1)(system)
system.registerOnTermination(latch.countDown())
system.actorFor("/") ! PoisonPill
latch.await(2 seconds)
}
"must enqueue unread messages from testActor to deadLetters" in {
val system, otherSystem = ActorSystem("AkkaSpec3", AkkaSpec.testConf)
try {
var locker = Seq.empty[DeadLetter]
implicit val timeout = system.settings.ActorTimeout
implicit val davyJones = otherSystem.actorOf(Props(new Actor {
def receive = {
case m: DeadLetter locker :+= m
}
}), "davyJones")
system.eventStream.subscribe(davyJones, classOf[DeadLetter])
val probe = new TestProbe(system)
probe.ref ! 42
/*
* this will ensure that the message is actually received, otherwise it
* may happen that the system.stop() suspends the testActor before it had
* a chance to put the message into its private queue
*/
probe.receiveWhile(1 second) {
case null
}
val latch = new TestLatch(1)(system)
system.registerOnTermination(latch.countDown())
system.stop()
latch.await(2 seconds)
// this will typically also contain log messages which were sent after the logger shutdown
locker must contain(DeadLetter(42, davyJones, probe.ref))
} finally {
system.stop()
otherSystem.stop()
}
}
}
}

View file

@ -173,8 +173,7 @@ class TestActorRefSpec extends AkkaSpec with BeforeAndAfterEach {
counter = 2
val boss = TestActorRef(Props(new TActor {
val impl = system.asInstanceOf[ActorSystemImpl]
val ref = new TestActorRef(impl, impl.dispatcherFactory.prerequisites, Props(new TActor {
val ref = TestActorRef(Props(new TActor {
def receiveT = { case _ }
override def preRestart(reason: Throwable, msg: Option[Any]) { counter -= 1 }
override def postRestart(reason: Throwable) { counter -= 1 }

View file

@ -6,6 +6,7 @@ package akka.tutorial.first.java;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.InternalActorRef;
import akka.actor.UntypedActor;
import akka.actor.UntypedActorFactory;
import akka.japi.Creator;
@ -115,7 +116,8 @@ public class Pi {
}
};
RoutedProps props = new RoutedProps(routerCreator, new LocalConnectionManager(actors), new akka.actor.Timeout(-1), true);
router = new RoutedActorRef(system(), props, getSelf(), "pi");
// FIXME REALLY this NEEDS to use getContext()!
router = new RoutedActorRef(system(), props, (InternalActorRef) getSelf(), "pi");
}
// message handler

View file

@ -6,6 +6,7 @@ package akka.tutorial.first.scala
import java.util.concurrent.CountDownLatch
import akka.routing.{ RoutedActorRef, LocalConnectionManager, RoundRobinRouter, RoutedProps }
import akka.actor.{ ActorSystemImpl, Actor, ActorSystem }
import akka.actor.InternalActorRef
object Pi extends App {
@ -55,8 +56,9 @@ object Pi extends App {
val workers = Vector.fill(nrOfWorkers)(system.actorOf[Worker])
// wrap them with a load-balancing router
// FIXME REALLY this needs to use context to create the child!
val props = RoutedProps(routerFactory = () new RoundRobinRouter, connectionManager = new LocalConnectionManager(workers))
val router = new RoutedActorRef(system, props, self, "pi")
val router = new RoutedActorRef(system, props, self.asInstanceOf[InternalActorRef], "pi")
// message handler
def receive = {

View file

@ -81,7 +81,8 @@ object AkkaBuild extends Build {
id = "akka-remote",
base = file("akka-remote"),
dependencies = Seq(stm, actorTests % "test->test", testkit % "test->test"),
settings = defaultSettings ++ multiJvmSettings ++ Seq(
// FIXME re-enable ASAP
settings = defaultSettings /*++ multiJvmSettings*/ ++ Seq(
libraryDependencies ++= Dependencies.cluster,
extraOptions in MultiJvm <<= (sourceDirectory in MultiJvm) { src =>
(name: String) => (src ** (name + ".conf")).get.headOption.map("-Dakka.config=" + _.absolutePath).toSeq