Merge pull request #1671 from akka/wip-3529-identify-sugar-patriknw

+act #3529 Add convenience helper for looking up an actor by identity
This commit is contained in:
Patrik Nordwall 2013-08-27 02:14:19 -07:00
commit 713478f963
9 changed files with 109 additions and 6 deletions

View file

@ -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").##

View file

@ -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)

View file

@ -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.
*
* <b>Recommended usage example:</b>
*

View file

@ -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)
}

View file

@ -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::

View file

@ -280,7 +280,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:
@ -433,6 +435,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 <remoting-java>` is enabled:
.. includecode:: code/docs/actor/UntypedActorDocTest.java#selection-remote

View file

@ -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 <actorSelection-java>` or
:ref:`docs for Scala <actorSelection-scala>`.

View file

@ -369,7 +369,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:
@ -516,6 +518,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 <remoting-scala>` is enabled:
.. includecode:: code/docs/actor/ActorDocSpec.scala#selection-remote

View file

@ -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::