Class UiScheduler

java.lang.Object
vaadinx.UiScheduler

public final class UiScheduler extends Object
INTERNAL — do not use. Not part of the emulated Swing API surface and has no stability guarantee; the signature exists only to serve emulator internals and may change without notice.

A shared per-VaadinSession ScheduledExecutorService for emulator machinery that needs to fire on a wall-clock cadence — Timer repeats, the FileDialog SAVE download-size watcher, and any future periodic chore. One single platform daemon thread per session, re-used by every caller; lazy-attached on first forSession(VaadinSession) and disposed (via shutdownNow()) when the session is destroyed. Concurrent sessions get independent schedulers.

Session-scoped, not UI-scoped, by design (F5 / @PreserveOnRefresh). On a browser refresh Vaadin closes the old UI and teleports the preserved component tree onto a fresh one; a UI-scoped scheduler would be shutdownNow()'d on the old UI's detach, silently killing any Timer that survived the teleport. Session scope outlives the refresh, so a repeating Timer keeps firing. The fire itself resolves the now-current UI lazily via EHelper.onSessionLiveUI(VaadinSession, Runnable) — see that method and UiScopedFeaturesF5Test.

The scheduler thread does no user work and holds no UI context: callers schedule a task that immediately hops back onto the session's live UI thread (via EHelper.onSessionLiveUI(VaadinSession, Runnable), and through EHelper.callSwing(Runnable) per R_callswing_envelope). Because every task returns quickly after enqueuing that hop, a single shared thread never meaningfully serializes its callers. Since the thread holds no lock, a task body reaches the session's ErrorHandler only through EHelper.reportUncaught(VaadinSession, Throwable).

A throwing task is reported and keeps repeating. That is the faithful answer, not the pool's: a JDK Timer does not stop firing because one fire threw — the EDT catches it and keeps pumping — whereas a bare ScheduledExecutorService cancels the future and discards the throwable, which would strand a repeating Timer forever with no log, no handler and nothing to grep for. See D_uncaught_handler_chain.

Scheduling requires the session lock — the scheduler is a session attribute, and reading or writing one does. Emulator callers reach here from a UI thread (Timer construction / start under callSwing, or a peer listener), where it is held.

  • Method Details

    • schedule

      public static ScheduledFuture<?> schedule(com.vaadin.flow.server.VaadinSession session, Runnable body, long delay, TimeUnit unit)
      Schedules body to run once after delay.
    • scheduleWithFixedDelay

      public static ScheduledFuture<?> scheduleWithFixedDelay(com.vaadin.flow.server.VaadinSession session, Runnable body, long initialDelay, long delay, TimeUnit unit)
      Schedules body to repeat, each run starting delay after the previous one finished. A run that throws is reported and the repeat continues (see class doc).