简体   繁体   中英

Play 2.4 create an actor for handle websocket with Guice

first of all thank you all for taking some time to review my problem. I'm a newbie in scala ecosystem so I think I'm confussing some concepts.

I'm introducing Guice in a Play 2.4 project and it's working in some REST controllers. I've edited build.sbt to append routesGenerator := InjectedRoutesGenerator as Play official doc recomends and edited my routes as you can see in the following example:

GET        /socket       @com.letgo.chat.controllers.ChatController.socket

My module for injecting actors looks like:

class ChatModule extends AbstractModule with ScalaModule with AkkaGuiceSupport with GuiceAkkaActorRefProvider {
  override def configure() {

    bindActor[TalkerProviderActor](TalkerProviderActor.name)
    ...

And all of this seems to be working.

But another endpoint handles a websocket with Play method WebSocket.acceptWithActor . I need to create an actor which also needs some dependencies.

The controller creates a ConnectionActor :

class ConnectionActor(
  @Assisted() websocket: ActorRef,
  monitoring: Monitoring,
  @Named(BouncerActor.name) bouncer: ActorRef,
) extends Actor

...

class ChatController @Inject() extends Controller {
  def socket(): WebSocket[String, JsValue] = WebSocket.acceptWithActor[String, JsValue] { request => out =>
    // I had the following statement in order to build the actor before introducing Guice:
    // ConnectionActor.props()
    // Now, I need some magic here
  }
}

So as you can see I need a websocket: ActorRef for the websocket output.

I've created some actors in other parts of the application using bindActorFactory method provided by AkkaGuiceSupport :

bindActorFactory[TalkerActor, TalkerActor.Factory]

But I don't know how I should create an actor for handling the websocket in this case. Can you guys help me?

Thank you

I believe this can do the trick:

package controllers

import javax.inject._

import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.stream.Materializer
import play.api.libs.streams.ActorFlow
import play.api.libs.ws.WSClient
import play.api.mvc._

import scala.concurrent.ExecutionContext.Implicits.global

@Singleton
class SocketActorProvider @Inject() (ws: WSClient) {
  def get(out: ActorRef) = Props(new SocketActor(out, ws))
}

class SocketActor(out: ActorRef, ws: WSClient) extends Actor {
  override def receive: Receive = {
    case "ping" => ws.url("http://localhost:9000/pong").get().foreach(pong => out ! pong.body)
  }
}

@Singleton
class HomeController @Inject() (implicit system: ActorSystem, ws: WSClient, materializer: Materializer, provider: SocketActorProvider) extends Controller {

  // route '/ws'
  def socket = WebSocket.accept[String, String] { request =>
    ActorFlow.actorRef(out => provider.get(out))
  }

  // route '/pong'
  def pong = Action {
    Ok("PONG!")
  }

  // route '/'
  def index = Action {
    Ok("""
      <script>
        var ws = new WebSocket('ws://localhost:9000/ws');
        ws.onopen = () => console.log('I am open!');
        ws.onmessage = m => console.log(m.data);

        setInterval(() => {console.log('ping'); ws.send('ping')}, 1000)
      </script>
    """).as("text/html")
  }

}

As you can see there is a provider injected by Guice which had WSClient injected and when building an actor it passes in the dependency

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM