org.querki.requester

Requester

Related Docs: object Requester | package requester

trait Requester extends Actor with RequesterImplicits

Easy and relatively safe variant of "ask".

The idea here is that it would be lovely to have a replacement for the "ask" pattern. Ask is powerful, but quite dangerous -- in particular, handling the response in the most obvious way, using the Future's completion callbacks, is a fine way to break your Actor's threading and cause strange timing bugs.

So the idea here is to build something with similar semantics to ask, but deliberately a bit dumbed-down and safer for routine use. Where ask returns a Future that you can then put callbacks on, request() takes those callbacks as parameters, and runs them *in this Actor's main thread*.

In other words, I want to be able to say something like:

def receive = { ... case MyMessage(a, b) => { otherActor.request(MyRequest(b)).foreach { case OtherResponse(c) => ... } } }

While OtherResponse is lexically part of MyRequest, it actually *runs* during receive, just like any other incoming message, so it isn't prone to the threading problems that ask is.

How does this work? Under the hood, it actually does use ask, but in a very specific and constrained way. We send the message off using ask, and then hook the resulting Future. When the Future completes, we wrap the response and the handler together in a RequestedResponse message, and loop that back around as a message to the local Actor.

Note that the original sender is preserved, so the callback can use it without problems. (This is the most common error made when using ask, and was one of the motivations for creating Requester.)

Note that, to make this work, the Request trait mixes in its own version of unhandled(). I *think* this should Just Work, but it's probably the part where I'm on least-comfortable ground, so watch for edge cases there. I have not yet tested how this interacts with Actor-defined unhandled(), and I'm mildly concerned about possible loops.

IMPORTANT: Requester is *not* compatible with stateful versions of become() -- that is, if you are using become() in a method where you are capturing the parameters in the closure of become(), Requester will probably not work right. This is because the body of the response handler will capture the closed-over parameter, and if the Actor has become() something else in the meantime, the handler will use the *old* data, not the new.

More generally, Requester should be used with great caution if your Actor changes state frequently. While it *can* theoretically be used with FSM, it may not be wise to do so, since the state machine may no longer be in a compatible state by the time the response is received. Requester is mainly intended for Actors that spend most or all of their time in a single state; it generally works quite well for those.

Linear Supertypes
RequesterImplicits, Actor, AnyRef, Any
Ordering
  1. Alphabetic
  2. By inheritance
Inherited
  1. Requester
  2. RequesterImplicits
  3. Actor
  4. AnyRef
  5. Any
  1. Hide All
  2. Show all
Learn more about member selection
Visibility
  1. Public
  2. All

Type Members

  1. type Receive = PartialFunction[Any, Unit]

    Definition Classes
    Actor
  2. implicit class RequestableActorRef extends AnyRef

    Hook to add the request() methods to a third-party Actor.

    Hook to add the request() methods to a third-party Actor.

    Definition Classes
    RequesterImplicits
  3. case class RequestedResponse[T](response: Try[T], handler: RequestM[T]) extends Product with Serializable

    The response from request() will be wrapped up in here and looped around.

    The response from request() will be wrapped up in here and looped around. You shouldn't need to use this directly.

Abstract Value Members

  1. abstract def receive: akka.actor.Actor.Receive

    Definition Classes
    Actor

