diff --git a/akka-actor-tests/src/test/scala/akka/actor/ActorLookupSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/ActorLookupSpec.scala index 25c4e2d2f8..1806c48830 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/ActorLookupSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/ActorLookupSpec.scala @@ -51,7 +51,11 @@ class ActorLookupSpec extends AkkaSpec with DefaultTimeout { 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 child "c2" child "c21") must be === c21 // test Java API system.actorFor(system / Seq("c2", "c21")) must be === c21 + + import scala.collection.JavaConverters._ + system.actorFor(system descendant Seq("c2", "c21").asJava) // test Java API } "find actors by looking up their string representation" in { @@ -75,7 +79,7 @@ class ActorLookupSpec extends AkkaSpec with DefaultTimeout { "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 + system.actorFor(c21.path.getElements) must be === c21 // test Java API } "find system-generated actors" in { @@ -190,6 +194,8 @@ class ActorLookupSpec extends AkkaSpec with DefaultTimeout { } "return deadLetters for non-existing paths" in { + import scala.collection.JavaConverters._ + def checkOne(looker: ActorRef, query: Query) { (looker ? query).get must be === system.deadLetters } @@ -198,6 +204,8 @@ class ActorLookupSpec extends AkkaSpec with DefaultTimeout { LookupString(""), LookupString("akka://all-systems/Nobody"), LookupPath(system / "hallo"), + LookupPath(looker.path child "hallo"), // test Java API + LookupPath(looker.path descendant Seq("a", "b").asJava), // test Java API LookupElems(Seq()), LookupElems(Seq("a"))) foreach (checkOne(looker, _)) } diff --git a/akka-actor/src/main/scala/akka/actor/ActorPath.scala b/akka-actor/src/main/scala/akka/actor/ActorPath.scala index 09438c17c3..b364fd5247 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorPath.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorPath.scala @@ -49,16 +49,37 @@ sealed trait ActorPath extends Comparable[ActorPath] { */ def /(child: String): ActorPath + /** + * ''Java API'': Create a new child actor path. + */ + def child(child: String): ActorPath = /(child) + /** * Recursively create a descendant’s path by appending all child names. */ def /(child: Iterable[String]): ActorPath = (this /: child)(_ / _) /** - * Sequence of names for this path. Performance implication: has to allocate a list. + * ''Java API'': Recursively create a descendant’s path by appending all child names. + */ + def descendant(names: java.lang.Iterable[String]): ActorPath = { + import scala.collection.JavaConverters._ + /(names.asScala) + } + + /** + * Sequence of names for this path from root to this. Performance implication: has to allocate a list. */ def elements: Iterable[String] + /** + * ''Java API'': Sequence of names for this path from root to this. Performance implication: has to allocate a list. + */ + def getElements: java.lang.Iterable[String] = { + import scala.collection.JavaConverters._ + elements.asJava + } + /** * Walk up the tree to obtain and return the RootActorPath. */ diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index b455da9c18..648e671c50 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -178,7 +178,7 @@ private[akka] abstract class InternalActorRef extends ActorRef with ScalaActorRe * trailing "" element must be disregarded. If the requested path does not * exist, return Nobody. */ - def getChild(name: Iterable[String]): InternalActorRef + def getChild(name: Iterator[String]): InternalActorRef } private[akka] case object Nobody extends MinimalActorRef { @@ -256,26 +256,27 @@ class LocalActorRef private[akka] ( } } - def getChild(names: Iterable[String]): InternalActorRef = { + def getChild(names: Iterator[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) + def rec(ref: InternalActorRef, name: Iterator[String]): InternalActorRef = + ref match { + case l: LocalActorRef ⇒ + val n = name.next() + val next = n match { + case ".." ⇒ l.getParent + case "" ⇒ l + case _ ⇒ l.getSingleChild(n) + } + if (next == Nobody || name.isEmpty) next else rec(next, name) + case _ ⇒ + ref.getChild(name) + } + if (names.isEmpty) this + else rec(this, names) } // ========= AKKA PROTECTED FUNCTIONS ========= @@ -326,9 +327,11 @@ case class SerializedActorRef(path: String) { trait MinimalActorRef extends InternalActorRef { def getParent: InternalActorRef = Nobody - def getChild(name: Iterable[String]): InternalActorRef = - if (name.size == 1 && name.head.isEmpty) this + def getChild(names: Iterator[String]): InternalActorRef = { + val dropped = names.dropWhile(_.isEmpty) + if (dropped.isEmpty) this else Nobody + } //FIXME REMOVE THIS, ticket #1416 //FIXME REMOVE THIS, ticket #1415 diff --git a/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala b/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala index 880e78bab3..d68a1349f0 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala @@ -287,7 +287,7 @@ trait ActorRefFactory { def actorFor(path: Iterable[String]): ActorRef = provider.actorFor(lookupRoot, path) /** - * Look-up an actor by applying the given path elements, starting from the + * ''Java API'': Look-up an actor by applying the given path elements, starting from the * current context, where `".."` signifies the parent of an actor. * * Example: @@ -307,7 +307,7 @@ trait ActorRefFactory { * * For maximum performance use a collection with efficient head & tail operations. */ - def actorFor(path: java.util.List[String]): ActorRef = { + def actorFor(path: java.lang.Iterable[String]): ActorRef = { import scala.collection.JavaConverters._ provider.actorFor(lookupRoot, path.asScala) } @@ -462,13 +462,17 @@ class LocalActorRefProvider( 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) + override def getChild(name: Iterator[String]): InternalActorRef = { + if (name.isEmpty) this + else { + val n = name.next() + if (n.isEmpty) this + else children.get(n) match { + case null ⇒ Nobody + case some ⇒ + if (name.isEmpty) some + else some.getChild(name) + } } } } @@ -497,7 +501,7 @@ class LocalActorRefProvider( def actorFor(ref: InternalActorRef, path: Iterable[String]): InternalActorRef = if (path.isEmpty) deadLetters - else ref.getChild(path) match { + else ref.getChild(path.iterator) match { case Nobody ⇒ deadLetters case x ⇒ x } diff --git a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala index 01f620182d..31caf6083b 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala @@ -218,11 +218,24 @@ abstract class ActorSystem extends ActorRefFactory { */ def /(name: String): ActorPath + /** + * ''Java API'': Create a new child actor path. + */ + def child(child: String): ActorPath = /(child) + /** * Construct a path below the application guardian to be used with [[ActorSystem.actorFor]]. */ def /(name: Iterable[String]): ActorPath + /** + * ''Java API'': Recursively create a descendant’s path by appending all child names. + */ + def descendant(names: java.lang.Iterable[String]): ActorPath = { + import scala.collection.JavaConverters._ + /(names.asScala) + } + /** * Start-up time in milliseconds since the epoch. */ diff --git a/akka-remote/src/main/scala/akka/remote/RemoteActorRefProvider.scala b/akka-remote/src/main/scala/akka/remote/RemoteActorRefProvider.scala index 3c05b9a158..44b756dfba 100644 --- a/akka-remote/src/main/scala/akka/remote/RemoteActorRefProvider.scala +++ b/akka-remote/src/main/scala/akka/remote/RemoteActorRefProvider.scala @@ -270,7 +270,7 @@ private[akka] case class RemoteActorRef private[akka] ( // FIXME RK def getParent = Nobody - def getChild(name: Iterable[String]) = Nobody + def getChild(name: Iterator[String]) = Nobody @volatile private var running: Boolean = true