对于没有在专门的测试用例中抽象这个问题表示歉意,我希望实际项目中的示例足够简单来描述这个问题。
我有一个JavaEE/JPA2 2/JSF应用程序,其中每个@Entity元素(或子类)都有一个模板化的view.xhtml页面,以及一个标准的链接生成器组合组件util:view_link.xhtml,以数据库ID作为参数调用GET。每个视图页的一部分(仅)表示专家系统摘要;该部分可以抽象为复合组件,以便包含在视图页或其他地方。
我介绍了一个Primefaces :对话框弹出,用于在单击视图链接旁边显示的一个小状态图标时显示专家系统摘要部分(和任何其他诊断)。如果让状态图标由x表示,则如下所示:
X Link_to_Element_by_ID
点击'Link_to_Element_by_ID‘,它将显示完整的视图页面。
单击'x‘图标(专家系统测试失败指示器),它弹出与专家系统摘要(仅)的p:对话框。
因此,视图页的专家系统部分作为复合组件共享。
但是,这会导致递归和堆栈溢出,如果其中之一是:
我尝试使用递归阻塞属性'preventRecursionOnDialog‘进行呈现的测试,但是失败了,显然是因为递归发生在构建阶段。
问:如何使用测试变量阻止可能的递归?
此外,我还尝试了c:if测试,而不是JSF‘呈现’测试,但在@ViewScoped下似乎无法使用测试变量。
例如,对于一个活动元素,其中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>util: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>util:list_activity_compact显示了一个具有状态图标指示符的列表(它反过来可以提供带有专家系统摘要的弹出p:对话框,并可以恢复)和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>问题的要点是,测试rendered=“{{ not cc.attrs.preventRecursionOnDialog}”不足以阻止递归,即使不会呈现将要递归的部分(被呈现的测试阻止),在JSF构建阶段仍然可以发生递归。
顺便说一句,当我只想在类型选择的子集中呈现绑定到类型的特定组合组件时,我经常会遇到类似的问题;在“呈现”中执行类型测试不足以防止类型错误。假设下面的“value”可能是包括活动在内的许多元素子类之一,但您只想显示一个活动的以下复合组件部分:
<util:component_for_Activity_only
activity="#{cc.attrs.value}"
rendered="#{cc.attrs.value['class'].simpleName=='Activity'}"
/>(请参阅instanceof check in EL expression language,并注意基于类字符串的类型测试解决方案不是很灵活,它不适用于子类或接口测试。)
同样,阻止“呈现”调用的尝试是不够的,似乎类型测试在构建阶段已经失败了。递归问题的解决方案也将为这一问题提供解决方案。即使在JSF2中引入实例(最后)(请在这里投票,PUBLIC-113)也无助于这里--如果只在‘呈现’中使用,
发布于 2013-05-22 04:46:55
这个问题是我不喜欢JSF的一个很好的例子:一旦您做了任何不平凡的事情(比如-喘息-试图大规模重用代码),您就需要了解JSF内部的知识。
JSF使用组件树表示视图。该树是由标记处理程序在视图定义的基础上构建的,是有状态的,直到用户离开视图为止。复合组件的包含是由标记处理程序完成的。c:if也由标记处理程序实现。
在请求处理生命周期的每个阶段都遍历组件树。但是,要由各个组件来决定是否(或多少次)处理它们的子组件。这就是实现呈现属性的方式:在每个阶段,组件检查它是否被呈现,如果没有,则跳过自身(以及其子属性)的处理。
JSF视图作用域保存在UIViewRoot中,后者是组件树的根节点。因此,在处理标记处理程序时,它是不可用的。这是它的许多缺点之一:-)
那你能做什么?
发布于 2013-05-28 05:18:28
经过进一步的研究和试验,回答自己的问题。
首先,多亏了meriton,你的回答并没有完全回答我的问题,而是让我走上了正确的道路。
简单地说,您可以使用c:if测试来控制自mojarra2.1.18以来@ViewScoped构建阶段的递归。在我的例子中,这现在起作用了:
<c:if test="#{not cc.attrs.preventRecursionOnDialog}">像往常一样,我得到了这个答案(需要升级到Mojarra >= 2.1.18,并解释如何改进@ViewScoped中JSTL的处理),BalusC对其他帖子的贡献包括:
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?
我认为这件事对任何使用JSF的人都是非常重要的!在@ViewScoped中轻松地控制构建的内容(而不是呈现的内容)解决了许多问题,并打开了许多可能性,我建议任何认真使用JSF的人都花时间阅读上面链接中的BalusC的评论。
BalusC,如果您读了这篇文章,请知道您是JavaServer Faces和Enterprise的真正英雄。我谨代表每个JSF爱好者向您表示感谢。
感谢Ed和Ted在报告和修复https://java.net/jira/browse/JAVASERVERFACES-1492方面所做的出色工作,这是JSF2的一个重大改进。
最后,我不得不使用一个肮脏的技巧将mojarra2.1.21安装到NetBeans7.1+Glassfish3.1.1上(因为第三方不兼容而需要安装),如下所示:JSF how upgrade to Mojarra 2.1.21 in Netbeans7.1 (just sub jsf-api.jar and jsf-impl.jar fails)
这对我的项目来说是个很好的结果。如果没有Stackoverflow,我该怎么办:)
https://stackoverflow.com/questions/16665705
复制相似问题