I am making a Play web-socket app. When a client connects, I want to send a welcome message.
The code I use is below:
package controllers
import play.api._
import play.api.mvc._
import play.api.libs.iteratee.Concurrent
import play.api.libs.iteratee.Iteratee
import play.api.libs.concurrent.Execution.Implicits.defaultContext
object Test extends Controller {
def index = WebSocket.using[String] { _ =>
val (out,channel) = Concurrent.broadcast[String]
channel.push("Welcome to MyWebSocket")
val in = Iteratee.foreach[String] {
_ match {
case any => channel.push(any)
}
}
(in, out)
}
}
The code works fine when a client sends a message and the server has to respond to it. However, the initial welcome message Welcome to MyWebSocket is not sent. How can I fix this code?
[EDIT]
I kind of figured out the problem, but not a solution yet. The problem probably occurs because the websocket is not yet initialized when the welcome message is being pushed. I modified the code and replaced:
channel.push("Welcome to MyWebSocket")
with
val a = scala.concurrent.Future {
Thread.sleep(1000)
channel.push("Welcome to MyWebSocket")
}
After this I get the expected results (welcome message received by client). I think using the above approach (Thread.sleep and Future) is not the right way to solve this problem, so other solutions are welcome. It could also be a problem with the client side code which takes a while to initialize the socket. I used Firefox and echo web-socket test for the client.
You can use WebSocket.acceptWithActor helper method (have a look at this) and in actor body make something like
out ! "Welcome to MyWebSocket"
It works nicely.
Related
Can I use Slick / Play Framework (Scala) to listen to PostgreSQL NOTIFY statements?
I want to do something similar to this:
http://bjorngylling.com/2011-04-13/postgres-listen-notify-with-node-js.html
I don't think Slick supports PostgreSQL's NOTIFY, but the postgresql-async library does. One can use the latter to create an Akka Streams Source and incorporate it in a Play endpoint that streams the database notifications to a client:
package controllers
import javax.inject.{Inject, Singleton}
import akka.actor._
import akka.stream._
import akka.stream.scaladsl._
import com.github.mauricio.async.db.postgresql.PostgreSQLConnection
import com.github.mauricio.async.db.postgresql.util.URLParser
import com.github.mauricio.async.db.util.ExecutorServiceUtils.CachedExecutionContext
import play.api.Logger
import play.api.http.ContentTypes
import play.api.libs.EventSource
import play.api.mvc._
import scala.concurrent.duration._
import scala.concurrent.Await
#Singleton
class DbNotificationController #Inject()(cc: ControllerComponents,
materializer: Materializer)
extends AbstractController(cc) {
implicit val mat = materializer
val configuration = URLParser.parse("jdbc:postgresql://localhost:5233/my_db?user=dbuser&password=pwd")
val connection = new PostgreSQLConnection(configuration)
Await.result(connection.connect, 5 seconds)
val (actor, dbSource) =
Source.actorRef[String](Int.MaxValue, OverflowStrategy.dropNew)
.toMat(BroadcastHub.sink[String])(Keep.both)
.run()
connection.sendQuery("LISTEN my_channel")
connection.registerNotifyListener { message =>
val msg = message.payload
Logger.debug(s"Sending the payload: $msg")
actor ! msg
}
def index() = Action {
Ok(views.html.scaladbnotification())
}
def streamDb() = Action {
Ok.chunked(dbSource via EventSource.flow).as(ContentTypes.EVENT_STREAM)
}
}
In the above controller, when the listener receives a notification from the database, the payload (a String) in the notification is logged and sent to an actor. The messages that are sent to this actor feed the Source that is used in the streamDb endpoint. Before the payload messages are sent to the client, they're converted to Play's EventSource class.
I adapted DbNotificationController from the Play streaming example application, which you can use to experiment. If you would like to do so, obviously you need to integrate DbNotificationController into that project:
Add "com.github.mauricio" %% "postgresql-async" % "0.2.21" to build.sbt.
Set up PostgreSQL as needed, including the NOTIFY, and adjust the database URL in the controller according to your configuration.
Copy and paste DbNotificationController into /app/controllers/.
Copy the following file (call it scaladbnotification.scala.html) into app/views/:
#main {
<h1>Server Sent Event from DB</h1>
<h1 id="db"></h1>
<p>
DB events are pushed from the Server using a Server Sent Event connection.
</p>
<script type="text/javascript" charset="utf-8">
if (!!window.EventSource) {
var stringSource = new EventSource("#routes.DbNotificationController.streamDb()");
stringSource.addEventListener('message', function(e) {
$('#db').html(e.data.replace(/(\d)/g, '<span>$1</span>'))
});
} else {
$("#db").html("Sorry. This browser doesn't seem to support Server sent event. Check <a href='http://html5test.com/compare/feature/communication-eventSource.html'>html5test</a> for browser compatibility.");
}
</script>
}
In the /conf/routes file, add the following lines:
GET /scala/dbNotification controllers.DbNotificationController.index()
GET /scala/dbNotification/liveDb controllers.DbNotificationController.streamDb()
Start the application with sbt run and navigate to the following URL in your browser:
http://localhost:9000/scala/dbNotification
I need to make some consuming calculations on the server side (such as DB querying and data analisys). And the results need to be printed in browser. For these purpose I send Future result from server to client (to load web page immediately and gradually print future results from server). For example, on the server side
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
def futureResult = Future {
val cc = ConsumingCalculations();
"some result"
}
on the client side
#import scala.concurrent.ExecutionContext.Implicits.global
#main{
#futureResult.onSuccess{ case res =>
#println("This line is printed in console: "+res);
<div>Any html code is NOT printed in browser</div>
}
Future result is NOT posted
}
In server consol we have: "This line is printed in console: some result"
But in the browser we have only: "Future result is NOT posted"
Play 2.1, scala 2.10 are currently used. What's may be wrong, are there any idea?
A future cannot be sent on client side, it must be resolved on server side before displaying to the client.
The classic exemple is to map the result of your future in your controller
def myAction = Action {
Async {
futureResult.map(result =>
Ok(views.html.myView(result))
)
}
}
And in your template, use the result, not the future.
I'd like to be able to send back a response to the client before I do my logging/cleanup for a request.
In play 1.x this was possible with the #Finally annotation. I've read through some posts that say that those annotations were replaced by action composition, but I'm unclear how to emulate the #Finally annotation using it.
It seems to me that the response will only be returned after all the logic in my custom actions has completed.
Have I missed something, or is there no way to do this in Play 2.0?
[EDIT FOR CLARITY]
In other words, I want to be able to run logic after I receive a request and send a response. So I'd like to be able to construct a timeline of the form:
Client sends a request to my server
Server sends back a 200 response, which the client receives
The server does additional processing, logging, etc
In play 1.x I believe I could annote my additional processing logic with a #Finally and have it work like I want it to.
Action composition is not sufficient to do the job, but Action composition + Future, or Action composition + Actors are good ways to achieve this.
Action Composition + Future
Generate your Response, launch your logging/processing in an async context and, in parallel, send the result.
def LoggedAction(f: Request[AnyContent] => Result) = Action { request =>
val result = f(request)
concurrent.future(myLogAction(request, result))
result
}
Action composition + Actors
It's a cleaner way to achieve that. As in the previous case, generate your response, send logging/processing event to your(s) actor(s), and in parallel, send the result.
import play.api._
import play.api.mvc._
import play.libs._
import akka.actor._
object Application extends Controller with Finally {
def index = LoggedAction { r =>
Ok(views.html.index("Your new application is ready."))
}
}
trait Finally {
self: Controller =>
lazy val logActor = Akka.system.actorOf(Props(new LogActor), name = "logActor")
def LoggedAction(f: Request[AnyContent] => Result) = Action { request =>
val result = f(request) // Generate response
logActor ! LogRequest(request) // Send log event to LogActor
println("-> now send page to client")
result
}
case class LogRequest(request: Request[AnyContent])
class LogActor extends Actor {
def receive = {
case LogRequest(req) => {
println(req.host)
// ....
}
}
}
}
// Console
-> now send page to client
127.0.0.1:9000
I am writing a simple chat server, and I want to keep it as simple as possible. My server listed below only receives connections and stores them in the clients set. Incoming messages are then broadcasted to all clients on that Server. The server works with no problem, but on the client side, the RemoteActor stops my program from termination. Is there a way to remove the Actor on my client without terminating the Actor on the Server?
I don't want to use a "one actor per client" model yet.
import actors.{Actor,OutputChannel}
import actors.remote.RemoteActor
object Server extends Actor{
val clients = new collection.mutable.HashSet[OutputChannel[Any]]
def act{
loop{
react{
case 'Connect =>
clients += sender
case 'Disconnect =>
clients -= sender
case message:String =>
for(client <- clients)
client ! message
}
}
}
def main(args:Array[String]){
start
RemoteActor.alive(9999)
RemoteActor.register('server,this)
}
}
my client would then look like this
val server = RemoteActor.select(Node("localhost",9999),'server)
server.send('Connect,messageHandler) //answers will be redirected to the messageHandler
/*do something until quit*/
server ! 'Disconnect
I would suggest placing the client side code into an actor itself - ie not calling alive/register in the main thread
(implied by http://www.scala-lang.org/api/current/scala/actors/remote/RemoteActor$.html)
something like
//body of your main:
val client = actor {
alive(..)
register(...)
loop {
receive {
case 'QUIT => exit()
}
}
}
client.start
//then to quit:
client ! 'QUIT
Or similar (sorry I am not using 2.8 so might have messed something up - feel free to edit if you make it actually work for you !).
So I want to write some network code that appears to be blocking, without actually blocking a thread. I'm going to send some data out on the wire, and have a 'queue' of responses that will come back over the network. I wrote up a very simple proof of concept, inspired by the producer/consumer example on the actor tutorial found here: http://www.scala-lang.org/node/242
The thing is, using receive appears to take up a thread, and so I'm wondering if theres anyway to not take up a thread and still get the 'blocking feel'. Heres my code sample:
import scala.actors.Actor._;
import scala.actors.Actor;
case class Request(val s:String);
case class Message(val s:String);
class Connection {
private val act:Actor = actor {
loop {
react {
case m:Message => receive { case r:Request => reply { m } }
}
}
}
def getNextResponse(): Message = {
return (act !? new Request("get")).asInstanceOf[Message];
}
//this would call the network layer and send something over the wire
def doSomething() {
generateResponse();
}
//this is simulating the network layer getting some data back
//and sending it to the appropriate Connection object
private def generateResponse() {
act ! new Message("someData");
act ! new Message("moreData");
act ! new Message("even more data");
}
}
object runner extends Application {
val conn = new Connection();
conn.doSomething();
println( conn.getNextResponse());
println(conn.getNextResponse());
println(conn.getNextResponse());
}
Is there a way to do this without using the receive, and thereby making it threadless?
You could directly rely on react which should block and release the thread:
class Connection {
private val act:Actor = actor {
loop {
react {
case r:Request => reply { r }
}
}
}
[...]
I expect that you can use react rather than receive and not have actors take up threads like receive does. There is thread on this issue at receive vs react.