Show Navigation

Sending Server Sent Events with Grails

This guide walks you through how to send Server Sent Events using Grails and RxJava.

Authors: Graeme Rocher

Grails Version: 3.3.1

1 Grails Training

Grails Training - Developed and delivered by the folks who created and actively maintain the Grails framework!.

2 Getting Started

In this guide you are going to build a Grails application that communicates with a JavaScript client via Server Sent Events.

2.1 What you will need

To complete this guide, you will need the following:

  • Some time on your hands

  • A decent text editor or IDE

  • JDK 1.7 or greater installed with JAVA_HOME configured appropriately

2.2 How to complete the guide

To get started do the following:

or

The Grails guides repositories contain two folders:

  • initial Initial project. Often a simple Grails app with some additional code to give you a head-start.

  • complete A completed example. It is the result of working through the steps presented by the guide and applying those changes to the initial folder.

To complete the guide, go to the initial folder

  • cd into grails-guides/server-sent-events/initial

and follow the instructions in the next sections.

You can go right to the completed example if you cd into grails-guides/server-sent-events/complete

3 Writing the Application

3.1 Add RxJava dependency

The first step is to configure the necessary Gradle dependencies. This basically means adding a dependency on the RxJava plugin for Grails in the build.gradle file:

dependencies {
    ...
    compile "org.grails.plugins:rxjava"
}

RxJava is used to facilitate a reactive programming model for sending events to the JavaScript client.

3.2 Create a Controller

Next create a new controller called TickTock. If you have Grails installed you can do this by running the grails create-controller command, otherwise you can use the included grailsw wrapper:

$ ./grailsw create-controller TickTock
The first time you run the grails command, application dependencies will be downloaded from the internet. Subsequent calls will be much faster.

The output of the command will look like:

| Created grails-app/controllers/example/TickTockController.groovy
| Created src/test/groovy/example/TickTockControllerSpec.groovy

As you can see a new controller was created in the grails-app/controllers/example directory. Grails will use the value defined by the grails.codegen.defaultPackage value in grails-app/conf/application.yml to decide which package to use.

3.3 Implement the Controller

Now that you have a controller you need to modify it to take advantage of RxJava features. To do this add the necessary imports and make the controller implement the grails.rx.web.RxController trait:

grails-app/controllers/example/TickTockController.groovy
import rx.Observer
import rx.Observable
import grails.rx.web.RxController
import java.util.concurrent.TimeUnit
import groovy.transform.CompileStatic

/**
 * Demo Server Sent Events Controller
 */
@CompileStatic
class TickTockController implements RxController {

The next step is to implement the index action. This is the default action that gets executed by the controller if no explicit action is specified.

The RxController trait adds a helper called rx that allows you to control the responses sent to the client from the controller.

In order to start sending Server Sent Events we will use the rx.stream(..) method:

grails-app/controllers/example/TickTockController.groovy
def index() {
    rx.stream { Observer observer ->
    }
}

The stream method accepts a closure that receives a rx.Observer instance which you can use to send events to the client when the data is ready to be sent via the rx.Observer interface.

By using the rx helper’s various methods you can pass values to the Observer’s onNext method. For example:

grails-app/controllers/example/TickTockController.groovy
for (i in (0 .. 5)) {
    if (i % 2 == 0) {
        observer.onNext(
            rx.render('Tick')
        )
    }
    else {
        observer.onNext(
            rx.render('Tock')
        )
    }
    sleep 1000
}

In this example we loop through values between 0 and 5 and call rx.render(..) for odd and even values in order to output "Tick" or "Tock" respectively. The call to sleep(..) is there to simulate a slow request like for example if the values were being obtained from an external web service.

Once you are done sending events you can use the Observer’s onCompleted event to finish-up:

grails-app/controllers/example/TickTockController.groovy
observer.onCompleted()

The final completed logic can be seen below:

grails-app/controllers/example/TickTockController.groovy
def index() {
    rx.stream { Observer observer ->

        for (i in (0 .. 5)) {
            if (i % 2 == 0) {
                observer.onNext(
                    rx.render('Tick')
                )
            }
            else {
                observer.onNext(
                    rx.render('Tock')
                )
            }
            sleep 1000
        }
        observer.onCompleted()
    }
}

Note that if the call to sleep(1000) was actually a concrete requirement for the application a more idiomatic way to write that would be with the interval method:

grails-app/controllers/example/TickTockController.groovy
@SuppressWarnings('DuplicateNumberLiteral')
def example() {
    int i = 0
    rx.stream(Observable
                .interval(1, TimeUnit.SECONDS)
                .map {
        i++
        if (i % 2 == 0) {
            rx.render('Tick')
        }
        else {
            rx.render('Tock')
        }
    })
}

3.4 Implement the Client

To implement the client first open up the grails-app/views/index.gsp file and add an additional header below the "Welcome to Grails" <h1> tag:

grails-app/views/index.gsp
<h1>Welcome to Grails</h1>
<h2 style="text-align:center;font-size:50px" id="message"></h2>

We will be using this header to place the content received from the Server Sent Events.

Next add the following <script> block at the bottom of the page:

grails-app/views/index.gsp
<script type="text/javascript">
    (function() {
        var eventSource = new EventSource("tickTock");
        eventSource.onmessage = function(event) {
            console.log("data: "+event.data)
            document.getElementById('message').innerHTML = event.data;
        };
    })();
</script>

The JavaScript code above using the Server Sent Event specification’s EventSource object to start receiving events from the controller:

grails-app/views/index.gsp
var eventSource = new EventSource("tickTock");
You will need a modern browser to use the EventSource object

It then registers a listener that will update the previously defined <h2> header with the response from the server:

grails-app/views/index.gsp
eventSource.onmessage = function(event) {
    console.log("data: "+event.data)
    document.getElementById('message').innerHTML = event.data;
};

And that is it! You are now ready to run the application!

4 Running the Application

To run the application use the ./gradlew bootRun command which will start the application on port 8080.

You can then navigate to http://localhost:8080 and you will see below the "Welcome to Grails!" message the messages "Tick" and "Tock" alternating as the browser receives events from the server.

5 Do you need help with Grails?

Object Computing, Inc. (OCI) sponsored the creation of this Guide. A variety of consulting and support services are available.

OCI is Home to Grails

Meet the Team