JSF:如何防止在构建阶段由于递归而导致堆栈溢出(尽管进行了测试) [英] JSF: how prevent stackoverflow due to recursion during build phase (despite rendered test)

查看:117
本文介绍了JSF:如何防止在构建阶段由于递归而导致堆栈溢出(尽管进行了测试)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在专用测试用例中不抽象该问题的道歉,我希望一个真实项目中的示例足够简单以描述问题.

我有一个JavaEE/JPA2/JSF Web应用程序,其中每个@Entity元素(或子类)都有一个模板化的view.xhtml页面,以及一个标准的链接生成器复合组件util:view_link.xhtml,它作为GET调用,数据库ID为范围.每个视图页面的一部分(仅一部分)代表专家系统摘要;该部分可以抽象为一个复合组件,以包含在视图页面或其他地方.

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.

我介绍了一个Primefaces p:dialog模式弹出窗口,用于在单击视图链接旁边显示的小状态图标时显示该专家系统摘要部分(以及所有其他诊断).如果让状态图标用x表示,则它看起来像这样:

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

x Link_to_Element_by_ID

单击"Link_to_Element_by_ID",它会显示整个视图页面.

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

单击"x"图标(专家系统测试失败指示符),它会弹出p:对话框,其中包含专家系统摘要(仅).

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.

但是,如果发生以下情况,则可能导致递归和Stackoverflow:

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

  1. 弹出的p:dialog专家系统摘要显示了被检查元素的状态图标指示符.

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

我包括其他元素视图链接以及状态指示器(状态指示器本身会启动p:dialog以获取专家系统摘要).

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

我尝试使用递归阻止属性'preventRecursionOnDialog'进行渲染测试,但是它失败了,显然是因为递归是在构建阶段发生的.

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 ?

此外,我尝试了c:if测试,而不是JSF渲染"测试,但似乎测试的变量在@ViewScoped下不可用.

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

例如,对于一个Activity元素,其中util_primefaces:dialog_summary仅仅是p:dialog的定制封装.

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

来自util:status_activity.xhtml:

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>

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

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>

util:list_activity_compact显示一个带有状态图标指示符的列表(依次提供弹出式p:dialog和专家系统摘要,并且可以递归)和util:view_link:

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>

问题的关键是,即使未渲染(被渲染的测试阻止),渲染的测试="= {not cc.attrs.preventRecursionOnDialog}"仍不足以阻止递归. ,似乎在JSF构建阶段仍然可以进行递归.

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.

顺便说一句,当我只想在类型选择的子集内仅渲染与类型绑定的特定复合组件时,我经常会遇到类似的问题.在"rendered"中执行类型测试不足以防止类型错误.想象下面的值"可能是包括Activity在内的许多Element子类之一,但是只想显示Activity的以下复合组件部分:

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'}"
            />

(请参见使用EL表达式语言进行检查的实例,请注意,该类基于类型测试解决方案不是很灵活,不适用于子类或接口测试.)

(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.)

再次,尝试用"rendered"阻止调用是不够的,看来类型测试在构建阶段已经失败了.递归问题的解决方案也将为此提供解决方案.甚至(最终)在JSF2中对instanceof的介绍(请在此处投票 http://java.net/jira/browse/JSP_SPEC_PUBLIC-113 )如果仅在渲染"中使用,将无济于事,

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

推荐答案

这个问题是为什么我不喜欢JSF的一个很好的例子:只要您做一些不平凡的事情(例如-喘着气-尝试在大型代码上重用代码)规模),您需要了解JSF内部知识.

That question is a perfect example why I dislike JSF: As soon as you do anything non-trivial (such as - gasp - trying to reuse code on a large scale), you need knowledge of JSF internals.

JSF表示带有组件树的视图.该树是由标签处理程序根据视图定义构建的,是有状态的,并且一直存在到用户离开视图为止.包含复合组件是由标签处理程序完成的. c:if也由标记处理程序实现.

JSF represents a view with a component tree. That tree is built out of the view definition by tag handlers, is stateful, and lives until the user leaves the view. Inclusion of a composite component is done by a tag handler. c:if is also implemented by a tag handler.

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

The component tree is traversed during every phase of the request processing lifecycle. It is however up to the individual components to decide whether (or how many times) their children are processed. That's how the rendered attribute is implemented: During every phase, the component checks whether it is rendered, and skips processing of itself (and its children) if not.

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

The JSF view scope is kept within the UIViewRoot, which is the root node of the component tree. It is therefore unavailable while tag handlers are processed. That's one of its many shortcomings :-)

那你该怎么办?

  1. 您可以在活动树中为每个活动包括一个复合组件,但是由于这种包含是在视图构建时发生的,因此无法按需进行.即使抛开相互递归的问题,在用户希望看到该特定对话框的偶然机会上为每个子活动创建对话框也很浪费.但是您当然可以使用c:if限制递归,您只需要将信息放在视图构建时可用的范围内即可.

  1. You can include a composite component for every activity in the activity tree, but because that inclusion happens at view build time, it can not occur on demand. Even setting aside problems of mutual recursion, it is likely wasteful to create a dialog for every subactivity on the off chance that the user will want to see that particular dialog. But you can of course limit recursion with c:if, you just need to put the information in a scope available at view build time.

或者,您可以在组件树中创建一个对话框,但是通过将当前活动绑定到您要更新其目标的EL表达式来使它在不同的时间显示不同的活动.当然,这意味着一次只显示一个对话框.如果需要堆叠子活动的对话框,则可以创建与树深一样多的对话框.

Alternatively, you can create a single dialog in the component tree, but have it show a different activity at different times by binding the current activity to an EL-expression whose target you update. Of course that means only a single dialog is shown at a time. If you need to stack the dialogs of subactivities, you might create as many dialogs as the tree is deep.

或者,您可以创建一个组件,该组件在请求处理生命周期的每个阶段中递归地处理自身.这可以是您适应的现有组件(例如树组件),也可以是从头开始编写的组件(由于组件树是有状态的,所以这并不容易,因为子组件的每次迭代都必须保留和恢复状态) ).

Alternatively, you can create a single component that recursively processes itself in every phase of the request processing lifecycle. That can be either an existing component you adapt (such as a tree component), or one written from scratch (which is not exactly trivial because it the component tree is stateful, and state must be preserved and restored for every iteration of the child components).

这篇关于JSF:如何防止在构建阶段由于递归而导致堆栈溢出(尽管进行了测试)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