Извинения за не абстрагирование этой проблемы в отдельном тестовом случае, я надеюсь, что пример из реального проекта достаточно прост, чтобы описать проблему.
У меня есть веб-приложение JavaEE/JPA2/JSF, в котором каждый элемент @Entity (или подкласс) имеет шаблонную страницу view.xhtml и стандартный составной компонент генератора ссылок: view_link.xhtml, вызывается как GET с идентификатором базы данных, как параметр. Часть (только) каждой страницы просмотра представляет сводку экспертной системы; эта часть может быть абстрагирована как составной компонент для включения на страницу просмотра или где-либо еще.
Я ввел мозаичное всплывающее окно Primefaces p: dialog для отображения этой части итоговой экспертной системы (и любой дополнительной диагностики) при нажатии маленького значка состояния, отображаемого рядом с ссылкой вида. Если вы укажете значок состояния x, он будет выглядеть так:
x Link_to_Element_by_ID
Нажмите "Link_to_Element_by_ID", и он отобразит страницу полного просмотра.
Нажмите значок "x" (индикатор сбоя теста экспертной системы), и он отобразит диалог p: с помощью сводки экспертной системы (только).
Таким образом, часть экспертной системы на странице просмотра совместно используется как составной компонент.
Но это может привести к рекурсии и Stackoverflow, если:
-
Сводка экспертной системы popup p: dialog отображает индикатор состояния элемента проверяемого элемента.
-
Я включаю дополнительные ссылки на представление элемента вместе со статусом (который сам запустит диалог p: для сводки экспертной системы).
Я попытался использовать обработанные тесты, используя атрибут блокировки рекурсии 'preventRecursionOnDialog', но он не работает, по-видимому, потому, что рекурсия происходит во время фазы сборки.
Q: Как заблокировать возможную рекурсию с помощью тестовой переменной?
Кроме того, я попытался использовать тесты c: if вместо JSF 'rendered', но, похоже, проверенная переменная недоступна в @ViewScoped.
Пример, для элемента Activity, где util_primefaces: dialog_summary - это просто индивидуальная инкапсуляция диалога p:.
От 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=".."
> </span>
Использовать: warn_insufficient_subactivities (который показывает, какие субактивности объединенной активности не прошли тест экспертной системы) может вызвать рекурсию:
<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>
И утилита: list_activity_compact показывает список со значками индикатора состояния (который, в свою очередь, может предложить всплывающее окно p: диалог со сводкой экспертной системы и может рекурсивно) и использовать: 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>
Точка вопроса заключается в том, что тест rendered = "# {not cc.attrs.preventRecursionOnDialog}" недостаточен для блокировки рекурсии, даже если часть, которая будет рекурсивно, не будет отображаться (заблокирована обработанным тестом), похоже, что рекурсия может произойти во время фазы сборки JSF.
Кстати. Я часто сталкиваюсь с аналогичной проблемой, когда хочу только сделать конкретный составной компонент привязанным к типу, в пределах подмножества выбора типа; выполнение теста типа в "rendered" недостаточно для предотвращения ошибки типа. Представьте себе, что "значение" может быть одним из многих подклассов Element, включая Activity, но нужно только отобразить следующую составную часть компонента для Activity:
<util:component_for_Activity_only
activity="#{cc.attrs.value}"
rendered="#{cc.attrs.value['class'].simpleName=='Activity'}"
/>
(cf. экземпляр проверки на языке выражений EL и обратите внимание, что это тестовое решение на основе класса String не очень гибко, оно не работает для подклассов или для интерфейсные тесты.)
Опять же, попытка блокировать вызов с помощью "rendered" недостаточна, похоже, что тест типа завершился неудачно уже во время фазы сборки. Решение проблемы рекурсии также предложит решение этой проблемы. Даже введение (наконец) экземпляра в JSF2 (проголосовать здесь пожалуйста http://java.net/jira/browse/JSP_SPEC_PUBLIC-113) здесь не помогло бы, если бы оно использовалось только в 'rendered',