jakartaee / jakartaee/faces

Specify the runtime mechanism for storing and restoring the UIViewRoot view-scope map (currently implementation-invented)

Open
#2,185 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
127
Forks
59
Avg merge
23h
Merged PRs (30d)
7

Description

### Background

Since Faces 2.0, `UIViewRoot.getViewMap()` exposes a view-scoped `Map`, and the spec defines its *behavior* — it's created lazily, a `PostConstructViewMapEvent` is published on creation, and `clear()` publishes a `PreDestroyViewMapEvent`. Faces 2.2 added `restoreViewScopeState()` for restoring the view-scope portion of state ahead of the full tree.

What the spec does **not** define is the *mechanism*: how the view-scope map is correlated to its view and persisted/restored across postbacks (the per-view id and the per-session collection of active maps), nor how the runtime supplies the map implementation.

### Problem

Because the mechanism is unspecified, the implementations invented their own — and they leak impl-specific identifiers straight into the spec class `jakarta.faces.component.UIViewRoot`, in mutually incompatible ways:

- **Mojarra:** `UIViewRoot.saveState`/`restoreState` hardcode impl string keys. A `viewMapId` (minted by the impl `ViewScopeManager`) is read/written via the transient state helper under one key, and `restoreState` re-binds by reading an LRU collection of active view maps from the **session** under another key. So the API component embeds two impl-owned attribute names and the cross-request correlation logic itself.
- **MyFaces:** `getViewMap()` reflectively loads an impl **class** by FQN (`org.apache.myfaces.view.ViewScopeProxyMap`) — a `StateHolder` that owns its own `viewScopeId` and persists through the generic `saveAttachedState`/`restoreAttachedState` mechanism; the active-scope collection is a `@SessionScoped` CDI bean (with a non-CDI session-key fallback). The API component embeds the impl class name.

Consequences:

- The spec class `UIViewRoot` carries impl-specific identifiers (attribute-key names in one impl, a class FQN in the other) that each implementation must invent and that are not portable.
- The correlation/persistence contract differs entirely between implementations, so neither the saved-state shape nor the storage mechanism is interoperable.
- The behavior is specified but the mechanism is not — the same gap as #2184, here on `UIViewRoot` rather than `UIData`.

### Proposal

Define the lookup as a **CDI contract**, the same way proposed for the `@FacesDataModel` registry in #2184. The runtime provides a **view-scope-map provider** as a CDI bean under a stable, spec-owned name (and/or qualifier); the `UIViewRoot` spec class obtains it via the standard `BeanManager` and delegates to it for creating, correlating, and persisting the per-view map.

This single CDI lookup subsumes both impl-specific identifiers that currently leak into `UIViewRoot`:

- the **active-scope collection** (Mojarra's session-stored `ACTIVE_VIEW_MAPS`) becomes state owned by the provider bean, not a session attribute the component names;
- the **per-view correlation id** (Mojarra's `VIEW_MAP_ID`) is owned by the map the provider supplies (e.g. a `StateHolder` that saves/restores its own id through the standard attached-state mechanism), so `UIViewRoot.saveState`/`restoreState` carry no impl key.

Result: `UIViewRoot` references one spec-defined CDI bean instead of hardcoding session/state attribute names (Mojarra) or an implementation class FQN (MyFaces) — exactly the decoupling #2184 brings to `UIData`. Faces 4.x mandates CDI, so CDI is the natural SPI here, with no separate factory interface warranted.

### Notes

- Same class of problem as #2184 (`@FacesDataModel` registry) — behavior standardized, mechanism left to impls, so impls leak private contracts into the API.
- Related: eclipse-ee4j/mojarra#5787 tracks Mojarra finishing its `restoreViewScopeState()` implementation, which is a prerequisite for it adopting the attached-state approach and dropping its session/state keys.
- Largely additive: it standardizes a contract both implementations already satisfy (differently).

Contributor guide

Open the contributing guide

Research direction

Start with jakarta.faces.component.UIViewRoot and compare its saveState, restoreState, getViewMap(), and restoreViewScopeState() behavior with the Mojarra and MyFaces mechanisms described in the issue. Review the related #2184 proposal and Mojarra issue #5787 for context. Done means the view-scope-map CDI contract is specified without implementation-specific keys or class names leaking into UIViewRoot.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.