Browse Source

Migrate UsingServerInitiatedEvents

tags/8.2.0.alpha2
Erik Lumme 6 years ago
parent
commit
317dd18fc5

+ 130
- 0
documentation/articles/UsingServerInitiatedEvents.asciidoc View File

@@ -0,0 +1,130 @@
[[using-server-initiated-events]]
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:

[source,java]
....
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()`` 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 `Runnable` 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 `VaadinSessions` 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.

[source,java]
....
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`:

[source,java]
....
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 link:EnablingServerPush.asciidoc[Enabling server push] or link:UsingPolling.asciidoc[Using polling].

+ 1
- 0
documentation/articles/contents.asciidoc View File

@@ -36,3 +36,4 @@ are great, too.
- link:AccessingWebPageAndBrowserInformation.asciidoc[Accessing web page and browser information]
- link:GeneratingDynamicResourcesBasedOnURIOrParameters.asciidoc[Generating dynamic resources based on URI or parameters]
- link:OptimizingTheWidgetSet.asciidoc[Optimizing the widget set]
- link:UsingServerInitiatedEvents.asciidoc[Using server-initiated events]

Loading…
Cancel
Save