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:
-
Download and unzip the source
or
-
Clone the Git repository:
git clone https://github.com/grails-guides/server-sent-events.git
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 theinitial
folder.
To complete the guide, go to the initial
folder
-
cd
intograils-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:
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:
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:
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:
observer.onCompleted()
The final completed logic can be seen below:
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:
@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:
<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:
<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:
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:
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.