unable to create another actor in Akka WebSocket - scala

class HomeController #Inject (implicit actorSystem:ActorSystem, materializer :Materializer) extends Controller
{
case class sendMsg(msg:JsValue)
class MYWSACTOR(out:ActorRef,mainActor:ActorRef) extends Actor
{
def receive =
{
case msg : JsValue =>
mainActor ! sendMsg(msg)
case sendMsg(msg) =>
{
/* validation part */
out ! msg
}
}
}
}
val mainActor = actorSystem.actorOf(Props[MYWSACTOR],"mainActor")
def object MYWSACTOR
{
def props(out:ActorRef) = Props(new MYWSACTOR(out,mainActor))
}
def socket = WebSocket.accept[JsValue,JsValue]
{
request =>
ActorFlow.actorRef(out => MYWSACTOR.props(out))
}
I am new to Akka and Scala . I am trying to make a chat application using Akka in Scala and Play framework 2.5.3 .I used the above code from the official documentation. I just wanted to create another actor (mainActor in above code) so that message from the client is validated first and then be sent back. But the problem is mainActor is unable to send the msg to another class case sendMsg. Also , if i try to create the actor at any different point, it gives me compilation error Unexpected Exception: Provisional Exception

Here is hopefully what you probably wanted:
case class SendMsg(msg: JsValue)
case class ReceivedMessage(wsActor: ActorRef, msg: JsValue)
class MyWsActor(out: ActorRef, mainActor:ActorRef) extends Actor {
def receive = {
case msg : JsValue =>
mainActor ! ReceivedMessage(self, msg)
case SendMsg(msg) =>
out ! msg
}
}
object MyWsActor {
def props(out: ActorRef, mainActor: ActorRef) = Props(new MyWsActor(out, mainActor))
//or if you want to instantiate your Props with reflection
def props2(out: ActorRef, mainActor: ActorRef) = Props(classOf[MyWsActor], out, mainActor)
}
class MainActor() extends Actor {
def receive = {
case ReceivedMessage(wsActor, msg) => {
/*
Do sth with msg
*/
val result = msg
wsActor ! SendMsg(result) //here sender is your MyWsActor
}
}
}
class HomeController #Inject() (
actorSystem: ActorSystem,
materializer: Materializer
) extends Controller {
val mainActor = actorSystem.actorOf(Props[MainActor], "mainActor")
def socket = WebSocket.accept[JsValue,JsValue] {
request =>
ActorFlow.actorRef(out => MyWsActor.props(out, mainActor))
}
}
The key is that you can send, with actual message, also ActorRef (or by default you can get a sender ActorRef calling sender()) and then you can replay to this ActorRef.

Related

Expect message on mocked actor when testing with TestKit

I am trying to test an actor, that depends on another actor. In the following test ActorA sends MessageA to ActorB and expects a MessageB in return.
Since this is a Unit test for ActorB, I am mocking ActorA. My problem now is, that i want to be sure that ActorA gets MessageA, but when i call the expectMsg (see the commented line) the whole autopilot does not seem to work anymore.
Am I doing something wrong here?
class MyTest(_system: ActorSystem) extends TestKit(_system)
with WordSpecLike
with BeforeAndAfterAll {
object Start
object MessageA
object MessageB
def this() = this(ActorSystem("TestSpec"))
override def afterAll() {
system.shutdown()
}
// will later be mocked
class ActorA extends Actor {
def receive = {
case MessageA => sender() ! MessageB
}
}
class ActorB(actorA: ActorRef) extends Actor {
def receive = {
case Start => actorA ! MessageA
case MessageB => println("Got MessageB")
}
}
"" should {
"work" in {
val actorA = TestProbe()
actorA.setAutoPilot(new AutoPilot {
override def run(sender: ActorRef, msg: Any): AutoPilot = {
msg match {
case MessageA => sender ! MessageB
}
TestActor.KeepRunning
}
})
// this will break the test
//actorA.expectMsg(MessageA)
val actor = system.actorOf(Props(new ActorB(actorA.ref)))
actor ! Start
}
}
}
You should expect MessageA after sending start to your ActorB.
val actor = system.actorOf(Props(new ActorB(actorA.ref)))
actor ! Start
// this will not break the test
actorA.expectMsg(MessageA)

