WebSockets in Grails 3.0

For those who aren’t familiar, WebSockets are a long-lived, interactive, two-way channel between a client browser and end server that allows ongoing communication without polling. They’ve been around for a few years now ever since being proposed by RFC 6455 and pretty much all of the major browsers have gained support since then. Spring, the ever-popular Java MVC framework, gained built-in WebSocket support in version 4.0 and that support has since been incorporated into Spring Boot and Grails (Grails support is supplied by this helpful plugin). This article is a high level overview of some tinkering I did to familiarize myself with WebSockets while also learning about the relatively-recent shakeup of Grails as it advanced to 3.0.

The Setup

Specifically, my use case was to examine ways to avoid manual server polling when sending data back and forth between a server and 1-n clients. For this I decided to use WebSockets managed by SockJS, a library that handles the nitty-gritty of establishing and maintaining those connections, and Stomp, a messaging protocol built for WebSockets. Being a developer with lots of Java experience, my initial reaction to this stack was “it’s JMS, but better”. Those who have worked with Java Message Service probably know what I’m referring to – JMS allows reliable, highly-customizable cross-system messaging but can be heavy, hard to configure, and definitely doesn’t have any graceful integration into the sort of web applications being developed these days. This stack supplies many of the same capabilities while being simpler, lighter-weight, and downright simple to integrate:

  1. Pub/Sub Model – messages are published to customizable endpoints (think of them as mailboxes), interested clients can subscribe/unsubscribe at-will
  2. Topics and Queues – determine how and to whom messages get delivered: broadcast, first-come, or targeted consumer
  3. Delivery Acknowledgement and Transactions – bundle sets of messages to guarantee an entire batch gets worked or rolled back in case of error
  4. Filtering – clients can specify what types of messages they wish to receive or ignore
  5. Security – integrates with Spring Security and readily supports common tools like CSRF tokens

The Application

For this example I decided to setup a very simple test application. This application would generate (fake) stock quotes at a relatively high frequency, high enough that it would become burdensome to have clients poll often enough to keep up. On top of showcasing this poll-less approach I also wanted to look into the ability to allow clients to interact and decided to go for the tried-and-true chat room use case.

All the code for this example is available out on GitHub so I will just be highlighting the most important functional bits here. First up is the code that generates data for my faux stock ticker – this code runs in the background on the server pushing new quotes out to a Stomp topic that clients can subscribe to.

Next, we need some more server-side code to arbitrate chat messages – effectively, this code reflects any incoming messages out to all listeners.

Now that all the server code is in place, we create a basic webpage. The code below provides the ability for the user to subscribe and unsubscribe to stock quotes, send chat messages, and receive messages sent by other users.

Put it all together and launch as a Grails application and you get something like you see below. You can open any number of browser tabs and they will all receive the same stock quote data at the same time (provided they each subscribe) and they all can participate in a shared chat. The best part? No background AJAX requests barraging the server for the latest data.
Screen Shot 2015-06-08 at 20.04.34

Wrap Up

Once you take out all the fluff, we were just able to put together a neat little app that handles some non-trivial interactions in just a few dozen lines of code. Of course, this is hardly a complex example of WebSockets or Grails but hopefully it has shown you a quick way to simplify a common use case using some neat tools. Take a look at the source out on GitHub, and happy coding!

About the Author

Object Partners profile.

