Class AppTab

java.lang.Object
vaadinx.AppTab

public final class AppTab extends Object
The app's identity within a session: the live UI it currently renders in — the delivery target for Timer fires, SwingWorker callbacks, EHelper.callSwing(Runnable) continuations, and blocking-dialog resume — plus the browser tab (TabScope) that hosts it, whose close ends the app. One app per session (D_active_ui_pointer), so one AppTab per session, held under a session attribute.
// MainWindowRoute, on first load AND every F5 rebind — bind this UI + its tab:
AppTab.markAppUI(ui);
// EHelper.singleLiveUI, from a background thread under the session lock:
UI target = AppTab.forSession(session).liveUI();   // null if the app UI is gone
// SwingBridgeEmulatorsBootstrap's tab-destroy listener:
AppTab.onTabClosed(session, destroyedScope);        // closes the session iff it was the app tab

The tab identity is F5-stable: a refresh keeps the same window.name → the same TabScope, so re-binding the fresh UI to the same tab is idempotent and only a real tab close destroys the scope. When no tab scope is available (browserless Karibu without opt-in, or a deployment that didn't wire SwingBridgeEmulatorsBootstrap) the tab is null: UI identity still drives delivery, only tab-close detection goes inert.

The single home for app-identity state — the live app UI (delivery) and its browser tab (close detection) — resolved by EHelper.singleLiveUI(VaadinSession), bound by MainWindowRoute, and torn down by SwingBridgeEmulatorsBootstrap's tab-destroy listener. See D_session_scoped_pools/D_active_ui_pointer. It also carries AppInstance's handle on the tab scope's values, which is what lets app-instance state resolve on the request-less teardown threads.

  • Method Summary

    Modifier and Type
    Method
    Description
    static AppTab
    forSession(com.vaadin.flow.server.VaadinSession session)
    The session's app tab, or null if none is recorded yet.
    com.vaadin.flow.component.UI
    The app's UI if it is still live — in VaadinSession.getUIs() and not isClosing() — else null (a stale record; a later tab can then claim the app).
    static void
    markAppUI(com.vaadin.flow.component.UI ui)
    Records ui as the session's app UI — on first load and on every F5 rebind — binding it to the current browser tab.
    static void
    onTabClosed(com.vaadin.flow.server.VaadinSession session, com.github.mvysny.vaadin.tabscope.TabScope scope)
    Tab-scope destroy hook (wired by SwingBridgeEmulatorsBootstrap): on a real app-tab close — the destroyed scope is this app's tab — fires the D_shutdown_lifecycle WINDOW_CLOSING dispatch and ends the session; a curtained (non-app) tab's close is ignored, leaving the app running.
    boolean
    ownsTab(com.github.mvysny.vaadin.tabscope.TabScope scope)
    True if scope is the browser tab hosting this app.

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • markAppUI

      public static void markAppUI(com.vaadin.flow.component.UI ui)
      Records ui as the session's app UI — on first load and on every F5 rebind — binding it to the current browser tab. Idempotent across F5 (same window.name → same tab): re-binds the fresh UI in place. A fresh tab claiming the app after the previous one is gone replaces the record. Must run under the session lock (a UI attach does).
    • forSession

      public static AppTab forSession(com.vaadin.flow.server.VaadinSession session)
      The session's app tab, or null if none is recorded yet.
    • liveUI

      public com.vaadin.flow.component.UI liveUI()
      The app's UI if it is still live — in VaadinSession.getUIs() and not isClosing() — else null (a stale record; a later tab can then claim the app). An F5 rebind has already moved the record to the fresh UI by the time the old one closes, so this never spuriously drops the app mid-refresh. Must be called under the session lock.
    • ownsTab

      public boolean ownsTab(com.github.mvysny.vaadin.tabscope.TabScope scope)
      True if scope is the browser tab hosting this app.
    • onTabClosed

      public static void onTabClosed(com.vaadin.flow.server.VaadinSession session, com.github.mvysny.vaadin.tabscope.TabScope scope)
      Tab-scope destroy hook (wired by SwingBridgeEmulatorsBootstrap): on a real app-tab close — the destroyed scope is this app's tab — fires the D_shutdown_lifecycle WINDOW_CLOSING dispatch and ends the session; a curtained (non-app) tab's close is ignored, leaving the app running. Best-effort — a session already tearing down is fine.

      Runs on the tab-scope reaper thread. The dispatch goes through JFrame.dispatchShutdownFromTabClose(VaadinSession) directly rather than leaning on session.close() → VaadinSession-destroy: that thread is request-less, so close() only sets state CLOSING and fireSessionDestroy (hence the session-destroy backstop dispatch) would not run until the HTTP-session timeout. We still call close() for the eventual teardown; the dispatch dedups so the backstop won't re-fire.