Scala: How to extend an Akka actor? - scala

I wrote an Akka base-actor that can handle some common messages. I want to reuse this basic behavior in a sub-actor, by extending the base-actor (not by composition of the base-actor).
I have seen several approaches in previous questions. They are all valid but also may be improved:
How extend behaviour of super actor in akka
and
How do I best share behavior among Akka actors?
To make the implementation cleaner, I am trying to achieve the following:
When defining the sub-actor, I try to extend only the base-actor (not both Actor and sub-actor). However, I was not able to force the compiler to do this.
I also try to not rename the receive partial function, because it is a kind of convention.
Here is a sample of my implementation:
//This is the base-actor that implements the common behavior
trait BetterActor extends Actor {
abstract override def receive = {
super.receive orElse { case _ => println("Missing pattern!") }
}
}
//This is the actor that implements the additional behavior.
//I actually wanted to extend BetterActor. It was not possible.
class MyActor extends Actor {
def receive = {
case i: Int =>
println(s"Yay!!! Got $i")
}
}
Here are two alternative ways how I can create an instance of the sub-actor, which combines both the common and the additional behaviors:
//1.
val myBetterActor = system.actorOf(Props(new MyActor with BetterActor), "myBetterActor")
//2.
class MyBetterActor extends MyActor with BetterActor
val myBetterActor = system.actorOf(Props[MyBetterActor], "myBetterActor")
Finally, I may invoke the sub-actor by:
myBetterActor ! 2
myBetterActor ! "a"
Problems with my implementation:
When I create an instance of the sub-actor, only then I can mix in the base-actor, either directly (1) or by defining a new class that mixes in the base-actor.
Like I said before, I would prefer to mix in the base-actor when I define the sub-actor. Not when I try to create an instance of the sub-actor.
P.S. It's fascinating that the designers of this framework did not see it necessary to make this common requirement easy to accomplish.

i suppose as long as you're not trying to override behavior with BetterActor, you could try something like this:
trait BetterActor extends Actor {
override def unhandled(message: Any): Unit = message match {
case e: CommonMessage => println("COMMON!")
case e => super.unhandled(e)
}
}
class MyActor extends BetterActor {
override def receive = {
case i: Int => println(s"Yay!!! Got $i")
}
}

Related

Explanation of Scala construct

I am busy with a major refactoring task on some legacy Scala/Akka code, and am being haunted by a construct that I am unable to explain, which is hampering my effort:
trait PerRequestCreator {
this: Actor =>
def perRequest(<some_params>): ActorRef = { body of function }
}
Which is then used as such:
class SomeActor extends PerRequestCreator with Actor {
def processRequest: Route = {
perRequest(<some_params_passed>)
}
}
I am having trouble understanding the this: Actor => ... part of the trait.
It's called self-type, and it expresses a requirement for the PerRequestCreator to be mixed in into something that extends Actor.
It's useful because now you can use anything defined in Actor inside the definition of PerRequestCreator and the compiler will check that you can only extend PerRequestCreator if you also extend Actor.
Example:
class SomeClass extends PerRequestCreator // this won't compile
class SomeClass extends PerRequestCreator with Actor // this is ok
You can read more about it here: https://docs.scala-lang.org/tour/self-types.html

Strategy pattern in Akka

This is an continuation of my previous question How do I get around type erasure on Akka receive method
I have 10 type of events which extends from Event that I need to handle.
I want to implement business logic for each event in separate trait, because because mixing all 10 event handler functions will produce several hundreds(if not thousands) lines of code.
I don't want to create different Actor types for each event. For example:
class Event1Actor extend Actor{
def receive ={
case Event1(e) => //event1 Business Logic
}
}
class Event2Actor extend Actor{
def receive ={
case Event2(e) => //event2 Business Logic
}
}
and the same Event3Actor, Event4Actor,etc....
Such code seems ugly to me, because I need to implement business Logic inside each Actor.
Implementing 10 different traits and 10 different Actor classes seems also as bad design.
I'm seeking for some kind generic solution based on design pattern, for example strategy pattern.
case class EventOperation[T <: Event](eventType: T)
class OperationActor extends Actor {
def receive = {
case EventOperation(eventType) => eventType.execute
}
}
trait Event {
def execute //implement execute in specific event class
}
class Event1 extends Event {/*execute implemented with business logic*/}
class Event2 extends Event {/*execute implemented with business logic*/}
hope this is what you are looking for and helps, I have used this patternt to remove the redundant amount of actors wrapping all actions under a single actor executing different type of events.
You could try something like this, which involves auto-composing receive functionality via a base trait. First the code:
case class Event1(s:String)
case class Event2(i:Int)
case class Event3(f:Float)
trait EventHandlingActor extends Actor{
var handlers:List[Receive] = List.empty
override def preStart = {
val composedReceive = handlers.foldLeft(receive)((r,h) => r.orElse(h))
context.become(composedReceive)
}
def addHandler(r:Receive) {
handlers = r :: handlers
}
def receive = PartialFunction.empty[Any,Unit]
}
trait Event1Handling{ me:EventHandlingActor =>
addHandler{
case Event1(s) => println(s"${self.path.name} handling event1: $s")
}
}
trait Event2Handling{ me:EventHandlingActor =>
addHandler{
case Event2(i) => println(s"${self.path.name} handling event2: $i")
}
}
trait Event3Handling{ me:EventHandlingActor =>
addHandler{
case Event3(f) => println(s"${self.path.name} handling event3: $f")
}
}
So you can see in the EventHandlingActor trait we set up a List of type Receive that can be added to by each specific handling trait that we stack into a concrete actor. Then you can see the definitions of the handling functionality for each event defined in a separate trait that is calling addHandler to add another piece of handling functionality. In preStart for any kind of EventHandlingActor impl the receive functions will be composed together with receive being the starting point (empty by default) before hot-swapping out the receive impl with context.become.
Now for a couple of impl actors as an example:
class MyEventHandlingActor extends EventHandlingActor
with Event1Handling with Event2Handling with Event3Handling
case class SomeOtherMessage(s:String)
class MyOtherEventHandlingActor extends EventHandlingActor with Event1Handling{
override def receive = {
case SomeOtherMessage(s) => println(s"otherHandler handling some other message: $s")
}
}
The first one only handles events, so all it needs to do is define which ones it handles my mixing in the appropriate traits. The second one handles one type of event but also some other message that is not an event. This class overrides the default empty receive and provides functionality to handle the non-event message.
If we tested the code like so:
val system = ActorSystem("test")
val handler = system.actorOf(Props[MyEventHandlingActor], "handler")
handler ! Event1("foo")
handler ! Event2(123)
handler ! Event3(123.456f)
val otherHandler = system.actorOf(Props[MyOtherEventHandlingActor], "otherHandler")
otherHandler ! Event1("bar")
otherHandler ! SomeOtherMessage("baz")
Then we would see output similar to this (with the order changing due to asynch handling of messages):
otherHandler handling event1: bar
handler handling event1: foo
handler handling event2: 123
handler handling event3: 123.456

Syntax to dynamically add mixin to an actor

I have an actor that sends messages to another actor. It also creates this other actor and does not know its type at compile time. On creation it needs to add a mixin. I am wondering how to solve this syntactically. So for example if we have:
trait MyMixin extends Actor {
abstract override def receive = {
case msg =>
// Do something
super.receive(msg)
}
}
class Sender(targetClass: Class) extends Actor {
// Here I want to create the actor such that I can add the mixin
// eg. If I knew the class at compile time I could do the following:
// val target = context.actorOf(Props(new TargetActor with MyMixin))
val target = context.actorOf(Props(???))
target ! someMsg
...
}
So the question is how to combine the two classes for a run time creation?
Thanks
Des

