0

I can't seem to get websocket communication to work in the Play Framework version 2.1.

I created a simple test that does nothing but send messages back and forth with a push of a button. All the code for it is below. But nothing shows up except for the button.

Has anybody seen this problem or can someone tell me what I may be doing wrong in the code below?

I am using the latest version of Chrome.

Here is my simple setup.

In Application.java

public static Result index() {
    return ok(index.render());
}

public static WebSocket<String> sockHandler() {
    return new WebSocket<String>() {
        // called when the websocket is established
        public void onReady(WebSocket.In<String> in,
                WebSocket.Out<String> out) {
            // register a callback for processing instream events
            in.onMessage(new Callback<String>() {
                public void invoke(String event) {
                    System.out.println(event);
                }
            });

            // write out a greeting
            out.write("I'm contacting you regarding your recent websocket.");
        }
    };
}

In Routes File GET / controllers.Application.index()

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file)
GET     /greeter                    controllers.Application.sockHandler()

In Index.Scala.html

@main(null) {

<div class="greeting"></div>
<button class="send">Send</button>

<script type="text/javascript" charset="utf-8">

    $(function() {
        var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
        var sock = new WS("@routes.Application.sockHandler()")

        sock.onmessage = function(event) {
            $('.greeting').append(event.data)
        }

        $('button.send').click(function() {
            sock.send("I'm sending a message now.")
        });            
    })

</script>

}

In Main.scala.html

@(title: String)(content: Html)
<!DOCTYPE html>
<html>
<head>
    <title>@title</title>
    <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
    <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
    <script src="@routes.Assets.at("javascripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
</head>
<body>
    @content
</body>

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129

2 Answers2

3

The problem is in

var sock = new WS("@routes.Application.sockHandler()")

you have to specify the protocol and the complete url in the format: ws://localhost:9000/greeter.

Check this question to do it in javascript: How to construct a WebSocket URI relative to the page URI?

Community
  • 1
  • 1
TizianoPiccardi
  • 487
  • 4
  • 13
3

you can use a Route's webSocketURL() method to retrieve a url that can be passed to a WebSocket's constructor. Here's an example from Play's websocket-chat sample code:

$(function() {            
  var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
  var chatSocket = new WS("@routes.Application.chat(username).webSocketURL()")

  var sendMessage = function() {
    chatSocket.send(JSON.stringify(
      {text: $("#talk").val()}
    ))
    $("#talk").val('')
  }

  // ...

So in your code you can use something like

var sock = new WS("@routes.Application.sockHandler().webSocketURL()");

Personally I don't like intermingling interpolated code with JS, since I think that any code executing on the client should only be concerned with the state of the client, and not the server (not to mention it makes refactoring the script out into an external file impossible), so I tend to do something like this:

<div class="container app-container" 
     data-ws-uri="@routes.Application.WSUri.webSocketURL()">
.......
</div>

Then in my JS I can just do something like

var sock = new WS(document.querySelector(".app-container").dataset.wsUri);
// ....
Travis Kaufman
  • 2,867
  • 22
  • 23
  • For some reason webSocketURL() does not work with Play 2.1. When I put that in I get this error saying it can't find the request header. There has to be a better way to handle these urls in Play 2.1 ... – user2196112 Apr 11 '13 at 15:46
  • to follow up on that: at the top of your template you'll want to have `@(implicit request: RequestHeader)` which will allow it to work for you. Unfortunately Play is a bit lax in documenting this... EDIT: I actually think you have to explicitly pass the request in. That's what I do in my code and it works. – Travis Kaufman Apr 12 '13 at 00:55
  • Hmm ... I tried it like so: 'var sock = new WS("@routes.Application.sockHandler().webSocketURL(request)");' But it gave me an error saying not found: value request – user2196112 Apr 12 '13 at 18:56
  • I also tried adding the implicit call at the top and after adding that, The error it gave me is 'Overloaded method value [webSocketURL] cannot be applied to (play.api.mvc.RequestHeader)' – user2196112 Apr 12 '13 at 19:00
  • Here's a link to my code which I have working: - Link to Controller (see the `index` method): https://github.com/traviskaufman/JAMLive/blob/master/app/controllers/Application.scala - Link to the view: https://github.com/traviskaufman/JAMLive/blob/master/app/views/index.scala.html This worked for me. I hope this helps! – Travis Kaufman Apr 12 '13 at 19:25
  • You got it! Glad it helped :) – Travis Kaufman Apr 16 '13 at 16:22