Reputation: 691
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
Upvotes: 1
Views: 391
Reputation: 2952
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
Upvotes: 3