Actor based classes with or without interfaces

I am playing around with Scala right now and tried to figure out some best practices about how to design classes. (Trying Scala since a week or so.)
Since my Erlang time I am a huge fan of message passing and actor based software. In most Scala examples actor classes are implemented like this:
object Foo
object Bar
class MyActor extends Actor {
def receive = {
case Foo => ...
case Bar => ...
case _ => ...
}
}
But what I learned from my object oriented (interfaces and polymorphism) carrier tells me that this concept is not very flexible.
MyActor could be replaced by MyAdvancedActor but there is no contract which defines which messages an MyActor implementation needs to implement.
When I think about writing Actors in Scala I tend to write a trait which specifies some methods. The MyActor implementation needs to implement this methods in which the can send its own private messages to itself.
With this approach we have a specified interface and can replace the MyActor implementation in a type-safe manner.
In my time of reading scala tutorials and examples I did not come across such an class design. Is it not common sense or are there better ways in doing this in Scala? Or are these tutorials just to small to cover such a topic?
Common practice is to use an algebraic data type in such cases: you could create a sealed base type for all messages like this:
sealed trait MyActorMessages
object Foo extends MyActorMessages
object Bar extends MyActorMessages
But this kind of contract is not enforced by compiler. You could use use Typed Channels to enforce this contract:
class MyActor extends Actor with Channels[TNil, (MyActorMessages, MyActorReply) :+: TNil] {
channel[MyActorMessages] { (req, snd) ⇒
req match {
case Foo ⇒ ...
case Bar ⇒ ... // You'll get a warning if you forget about `Bar`
}
}
}
Compiler will force you (with warning) to process all possible message types (in this case all subtypes of MyActorMessages), and senders will be forced to send only valid messages using <-!- method (with compilation error).
Note that senders can also use unsafe method ! to send invalid messages.
I really like the solution from #senia. It's an effective use of Akka's new Typed Channels feature. But if that solution does not suit you, I can offer something a bit more traditional to the OO world. In this solution you specify the actual message handling behavior for the actor via a strategy implementation that the actor is constructed with. The code would look something like this:
//Strategy definition
trait FooStrategy{
def foo1(s:String):String
def foo2(i:Int):Int
}
//Regular impl
class RegularFoo extends FooStrategy{
def foo1(s:String) = ...
def foo2(i:Int) = ...
}
//Other impl
class AdvancedFoo extends FooStrategy{
def foo1(s:String) = ...
def foo2(i:Int) = ...
}
//Message classes for the actor
case class Foo1(s:String)
case class Foo2(i:Int)
//Actor class taking the strategy in the constructor
class FooActor(strategy:FooStrategy) extends Actor{
def receive = {
case Foo1(s) => sender ! strategy.foo1(s)
case Foo2(i) => sender ! strategy.foo2(i)
}
}
Then to create instances of this actor:
val regFooRef = system.actorOf(Props(classOf[FooActor], new RegularFoo))
val advFooRef = system.actorOf(Props(classOf[FooActor], new AdvancedFoo))
One benefit here is that you are decoupling the business logic of the actor from it's normal message handling behavior. You are letting the actor class just do actor stuff (receive from mailbox, reply to sender, etc...) and then the real business logic is encapsulated in a trait. This also makes it much easier to test the business logic in isolation with unit tests for the trait impls. If you needed actor type stuff in the trait impls, then you could always specify an implicit ActorContext to the methods on FooStrategy, but then you would lose the complete decoupling of actor and business logic.
Like I said earlier, I like the solution from #senia; I just wanted to give you another option that might be more traditional OO.

How do I best share behavior among Akka actors?

