diff --git a/akka-actor-tests/src/test/scala/akka/actor/ActorSelectionSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/ActorSelectionSpec.scala
index df1c3471ff..87c9f9057f 100644
--- a/akka-actor-tests/src/test/scala/akka/actor/ActorSelectionSpec.scala
+++ b/akka-actor-tests/src/test/scala/akka/actor/ActorSelectionSpec.scala
@@ -61,6 +61,12 @@ class ActorSelectionSpec extends AkkaSpec("akka.loglevel=DEBUG") with DefaultTim
val asked = Await.result((selection ? Identify(selection)).mapTo[ActorIdentity], timeout.duration)
asked.ref must be(result)
asked.correlationId must be(selection)
+
+ implicit val ec = system.dispatcher
+ val resolved = Await.result(selection.resolveOne(timeout.duration).mapTo[ActorRef] recover { case _ ⇒ null },
+ timeout.duration)
+ Option(resolved) must be(result)
+
result
}
@@ -291,6 +297,24 @@ class ActorSelectionSpec extends AkkaSpec("akka.loglevel=DEBUG") with DefaultTim
expectNoMsg(1 second)
}
+ "resolve one actor with explicit timeout" in {
+ val s = system.actorSelection(system / "c2")
+ // Java and Scala API
+ Await.result(s.resolveOne(1.second.dilated), timeout.duration) must be === c2
+ }
+
+ "resolve one actor with implicit timeout" in {
+ val s = system.actorSelection(system / "c2")
+ // Scala API; implicit timeout from DefaultTimeout trait
+ Await.result(s.resolveOne(), timeout.duration) must be === c2
+ }
+
+ "resolve non-existing with Failure" in {
+ intercept[ActorNotFound] {
+ Await.result(system.actorSelection(system / "none").resolveOne(1.second.dilated), timeout.duration)
+ }
+ }
+
"compare equally" in {
ActorSelection(c21, "../*/hello") must be === ActorSelection(c21, "../*/hello")
ActorSelection(c21, "../*/hello").## must be === ActorSelection(c21, "../*/hello").##
diff --git a/akka-actor/src/main/scala/akka/actor/ActorSelection.scala b/akka-actor/src/main/scala/akka/actor/ActorSelection.scala
index 2417768dc3..7312043480 100644
--- a/akka-actor/src/main/scala/akka/actor/ActorSelection.scala
+++ b/akka-actor/src/main/scala/akka/actor/ActorSelection.scala
@@ -4,11 +4,19 @@
package akka.actor
import language.implicitConversions
-import scala.collection.immutable
-import java.util.regex.Pattern
-import akka.util.Helpers
-import akka.routing.MurmurHash
import scala.annotation.tailrec
+import scala.collection.immutable
+import scala.concurrent.Future
+import scala.concurrent.Promise
+import scala.concurrent.duration._
+import scala.util.Success
+import scala.util.Failure
+import java.util.regex.Pattern
+import akka.pattern.ask
+import akka.routing.MurmurHash
+import akka.util.Helpers
+import akka.util.Timeout
+import akka.dispatch.ExecutionContexts
/**
* An ActorSelection is a logical view of a section of an ActorSystem's tree of Actors,
@@ -40,6 +48,38 @@ abstract class ActorSelection extends Serializable {
anchor.tell(toMessage(msg, path, path.length - 1), sender)
}
+ /**
+ * Resolve the [[ActorRef]] matching this selection.
+ * The result is returned as a Future that is completed with the [[ActorRef]]
+ * if such an actor exists. It is completed with failure [[ActorNotFound]] if
+ * no such actor exists or the identification didn't complete within the
+ * supplied `timeout`.
+ *
+ * Under the hood it talks to the actor to verify its existence and acquire its
+ * [[ActorRef]].
+ */
+ def resolveOne()(implicit timeout: Timeout): Future[ActorRef] = {
+ implicit val ec = ExecutionContexts.sameThreadExecutionContext
+ val p = Promise[ActorRef]()
+ this.ask(Identify(None)) onComplete {
+ case Success(ActorIdentity(_, Some(ref))) ⇒ p.success(ref)
+ case _ ⇒ p.failure(ActorNotFound(this))
+ }
+ p.future
+ }
+
+ /**
+ * Resolve the [[ActorRef]] matching this selection.
+ * The result is returned as a Future that is completed with the [[ActorRef]]
+ * if such an actor exists. It is completed with failure [[ActorNotFound]] if
+ * no such actor exists or the identification didn't complete within the
+ * supplied `timeout`.
+ *
+ * Under the hood it talks to the actor to verify its existence and acquire its
+ * [[ActorRef]].
+ */
+ def resolveOne(timeout: FiniteDuration): Future[ActorRef] = resolveOne()(timeout)
+
override def toString: String = {
(new java.lang.StringBuilder).append("ActorSelection[").
append(anchor.toString).
@@ -107,3 +147,11 @@ trait ScalaActorSelection {
def !(msg: Any)(implicit sender: ActorRef = Actor.noSender) = tell(msg, sender)
}
+
+/**
+ * When [[ActorSelection#resolveOne]] can't identify the actor the
+ * `Future` is completed with this failure.
+ */
+@SerialVersionUID(1L)
+case class ActorNotFound(selection: ActorSelection) extends RuntimeException("Actor not found for: " + selection)
+
diff --git a/akka-actor/src/main/scala/akka/pattern/Patterns.scala b/akka-actor/src/main/scala/akka/pattern/Patterns.scala
index 34f137f27c..3c3a854577 100644
--- a/akka-actor/src/main/scala/akka/pattern/Patterns.scala
+++ b/akka-actor/src/main/scala/akka/pattern/Patterns.scala
@@ -146,6 +146,8 @@ object Patterns {
* Register an onComplete callback on this [[scala.concurrent.Future]] to send
* the result to the given [[akka.actor.ActorRef]] or [[akka.actor.ActorSelection]].
* Returns the original Future to allow method chaining.
+ * If the future was completed with failure it is sent as a [[akka.actor.Status.Failure]]
+ * to the recipient.
*
* Recommended usage example:
*
diff --git a/akka-actor/src/main/scala/akka/pattern/PipeToSupport.scala b/akka-actor/src/main/scala/akka/pattern/PipeToSupport.scala
index dc6594df73..ca992dbbbd 100644
--- a/akka-actor/src/main/scala/akka/pattern/PipeToSupport.scala
+++ b/akka-actor/src/main/scala/akka/pattern/PipeToSupport.scala
@@ -51,6 +51,9 @@ trait PipeToSupport {
* pipe(someFuture) to nextActor
*
* }}}
+ *
+ * The successful result of the future is sent as a message to the recipient, or
+ * the failure is sent in a [[akka.actor.Status.Failure]] to the recipient.
*/
implicit def pipe[T](future: Future[T])(implicit executionContext: ExecutionContext): PipeableFuture[T] = new PipeableFuture(future)
}
diff --git a/akka-docs/rst/java/remoting.rst b/akka-docs/rst/java/remoting.rst
index 512d25be6a..ae5c346bf4 100644
--- a/akka-docs/rst/java/remoting.rst
+++ b/akka-docs/rst/java/remoting.rst
@@ -71,6 +71,8 @@ To acquire an :class:`ActorRef` for an :class:`ActorSelection` you need to
send a message to the selection and use the ``getSender`` reference of the reply from
the actor. There is a built-in ``Identify`` message that all Actors will understand
and automatically reply to with a ``ActorIdentity`` message containing the
+:class:`ActorRef`. This can also be done with the ``resolveOne`` method of
+the :class:`ActorSelection`, which returns a ``Future`` of the matching
:class:`ActorRef`.
.. note::
diff --git a/akka-docs/rst/java/untyped-actors.rst b/akka-docs/rst/java/untyped-actors.rst
index 00cb8885cc..59c89c54c8 100644
--- a/akka-docs/rst/java/untyped-actors.rst
+++ b/akka-docs/rst/java/untyped-actors.rst
@@ -274,7 +274,9 @@ occupying it. ``ActorSelection`` cannot be watched for this reason. It is
possible to resolve the current incarnation's ``ActorRef`` living under the
path by sending an ``Identify`` message to the ``ActorSelection`` which
will be replied to with an ``ActorIdentity`` containing the correct reference
-(see :ref:`actorSelection-java`).
+(see :ref:`actorSelection-java`). This can also be done with the ``resolveOne``
+method of the :class:`ActorSelection`, which returns a ``Future`` of the matching
+:class:`ActorRef`.
.. _deathwatch-java:
@@ -427,6 +429,12 @@ of that reply is guaranteed, it still is a normal message.
.. includecode:: code/docs/actor/UntypedActorDocTest.java
:include: import-identify,identify
+You can also acquire an :class:`ActorRef` for an :class:`ActorSelection` with
+the ``resolveOne`` method of the :class:`ActorSelection`. It returns a ``Future``
+of the matching :class:`ActorRef` if such an actor exists. It is completed with
+failure [[akka.actor.ActorNotFound]] if no such actor exists or the identification
+didn't complete within the supplied `timeout`.
+
Remote actor addresses may also be looked up, if :ref:`remoting ` is enabled:
.. includecode:: code/docs/actor/UntypedActorDocTest.java#selection-remote
diff --git a/akka-docs/rst/project/migration-guide-2.1.x-2.2.x.rst b/akka-docs/rst/project/migration-guide-2.1.x-2.2.x.rst
index f2277aab07..013aadefcb 100644
--- a/akka-docs/rst/project/migration-guide-2.1.x-2.2.x.rst
+++ b/akka-docs/rst/project/migration-guide-2.1.x-2.2.x.rst
@@ -262,6 +262,12 @@ the actor. There is a built-in ``Identify`` message that all Actors will underst
and automatically reply to with a ``ActorIdentity`` message containing the
:class:`ActorRef`.
+You can also acquire an :class:`ActorRef` for an :class:`ActorSelection` with
+the ``resolveOne`` method of the :class:`ActorSelection`. It returns a ``Future``
+of the matching :class:`ActorRef` if such an actor exists. It is completed with
+failure [[akka.actor.ActorNotFound]] if no such actor exists or the identification
+didn't complete within the supplied `timeout`.
+
Read more about ``actorSelection`` in :ref:`docs for Java ` or
:ref:`docs for Scala `.
diff --git a/akka-docs/rst/scala/actors.rst b/akka-docs/rst/scala/actors.rst
index c526d14e5b..e3f380b102 100644
--- a/akka-docs/rst/scala/actors.rst
+++ b/akka-docs/rst/scala/actors.rst
@@ -363,7 +363,9 @@ occupying it. ``ActorSelection`` cannot be watched for this reason. It is
possible to resolve the current incarnation's ``ActorRef`` living under the
path by sending an ``Identify`` message to the ``ActorSelection`` which
will be replied to with an ``ActorIdentity`` containing the correct reference
-(see :ref:`actorSelection-scala`).
+(see :ref:`actorSelection-scala`). This can also be done with the ``resolveOne``
+method of the :class:`ActorSelection`, which returns a ``Future`` of the matching
+:class:`ActorRef`.
.. _deathwatch-scala:
@@ -510,6 +512,12 @@ of that reply is guaranteed, it still is a normal message.
.. includecode:: code/docs/actor/ActorDocSpec.scala#identify
+You can also acquire an :class:`ActorRef` for an :class:`ActorSelection` with
+the ``resolveOne`` method of the :class:`ActorSelection`. It returns a ``Future``
+of the matching :class:`ActorRef` if such an actor exists. It is completed with
+failure [[akka.actor.ActorNotFound]] if no such actor exists or the identification
+didn't complete within the supplied `timeout`.
+
Remote actor addresses may also be looked up, if :ref:`remoting ` is enabled:
.. includecode:: code/docs/actor/ActorDocSpec.scala#selection-remote
diff --git a/akka-docs/rst/scala/remoting.rst b/akka-docs/rst/scala/remoting.rst
index 31cf8667b5..7d1ddc1359 100644
--- a/akka-docs/rst/scala/remoting.rst
+++ b/akka-docs/rst/scala/remoting.rst
@@ -78,6 +78,8 @@ To acquire an :class:`ActorRef` for an :class:`ActorSelection` you need to
send a message to the selection and use the ``sender`` reference of the reply from
the actor. There is a built-in ``Identify`` message that all Actors will understand
and automatically reply to with a ``ActorIdentity`` message containing the
+:class:`ActorRef`. This can also be done with the ``resolveOne`` method of
+the :class:`ActorSelection`, which returns a ``Future`` of the matching
:class:`ActorRef`.
.. note::