Bauke Scholtz created MYFACES-4776:
--------------------------------------
Summary: Restoring a component value set to an unmodifiable List
fails with "Could not restore StateHolder"
Key: MYFACES-4776
URL: https://issues.apache.org/jira/browse/MYFACES-4776
Project: MyFaces Core
Issue Type: Bug
Affects Versions: 4.1.5
Reporter: Bauke Scholtz
A {{List}} without a public zero-argument constructor, such as
{{List.of(...)}}, which Java code sets as the value of a component, cannot be
restored. The next postback fails with HTTP 500.
h3. Reproducer
{code:xml}
<h:form id="form">
<h:dataTable id="table" var="item">
<h:column><h:outputText value="#{item}" /></h:column>
</h:dataTable>
<h:commandButton id="set" value="Set" action="#{bean.setValue}" />
<h:commandButton id="submit" value="Submit" />
</h:form>
{code}
{code:java}
@Named
@RequestScoped
public class Bean {
public void setValue() {
((UIData)
FacesContext.getCurrentInstance().getViewRoot().findComponent("form:table")).setValue(List.of("b",
"a", "b"));
}
}
{code}
# Click Set. The table shows the rows {{b}}, {{a}} and {{b}}.
# Click Submit.
h3. Expected
The table shows the same rows.
h3. Actual
HTTP 500:
{noformat}
java.lang.IllegalStateException: Error restoring component: form:table
at
org.apache.myfaces.view.facelets.PartialStateManagementStrategy.restoreStateFromMap(PartialStateManagementStrategy.java:892)
...
Caused by: java.lang.RuntimeException: Could not restore StateHolder of type
java.util.ImmutableCollections$ListN (missing no-args constructor?)
at
jakarta.faces.component.UIComponentBase.restoreAttachedState(UIComponentBase.java:1821)
at
jakarta.faces.component._DeltaStateHelper.restoreState(_DeltaStateHelper.java:729)
at
jakarta.faces.component.UIComponentBase.restoreState(UIComponentBase.java:2064)
at jakarta.faces.component.UIData.restoreState(UIData.java:1206)
Caused by: java.lang.InstantiationException:
java.util.ImmutableCollections$ListN
Caused by: java.lang.NoSuchMethodException:
java.util.ImmutableCollections$ListN.<init>()
{noformat}
Reproduced on MyFaces 4.1.5-SNAPSHOT on Tomcat 11, with server side state
saving. With {{new ArrayList<>(List.of("b", "a", "b"))}} it works.
h3. Cause
{{UIComponentBase.saveAttachedState()}} saves every {{Collection}} other than
an {{ArrayList}} together with its class:
{code:java}
return new _AttachedCollectionStateWrapper(attachedObject.getClass(), lst);
{code}
{{UIComponentBase.restoreAttachedState()}} then creates a new instance of that
class:
{code:java}
restoredList = (Collection) clazz.newInstance();
{code}
That needs a public zero-argument constructor, which the unmodifiable
collections of the JDK do not have. They cannot be filled through {{add()}}
either.
h3. Spec
The javadoc of {{UIComponentBase.saveAttachedState()}}:
{quote}
This method supports saving attached objects of the following type:
{{Object}}s, {{null}} values, and {{Collection}}s of these objects. If any
contained objects are not {{Collection}}s and do not implement {{StateHolder}},
they must have zero-argument public constructors.
{quote}
The javadoc of {{UIComponentBase.restoreAttachedState()}}:
{quote}
This method supports restoring all attached objects types supported by
{{saveAttachedState}}.
{quote}
A {{Collection}} is supported, and the zero-argument constructor is required
only of the objects it contains, not of the collection itself.
h3. Proposed fix
In {{restoreAttachedState()}}, when the saved collection class has no
accessible zero-argument constructor, fall back to a collection of the same
kind: an {{ArrayList}} for a {{List}}, a {{LinkedHashSet}} for a {{Set}}, and
an {{ArrayList}} otherwise.
TCK: {{Issue6125IT.testUnmodifiableValueSetWholeIsRestored}} of the Jakarta
Faces 5.0 TCK
([jakartaee/faces#2267|https://github.com/jakartaee/faces/pull/2267]).
🤖 Generated with Claude Opus 5.5
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)