Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Play Scala Akka WebSockets change actor path

I've followed the example for creating Web Sockets with Scala Play and Akka actors:

https://www.playframework.com/documentation/2.5.x/ScalaWebSockets#Handling-WebSockets-with-Akka-Streams-and-actors

On resume, the controller:

import play.api.mvc._
import play.api.libs.streams._

class Controller1 @Inject() (implicit system: ActorSystem, materializer: Materializer) {

  def socket = WebSocket.accept[String, String] { request =>
    ActorFlow.actorRef(out => MyWebSocketActor.props(out))
  }

And the Actor:

import akka.actor._

object MyWebSocketActor {
  def props(out: ActorRef) = Props(new MyWebSocketActor(out))
}

class MyWebSocketActor(out: ActorRef) extends Actor {
  def receive = {
    case msg: String =>
      out ! ("I received your message: " + msg)
  }
}

The actors created (one per websocket connection) are child of /user actor. I've created 3 connections and the actor created were:

  1. /user/$b
  2. /user/$c
  3. /user/$d

I want to change the actors' name based in a field of the web socket message. How could i do this?.

like image 891
gutiory Avatar asked Mar 12 '23 07:03

gutiory


1 Answers

You can set the name of the actor as follows:

  1. Create a file BetterActorFlow.scala

    package your.package
    
    import akka.actor._
    import akka.stream.scaladsl.{Keep, Sink, Source, Flow}
    import akka.stream.{Materializer, OverflowStrategy}
    
    object BetterActorFlow {
    
      def actorRef[In, Out](props: ActorRef => Props, bufferSize: Int = 16, overflowStrategy: OverflowStrategy = OverflowStrategy.dropNew, maybeName: Option[String] = None)(implicit factory: ActorRefFactory, mat: Materializer): Flow[In, Out, _] = {
    
        val (outActor, publisher) = Source.actorRef[Out](bufferSize, overflowStrategy)
                            .toMat(Sink.asPublisher(false))(Keep.both).run()
    
        def flowActorProps: Props = {
          Props(new Actor {
            val flowActor = context.watch(context.actorOf(props(outActor), "flowActor"))
    
            def receive = {
              case Status.Success(_) | Status.Failure(_) => flowActor ! PoisonPill
              case Terminated(_) => context.stop(self)
              case other => flowActor ! other
            }
    
            override def supervisorStrategy = OneForOneStrategy() { case _ => SupervisorStrategy.Stop }
          })
        }
    
        def actorRefForSink =
          maybeName.fold(factory.actorOf(flowActorProps)) { name => factory.actorOf(flowActorProps, name) }
    
        Flow.fromSinkAndSource(Sink.actorRef(actorRefForSink, Status.Success(())), Source.fromPublisher(publisher))
    
      }
    }
    
  2. Use BetterActorFlow instead of ActorFlow:

    BetterActorFlow.actorRef(out =>
      ChatActor.props(out), 16, OverflowStrategy.dropNew, Some("alicebob"))
    

This worked for me. The created actor is at user/alicebob (use this with context.system.actorSelection("user/alicebob"))

like image 198
Shafique Jamal Avatar answered Mar 21 '23 01:03

Shafique Jamal