In my backing-bean I have a collection of objects of different subclasses sharing a common interface. Inside the view, an ui:repeat
iterates over this collection. Inside this loop, different properties have to be rendered depending on the concrete implementation of the interface.
I reduced the problem to the following backing bean:
@Named
@SessionScoped
public class DummyBean implements Serializable {
private List<Type> objects = new ArrayList<Type>();
public void add1() {
objects.add(new Type1());
}
public void add2() {
objects.add(new Type2());
}
public void remove(Type o) {
objects.remove(o);
}
// Getter.
}
And the following Type
implementations which are added by the methods add1
and add2
. They have some common properties, but they may also have different ones, such as getType1OnlyMethod()
in below example:
public class Type1 implements Type {
@Override
public String getType() { return "1"; }
public List<String> getType1OnlyMethod() {
return Arrays.asList("only1", "only2");
}
// ...
}
public class Type2 implements Type {
@Override
public String getType() { return "2"; }
// ...
}
In the view I want to iterate over all objects which are stored in the DummyBean
and display their contents. Depending on the concrete type of the object, the method which is only implemented in the class Type1
may be called using the rendered
-attribute for conditional rendering:
<h:form id="dummyForm">
<ui:repeat value="#{dummyBean.objects}" var="_obj" >
<h:outputText value="Type of the Object is #{_obj.type}." />
<h:panelGroup rendered="#{_obj.type eq '1'}">
<ui:repeat value="#{_obj.type1OnlyMethod}" var="_oneOnlyObject" varStatus="_status" >
<h:outputText value="#{_oneOnlyObject}" />
</ui:repeat>
</h:panelGroup>
<h:commandLink value="remove" action="#{dummyBean.remove(_obj)}">
<f:ajax render="dummyForm" />
</h:commandLink>
<br/>
</ui:repeat>
<h:commandLink value="add1" action="#{dummyBean.add1}">
<f:ajax render="dummyForm" />
</h:commandLink>
<br/>
<h:commandLink value="add2" action="#{dummyBean.add2}">
<f:ajax render="dummyForm" />
</h:commandLink>
</h:form>
Using the h:commandLink
s on the page I can add and remove Type1
objects at will. If I add Type2
objects, they are rendered as expected. But when I try to remove one of the Type2
objects, I get an exception in the render-response phase (nevertheless if there are other elements in the list or not):
2014-03-24 15:42:46,754 SEVERE [javax.enterprise.resource.webcontainer.jsf.application] (default task-11) Error Rendering View[/transition/beginTestSite.xhtml]:
javax.el.PropertyNotFoundException: /transition/beginTestSite.xhtml @23,96 value="#{_obj.type1OnlyMethod}": The class 'at.co.xss.Type2' does not have the property 'type1OnlyMethod'.
at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:111) [jsf-impl-2.2.5-jbossorg-3.jar:]
at com.sun.faces.facelets.component.UIRepeat.getValue(UIRepeat.java:279) [jsf-impl-2.2.5-jbossorg-3.jar:]
at com.sun.faces.facelets.component.UIRepeat.getDataModel(UIRepeat.java:255) [jsf-impl-2.2.5-jbossorg-3.jar:]
at com.sun.faces.facelets.component.UIRepeat.setIndex(UIRepeat.java:523) [jsf-impl-2.2.5-jbossorg-3.jar:]
at com.sun.faces.facelets.component.UIRepeat.doVisitChildren(UIRepeat.java:790) [jsf-impl-2.2.5-jbossorg-3.jar:]
at com.sun.faces.facelets.component.UIRepeat.visitTree(UIRepeat.java:748) [jsf-impl-2.2.5-jbossorg-3.jar:]
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700) [jboss-jsf-api_2.2_spec-2.2.5.jar:2.2.5]
at com.sun.faces.facelets.component.UIRepeat.visitTree(UIRepeat.java:754) [jsf-impl-2.2.5-jbossorg-3.jar:]
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700) [jboss-jsf-api_2.2_spec-2.2.5.jar:2.2.5]
at javax.faces.component.UIForm.visitTree(UIForm.java:371) [jboss-jsf-api_2.2_spec-2.2.5.jar:2.2.5]
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700) [jboss-jsf-api_2.2_spec-2.2.5.jar:2.2.5]
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700) [jboss-jsf-api_2.2_spec-2.2.5.jar:2.2.5]
at com.sun.faces.application.view.FaceletPartialStateManagementStrategy.saveView(FaceletPartialStateManagementStrategy.java:472) [jsf-impl-2.2.5-jbossorg-3.jar:]
at com.sun.faces.application.StateManagerImpl.saveView(StateManagerImpl.java:89) [jsf-impl-2.2.5-jbossorg-3.jar:]
at javax.faces.application.StateManager.getViewState(StateManager.java:593) [jboss-jsf-api_2.2_spec-2.2.5.jar:2.2.5]
at com.sun.faces.context.PartialViewContextImpl.renderState(PartialViewContextImpl.java:486) [jsf-impl-2.2.5-jbossorg-3.jar:]
at com.sun.faces.context.PartialViewContextImpl.processPartial(PartialViewContextImpl.java:328) [jsf-impl-2.2.5-jbossorg-3.jar:]
at javax.faces.context.PartialViewContextWrapper.processPartial(PartialViewContextWrapper.java:219) [jboss-jsf-api_2.2_spec-2.2.5.jar:2.2.5]
at org.primefaces.context.PrimePartialViewContext.processPartial(PrimePartialViewContext.java:57) [primefaces-4.0.jar:4.0]
at org.richfaces.context.ExtendedPartialViewContextImpl.processPartial(ExtendedPartialViewContextImpl.java:218) [richfaces-core-impl-4.3.5.Final.jar:4.3.5.Final]
at javax.faces.component.UIViewRoot.encodeChildren(UIViewRoot.java:1004) [jboss-jsf-api_2.2_spec-2.2.5.jar:2.2.5]
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1856) [jboss-jsf-api_2.2_spec-2.2.5.jar:2.2.5]
at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:435) [jsf-impl-2.2.5-jbossorg-3.jar:]
at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:133) [jsf-impl-2.2.5-jbossorg-3.jar:]
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337) [jboss-jsf-api_2.2_spec-2.2.5.jar:2.2.5]
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337) [jboss-jsf-api_2.2_spec-2.2.5.jar:2.2.5]
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337) [jboss-jsf-api_2.2_spec-2.2.5.jar:2.2.5]
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337) [jboss-jsf-api_2.2_spec-2.2.5.jar:2.2.5]
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:120) [jsf-impl-2.2.5-jbossorg-3.jar:]
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) [jsf-impl-2.2.5-jbossorg-3.jar:]
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:219) [jsf-impl-2.2.5-jbossorg-3.jar:]
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:647) [jboss-jsf-api_2.2_spec-2.2.5.jar:2.2.5]
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85) [undertow-servlet-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:130) [undertow-servlet-1.0.0.Final.jar:1.0.0.Final]
at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449) [shiro-web-1.2.2.jar:1.2.2]
at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365) [shiro-web-1.2.2.jar:1.2.2]
at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90) [shiro-core-1.2.2.jar:1.2.2]
at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83) [shiro-core-1.2.2.jar:1.2.2]
at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383) [shiro-core-1.2.2.jar:1.2.2]
at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362) [shiro-web-1.2.2.jar:1.2.2]
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) [shiro-web-1.2.2.jar:1.2.2]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:56) [undertow-servlet-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:132) [undertow-servlet-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:85) [undertow-servlet-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:61) [undertow-servlet-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) [undertow-servlet-1.0.0.Final.jar:1.0.0.Final]
at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:113) [undertow-servlet-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.security.handlers.AuthenticationCallHandler.handleRequest(AuthenticationCallHandler.java:52) [undertow-core-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:45) [undertow-core-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:61) [undertow-servlet-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:70) [undertow-servlet-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.security.handlers.SecurityInitialHandler.handleRequest(SecurityInitialHandler.java:76) [undertow-core-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.0.Final.jar:1.0.0.Final]
at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:240) [undertow-servlet-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:227) [undertow-servlet-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:73) [undertow-servlet-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:146) [undertow-servlet-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:168) [undertow-core-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:687) [undertow-core-1.0.0.Final.jar:1.0.0.Final]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_45]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_45]
at java.lang.Thread.run(Thread.java:744) [rt.jar:1.7.0_45]
Caused by: javax.el.PropertyNotFoundException: The class 'at.co.xss.Type2' does not have the property 'type1OnlyMethod'.
at javax.el.BeanELResolver.getBeanProperty(BeanELResolver.java:730) [javax.el-3.0.0.jar:3.0.0]
at javax.el.BeanELResolver.getValue(BeanELResolver.java:351) [javax.el-3.0.0.jar:3.0.0]
at com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:176) [jsf-impl-2.2.5-jbossorg-3.jar:]
at com.sun.faces.el.DemuxCompositeELResolver.getValue(DemuxCompositeELResolver.java:203) [jsf-impl-2.2.5-jbossorg-3.jar:]
at com.sun.el.parser.AstValue.getValue(AstValue.java:140) [javax.el-3.0.0.jar:3.0.0]
at com.sun.el.parser.AstValue.getValue(AstValue.java:204) [javax.el-3.0.0.jar:3.0.0]
at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:226) [javax.el-3.0.0.jar:3.0.0]
at org.jboss.weld.el.WeldValueExpression.getValue(WeldValueExpression.java:50) [weld-core-impl-2.1.2.Final.jar:2014-01-09 09:23]
at org.jboss.weld.el.WeldValueExpression.getValue(WeldValueExpression.java:50) [weld-core-impl-2.1.2.Final.jar:2014-01-09 09:23]
at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:109) [jsf-impl-2.2.5-jbossorg-3.jar:]
... 66 more
It seems that in the render-response phase the nested ui:repeat
is "executed" for the currently deleted Type2
object.
If I remove the second (nested) ui:repeat
and add some plain h:outputText
to access the property which is only available in Type1
, everything works again without exception when I add or remove elements:
<h:form id="dummyForm">
<ui:repeat value="#{dummyBean.objects}" var="_obj" >
<!-- ... -->
<h:panelGroup rendered="#{_obj.type eq '1'}">
<h:outputText value="#{_obj.type1OnlyMethod.size()}" />
</h:panelGroup>
<!-- ... -->
</h:form>
Does anyone have any idea on this topic?
(Running on JEE7 application server (Wildfly 8.0.0.Final))
This is caused by a bug in state management of Mojarra's
<ui:repeat>
which is fixed as per issue 3215 as reported by my fellow Arjan Tijms. We also faced this problem when iterating over differentBlock
instances at zeef.com (LinkBlock
,ImageBlock
,TextBlock
andFeedBlock
). The fix is available in Mojarra 2.2.7 and for JSF 2.0/2.1 backported to Mojarra 2.1.29 as per issue 3221. So upgrading to at least that version (or just the latest available as per Mojarra homepage) should do it.Otherwise, your best bet is to replace
<ui:repeat>
by<c:forEach>
.In my case I didn't had the option to upgrade Mojarra's version and to avoid c:forEach (that causes many side-effects when used with ui optionally rendered components) I replaced ui:repeat with a p:dataList and it worked. You'll have to do some CSS styling to hide the bullets but I think it's worth the price. I hope it helps someone ;)