Nested JSF Composite Components leading to a Stack Overflow exception

江枫思渺然 提交于 2019-11-27 09:28:22

The problem was in the context of the #{cc} and the statefulness of the composite attribute. The #{cc} in any attribute of the nested composite references itself instead of the parent. The attribute being stateful means that the #{cc} was re-evaluated in every child which in turn ultimately references itself instead of the parent. Hence the stack overflow. It's evaluating the depth of itself in an infinite loop.

I tricked the statefulness of the attribute by making it stateless using a backing component as below which immediately evaluates it and assigns it as a component property:

@FacesComponent("treeComposite")
public class TreeComposite extends UINamingContainer {

    private Integer depth;

    @Override
    public void setValueExpression(String name, ValueExpression binding) {
        if ("depth".equals(name)) {
            setDepth((Integer) binding.getValue(getFacesContext().getELContext()));
        }
        else {
            super.setValueExpression(name, binding);
        }
    }

    public Integer getDepth() {
        return depth;
    }

    public void setDepth(Integer depth) {
        this.depth = depth;
    }

}

Which is to be declared in interface's componentType as below:

<cc:interface componentType="treeComposite">
    <cc:attribute name="depth" type="java.lang.Integer" />
</cc:interface>

And, in the implementation you should in the test reference the stateless property and in the nested composite reference the one of the parent (because #{cc} in the attribute of the nested composite references the nested composite itself):

<cc:implementation>
    <br />We're at depth #{cc.depth}.
    <c:if test="#{cc.depth gt 0}">
        <my:tree depth="#{cc.parent.depth - 1}" />
    </c:if>
</cc:implementation>

I only changed the meaning of "depth" here to be the other way round so that it's just declarative from the client on without the need to edit it in the implementation. So, in the client you have to say depth="#{3}" if you want 3 nested children:

<my:tree depth="#{3}" />

Note the importance of it being an EL expression rather than a literal. Otherwise setValueExpression() in the backing component won't be called.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!