Using server-initiated events
The traditional way of communicating with the server is always client initiated. Whenever the user interacts with the application so a server visit is needed, the browser connects to the server, sends a request and waits for the response. The response is handled when received by the browser and the UI is updated accordingly. For basic applications this is all that is needed but there are also many cases when you want the server to be able to initiate events: updating the progress of a long running application, notifying you of changes in the back end, providing interaction between users and so on.
Starting from Vaadin 7.1 you have a couple of ways you can implement server initiated events without requiring any external add-ons: polling and push. Polling is based on the client actively polling the server, asking “are there any news for me?” whereas push is based on keeping a connection constantly open to the server, allowing the server to, whenever it likes, send something to the client. Which you want to use is dependent on your use case. If you have events which occur seldom but you want to deliver them immediately to the user, then push might serve you better. If you want to avoid constant server connections and only need to check now and then if something has happened, polling may work out well for you. For any use case you should consider the two options and which one will better suit your needs.
An important part to consider when using server initiated events is locking. Vaadin is built around the fact that only one thread can access any VaadinSession instance at any time. This is to prevent inconsistencies from occuring and needs to be taken into account when using background threads to update a UI (which lives inside a VaadinSession). To do proper locking when you are updating the UI you use the UI.access method:
theUI.access(new Runnable() {
@Override
public void run() {
theUI.setContent(new Label("This is the real content"));
}
});
The access method ensure your update is safe to do (you have exclusive
access to the VaadinSession
), it ensures that threadlocals work also
inside the run()
is executed immediately - on the contrary
it will be run as soon as it is safe to run it without causing deadlocks
by locking multiple method (e.g. `UI.getCurrent()
) and finally it deals with
some typical deadlock situations. To be able to do all this it does not
guarantee that the `RunnableVaadinSessions
at once.
A typical use case is the one-shot background operation which does some
heavy lifting, updates the UI
based on the result (which is then
fetched by the browser by polling or pushed to the browser) and then
goes away. This can be implemented as a Runnable
, e.g.
class AntCounter implements Runnable {
@Override
public void run() {
// Do some heavy work
final int result = calculateNumberOfAntsInTheWorld();
// Wrap UI updates in access to properly deal with locking
theUI.access(new Runnable() {
@Override
public void run() {
theUI.setContent(new Label("The result is " + result));
}
});
}
}
The Runnable is typically run in a background thread using e.g. an
ExecutorService
.
The other typical case is a long running background task which checks
for some event and, in case that events happens, alerts the user. The
event can originate from another user, from a change in the database or
from somewhere else. Similarly to before, this can be implemented as a
Runnable
:
class AntTracker implements Runnable {
@Override
public void run() {
// Do some initial work
int antPopulation = calculateNumberOfAntsInTheWorld();
while (true) {
// Loop forever and ever
final int antPopulationNow = calculateNumberOfAntsInTheWorld();
if (antPopulationNow != antPopulation) {
antPopulation = antPopulationNow;
// Wrap UI updates in access to properly deal with locking
theUI.access(new Runnable() {
@Override
public void run() {
new Notification("Ant population is now "
+ antPopulationNow, Type.TRAY_NOTIFICATION)
.show(theUI.getPage());
}
});
}
// Wait for a while before calculating again
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
return;
}
}
}
private int calculateNumberOfAntsInTheWorld() {
return (int) (new Date().getTime()/1500);
}
}
The same thing can of course be implemented without the while(true)`
loop
by using for instance a ScheduledExecutorService
instead to make the
executor service handle the iteration and interval (e.g. Executors.newScheduledThreadPool(1).scheduleAtFixedRate(…)
).
For more information on how to enable push or polling in your application, see Enabling server push or Using polling.