Good day. I'm making a simple program which check's some server state and faced the issue with pattern matching.
Here is the code:
Entry point:
object Run extends App with StateActor.Api{
private implicit val system = ActorSystem()
implicit val blockingDispatcher: MessageDispatcher = system.dispatchers.lookup("blocking-dispatcher")
protected val log: LoggingAdapter = Logging(system, getClass)
protected implicit val materializer: ActorMaterializer = ActorMaterializer()
import scala.concurrent.duration._
implicit val timeout = Timeout(17 seconds)
val listener = system.actorOf(StateActor.props)
system.scheduler.schedule(
0 milliseconds,
5 minutes,
listener,
Ping
)
}
Actor:
class StateActor(implicit val blockingDispatcher: MessageDispatcher) extends Actor with StateActor.Api with ActorLogging {
import akka.pattern.pipe
private val formatter = JSONFormat.defaultFormatter
private val mHookUrl = ...
var mState: State = UNDEFINED
override def receive: Receive = {
case Ping =>
log.debug("Ping")
Future(Http("http://...").timeout(15000, 15000).asString)
.map {
case HttpResponse(_, 200, _) => UpResponse
case HttpResponse(body, code, _) => DownResponse(s"Code: $code, body:\n $body")
case rest => DownResponse(s"Undefined object: ${rest.toString}")
} recover { case e => DownResponse(e.getMessage) } pipeTo self
case UpResponse =>
if (mState == DOWN || mState == UNDEFINED) {
mState == UP
reportToSlack("Client Up")
}
case DownResponse(reason) =>
if (mState == UP || mState == UNDEFINED) {
mState == DOWN
reportToSlack(s"Client DOWN!\n Reason: $reason")
}
case other =>
println(other)
println(other.getClass)
}
def reportToSlack(message: String): Unit = {
...
}
}
object StateActor {
trait Api {
case object Ping
sealed trait State
case object UP extends State
case object DOWN extends State
case object UNDEFINED extends State
sealed trait StateMessage
case object UpResponse extends StateMessage
case class DownResponse(reason: String) extends StateMessage
}
def props(implicit blockingDispatcher: MessageDispatcher) = Props(new StateActor())
}
As you can see, I put all messages and other stuff intoto trait "API" inside "StateActor" companion object. But when scheduler sends "Ping" to actor, it matches 'case other', not 'case Ping'. Problem can be solved just by moving 'case object Ping' out from trait and companion object and making it 'stand alone' object. Like this:
case object Ping
object StateActor {
trait Api {
...
}
...
}
But why it doesn't work when it's inside trait? All other case classes and objects in trait pattern match just fine.
Run and StateActor both extend the trait separately, so each has its own Ping object and they shouldn't match. The only reason other messages match is because the StateActor is sending them to itself! It wouldn't even work with two different StateActor instances.
Instead of
moving 'case object Ping' out from trait and companion object
you should make Api an object and make the messages accessible by importing them: import StateActor.Api._ instead of extends StateActor.Api (or put them directly into object StateActor).
Related
I have a parent actor named "manager" which creates several child actors.
These child actors then send back their response via "sender tell", i.e directly back to "manager".
I want to create a unit test for this manager actor, and therefore need to inject a probe to forward the messages from the manager to its children.
I used the following post:
http://www.superloopy.io/articles/2013/injecting-akka-testprobe.html
However i'm still having some trouble getting this done correctly.
In order to simplify the situation, attached is code describing the actors and unit test i wrote for just one child.
Manager class:
trait ManagerChildProvider {
def createTimestampPointChild: Actor
}
trait ProductionManagerChildProvider extends ManagerChildProvider {
def createTimestampPointChild = new TimeDifferenceCalculationActor
}
object Manager {
def apply() = new Manager("cid1") with ProductionManagerChildProvider
}
class Manager(name: String) extends Actor with ActorLogging {
this: ManagerChildProvider =>
#Autowired private val modelParams = new ModelParams //list of parameters
val timeDifference = context.actorOf(Props(createTimestampPointChild))
def receive = {
case p#TimePoint(tPoint) =>
timeDifference ! p
case _ =>
log.error("Unknown message type")
}
}
Child class:
class TimeDifferenceCalculationActor extends Actor with ActorLogging {
var previousTimestamp: Long = -1
def receive = {
case tPoint(timestamp) =>
if (previousTimestamp != -1) {
sender ! Result(1)
}
case _ =>
log.error("Unknown message type")
}
}
Test class:
object BarSpec {
class Wrapper(target: ActorRef) extends Actor {
def receive = {
case x => target forward x
}
}
}
trait ChildrenProvider {
def newFoo: Actor
}
class BarSpec extends TestKitSpec("BarSpec") {
import Manager._
import BarSpec._
trait TestCase {
val probe = TestProbe()
trait TestChildrenProvider extends ManagerChildProvider {
def newBar = new Wrapper(probe.ref)
}
val actor = system.actorOf(Props(new Manager(componentId = "cid1") with TestChildrenProvider))
}
"Bar" should {
"involve child in doing something" in new TestCase {
actor ! tPoint(1)
actor ! tPoint(2)
probe.expectMsg(tPoint(1))
//probe.reply("ReplyFromChild")
//expectMsg("ReplyFromParent")
}
}
}
Additional test class:
abstract class TestKitSpec(name: String) extends TestKit(ActorSystem(name)) with MustMatchers with BeforeAndAfterAll with ImplicitSender with WordSpecLike{
override def afterAll() {
system.shutdown()
}
}
Currently i get the following errors:
Error:(36, 42) object creation impossible, since method > createTimestampPointChild in trait ManagerChildProvider of > type => akka.actor.Actor is not defined
val actor = system.actorOf(Props(new Manager(componentId = "cid1") with TestChildrenProvider))
Error:(11, 16) overriding method run in trait BeforeAndAfterAll of type > (testName: Option[String], args: > org.scalatest.Args)org.scalatest.Status;
method run in trait WordSpecLike of type (testName: Option[String], > args: org.scalatest.Args)org.scalatest.Status needs `abstract override' > modifiers
abstract class TestKitSpec(name: String) extends > TestKit(ActorSystem(name))
any help with these specific errors or with the task in general would be highly appreciated
Using Spray with spray-json for a system, version:
"io.spray" %% "spray-json" % "1.2.6"
I cannot figure how to get custom JsonFormat definitions to work for serialization that is being handled by spray-routing.
I've had two separate circumstances that have failed.
1. Nested Case Classes
Basic case class JSON serialization has worked fine
case class Something(a: String, b: String)
implicit val something2Json = jsonFormat3(Something)
However if I have a nested case class in the case class to be serialized, I can resolve compile issues by providing another JsonFormat implicit, yet at run-time it refuses to serialize
case class Subrecord(value: String)
case class Record(a: String, b: String, subrecord: Subrecord)
object MyJsonProtocol extends DefaultJsonProtocol {
implicit object SubrecordJsonFormat extends JsonFormat[Subrecord] {
def write(sub: Subrecord) = JsString(sub.value)
def read(value: JsValue) = value match {
case JsString(s) => Subrecord(s)
case _ => throw new DeserializationException("Cannot parse Subrecord")
}
}
implicit val record2Json = jsonFormat3(Record)
}
This will throw a MappingException at runtime, explaining there is no usable value for subrecord
2. Trait with various 0-N case extensions
Here I have a trait that serves as a capturing type for a group of case classes. Some of the extending classes have vals while others have no vals and are objects. When serialization occurs, it seems like my implicit defined JsonFormat is completely ignored and I'm just give an empty JsObject, particularly when the actual underlying type was one of the case object's with no vals.
sealed trait Errors
sealed trait ErrorsWithReason extends Errors {
def reason: String
}
case class ValidationError(reason: String) extends ErrorsWithReason
case object EntityNotFound extends Errors
case class DatabaseError(reason: String) extends ErrorsWithReason
object MyJsonProtocol extends DefaultJsonProtocol {
implicit object ErrorsJsonFormat extends JsonFormat[Errors] {
def write(err: Errors) = failure match {
case e: ErrorsWithReason => JsString(e.reason)
case x => JsString(x.toString())
}
def read(value: JsValue) = {
value match {
//Really only intended to serialize to JSON for API responses
case _ => throw new DeserializationException("Can't reliably deserialize Error")
}
}
}
}
So given the above, if the actual type being serialized is EntityNotFound, then the serialization becomes a RootJsonFormat turning into {}. If it's an ErrorsWithReason then it becomes a RootJsonFormat turning into { "reason": "somevalue" }. I may be confused with how the JsonFormat definition is supposed to work, but it doesn't seem to be using my write method at all and instead has suddenly figured out how to serialize on its own.
EDIT
Specific serialization cases are using read/deserialization like:
entity(as[JObject]) { json =>
val extraction: A = json.extract[A]
}
And write/serialization with the complete directive.
I now am realizing thanks to the first answer posted here that my JsonDefaultProtocol and JsonFormat implementations are for spray-json classes, meanwhile the entity directive extraction in the deserialization is using json4s JObject as opposed to spray-json JsObject.
Another approach for clean JSON output
import spray.json._
import spray.json.DefaultJsonProtocol._
// #1. Subrecords
case class Subrecord(value: String)
case class Record(a: String, b: String, subrecord: Subrecord)
implicit object RecordFormat extends JsonFormat[Record] {
def write(obj: Record): JsValue = {
JsObject(
("a", JsString(obj.a)),
("b", JsString(obj.b)),
("reason", JsString(obj.subrecord.value))
)
}
def read(json: JsValue): Record = json match {
case JsObject(fields)
if fields.isDefinedAt("a") & fields.isDefinedAt("b") & fields.isDefinedAt("reason") =>
Record(fields("a").convertTo[String],
fields("b").convertTo[String],
Subrecord(fields("reason").convertTo[String])
)
case _ => deserializationError("Not a Record")
}
}
val record = Record("first", "other", Subrecord("some error message"))
val recordToJson = record.toJson
val recordFromJson = recordToJson.convertTo[Record]
println(recordToJson)
assert(recordFromJson == record)
If you need both reads and writes you can do it this way:
import spray.json._
import spray.json.DefaultJsonProtocol._
// #1. Subrecords
case class Subrecord(value: String)
case class Record(a: String, b: String, subrecord: Subrecord)
implicit val subrecordFormat = jsonFormat1(Subrecord)
implicit val recordFormat = jsonFormat3(Record)
val record = Record("a", "b", Subrecord("c"))
val recordToJson = record.toJson
val recordFromJson = recordToJson.convertTo[Record]
assert(recordFromJson == record)
// #2. Sealed traits
sealed trait Errors
sealed trait ErrorsWithReason extends Errors {
def reason: String
}
case class ValidationError(reason: String) extends ErrorsWithReason
case object EntityNotFound extends Errors
case class DatabaseError(reason: String) extends ErrorsWithReason
implicit object ErrorsJsonFormat extends JsonFormat[Errors] {
def write(err: Errors) = err match {
case ValidationError(reason) =>
JsObject(
("error", JsString("ValidationError")),
("reason", JsString(reason))
)
case DatabaseError(reason) =>
JsObject(
("error", JsString("DatabaseError")),
("reason", JsString(reason))
)
case EntityNotFound => JsString("EntityNotFound")
}
def read(value: JsValue) = value match {
case JsString("EntityNotFound") => EntityNotFound
case JsObject(fields) if fields("error") == JsString("ValidationError") =>
ValidationError(fields("reason").convertTo[String])
case JsObject(fields) if fields("error") == JsString("DatabaseError") =>
DatabaseError(fields("reason").convertTo[String])
}
}
val validationError: Errors = ValidationError("error")
val databaseError: Errors = DatabaseError("error")
val entityNotFound: Errors = EntityNotFound
assert(validationError.toJson.convertTo[Errors] == validationError)
assert(databaseError.toJson.convertTo[Errors] == databaseError)
assert(entityNotFound.toJson.convertTo[Errors] == entityNotFound)
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!
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!
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.
I think that you need something like this
package akka
import akka.MsgsProt._
import akka.actor.{ Actor, ActorSystem, Props }
import scala.concurrent.duration._
sealed trait MsgProt
object MsgsProt {
case object FooMsg extends MsgProt
case object BarMsg extends MsgProt
}
trait Foo extends Actor {
override protected[akka] def aroundReceive(receive: Actor.Receive, msg: Any): Unit = msg match {
case FooMsg => println("Foo message")
case msg => super.aroundReceive(receive, msg)
}
}
trait Bar extends Actor {
override protected[akka] def aroundReceive(receive: Actor.Receive, msg: Any): Unit = msg match {
case BarMsg => println("Bar message")
case msg => super.aroundReceive(receive, msg)
}
}
class MyActor extends Actor with Foo with Bar {
override def receive: Actor.Receive = {
case _ => println("Nothing I know")
}
}
object Foo extends App {
val system = ActorSystem("foobar")
val myActor = system.actorOf(Props[MyActor])
implicit val timeout = 2 seconds
myActor ! FooMsg
myActor ! BarMsg
myActor ! "wrong message"
system.awaitTermination(10 seconds)
}
The output of this program is:
Foo message
Bar message
Nothing I know
Most important part is that package declaration - akka. Because method aroundReceive is limited only to akka package so you have to have some.package.akka and inside you can use that method aroundReceive. I think that it looks more like a hack not a solution but works. You can see more usage of this inside Akka itself ex. akka.DiagnosticActorLogging. But this is basically solution that you want to do with stacking Actors behaviour.
I have a trait and implementing akka actor that can't properly differentiate the types of incoming messages due to type erasure, and so the first case is matching all messages.
I'm using scala 2.10.x, and from reading many other answers I know that it is possible to recover type information inside a trait using TypeTag or ClassTag, but I can't figure out how to apply it (if it is possible) within the akka receive.
My (very simplified) example is as follows. Is it possible properly match the generic types?
package com.ebay.box.canada.batch.jobs.siteMap
import akka.actor.Actor
import akka.actor.ActorSelection
import akka.actor.Actor.Receive
import scala.reflect.ClassTag
trait MessageProcessor[A,B] {
this: Actor =>
val destA: ActorSelection
val destB: ActorSelection
def processA(a: A): A
def processB(a: B): B
def receive: PartialFunction[Any,Unit] = {
case a: A =>
destA ! processA(a)
case b: B =>
destB ! processB(b)
}
}
class StringIntProcessor(val destA: ActorSelection, val destB: ActorSelection) extends MessageProcessor[String,Int] with Actor {
def processA(a: String) = { a + "1" }
def processB(b: Int) = { b + 1 }
}
I don't think you can get at TypeTag[A] or ClassTag[A] in your trait -- type tags/class tags are always part of the implicit argument list to a method call. You might be able to use an abstract class instead, with implicit constructor arguments:
import scala.reflect.runtime.universe._
abstract class MessageProcessor[A,B]()(implicit cta: ClassTag[A], ctb: ClassTag[B]) {
def receive = {
case a: Any if a.getClass() == cta.runtimeClass =>
process(a.asInstanceOf[A])
...
}
...
}
(Not tested!)
Supposing that you can change the code that sends the message, can I suggest the following design instead? MessageProcessor is now a typeclass, so that you can add any number of message types. By sending a closure as the message, you can smuggle any amount of context into the call site.
class MessageReceiver extends Actor {
def receive = {
case fn: Function0[Unit] =>
fn()
}
}
trait MessageProcessor[A] {
val dest: ActorSelection
def process(a: A): A
}
object Processors {
implicit object StringProcessor extends MessageProcessor[String] {
val dest: ActorSelection = Wellknown.stringDest
def process(a: String): String = a + "1"
}
implicit object IntProcessor extends MessageProcessor[Int] {
val dest: ActorSelection = Wellknown.intDest
def process(a: Int): Int = a + 1
}
def sendMessage[A](msg: A)(implicit ev:[MessageProcessor[A]]): Unit = {
val block: Function0[Unit] = { () =>
ev.dest ! ev.process(msg)
}
val msgDest = system.actorOf[Props[MessageReceiver]], "msgDest")
msgDest ! block
}
}
(Also not tested!)
Consider these two traits:
trait Poked extends Actor {
override def receive = {
case Poke(port, x) => ReceivePoke(port, x)
}
def ReceivePoke(port: String, x: Any)
}
trait Peeked extends Actor {
override def receive = {
case Peek(port) => ReceivePeek(port)
}
def ReceivePeek(port: String)
}
Now consider I can create a new Actor that implements both traits:
val peekedpoked = actorRef(new Actor extends Poked with Peeked)
How do I compose the receive handlers? i.e., the receiver should be something like the following code, though "automatically generated" (i.e., all traits should compose):
def receive = (Poked.receive: Receive) orElse (Peeked.receive: Receive) orElse ...
You can use super[T] to reference members of particular super classes/traits.
For example:
trait IntActor extends Actor {
def receive = {
case i: Int => println("Int!")
}
}
trait StringActor extends Actor {
def receive = {
case s: String => println("String!")
}
}
class IntOrString extends Actor with IntActor with StringActor {
override def receive = super[IntActor].receive orElse super[StringActor].receive
}
val a = actorOf[IntOrString].start
a ! 5 //prints Int!
a ! "Hello" //prints String!
Edit:
In response to Hugo's comment, here's a solution that allows you to compose the mixins without having to manually wire their receives together. Essentially it involves a base trait with a mutable List[Receive], and each mixed-in trait calls a method to add its own receive to the list.
trait ComposableActor extends Actor {
private var receives: List[Receive] = List()
protected def registerReceive(receive: Receive) {
receives = receive :: receives
}
def receive = receives reduce {_ orElse _}
}
trait IntActor extends ComposableActor {
registerReceive {
case i: Int => println("Int!")
}
}
trait StringActor extends ComposableActor {
registerReceive {
case s: String => println("String!")
}
}
val a = actorOf(new ComposableActor with IntActor with StringActor).start
a ! 5 //prints Int!
a ! "test" //prints String!
The only thing to keep in mind is that the order of the receives should not be important, since you won't be able to easily predict which one is first in the chain, though you could solve that by using a mutable hashmap instead of a list.
You can use empty Receive in base actor class and chain receives in their definitions.
Sample for Akka 2.0-M2:
import akka.actor.Actor
import akka.actor.Props
import akka.event.Logging
import akka.actor.ActorSystem
class Logger extends Actor {
val log = Logging(context.system, this)
override def receive = new Receive {
def apply(any: Any) = {}
def isDefinedAt(any: Any) = false
}
}
trait Errors extends Logger {
override def receive = super.receive orElse {
case "error" => log.info("received error")
}
}
trait Warns extends Logger {
override def receive = super.receive orElse {
case "warn" => log.info("received warn")
}
}
object Main extends App {
val system = ActorSystem("mysystem")
val actor = system.actorOf(Props(new Logger with Errors with Warns), name = "logger")
actor ! "error"
actor ! "warn"
}