I have two Akka actors that respond to some messages in the same way, but others in a different way. They both respond to the same set of messages. Wondering how to design my two actors with their receive methods, via inheritance, composure, etc? I tried chaining together partial functions from other traits with "orElse", which unfortunately exposes the class to its trait's functionality, plus I wasn't sure how the trait's receive could easily access the actor context. A drop-in, modularized solution would be ideal, but I'm wondering if this is a solved problem somewhere?
There's really a bunch of ways that you can go about this. I'll list two from the OO way (similar to what #Randal Schulz is suggesting) and 1 more functional way. For the first possible solution, you could do something simple like this:
case class MessageA(s:String)
case class MessageB(i:Int)
case class MessageC(d:Double)
trait MyActor extends Actor{
def receive = {
case a:MessageA =>
handleMessageA(a)
case b:MessageB =>
handleMessageB(b)
case c:MessageC =>
handleMessageC(c)
}
def handleMessageA(a:MessageA)
def handleMessageB(b:MessageB) = {
//do handling here
}
def handleMessageC(c:MessageC)
}
class MyActor1 extends MyActor{
def handleMessageA(a:MessageA) = {}
def handleMessageC(c:MessageC) = {}
}
class MyActor2 extends MyActor{
def handleMessageA(a:MessageA) = {}
def handleMessageC(c:MessageC) = {}
}
With this approach, you define basically an abstract actor impl where the receive function is defined for all messages that are handled. The messages are delegated to defs where the real business logic would be. Two are abstract, letting the concrete classes define the handling and one is fully implemented for a case where the logic does not need to differ.
Now a variant on this approach using the Strategy Pattern:
trait MessageHandlingStrategy{
def handleMessageA(a:MessageA)
def handleMessageB(b:MessageB) = {
//do handling here
}
def handleMessageC(c:MessageC)
}
class Strategy1 extends MessageHandlingStrategy{
def handleMessageA(a:MessageA) = {}
def handleMessageC(c:MessageC) = {}
}
class Strategy2 extends MessageHandlingStrategy{
def handleMessageA(a:MessageA) = {}
def handleMessageC(c:MessageC) = {}
}
class MyActor(strategy:MessageHandlingStrategy) extends Actor{
def receive = {
case a:MessageA =>
strategy.handleMessageA(a)
case b:MessageB =>
strategy.handleMessageB(b)
case c:MessageC =>
strategy.handleMessageC(c)
}
}
Here the approach is to pass in a strategy class during construction that defines the handling for a and c, with b again being handled the same regardless. The two approaches are pretty similar and accomplish the same goal. The last approach uses partial function chaining and could look like this:
trait MessageAHandling{
self: Actor =>
def handleA1:Receive = {
case a:MessageA => //handle way 1
}
def handleA2:Receive = {
case a:MessageA => //handle way 2
}
}
trait MessageBHandling{
self: Actor =>
def handleB:Receive = {
case b:MessageB => //handle b
}
}
trait MessageCHandling{
self: Actor =>
def handleC1:Receive = {
case c:MessageC => //handle way 1
}
def handleC2:Receive = {
case c:MessageC => //handle way 2
}
}
class MyActor1 extends Actor with MessageAHandling with MessageBHandling with MessageCHandling{
def receive = handleA1 orElse handleB orElse handleC1
}
class MyActor2 extends Actor with MessageAHandling with MessageBHandling with MessageCHandling{
def receive = handleA2 orElse handleB orElse handleC2
}
Here, some traits are setup that define message handling behaviors for the 3 message types. The concrete actors mix in those traits and then pick which behaviors they want when building out their receive function by using partial function chaining.
There are probably many other ways to do what you seek, but I just figured I'd throw a few options out there for you. Hope it helps.
So far I have not had reason to regret pushing as much of my services' actual functionality (the so-called "business logic") into a lower layer, "conventional" and synchronous (and sometimes blocking) library that can be unit-tested w/o the complication of actors. The only thing I place in Actor classes is the shared, long-term mutable state on which that conventional library code acts. That, of course, and the message-decoding and dispatching logic of the Akka Actor receive function.
If you do this, sharing logic in the way you seek is trivial.