1

I have DTO which contains itself (it is nested).

public class MyDTO {

   private SomeData someData;
   private MyDTO nested;

   // getters and setters
}

I created composite component, which is calls itself recursively. I call it like this:

<screen:my-dto-screen dto="#{myDTOBean.myDto}" />

The definition is:

  <composite:interface>
    <composite:attribute name="dto"/>
</composite:interface>
        -- display "someData" here --
    <p:panel rendered="#{cc.attrs.dto.nested != null}" /> -- this acts as recursion bottom --
       <screen:my-dto-screen dto="#{cc.attrs.dto.nested}" />            
    </p:panel>
<composite:implementation>

The real code is not simple as this, but I wrote this in order to give you a simple idea what the problem is about. Everything seems fine, but I get some strange exceptions in view.

javax.servlet.ServletException
javax.faces.webapp.FacesServlet.service(FacesServlet.java:422)
org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:79)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:368)
cayetano.games.common.web.security.OperatorPermissionsFilter.doFilter(OperatorPermissionsFilter.java:74)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:97)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:100)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:78)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:35)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
cayetano.games.common.web.security.WebUsernamePasswordAuthenticationFilter.actualDoFilter(WebUsernamePasswordAuthenticationFilter.java:65)
cayetano.games.common.web.security.WebUsernamePasswordAuthenticationFilter.doFilter(WebUsernamePasswordAuthenticationFilter.java:43)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:169)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)

root cause

java.lang.StackOverflowError
java.util.regex.Pattern$Curly.match(Pattern.java:3754)
java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3383)
java.util.regex.Pattern$Slice.match(Pattern.java:3499)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3383)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$CharProperty.match(Pattern.java:3362)
java.util.regex.Pattern$Start.match(Pattern.java:3072)
java.util.regex.Matcher.search(Matcher.java:1116)
java.util.regex.Matcher.find(Matcher.java:552)
com.sun.faces.el.ELUtils.isCompositeComponentExpr(ELUtils.java:195)
com.sun.faces.facelets.tag.TagAttributeImpl.getValueExpression(TagAttributeImpl.java:388)
com.sun.faces.facelets.tag.TagAttributeImpl.getValueExpression(TagAttributeImpl.java:351)
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler$CompositeComponentRule$CompositeExpressionMetadata.applyMetadata(CompositeComponentTagHandler.java:588)
com.sun.faces.facelets.tag.MetadataImpl.applyMetadata(MetadataImpl.java:81)
javax.faces.view.facelets.MetaTagHandler.setAttributes(MetaTagHandler.java:129)
javax.faces.view.facelets.DelegatingMetaTagHandler.setAttributes(DelegatingMetaTagHandler.java:102)
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.setAttributes(CompositeComponentTagHandler.java:226)
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyNextHandler(CompositeComponentTagHandler.java:183)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
com.sun.faces.facelets.tag.composite.ImplementationHandler.apply(ImplementationHandler.java:81)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93)
com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:86)
com.sun.faces.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:152)
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyCompositeComponent(CompositeComponentTagHandler.java:349)
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyNextHandler(CompositeComponentTagHandler.java:190)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
com.sun.faces.facelets.tag.composite.ImplementationHandler.apply(ImplementationHandler.java:81)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93)
com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:86)
com.sun.faces.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:152)
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyCompositeComponent(CompositeComponentTagHandler.java:349)
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyNextHandler(CompositeComponentTagHandler.java:190)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
com.sun.faces.facelets.tag.composite.ImplementationHandler.apply(ImplementationHandler.java:81)
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
user358448
  • 1,127
  • 3
  • 20
  • 39

2 Answers2

3

This exception occurred during view build time. The rendered attribute is not evaluated during view build time. It's only evaluated during view render time (and during apply request values phase on input and command components, but that's a story apart). So your approach will end up in an infinite recursion loop which ultimately ends up in a stack overflow in the memory.

Basically, you want to evaluate the condition during view build time instead. So you need a tag/attribute which runs during view build time. One of them is JSTL <c:if>.

<c:if test="#{cc.attrs.dto.nested != null}">
    <p:panel>
       <screen:my-dto-screen dto="#{cc.attrs.dto.nested}" />            
    </p:panel>
</c:if>

However, this does not work if the #{cc.attrs.dto.nested} value is provided by a render-time attribute, so the recursion won't work. Only the top-level component will be shown. You're looking for an iterative approach. Consider looking at an existing JSF tree component, such as PrimeFaces' <p:tree>.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
2

I had a similar problem to the OP and managed to work my around it with foreach and the following blog post also provided by BalusC. All content below is quoted from this source and all credit to BalusC:

Recursive tree of composite components

As BalusC already mentioned, it is important to use tag/attributes which runs during view build time such as c:if and c:foreach. This example code below will however still yield a stackoverflowerror

<cc:interface>
    <cc:attribute name="node" type="com.example.SomeTreeModel" />
</cc:interface>
<cc:implementation>
    <c:if test="#{not empty cc.attrs.node.children}">
        <ul>
            <c:forEach items="#{cc.attrs.node.children}" var="node">
                <li>
                    #{node.data}
                    <my:tree node="#{node}" />
                </li>
            </c:forEach>
        </ul>
    </c:if>
</cc:implementation>

What turns out, when you pass #{node} to the nested composite, then you're technically basically passing #{cc.attrs.node.children[index]} to it. The nested composite is in turn thus interpreting its #{cc.attrs.node} as #{cc.attrs[cc.attrs.node.children[index]]}. But ... in the context of the nested composite, the #{cc} is referring the nested composite itself! So, the #{cc.attrs.node} actually refers to the nested composite's own node. This results in a never ending loop.

To avoid the stackoverflowerror we want:

  1. to refer to the parent component

  2. the composite to internally immediately evaluate the "required variable" and store it somewhere as a request based instance variable instead of storing only the ValueExpression object in the component state. A backing component is a good candidate.

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

    private SomeTreeModel node;

    @Override
    public void setValueExpression(String name, ValueExpression expression) {
        if ("node".equals(name)) {
            setNode((SomeTreeModel) expression.getValue(getFacesContext().getELContext()));
        }
        else {
            super.setValueExpression(name, expression);
        }
    }

    public SomeTreeModel getNode() {
        return node;
    }

    public void setNode(SomeTreeModel node) {
        this.node = node;
    }

}

and the component

<cc:interface componentType="treeComposite">
    <cc:attribute name="node" type="com.example.SomeTreeModel" />
</cc:interface>
<cc:implementation>
    <c:if test="#{not empty cc.node.children}">
        <ul>
            <c:forEach items="#{cc.node.children}" var="node" varStatus="loop">
                <li>
                    #{node.data}
                    <my:tree node="#{cc.parent.node.children[loop.index]}" />
                </li>
            </c:forEach>
        </ul>
    </c:if>
</cc:implementation>
Annanraen
  • 21
  • 6
  • @Kukeltje I've edited my answer to include the most important parts of the blog post, as per the consensus in the link you provided. As such, hopefully I've countered the link gone missing issue :) – Annanraen Feb 12 '20 at 07:37