How to mock an Akka Actor to Unit Test a class?

I have a Controller class which controls the requests sent to a Akka actor which is injected in to the controller.
Controller's code:
class Controller(actor: ActorRef) {
def control(msg: String): Future[String] = {
actor.ask(msg)(Timeout(2 seconds)).mapTo[String]
}
}
And my Actor's code is:
class ActorA extends Actor {
override def receive: Receive = {
case msg: String => sender ! msg
case msg: Int => sender ! msg.toString
case _ => "Invalid command!"
}
Now I need to mock ActorA's behaviour to unit test Controller. Is there a way to do so via Akka TestKit ?
Use a TestProbe. From the testing documentation:
val probe = TestProbe()
val future = probe.ref ? "hello"
probe.expectMsg(0 millis, "hello")
probe.reply("world")
assert(future.isCompleted && future.value == Some(Success("world")))

How could actor know if it has all the responses it needs in order to proceed?

I am fairly new to akka and not sure about approaching this problem.
I have a Monitor actor that spawns 2 other actors as DiskMonitor and MemoryMonitor
DiskMonitor checks the diskUsage and report DiskReport message
MemoryMonitor checks the diskUsage and report MemoryReport message
I want to combine the DiskReport and MemoryReport into Report Object and send to to remote machine via an API.
Questions
Since DiskMonitor and MemoryMonitor both are actors they send the response back to caller once they are done with their work
Monitor
diskMonitor ! DiskMonitorRequest
memoryMonitor ! MemoryMonitorRequest
How would Monitor know that it has all the results that it needs so that it can create Report object and send it via API?
Are actors good way to approach this problem?
I also read about future but I don't understand them well and not sure if they would be helpful in this problem context
This is a simple way of doing these things. Other options can be through using context.become or the trait FSM.
class Monitor extends Actor {
// Somewhere do this:
diskMonitor ! DiskMonitorRequest
memoryMonitor ! MemoryMonitorRequest
var diskMonitorResult = Option.empty[DiskMonitor]
var memoryMonitorResult = Option.empty[MemoryMonitor]
def recieve = {
case d: DiskMonitor =>
diskMonitorResult = Some(d)
checkIfCompleted()
case m: MemoryMonitor =>
memoryMonitorResult = Some(m)
checkIfCompleted()
}
def checkIfCompleted = {
(diskMonitorResult, memoryMonitorResult) match {
case (Some(diskMonitor), Some(memoryMonitor)) =>
// send to external API
externalApi ! Report(diskMonitor, memoryMonitor)
// Possibly stop this actor
case _ => // do nothing
}
}
}
You can use ask pattern and combine futures..
import akka.actor._
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.ExecutionContextExecutor
import scala.concurrent.duration._
import scala.language.postfixOps
class Monitor extends Actor with ActorLogging {
implicit val timeout: Timeout = Timeout(5 seconds)
implicit val ec: ExecutionContextExecutor = context.dispatcher
val diskM: ActorRef = context.system.actorOf(Props[DiskMonitor])
val memoryM: ActorRef = context.system.actorOf(Props[MemoryMonitor])
val remoteM: ActorRef = context.system.actorOf(Props[RemoteMachine])
override def preStart: Unit = {
log.info("Monitor Created..")
self ! GenerateReport
}
override def receive: Receive = {
case GenerateReport =>
(diskM ? MakeDiskReport).mapTo[DiskReport].zip((memoryM ? MakeMemoryReport)
.foreach { case (diskR, memR) =>
remoteM ! Report(memR.report, diskR.report)
}
}
}
class DiskMonitor extends Actor with ActorLogging {
override def receive: Receive = {
case MakeDiskReport =>
log.info("Creating DiskReport..")
val client = sender
client ! DiskReport("DiskReport")
}
}
class MemoryMonitor extends Actor with ActorLogging {
override def receive: Receive = {
case MakeMemoryReport =>
log.info("Creating MemoryReport..")
val client = sender
client ! MemoryReport("MemoryReport")
}
}
class RemoteMachine extends Actor with ActorLogging {
override def receive: Receive = {
case Report(memr, diskr) =>
log.info(s"Final Report.. $memr, $diskr")
}
}
object Main extends App {
val sys = ActorSystem()
sys.actorOf(Props[Monitor])
}
sealed trait Message
case object GenerateReport extends Message
case object MakeDiskReport extends Message
case object MakeMemoryReport extends Message
case class DiskReport(report: String) extends Message
case class MemoryReport(report: String) extends Message
case class Report(memReport: String, diskReport: String) extends Message

Private Communication between client and server using websockets play 2.3.x

I am newbie to scala and can't figure out sending private message to client using websocket.
Here is my controller:
object Client extends Controller {
def socket(uuid: String) = WebSocket.acceptWithActor[String, String] { request =>
out => ClientWebSocket.props(uuid)
}
// Test Method to send message to websocket connected client
def sendMessage(guid: String) = Action { implicit request =>
val system = ActorSystem("default")
val out = system.actorOf(Props(classOf[ClientWebSocket], guid))
out ! SendUpdate("Message Recieved")
Ok
}
}
Here is my actor class:
object ClientWebSocket {
def props(uuid: String) = Props(new ClientWebSocket(uuid))
case class SendUpdate(msg:String)
}
class ClientWebSocket(uuid: String) extends Actor {
import ClientWebSocket._
def receive = {
case SendUpdate(msg:String) =>
sender ! "Message is " + msg
}
}
When I call sendMessage with uuid of client, I am getting akka dead letters encountered error.
Any help is really appreciated.
First off, in your WebSocket method, you need to use the provided ActorRef instead of sender. sender is probably something else in the hierarchy.
class ClientWebSocket(uuid: String, out: ActorRef) extends Actor {
import ClientWebSocket._
def receive = {
case SendUpdate(msg: String) =>
out ! s"Message is $msg"
}
}
Second, you are receiving dead letters in sendMessage because you are replying to your controller, which unfortunately is not an Actor.
The problem is that you cannot get hold of the ActorRef and since you don't know the name of the actor, you cannot use an ActorSelection. So you would need to make a WebSocket call to your own app, call it from web browser / JavaScript or do some hacking with the action to get to the actor.
Edit
Your ClientWebSocket could register (e.g. in preStart via ActorSelection or via an ActorRef as props parameter) with another actor that keeps a Map[String, ActorRef] reference to all websockets and in turn monitors them using death watch. That actor would then forward your SendUpdate to the correct websocket actor. Since you can only return Props in acceptWithActor, you are not able to form a real hierarchy.
The out parameter is an ActorRef, you will use it to send messages to the Client, therefore you need to keep it in your Actor.
object Client extends Controller {
def socket(uuid: String) = WebSocket.acceptWithActor[String, String] { request =>
// out is an actor to what the outbound messages should go to respond to remote client
out => ClientWebSocket.props(uuid, out)
}
}
Your ClientWebSocket actor is as follows:
class ClientWebSocket(uuid: String, out: ActorRef) extends Actor ...
And the companion object is as follows
object ClientWebSocket {
def props(uuid: String, out: ActorRef) = Props(classOf[ClientWebSocket], uuid, out)
}
In the ClientWenSocket you can Register/Deregister the Actor with a CentralWebSocketControl Actor as:
class ClientWebSocket(uuid: String, out: ActorRef) extends Actor {
#throws[Exception](classOf[Exception])
override def preStart(): Unit = {
super.preStart()
CentralWebSocketControl.instance ! RegisterMe(uuid)
}
#throws[Exception](classOf[Exception])
override def postStop(): Unit = {
CentralWebSocketControl.instance ! UnregisterMe(uuid)
super.postStop()
}
}
The CentralWebSocketControl Actor can be as:
class CentralWebSocketControl extends Actor {
val clientActors = scala.collection.mutable.HashMap[String, ActorRef]()
override def receive: Actor.Receive = {
case RegisterMe(uuid) =>
clientActors += uuid -> sender()
case UnregisterMe(uuid) =>
clientActors -= uuid
}
}
object CentralWebSocketControl {
val instance = Akka.system.actorOf(Props(classOf[CentralWebSocketControl]), name = "CentralWebSocketControl")
}
To send a message to a given ClientWebSocket identified by an uuid, you can send a message to CentralWebSocketControl that may delegate the message to the registered ClientWebSocket.
class CentralWebSocketControl extends Actor {
val clientActors = scala.collection.mutable.HashMap[String, ActorRef]()
override def receive: Actor.Receive = {
...
case SendUpdateTo(uuid: String, msg: String) =>
clientActors.get(uuid) match {
case Some(actor) =>
actor ! SendUpdate(msg)
case None =>
???
}
}
}
and finally
class ClientWebSocket(uuid: String, out: ActorRef) extends Actor {
override def receive: Receive = {
case SendUpdate(msg) =>
out ! msg
}
...
}

Reduce code duplication by abstracting mixing class

I have to actor classes which looks similar to this form:
class ActorSupervisorOne(prop: Prop) extends Actor {
val dbSuper = context.actorOf(prop)
val subActor = context.actorOf(Props(new SubActorClass(dbSuper) with **SomeHandlersOne**))
def receive = {
case msg =>
subActor forward msg
}
}
class ActorSupervisorTwo(prop: Prop) extends Actor {
val dbSuper = context.actorOf(prop)
val subActor = context.actorOf(Props(new SubActorClass(dbSuper) with **SomeHandlersTwo**))
def receive = {
case msg =>
subActor forward msg
}
}
The only difference between them in mixing trait. Abstract it with type parameter or abstract type member won't work. I've tried the following solution, but it looks ugly and still have code duplication:
abstract class Super extends Actor {
_: {
val handler: Props
} =>
lazy val actor = context.actorOf(handler)
def receive = {
case msg =>
actor forward msg
}
}
class ActorSupervisorOne(val dbSuper: ActorRef) extends Super {
val handler = Props(new SubActorClass(dbSuper) with SomeHandlersOne)
actor
}
class ActorSupervisorTwo(val dbSuper: ActorRef) extends Super {
val handler = Props(new SubActorClass(dbSuper) with SomeHandlersTwo)
actor
}
But in this case i have to call actor to initialize it correctly or it won't work. Are there any other solution how this can be reduced?
You could probably use reflection to choose the SomeHandlersXYZ at runtime, but if you don't want to resort to reflection then I don't think that there is a way of achieving what you want without at least duplicating the subactor instantiation code (see this answer of mine for an explanation). You could do it the following way (sketched), where you basically pass in a factory function:
class ActorSupervisor(prop: Prop, getSubActor: Actor => SubActorClass) extends Actor {
val dbSuper = context.actorOf(prop)
val subActor = context.actorOf(Props(getSubActor(dbSuper)))
def receive = {
case msg =>
subActor forward msg
}
}
val asOne = new ActorSupervisor(..., a => new SubActorClass(a) with SomeHandlersOne)
val asTwo = new ActorSupervisor(..., a => new SubActorClass(a) with SomeHandlersTwo)
What about this solution:
class ActorSupervisor(subActor: => Actor) extends Actor {
val actor = context.actorOf(Props(statsProps))
def receive = {
case msg =>
actor forward msg
}
}
and then, like in Malte Schwerhoff you can create new actor like this:
val first = new ActorSupervisor(new SubActorClass(...) with SomeHandlersOne)
I think that more elegant colution can be achived with macros, but i'm not good at them
You can pass the puck on onReceive.
class ClassMixed(params: Options*)
extends BaseClass(params: _*)
with Mixin {
override def receive =
mixinReceive orElse receive
}
where Mixin has a method called mixinReceive and BaseClass overrides receive