I would like to propagate a request context implicitly in a system of collaborating actors.
To simplify and present the situation, my system has multiple actors and the messages passed to these actors need to include this RequestContext object.
ActorA receives messages of type MessageA ActorB receives messages of type MessageB
when ActorA needs to send a message to ActorB, as part of the handling of MessageA, it performs business logic, and then constructs a MessageB from results of the logic as well as the RequestContext available in MessageA and then sends it to ActorB
def handle(ma:MessageA) {
val intermediateResult = businessLogic(ma)
actorB ! MessageB(intermediateResult, ma.requestContext)
}
We have a slew of messages to be handled, and explicitly passing around the requestContext is cumbersome.
I am trying of creative ways to use Scala's implicits feature to avoid explicitly injecting the RequestContext embedded within incoming message into the outgoing message.
The messages are case classes (and they need to be). I have read about implicits rules but bringing an attribute of an object into current implicit scope appears far-fetched.
This, I am sure should be a common requirement. Any suggestions ?
Thanks.
The actor context - the view of the actor cell from the actor. Exposes contextual information for the actor and the current message. There are several possibilities for creating actors (see Props for details on props ): // Java or Scala context. actorOf(props, "name") context.
1) Akka Actor tell() Method It works on "fire-forget" approach. You can also use ! (bang) exclamation mark to send message. This is the preferred way of sending messages.
Start Hook This method is called when the actor is first created.
ActorRefFactory, an interface which is implemented by ActorSystem and akka. actor. ActorContext. This means actors can be created top-level in the ActorSystem or as children of an existing actor, but only from within that actor. ActorRefs can be freely shared among actors by message passing.
In my opinion, the easiest way is to make your val implicit in the case class.
case class MessageA(req: RequestA)(implicit val ctx: RequestContext)
case class MessageB(req: RequestB)(implicit val ctx: RequestContext)
def businessLogic(req:RequestA):RequestB
def handle(ma: MessageA): Unit = {
// import all the members of ma so that there is a legal implicit RequestContext in scope
import ma._
val intermediateResult = businessLogic(req)
actorB ! MessageB(intermediateResult)
}
In your example your handling of the message in question is factored out into a method already, which makes this straight-forward:
trait RequestContext
case class MessageA(req: RequestA, ctx: RequestContext)
object MessageA {
def apply(req: RequestA)(implicit ctx: RequestContext) = MessageA(req, ctx)
}
case class MessageB(req: RequestB, ctx: RequestContext)
object MessageB {
def apply(req: RequestB)(implicit ctx: RequestContext) = MessageB(req, ctx)
}
class Example extends Actor {
def receive = {
case MessageA(req, ctx) => handle(req)(ctx)
}
def handle(req: RequestA)(implicit ctx: RequestContext): Unit = {
val intermediateResult = businessLogic(req) // could take implicit ctx as well
actorB ! MessageB(intermediateResult)
}
}
But as you can see there is still some overhead when declaring the message types, and the handle
method’s signature also needs to be altered. Whether this scheme is worth it depends on the ratio between consumers and producers of these implicit values (i.e. if more than one thing in handle
uses the context it makes more sense).
A variation of the above could be:
case class MessageA(req: RequestA, ctx: RequestContext)
object MessageA {
def apply(req: RequestA)(implicit ctx: RequestContext) = MessageA(req, ctx)
implicit def toContext(implicit msg: MessageA) = msg.ctx
}
case class MessageB(req: RequestB, ctx: RequestContext)
object MessageB {
def apply(req: RequestB)(implicit ctx: RequestContext) = MessageB(req, ctx)
implicit def toContext(implicit msg: MessageB) = msg.ctx
}
...
def handle(implicit ma: MessageA): Unit = {
val intermediateResult = businessLogic(req)
actorB ! MessageB(intermediateResult)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With