Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Akka intercepting receive with stackable behavior

Akka and Scala newbie here, please feel free to edit the question as necessary in order to clearly articulate my intent in the domain of Scala and Akka.

Before I show code snippets, here's the problem I want to solve: I essentially want to develop a common module for my team to use when they're developing their applications using Akka actors. I want to allow them to mixin a trait which will extend their receive functionality at runtime, mainly for logging purposes. I'm running into compile errors, which I'll explain soon.

But first, take for example, a simple main:

object Test extends App {

   val system = ActorSystem("system")
   val myActor = system.actorOf(Props(new MyActor), "myActor")
   myActor ! "Hello world!"
}

Here's an example implementation of an actor that a team member might implement in his application:

class MyActor extends Actor with ActorLogger {

   override def receive: Receive = {
       case msg => {
          log.info("testing ...")
       }
       case _ => throw new RuntimeException("Runtime Ex")
   }
}

And here's an example of how I would provide a common trait for them to mixin:

trait ActorLogger extends Actor {

    val log: DiagnosticLoggingAdapter = Logging(this)

    abstract override def receive: Receive = {

         case msg: Any => {
            if (msg.isInstanceOf[String]) {
              println("enter")
              log.mdc(Map[String, Any]("someKey" -> 123))
              super.receive(msg)
              log.clearMDC()
               println("exit")
            }
          }

         case _ => throw new RuntimeException("Runtime Ex")
    }

}

As you can see, I'm trying to add data to an MDC if the message so happens to be String (a basic example, in reality, I would check for some custom type of our own).

The error I get is:

 Error:(29, 16) overriding method receive in trait ActorLogger of type =>   
 MyActor.this.Receive;
 method receive needs `abstract override' modifiers
 override def receive: Receive = {
           ^

What's wrong here? And is stackable traits the right to go away to achieve something like this? If not, what is the most idiomatic way?

More generally, is there another pattern being applied here besides "interceptor" pattern?

Thanks for all the help!

like image 443
HiChews123 Avatar asked Apr 11 '26 15:04

HiChews123


2 Answers

A solution without a hack with akka package:

import akka.actor.{Actor, ActorSystem, Props}

trait MyActorExtension extends Actor {

  def receiveExtension: Receive = PartialFunction.empty

}

abstract class MyActor extends MyActorExtension {

  protected def receiveMsg: Receive

  def receive: Receive = receiveExtension orElse receiveMsg

}

trait ActorLogger1 extends MyActor with MyActorExtension {

  abstract override def receiveExtension = {
    case msg =>
      println(s"********** Logging # 1: $msg")
      super.receiveExtension.applyOrElse(msg, receiveMsg)
  }

}

trait ActorLogger2 extends MyActor with MyActorExtension {

  abstract override def receiveExtension = {
    case msg =>
      println(s"########## Logging # 2: $msg")
      super.receiveExtension.applyOrElse(msg, receiveMsg)
  }
}

class SpecificActor extends MyActor with ActorLogger1 with ActorLogger2 {

  def receiveMsg = {
    case someMsg =>
      println(s"SpecificActor: $someMsg")
  }
}

object Test extends App {

  val system = ActorSystem("system")
  val mySpecificActor = system.actorOf(Props(new SpecificActor), "SpecificActor")
  mySpecificActor ! "Hello world!"
}
#### Logging # 2: Hello world!

****** Logging # 1: Hello world!

SpecificActor: Hello world!

like image 152
Sergiy Prydatchenko Avatar answered Apr 13 '26 04:04

Sergiy Prydatchenko


aroundReceive is for Akka internal use and the stackable trair pattern is not that comfortable for this case. I recommend you using Receive Pipeline for easy message interception.

like image 33
ktonga Avatar answered Apr 13 '26 04:04

ktonga



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!