Concrete Value Members

  1. final def !=(arg0: Any): Boolean

    Definition Classes
    AnyRef → Any
  2. final def ##(): Int

    Definition Classes
    AnyRef → Any
  3. final def ==(arg0: Any): Boolean

    Definition Classes
    AnyRef → Any
  4. def aroundPostRestart(reason: Throwable): Unit

    Attributes
    protected[akka]
    Definition Classes
    Actor
  5. def aroundPostStop(): Unit

    Attributes
    protected[akka]
    Definition Classes
    Actor
  6. def aroundPreRestart(reason: Throwable, message: Option[Any]): Unit

    Attributes
    protected[akka]
    Definition Classes
    Actor
  7. def aroundPreStart(): Unit

    Attributes
    protected[akka]
    Definition Classes
    Actor
  8. def aroundReceive(receive: akka.actor.Actor.Receive, msg: Any): Unit

    Attributes
    protected[akka]
    Definition Classes
    Actor
  9. final def asInstanceOf[T0]: T0

    Definition Classes
    Any
  10. def clone(): AnyRef

    Attributes
    protected[java.lang]
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  11. implicit val context: ActorContext

    Definition Classes
    Actor
  12. def doRequest[T](otherActor: ActorRef, msg: Any, handler: RequestM[T])(implicit tag: ClassTag[T]): Unit

    Send a request, and specify the handler for the received response.

    Send a request, and specify the handler for the received response. You may also specify a failHandler, which will be run if the operation fails for some reason. (Most often, because we didn't receive a response within the timeout window.)

  13. final def eq(arg0: AnyRef): Boolean

    Definition Classes
    AnyRef
  14. def equals(arg0: Any): Boolean

    Definition Classes
    AnyRef → Any
  15. def finalize(): Unit

    Attributes
    protected[java.lang]
    Definition Classes
    AnyRef
    Annotations
    @throws( classOf[java.lang.Throwable] )
  16. final def getClass(): Class[_]

    Definition Classes
    AnyRef → Any
  17. def handleRequestResponse: akka.actor.Actor.Receive

  18. def hashCode(): Int

    Definition Classes
    AnyRef → Any
  19. final def isInstanceOf[T0]: Boolean

    Definition Classes
    Any
  20. final def ne(arg0: AnyRef): Boolean

    Definition Classes
    AnyRef
  21. final def notify(): Unit

    Definition Classes
    AnyRef
  22. final def notifyAll(): Unit

    Definition Classes
    AnyRef
  23. def postRestart(reason: Throwable): Unit

    Definition Classes
    Actor
    Annotations
    @throws( classOf[java.lang.Exception] )
  24. def postStop(): Unit

    Definition Classes
    Actor
    Annotations
    @throws( classOf[java.lang.Exception] )
  25. def preRestart(reason: Throwable, message: Option[Any]): Unit

    Definition Classes
    Actor
    Annotations
    @throws( classOf[java.lang.Exception] )
  26. def preStart(): Unit

    Definition Classes
    Actor
    Annotations
    @throws( classOf[java.lang.Exception] )
  27. def requestFuture[R](reqFunc: (Promise[R]) ⇒ Any)(implicit tag: ClassTag[R]): Future[R]

    This is a wrapper, intended to surround a request clause whose value you want to return as a Future.

    This is a wrapper, intended to surround a request clause whose value you want to return as a Future. There is no magic here -- it is simply syntactic sugar to make this pattern easier. You should use it along these lines:

    requestFuture[TargetType] { implicit promise =>
      for {
        v1 <- someActor.request(MyMessage)
        v2 <- anotherActor.request(AnotherMessage(v1))
      }
        promise.success(v2)
    }

    That is, requestFuture expects a block that takes a Promise of the type you want to return. This block should declare that Promise as implicit -- that implicit Promise gets sucked into the requests, and is used if an exception is thrown.

    reqFunc

    An arbitrarily-complex request clause, which eventually calls promise.success() when it gets the desired answer.

    Definition Classes
    RequesterImplicits
  28. implicit val requestTimeout: Timeout

    Override this to specify the timeout for requests

  29. val requester: Requester

    The actual Requester that is going to send the requests and process the responses.

    The actual Requester that is going to send the requests and process the responses. If you mix RequesterImplicits into a non-Requester, this must point to that Actor, which does all the real work. (If you are using this from within Requester, it's already set.)

    Definition Classes
    RequesterRequesterImplicits
  30. implicit final val self: ActorRef

    Definition Classes
    Actor
  31. final def sender(): ActorRef

    Definition Classes
    Actor
  32. def supervisorStrategy: SupervisorStrategy

    Definition Classes
    Actor
  33. final def synchronized[T0](arg0: ⇒ T0): T0

    Definition Classes
    AnyRef
  34. def toString(): String

    Definition Classes
    AnyRef → Any
  35. def unhandled(message: Any): Unit

    Definition Classes
    Actor
  36. final def wait(): Unit

    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  37. final def wait(arg0: Long, arg1: Int): Unit

    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  38. final def wait(arg0: Long): Unit

    Definition Classes
    AnyRef
    Annotations
    @throws( ... )

Inherited from RequesterImplicits

Inherited from Actor

Inherited from AnyRef

Inherited from Any

Ungrouped