3
votes

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.

1
Tried a newer version? Tried myfaces? Just try, not saying to use in productionKukeltje

1 Answers

3
votes

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.

See also: