AppContext
A process-wide key-value store for the shared, cross-cutting data your
flows need — the current user, a selected record, feature flags — without
coupling unrelated screens to one another. Backed by a
ConcurrentHashMap, so reads and writes are lock-free on the
common path and safe from any thread.
Un almacén clave-valor a nivel de proceso para los datos compartidos y
transversales que tus flujos necesitan — el usuario actual, un registro
seleccionado, feature flags — sin acoplar entre sí pantallas que no se
relacionan. Respaldado por un ConcurrentHashMap, así que las
lecturas y escrituras son lock-free en la ruta común y seguras desde
cualquier hilo.
OverviewVisión general
Where FlowController handles navigation
and StageManager handles windows,
AppContext holds the data both lean on. It is the singleton
you reach for whenever two screens need to agree on something but
shouldn't know about each other.
Mientras FlowController se encarga de la
navegación y StageManager de las ventanas,
AppContext guarda los datos en los que ambos se apoyan. Es el
singleton al que recurres cuando dos pantallas necesitan ponerse de acuerdo
en algo pero no deberían conocerse entre sí.
Keys must never be null or blank, and values must never be null — both are rejected with descriptive exceptions (matching ConcurrentHashMap, which forbids null keys/values). Lookups for a missing key never throw; they return null or your default.Las claves nunca pueden ser null ni estar en blanco, y los valores nunca pueden ser null — ambos se rechazan con excepciones descriptivas (acorde a ConcurrentHashMap, que prohíbe claves/valores null). Buscar una clave inexistente nunca lanza excepción; devuelve null o tu valor por defecto.
Quick startInicio rápido
AppContext ctx = AppContext.getInstance();
// After login:
ctx.put("currentUser", user);
// Anywhere else — the type is inferred from the assignment:
User current = ctx.get("currentUser");
String role = ctx.getOrDefault("role", "guest").toString();
if (ctx.contains("currentUser")) { /* ... */ }
Write operationsOperaciones de escritura
Stores value under key, replacing any previous value. Throws IllegalArgumentException for a null/blank key and NullPointerException for a null value.Almacena value bajo key, reemplazando cualquier valor previo. Lanza IllegalArgumentException ante una clave null/en blanco y NullPointerException ante un valor null.
Stores only if the key is currently unset. Returns the existing value (or null if none, meaning the new value was stored). Same validation as put.Almacena solo si la clave no está asignada actualmente. Devuelve el valor existente (o null si no había, lo que significa que el nuevo valor se almacenó). Misma validación que put.
Removes the entry under key, if any. Validates the key.Elimina la entrada bajo key, si existe. Valida la clave.
Removes every entry.Elimina todas las entradas.
Read operationsOperaciones de lectura
Returns the value, cast to the type inferred from the call site. Returns null if absent. Validates the key.Devuelve el valor, casteado al tipo inferido en el sitio de llamada. Devuelve null si no existe. Valida la clave.
Note: the cast is unchecked — a wrong target type surfaces as a ClassCastException at the use site, not here.Nota: el cast no está comprobado — un tipo destino equivocado aflora como ClassCastException en el sitio de uso, no aquí.
The stored value, or defaultValue (which may be null) when the key is absent.El valor almacenado, o defaultValue (que puede ser null) cuando la clave no existe.
true if a value is stored under key.true si hay un valor almacenado bajo key.
Whether the store holds no entries, and the entry count.Si el almacén no contiene entradas, y el número de entradas.
Because get is generic, User u = ctx.get("user"); needs no cast. When there's no target type, T infers to Object.Como get es genérico, User u = ctx.get("user"); no necesita cast. Cuando no hay tipo destino, T se infiere como Object.
Recommendations & pitfallsRecomendaciones y trampas
Do centralize your key names as constants (public static final String CURRENT_USER = "currentUser";) to avoid typos — blank keys are rejected, but misspelled ones silently miss.Centraliza los nombres de tus claves como constantes (public static final String CURRENT_USER = "currentUser";) para evitar errores de tipeo — las claves en blanco se rechazan, pero las mal escritas fallan en silencio.
Do store domain objects, not UI nodes — this is application state, not a scene graph.Almacena objetos de dominio, no nodos de UI — esto es estado de aplicación, no un grafo de escena.
Avoid leaving stale entries on logout: call remove or clear to prevent the next user from seeing the previous session's data.Evita dejar entradas obsoletas al cerrar sesión: llama a remove o clear para impedir que el siguiente usuario vea los datos de la sesión anterior.
Each operation is individually thread-safe, but a contains-then-put sequence is not atomic. Use putIfAbsent when you need check-and-set in one step.Cada operación es individualmente thread-safe, pero una secuencia contains-luego-put no es atómica. Usa putIfAbsent cuando necesites comprobar-y-asignar en un solo paso.
For diagnostics, toString() reports only the entry count — never the values — so application state can't leak into logs.Para diagnóstico, toString() reporta solo el número de entradas — nunca los valores — para que el estado de la aplicación no se filtre a los logs.