Server Push
|
This section documents the current Vaadin release line — Vaadin 24 LTS / 25.x, Java 17+, Spring Boot 3 / Jakarta EE 10 — as published at the official Vaadin documentation, which is the reference these pages are written and verified against. No specific patch version is pinned. Flow (server-side Java) is the authoring style used throughout, with Hilla / React shown where it differs; Vaadin 7 and the pre-Flow architecture appear only as migration contrast. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, since Vaadin ships major releases roughly twice a year and its ecosystem iterates. This section’s bibliography lists the reference material consulted while preparing these pages. |
By default a Flow UI only changes in response to a user action. Server push keeps a channel open so the server can send DOM updates at any time — for progress bars, dashboards, chat, or notifying one user of another’s change. This page follows Server Push and the Building Apps guide.
Enabling push
Add @Push to the class that implements AppShellConfigurator (there is exactly one per application):
@Push
public class AppShell implements AppShellConfigurator { }
@Push(transport = Transport.WEBSOCKET) is the default; Transport.LONG_POLLING falls back to a series of
held-open HTTP requests where a WebSocket cannot be established (some proxies). @Push(PushMode.MANUAL) means
you decide when a batch of changes is flushed with ui.push(); the default PushMode.AUTOMATIC flushes
after every access block.
The session lock
A VaadinSession and its UIs are not thread-safe. Every read or write of a component from a thread other
than the one handling the current request must run inside UI.access(Command), which acquires the session
lock, runs the command, and (in automatic mode) pushes the result:
UI ui = UI.getCurrent(); // capture on the request thread, before starting the work
executor.execute(() -> {
String report = reportService.build(); // slow — off the UI thread
ui.access(() -> {
downloadLink.setHref(report);
Notification.show("Report ready");
});
});
ui.access(…) is asynchronous and safe to call from any thread; ui.accessSynchronously(…) blocks the
caller and must never be called from a request thread already holding the lock.
Running background work
Prefer the managed VaadinExecutor — it propagates the Vaadin and (with Spring) security context into the
worker thread, and its tasks are cancelled when the UI detaches:
VaadinExecutor executor = VaadinService.getCurrent().getContext()
.getAttribute(VaadinExecutor.class);
executor.execute(ui, () -> {
for (int i = 0; i <= 100; i += 10) {
int pct = i;
doChunk();
ui.access(() -> progressBar.setValue(pct / 100.0));
}
});
A plain ExecutorService or a Spring @Async method works too; you are then responsible for capturing the
UI reference and for not leaking threads when the user closes the tab.
Broadcasting to many UIs
For "notify everyone" features, keep a thread-safe registry of listeners and fan out to each UI through its own
access(…):
public final class Broadcaster {
private static final Set<Consumer<String>> LISTENERS = new CopyOnWriteArraySet<>();
public static Registration register(Consumer<String> listener) {
LISTENERS.add(listener);
return () -> LISTENERS.remove(listener);
}
public static void broadcast(String message) {
LISTENERS.forEach(l -> l.accept(message));
}
}
// in a view
private Registration registration;
@Override
protected void onAttach(AttachEvent event) {
UI ui = event.getUI();
registration = Broadcaster.register(msg ->
ui.access(() -> Notification.show(msg)));
}
@Override
protected void onDetach(DetachEvent event) {
registration.remove(); // avoid leaking the listener and the UI
}
An ui.access(…) scheduled against a UI that has since detached throws UIDetachedException; unregistering
in onDetach (as above) prevents it, and wrapping the body in a try/catch handles the race.
The no-push alternative
If push cannot be enabled, ui.setPollInterval(millis) makes the browser poll on a timer; a PollListener
runs on each poll and can refresh the UI. It costs a request per interval per open tab, so use push where you
can:
ui.setPollInterval(2000);
ui.addPollListener(e -> grid.getDataProvider().refreshAll());
See also
-
Architecture — the request lifecycle, the session lock and the client engine.
-
Advanced Topics —
VaadinExecutor, long-running tasks and lifecycle listeners. -
Production and Deployment — WebSocket support behind reverse proxies and in clusters.
-
The Grid Component — refreshing a
DataProviderafter a background write. -
Server Push — the official reference.