Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSF: how prevent stackoverflow due to recursion during build phase (despite rendered test)

Apologies for not abstracting this problem in a dedicated test case, I hope the example from a real project is simple enough to describe the problem.

I have a JavaEE/JPA2/JSF web application where every @Entity Element (or subclass) has a templated view.xhtml page, and a standard link generator composite component util:view_link.xhtml, invoked as GET with database ID as parameter. A portion (only) of each view page represents an expert system summary; that portion can be abstracted as a composite component, for inclusion in a view page or elsewhere.

I have introduced a Primefaces p:dialog modal popup for displaying that expert system summary portion (and any additional diagnostics) when clicking on a small status icon displayed next to the view link. If you let the status icon be represented by x it looks like this:

x Link_to_Element_by_ID

Click on 'Link_to_Element_by_ID' and it brings up the full view page.

Click on the 'x' icon (expert system test failure indicator) and it pops up the p:dialog with the expert system summary (only).

So the expert system portion of the view page is shared as a composite component.

But this can lead to recursion and a Stackoverflow if either:

  1. The popup p:dialog expert system summary shows the status icon indicator of the Element being inspected.

  2. I include additional element view links along with with status indicators (that would themselves launch a p:dialog for an expert system summary).

I have tried using rendered tests using a recursion blocking attribute 'preventRecursionOnDialog' but it fails, apparently because the recursion is happening during the build phase.

Q: How can I block possible recursion using a test variable ?

Also, I have tried c:if tests instead of JSF 'rendered' tests, but it seems the variable tested are not available under @ViewScoped.

Example, for an Activity element, where util_primefaces:dialog_summary is merely a customised encapsulation of a p:dialog.

From util:status_activity.xhtml:

     <composite:attribute 
        name="activity"
        required="true"
        type="com.example.entity.Activity"
      />
    <composite:attribute
        name="preventRecursionOnDialog"
        required="false"
        default="false"
        type="java.lang.Boolean"
        />
</composite:interface>

<composite:implementation> 
    <util_primefaces:dialog_summary 
        header="Expert system summary report"
        rendered="#{not cc.attrs.preventRecursionOnDialog}"
        element="#{cc.attrs.activity}">

        <!-- causes StackOverflowError -->

        <util:warn_insufficient_subactivities 
            activityContainer="#{cc.attrs.activity}"
            humanTypeDescription="composite activity"
            preventRecursionOnDialog="true"
            />

        <util:expertsystem_activity activity="#{cc.attrs.activity}"/>

    </util_primefaces:dialog_summary>
    ..
    <span 
        onclick="#{not cc.attrs.preventRecursionOnDialog ? ('dialog'.concat(cc.attrs.activity.id).concat('.show();')) : ''}" 
        style="float:left;" 
        class="icon-completed-#{cc.attrs.activity.acceptedEffective}-small"
        title=".."
        >&nbsp;</span>

The util:warn_insufficient_subactivities (which shows which subactivities of a composite activity have not passed an expert system test) can cause recursion:

    <cc:interface>
    <cc:attribute name="activityContainer" required="true" type="com.example.entity.IActivityContainer"/>
    <cc:attribute name="humanTypeDescription" required="true" type="java.lang.String"/>
    <cc:attribute
        name="preventRecursionOnDialog"
        required="false"
        default="false"
        type="java.lang.Boolean"
        /> 
</cc:interface> 

<cc:implementation>     
    <h:panelGroup 
        rendered="#{not cc.attrs.activityContainer.sufficientSubActivitiesAccepted}">
        <util:warn_box 
            message=".."
            >
            <!-- CAUTION: can cause Stackoverflow when list included in expertsystem p:dialog popup -->
            <util:list_activity_compact 
                list="#{cc.attrs.activityContainer.activities}" 
                preventRecursionOnDialog="#{cc.attrs.preventRecursionOnDialog}"
                rendered="#{not cc.attrs.preventRecursionOnDialog}"
                />                
        </util:warn_box>

