Class VaadinPreferencesFactory

java.lang.Object
vaadinx.util.prefs.VaadinPreferencesFactory
All Implemented Interfaces:
PreferencesFactory

public final class VaadinPreferencesFactory extends Object implements PreferencesFactory
Plugs vaadinx's localStorage-backed preferences into the JDK via the standard PreferencesFactory SPI. Registered in META-INF/services/java.util.prefs.PreferencesFactory, so the JDK selects it JVM-wide with zero host configuration — migrated code keeps calling Preferences.userRoot() / userNodeForPackage(...) unchanged, with no import swap (java.util.prefs is not javax.swing).

The JDK consults the -Djava.util.prefs.PreferencesFactory system property before this ServiceLoader entry, so a deployment that shares its JVM with non-UI code using Preferences for real persistence can override us for free. See D_preferences for the transparent-SPI-with-override rationale.

App-scoped vs. JVM-scoped (D_prefs_scope_split). The SPI is selected JVM-wide, so this factory also fields Preferences calls that have nothing to do with a migrated Swing app — most concretely Vaadin's commercial-component license checker, which reads userNodeForPackage(...) off any UI when an RTE-backed JEditorPane renders. userRoot() classifies by whether an EmulatorContext is active on the calling thread: present ⇒ migrated app code, whose prefs live in the browser's localStorage (PrefsCache); absent ⇒ a JVM-scoped caller, routed to a process-local JvmLocalPreferences that needs no UI and never throws.

Must be public with a public no-arg constructor for ServiceLoader.

  • Constructor Details

    • VaadinPreferencesFactory

      public VaadinPreferencesFactory()
  • Method Details

    • userRoot

      public Preferences userRoot()
      App-scoped ⇒ per-UI, backed by the browser's localStorage; JVM-scoped ⇒ the in-memory JvmLocalPreferences. See D_prefs_scope_split. The discriminator is whether we can safely serve localStorage, i.e. whether the session lock is held:
      • A current session whose lock we hold — real UI-thread access or an EHelper.callSwing continuation. App-scoped ⇒ PrefsCache.
      • A current session we do not hold the lock on — the RTE license checker (observed: a Jetty request-pool thread with the session set but no UI and no lock) and any other library that touches Preferences on a session-associated but unlocked thread. Anything that reads session/UI state here — even EmulatorContext.getOrNull(), which reads BrowserTimeZone off the session — throws Vaadin's "cannot access state … without locking", so this branch must decide on hasLock() alone (which needs no lock) and never touch the context. JVM-scoped ⇒ JvmLocalPreferences.
      • No current session but a propagated EmulatorContext — app background code (a SwingWorker). App-scoped ⇒ BridgedPreferences, which reaches localStorage through the session's live UI via EHelper.callOnLiveUISync(VaadinSession, Supplier) (throws loud if no single live UI is reachable — D_prefs_scope_split).
      • Nothing — JVM init / a library off any Vaadin thread. JVM-scoped ⇒ JvmLocalPreferences.
      Specified by:
      userRoot in interface PreferencesFactory
    • systemRoot

      public Preferences systemRoot()
      Deferred, inert node — see SystemPreferences / D_preferences.
      Specified by:
      systemRoot in interface PreferencesFactory
    • setTestMode

      public static void setTestMode(boolean testMode)
      Toggle in-memory test mode for Karibu unit tests: the per-UI cache treats itself as warm-and-empty and every write stays server-side, bypassing the executeJs browser round-trip (and its virtual-thread requirement). A current UI is still required, so the off-context throw contract stays testable. Mirrors WebClipboard.setTestMode. Production leaves it alone.
    • isTestMode

      public static boolean isTestMode()
      Whether test mode is currently engaged.