JSF: jak zapobiec przepływowi stosu z powodu rekursji podczas fazy kompilacji (pomimo renderowanego testu)

Przepraszam, że nie wyodrębniłem tego problemu w dedykowanym przypadku testowym, mam nadzieję, że przykład z prawdziwego projektu jest wystarczająco prosty, aby opisać problem.

Mam aplikację webową JavaEE / JPA2 / JSF, w której każdy element @Entity (lub podklasa) ma stronę view.xhtml z szablonami i standardowy komponent kompozytowy generator linków util: view_link.xhtml, wywoływany jako GET z identyfikatorem bazy danych jako parametrem. Część (tylko) każdej strony widoku przedstawia podsumowanie systemu eksperckiego; ta część może być abstrakcyjna jako komponent złożony, w celu włączenia jej do strony widoku lub gdzie indziej.

Wprowadziłem wyskakujące okienko modalne Primefaces p: do wyświetlania tej części podsumowania systemu eksperckiego (i dowolnej dodatkowej diagnostyki) po kliknięciu małej ikony statusu wyświetlanej obok łącza widoku. Jeśli pozwolisz, aby ikona statusu była reprezentowana przez x, wygląda to tak:

x Link_to_Element_by_ID

Kliknij link „Link_to_Element_by_ID”, aby wyświetlić stronę pełnego widoku.

Kliknij ikonę „x” (wskaźnik awarii systemu eksperckiego) i wyświetli się okno dialogowe p: z podsumowaniem systemu eksperckiego (tylko).

Dlatego część strony widoku systemu eksperckiego jest współużytkowana jako komponent złożony.

Ale może to prowadzić do rekurencji i Stackoverflow, jeśli:

Podsumowanie wyskakującego systemu p: dialog pokazuje wskaźnik ikony statusu kontrolowanego elementu.

Dołączam dodatkowe łącza widoku elementów wraz ze wskaźnikami stanu (które same uruchamiają okno dialogowe p: dla podsumowania systemu eksperckiego).

Próbowałem użyć renderowanych testów przy użyciu atrybutu blokowania rekurencji 'preventRecursionOnDialog', ale zawodzi, najwyraźniej dlatego, że rekursja ma miejsce podczas fazy budowania.

P: Jak mogę zablokować możliwą rekursję za pomocą zmiennej testowej?

Próbowałem także testów c: if zamiast JSF 'rendered', ale wydaje się, że testowana zmienna nie jest dostępna w @ViewScoped.

Przykład dla elementu Activity, w którym util_primefaces: dialog_summary jest tylko niestandardową enkapsulacją okna dialogowego p :.

Z 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>

Funkcja util: warn_insufficient_subactivities (która pokazuje, które poddziałania działania złożonego nie przeszły testu systemu eksperckiego) może spowodować rekursję:

    <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>

A util: list_activity_compact pokazuje listę ze wskaźnikami ikony stanu (które z kolei mogą oferować wyskakujące okno p: z podsumowaniem systemu eksperckiego i może powtarzać się) oraz 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>

Problem polega na tym, że test rendered = "# {not cc.attrs.preventRecursionOnDialog}" nie jest wystarczający do zablokowania rekurencji, nawet jeśli część, która ma być rekurencyjna, nie jest renderowana (blokowana przez renderowany test), wydaje się, że rekursja może nadal występować podczas fazy budowania JSF.

BTW Często spotykam podobny problem, gdy chcę tylko renderować określony komponent złożony związany z typem, w podzbiorze wyborów typu; wykonanie testu typu „renderowane” nie jest wystarczające, aby zapobiec błędowi typu. Wyobraźmy sobie, że poniżej tej „wartości” może znajdować się jedna z wielu podklas Elementów, w tym Aktywność, ale chcemy tylko wyświetlić następującą część składową złożoną dla Działania:

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

(por.instanceof check w języku wyrażenia ELi zauważ, że rozwiązanie testowe typu oparte na klasie String nie jest zbyt elastyczne, nie działa dla podklas ani dla testów interfejsów.)

Ponownie, próba zablokowania wywołania „renderowane” nie wystarczy, wydaje się, że test typu nie powiedzie się już w fazie kompilacji. Rozwiązanie problemu rekursji mogłoby również rozwiązać ten problem. Nawet wprowadzenie (w końcu) wystąpienia w JSF2 (głosuj tutaj, proszęhttp://java.net/jira/browse/JSP_SPEC_PUBLIC-113) nie pomogłoby tutaj, gdyby użyto go tylko w „renderowanym”,

questionAnswers(2)

yourAnswerToTheQuestion