We are developing an enterprise application with pretty large and complex views based on Mojarra 2.2.8. Our largest views produce JSF component trees of >20.000 nodes. Of coure we ran into performance issues, so we started to reduce the tree size by replacing <ui:repeat>
with <c:forEach>
and by replacing the rendered
attribute with <c:if>
blocks as recommended in this very informative post by BalusC.
These measures reduced the component tree by a huge amount and brought significant improvements in performance.
One measure did not work out though: Our views contain severals tabs in a tab group. When we tried to remove contents of invisible tabs via <c:if>
tags, the component tree of these tabs was still present and there was no benefit in performance.
We figured out, that the only difference between this usage of <c:if>
and several other places was, that this time <c:if>
was part of a composite component and its contents were injected through a <composite:insertChildren/>
tag. The process of injecting the children of a composite component tag to its definition seems to somehow conflict with the <c:if>
tag. We also tried to replace the insertChildren
tag with a facet
based approach, but the result remained the same.
To demonstrate this, here is a composite component, that wraps all its contents in a <c:if>
tag, that always evaluates to false
:
<composite:interface>
</composite:interface>
<composite:implementation>
<c:if test="false">
<composite:insertChildren />
</c:if>
</composite:implementation>
This component shoud display nothing, even if it is used with a lot of child elements in a view. And it does display nothing but the component tree is still there and there is no performance benefit.
Unfortunately, this makes it impossible for us, to develop a performance optimized tab group composite component. Can anybody please explain, why this happens and if there is a way around it?
We are on mojarra 2.2.8, el-api 2.2.5, tomcat 8.0.
Technical problem is that the <c:if>
in this construct only prevents inclusion (and execution) of the <cc:insertChildren>
tag itself. The <cc:insertChildren>
in turn is actually responsible for relocating any composite component children to the declared place in the composite component implementation so that they will be actually rendered. By default, a composite component does not render any children. The declared children are actually stored as a facet of the composite component.
In other words, the <c:if>
actually doesn't prevent those component children to end up in the component tree. It only prevents the <cc:insertChildren>
from being invoked. The observed behavior is therefore working as designed. To achieve the desired behavior, you should actually be moving back the <c:if>
to the client.
<your:composite ...>
<c:if test="#{false}">
...
</c:if>
</your:composite>
I understand that this is unintuitive. Actually, you should take a step back from using composite components to compose the template. They are not well suited for that. You should use <ui:composition>
, <ui:include>
, <ui:decorate>
or tag files for that instead. Composite components should only be used to compose a component.