processPartial should select component resources by renderer type, not by resource name
- Dominant language
- Java
- Stars
- 127
- Forks
- 59
- Avg merge
- 23h
- Merged PRs (30d)
- 7
Description
Discovered while analyzing eclipse-ee4j/mojarra#5969.
`PartialViewContext#processPartial` requires component resources to be selected for the partial response by a lookup keyed on the resource *name*:
> If `isRenderAll()` returns `false`, then render any component resource of `UIViewRoot` whose `ResourceHandler#getRendererTypeForResourceName(String)` does not return `null`, and whose `UIComponent#getChildCount()` is zero, and whose `ResourceHandler#isResourceRendered(FacesContext, String, String)` returns `false`, in an `update` element with an identifier of `jakarta.faces.Resource`.
The intent is "render the component resources that a resource renderer will render". But the condition tests the name rather than the component, and the component already carries the answer: its renderer type was set when it was created, either from the tag or from the `@ResourceDependency` algorithm, which itself obtains it from `getRendererTypeForResourceName`.
### Why the proxy fails
`getRendererTypeForResourceName` is specified only by an example table of two rows, `mycomponent.js` to `jakarta.faces.resource.Script` and `mystyle.css` to `jakarta.faces.resource.Stylesheet`, plus "If no `renderer-type` can be determined, `null` must be returned". A component resource whose name is not a plain file name therefore drops out of the partial response even though it has a resource renderer type and renders correctly on a full page render.
The case that surfaced this is a name carrying a query string, `theme.css?v=1`, which both implementations accept on `h:outputStylesheet` (see the companion issue on the resource identifier grammar). Mojarra derives the renderer type from the content type, the content type comes back `null` for that name, the resource is skipped, and in Development stage a warning is logged on every ajax request. The same hole is open for any name whose extension the runtime cannot map.
### How implementations differ
Mojarra implements the requirement literally, in `PartialViewContextImpl#renderComponentResources`, and inherits the hole.
MyFaces does not, and it does not even use the same mechanism. Its `UIViewRoot#addComponentResource` registers the added component with `RequestViewContext` as a render target whenever the request is an ajax request and the view is not building its initial state, and `PartialViewContextImpl#processRenderResource` iterates only those, that is, only the component resources added during the current request. It never calls `getRendererTypeForResourceName` in this path. A script or stylesheet renderer type carrying a name is deduplicated through `isResourceRendered`, while a component resource with any other renderer type, or with no name, is rendered unconditionally.
So the two implementations differ in the selection rule and in what they select from: Mojarra scans every component resource of the view and filters, MyFaces tracks additions. An implementation cannot be both conformant and correct here.
### Component resources without a name
A component resource is not required to have a *name*. `` with an inline body and no `name` is sanctioned by the Standard HTML RenderKit, under *Common Encode Behavior*: "If there is no *name* attribute and the argument `component` **does** have children, the renderer must ensure that those children are encoded as usual." Such a resource has no identity for `markResourceRendered` and `isResourceRendered`, which key on `libraryName + ':' + resourceName`, so the question "has this already been rendered" cannot be answered for it at all.
The render response sequence already answers it, in its first condition rather than its second: zero child count excludes every inline body resource from the partial response. A nameless resource *without* a body renders nothing anyway, the Script renderer being required to log a `FacesMessage` outside Production and take no further action. A nameless component resource is therefore never delivered into a partial response today, and this issue does not propose changing that.
It matters here only because the second condition enforces the same outcome as a side effect, `getRendererTypeForResourceName(null)` returning `null`, and the change proposed below removes that side effect.
### Proposal
Change the second bullet of the `processPartial` render response sequence to select component resources whose `UIComponent#getRendererType()` is non-`null`, rather than those whose *name* maps to a renderer type via `ResourceHandler#getRendererTypeForResourceName`.
Testing the component rather than the name keeps `getRendererTypeForResourceName` in the role it was introduced for, deriving a renderer type from a bare name when a component resource is created from `@ResourceDependency`, and it keeps working for a `ResourceHandler` that maps its own extensions to its own renderer types, since that answer is already on the component. Enumerating the two predefined renderer types in the condition would not: it would exclude every custom resource renderer type, which the present name based condition does support. At the same time a component resource whose name the runtime cannot map to a content type is no longer dropped even though it has a perfectly good renderer.
The condition must additionally require a non-`null` *name*, which the name based condition enforces implicitly and the renderer type based one does not. That keeps the outcome for nameless component resources exactly as it is described above, and it keeps a component which is not a resource instance at all, and which `addComponentResource` is not specified to accept, out of the partial response.
The remaining two conditions, zero child count and `isResourceRendered` returning `false`, are unaffected.
This is also cheaper: it replaces a per resource content type lookup, which in at least one implementation reaches into the container's mime type mapping, with a field read on a component that is already in hand.
🤖 Generated with Claude Opus 5
Contributor guide
Research direction
Start with Mojarra's PartialViewContextImpl#renderComponentResources and trace the processPartial render-response conditions. Compare the selection behavior with MyFaces UIViewRoot#addComponentResource and PartialViewContextImpl#processRenderResource. Done means selecting named component resources by non-null UIComponent#getRendererType(), while preserving the child-count and isResourceRendered checks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend-api-design
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100