3

According to the Play documentation on WebSockets the standard way to establish a WebSocket is to use ActorFlow.actorRef, which takes a function returning the Props of my actor. My goal is to get a reference to this underlying ActorRef, for instance in order to send a first message or to pass the ActorRef to another actor's constructor.

In terms of the minimal example from the documentation, I'm trying to achieve this:

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

  def socket = WebSocket.accept[String, String] { request =>
    val flow = ActorFlow.actorRef { out => MyWebSocketActor.props(out) }
    // How to get the ActorRef that is created by MyWebSocketActor.props(out)?
    // Fictitious syntax (does not work)
    flow.underlyingActor ! "first message send"
    flow
  }
}

How can I get a reference to the actor that is created?

If it is not possible to get an ActorRef at this point (does it require materialization of the flow?), what would be the easiest way to store a reference to the created actor?

bluenote10
  • 23,414
  • 14
  • 122
  • 178

1 Answers1

1

Using Actor.preStart() hook you can do some tricks to access the actorRef:

class MyWebSocketActor(
  out: ActorRef, 
  firstMessage: Any, 
  actorRegistry: ActorRef
) extends Actor {
  import play.api.libs.json.JsValue
  override def preStart(): Unit = {
    self ! firstMessage
    actorRegistry ! self
  }
  ...
}

def socket = WebSocket.accept[String, String] { request =>
  ActorFlow.actorRef { out => 
    Props(new MyWebSocketActor(out, "First Message", someRegistryActorRef)) 
  }
}
Tair
  • 3,779
  • 2
  • 20
  • 33
  • I was hoping I can avoid the `actorRegistry` approach. I have implemented a solution going in this direction in my actual use case, but the resulting code is fairly complex and it feels like I have to scatter a very simple logic into many unwanted pieces. – bluenote10 Mar 22 '17 at 10:39
  • @bluenote10 I would argue, that getting the underlying actorRef breaks encapsulation, because its lifecycle is managed by the flow, and you can end up with a ref to a dead actor, because that actor's name is not unique, thus you can't count on its mailbox being delivered after respawn. – Tair Mar 22 '17 at 11:19
  • @bluenote10 I don't really know the _right_ way, but there is nothing stopping you from reimplementing `ActorFlow.actorOf`. The source code is open :) – Tair Mar 22 '17 at 11:22
  • I tried but I don't think it is possible, because the reference to the output actor is only available later. – bluenote10 Mar 24 '17 at 19:59
  • 1
    @bluenote10 It is possible, here is a quick prototype https://gist.github.com/tsabirgaliev/4168639e991bd13e364c54def5c6d2cd – Tair Mar 24 '17 at 20:45