And the util:list_activity_compact shows a list with status icon indicators (which in turn can offer a popup p:dialog with expert system summary, and can recurse) and util:view_link:

    <cc:interface>

    <cc:attribute 
        name="list" required="true" type="java.util.List"
        />

    <cc:attribute
        name="preventRecursionOnDialog"
        required="false"
        default="false"
        type="java.lang.Boolean"
        />

</cc:interface>

<cc:implementation>
    <h:panelGroup display="block">
        <ul class="view-field-list-medium">
            <ui:repeat var="a" value="#{cc.attrs.list}">
                <li class="view-field-list">
                    <util:status_activity 
                        activity="#{a}" 
                        preventRecursionOnDialog="#{cc.attrs.preventRecursionOnDialog}"/> 
                    <util:view_link element="#{a}"/>
                </li>
            </ui:repeat>
        </ul>
    </h:panelGroup>
</cc:implementation>

The point of the question is that the test rendered="#{not cc.attrs.preventRecursionOnDialog}" is not sufficient to block the recursion, even though the part that would recurse is not rendered (blocked by the rendered test), it seems recursion can still happen during the JSF build phase.


BTW I often encounter a similar problem when I only want to render a particular composite component bound to a type, within a subset of type choices; performing a type test in 'rendered' is not enough to prevent a type error. Imagine below that 'value' could be one of many Element subclasses including Activity, but one only wants to display the following composite component portion for an Activity:

        <util:component_for_Activity_only 
            activity="#{cc.attrs.value}"
            rendered="#{cc.attrs.value['class'].simpleName=='Activity'}"
            />

(cf. instanceof check in EL expression language, and note that that Class String based type test solution is not very flexible, it does not work for subclasses or for interface tests.)

Again, the attempt to block calling with 'rendered' is not enough, it seems the type test fails already during the build phase. A solution to the recursion problem would also offer a solution to this. Even the introduction (finally) of instanceof in JSF2 (vote here please http://java.net/jira/browse/JSP_SPEC_PUBLIC-113) would not help here if only used in 'rendered',

like image 254
Webel IT Australia - upvoter Avatar asked Feb 05 '26 08:02

Webel IT Australia - upvoter


1 Answers

Answering own questions after further research and trials.

Firstly, thanks to meriton, your response did not exactly answer my question but put me on the right path.

The short answer is that you can use c:if tests to control recursion during the build phase for @ViewScoped since Mojarra 2.1.18. In my case this now works:

        <c:if test="#{not cc.attrs.preventRecursionOnDialog}">

As so often, I was led to this answer (the need to upgrade to Mojarra >= 2.1.18 and explanations about improved handling of JSTL taglibs in @ViewScoped) by the contributions of BalusC to other postings including:

https://java.net/jira/browse/JAVASERVERFACES-1492

JSF2 Viewscope validation issue

http://balusc.blogspot.com.au/2010/06/benefits-and-pitfalls-of-viewscoped.html

JSTL in JSF2 Facelets... makes sense?

What are the main disadvantages of Java Server Faces 2.0?

I consider this matter extremely important for anybody working with JSF ! The ability to control what is built - as opposed to what is rendered - easily in @ViewScoped solves many problems and opens up many possibilities, and I recommend that anybody who is seriously working with JSF takes the time to read the remarks by BalusC in the above links.

BalusC, in case your read this, please know that you are are a true hero of JavaServer Faces and Enterprise Java. On behalf of every JSF enthusiast may I thank you.

And thanks to Ed Burns and Ted Goddard for your excellent work in reporting and fixing this: https://java.net/jira/browse/JAVASERVERFACES-1492 It is a major improvement for JSF2.

Finally, I had to use a dirty trick to get Mojarra 2.1.21 installed on NetBeans7.1+Glassfish3.1.1 (required because of 3rd party incompatibilities) as explained here: JSF how upgrade to Mojarra 2.1.21 in Netbeans7.1 (just sub jsf-api.jar and jsf-impl.jar fails)

This was an excellent result for my project. What would I do without Stackoverflow :)

like image 89
Webel IT Australia - upvoter Avatar answered Feb 12 '26 21:02

Webel IT Australia - upvoter



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!