One thought on “WebSockets in Grails 3.0

  1. Ederson says:

    Great post ,
    Mike You could do a quick example of sending a message from the server to a specific client in chat

    1. Mike Plummer says:

      I’m glad you liked it. I’ve committed an update to the code out on GitHub that should give you a basic example of how this can work. Basically there’s now a /secure page you can login to as “user” or “user2” (password is “password”) which will determine whether you see messages the server is sending specifically to “user”.

  2. Ederson says:

    Mike thanks for attention,
    Your code helped a lot,

  3. zyro says:

    first class content.

    do not hesitate to give feedback or raise issues if you see options for improvements reg. grails-spring-websocket. im sure theres a lot of room for that 🙂

    and do you plan to keep that sample app up-to-date? if so, i would be glad to add it to some kind of “useful links” list in the grails-spring-websocket readme.

    thanks, zyro

    1. Mike Plummer says:

      Thanks! I’m planning to add a few more features over the next couple weeks as an academic exercise (likely queue vs. topic and message-filtering examples) but wasn’t planning to turn it into an authoritative or exhaustive example. You’re welcome to link to it if you’d like, or you can copy the code to make an example project of your own.

  4. Przemek Galazka says:

    super useful stuff – i’m going to use it right away 🙂 thx

  5. moustapha says:

    hello, I’m inspired of your tutorial to use websockets in my grails project. At the views index.gsp, I replace section by g:form but this make the connection lost.
    When I see the console, I get this error : The connection to ws://localhost:8080/stomp/414/h0jvmvej/websocket was interrupted while the page was loading. I need some help to continue in my project. Thank you. your tutorial is very useful.

    1. Mike Plummer says:

      Without seeing your code I don’t think I’ll be able to help you very much. If you’ve got it out in a public Git repo I’d be happy to pull it down and help you troubleshoot.

  6. moustapha says:

    here are my codes (in the view):

    Brainstorming

    Send

    so I want to replace “section” by “g:form url = [ ….]” and get recover the input in my controller.

    I have this in the controller:
    class BrainstormingController {

    def index() { }

    /**
    * Accepts incoming chat messages sent by browsers and routes them
    * to the ‘chat’ topic that all browser clients are subscribed to.
    **/
    @MessageMapping(“/brainstorming”)
    @SendTo(“/topic/brainstorming”)
    protected String brainstorming(String chatMsg) {
    /**
    * Use the awesome Groovy JsonBuilder to convert a dynamically-defined
    * data structure to JSON.
    **/
    def builder = new JsonBuilder()
    builder {
    message(chatMsg)
    timestamp(new Date().getTime())
    }
    builder.toString()
    }
    }

  7. moustapha says:

    sorry: Here are the code in my view:

    Brainstorming

    Send

    1. moustapha says:

      Apparently i can’t put my views code. So it the same that yours. juste an input field and a button to submit.

      1. Mike Plummer says:

        I updated the Git repo with an example of using a Grails form to submit a message to a controller that publishes it to a websocket topic. It can be accessed at “/formMessage” when running locally. Hopefully that helps!

        1. moustapha says:

          Thanks you so much !

  8. moustapha says:

    is there a possibility to reveal the chat when refrashing the page ? I mean to let appear the chat already made when we click on send button ?

    1. Mike Plummer says:

      The websockets themselves have no storage capability – they’re just a message broker. Using your Grails Form approach you would have to introduce your own persistence solution to store previously-sent messages either in a database server-side or client-side in something like LocalStorage. In my original example I used an AJAX listener instead of a Grails Form to prevent a page refresh when submitting messages to avoid this issue.

      1. moustapha says:

        ok I understand better now. Thank you for your precision.

  9. moustapha says:

    Is it possible to send to chat controller from the view index.gsp multiple value (or an array) by this call : client.send(“/app/chat”, {}, JSON.stringify($(“#chatMessage”).val()));
    something like client.send(“/app/chat”, {}, JSON.stringify($(“#chatMessage”).val()), “other”); ?

  10. stof says:

    Great post Mike !
    Just a question, what can I do if a want to send a message to a specific user ?
    Just to avoid the websocket’s broadcast.
    thanks !

    1. Mike Plummer says:

      There’s already an example of that in the code, please reference the “Private Messages” section of the README.

  11. bhuv says:

    Very useful example, i m using React/Redux client and get 403 for GET ‘/api/stomp/info’. I have added [pattern: ‘/stomp/**’, access: [‘permitAll’]] in application.groovy.

Leave a Reply to Mike Plummer Cancel reply

Your email address will not be published.

Related Blog Posts
Natively Compiled Java on Google App Engine
Google App Engine is a platform-as-a-service product that is marketed as a way to get your applications into the cloud without necessarily knowing all of the infrastructure bits and pieces to do so. Google App […]
Building Better Data Visualization Experiences: Part 2 of 2
If you don't have a Ph.D. in data science, the raw data might be difficult to comprehend. This is where data visualization comes in.
Unleashing Feature Flags onto Kafka Consumers
Feature flags are a tool to strategically enable or disable functionality at runtime. They are often used to drive different user experiences but can also be useful in real-time data systems. In this post, we’ll […]
A security model for developers
Software security is more important than ever, but developing secure applications is more confusing than ever. TLS, mTLS, RBAC, SAML, OAUTH, OWASP, GDPR, SASL, RSA, JWT, cookie, attack vector, DDoS, firewall, VPN, security groups, exploit, […]