首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >JSF:如何防止构建阶段由于递归而导致的堆栈溢出(尽管进行了测试)

JSF:如何防止构建阶段由于递归而导致的堆栈溢出(尽管进行了测试)
EN

Stack Overflow用户
提问于 2013-05-21 08:46:44
回答 2查看 2.1K关注 0票数 3

对于没有在专门的测试用例中抽象这个问题表示歉意,我希望实际项目中的示例足够简单来描述这个问题。

我有一个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:对话框。

因此,视图页的专家系统部分作为复合组件共享。

但是,这会导致递归和堆栈溢出,如果其中之一是:

  1. 弹出的p:对话框专家系统摘要显示被检查元素的状态图标指示符。
  2. 我包括额外的元素视图链接以及状态指示器(这本身就会为专家系统摘要启动一个p:对话框)。

我尝试使用递归阻塞属性'preventRecursionOnDialog‘进行呈现的测试,但是失败了,显然是因为递归发生在构建阶段。

问:如何使用测试变量阻止可能的递归?

此外,我还尝试了c:if测试,而不是JSF‘呈现’测试,但在@ViewScoped下似乎无法使用测试变量。

例如,对于一个活动元素,其中util_primefaces:dialog_summary只是一个p:对话框的自定义封装。

来自util:status_activity.xhtml:

代码语言:javascript
复制
     <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>

util:warn_insufficient_subactivities (它显示复合活动的哪些子活动尚未通过专家系统测试)可能导致递归:

代码语言:javascript
复制
    <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:

代码语言:javascript
复制
    <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”可能是包括活动在内的许多元素子类之一,但您只想显示一个活动的以下复合组件部分:

代码语言:javascript
复制
        <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)也无助于这里--如果只在‘呈现’中使用,

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-05-22 04:46:55

这个问题是我不喜欢JSF的一个很好的例子:一旦您做了任何不平凡的事情(比如-喘息-试图大规模重用代码),您就需要了解JSF内部的知识。

JSF使用组件树表示视图。该树是由标记处理程序在视图定义的基础上构建的,是有状态的,直到用户离开视图为止。复合组件的包含是由标记处理程序完成的。c:if也由标记处理程序实现。

在请求处理生命周期的每个阶段都遍历组件树。但是,要由各个组件来决定是否(或多少次)处理它们的子组件。这就是实现呈现属性的方式:在每个阶段,组件检查它是否被呈现,如果没有,则跳过自身(以及其子属性)的处理。

JSF视图作用域保存在UIViewRoot中,后者是组件树的根节点。因此,在处理标记处理程序时,它是不可用的。这是它的许多缺点之一:-)

那你能做什么?

  1. 您可以为活动树中的每个活动包含一个复合组件,但由于该包含发生在视图构建时,因此不能按需进行。即使不考虑相互递归的问题,在用户不希望看到特定对话框的情况下,为每个子活动创建一个对话框也可能是浪费的。但是,您当然可以用c:if限制递归,只需要在视图构建时将信息放在一个可用的范围中。
  2. 或者,您可以在组件树中创建单个对话框,但通过将当前活动绑定到更新目标的EL-表达式,使其在不同的时间显示不同的活动。当然,这意味着一次只显示一个对话框。如果您需要堆叠子活动的对话框,那么您可以创建尽可能多的对话框,就像树的深度一样。
  3. 或者,您可以创建一个组件,在请求处理生命周期的每个阶段递归地处理自己。这可以是您所适应的现有组件(例如树组件),也可以是从头编写的组件(这并不简单,因为组件树是有状态的,必须为子组件的每次迭代保留和恢复状态)。
票数 3
EN

Stack Overflow用户

发布于 2013-05-28 05:18:28

经过进一步的研究和试验,回答自己的问题。

首先,多亏了meriton,你的回答并没有完全回答我的问题,而是让我走上了正确的道路。

简单地说,您可以使用c:if测试来控制自mojarra2.1.18以来@ViewScoped构建阶段的递归。在我的例子中,这现在起作用了:

代码语言:javascript
复制
        <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,我该怎么办:)

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16665705

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档