3

この問題を専用のテストケースで抽象化していないことをお詫びします。実際のプロジェクトの例が問題を説明するのに十分単純であることを願っています。

すべての @Entity Element (またはサブクラス) にテンプレート化された view.xhtml ページと、データベース ID をパラメーターとして GET として呼び出される標準リンク ジェネレーター複合コンポーネント util:view_link.xhtml がある JavaEE/JPA2/JSF Web アプリケーションがあります。各ビュー ページの一部 (のみ) は、エキスパート システムの概要を表します。その部分は、ビュー ページまたは他の場所に含めるために、複合コンポーネントとして抽象化できます。

表示リンクの横に表示される小さなステータス アイコンをクリックすると、そのエキスパート システムの概要部分 (および追加の診断) を表示するための Primefaces p:dialog モーダル ポップアップを導入しました。ステータス アイコンを x で表すと、次のようになります。

x Link_to_Element_by_ID

「Link_to_Element_by_ID」をクリックすると、フル ビュー ページが表示されます。

「x」アイコン (エキスパート システム テストの失敗インジケータ) をクリックすると、エキスパート システムの概要 (のみ) を含む p:dialog がポップアップします。

そのため、ビュー ページのエキスパート システム部分は複合コンポーネントとして共有されます。

ただし、次のいずれかの場合、再帰と Stackoverflow が発生する可能性があります。

  1. ポップアップ p:dialog エキスパート システム サマリーには、検査中のエレメントのステータス アイコン インジケータが表示されます。

  2. 追加の要素ビュー リンクとステータス インジケーターを含めます (エキスパート システムの概要を表示するために ap:dialog を起動します)。

再帰ブロック属性「preventRecursionOnDialog」を使用してレンダリングされたテストを使用しようとしましたが、ビルド段階で再帰が発生しているため、失敗しました。

Q: テスト変数を使用して再帰の可能性をブロックするにはどうすればよいですか?

また、JSF「レンダリング」テストの代わりに c:if テストを試しましたが、テストされた変数は @ViewScoped では使用できないようです。

たとえば、Activity 要素の場合、util_primefaces:dialog_summary は単に ap:dialog のカスタマイズされたカプセル化です。

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 (複合アクティビティのどのサブアクティビティがエキスパート システム テストに合格しなかったかを示します) は、再帰を引き起こす可能性があります。

    <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 を含むリストを表示します。

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

質問のポイントは、再帰する部分がレンダリングされていない(レンダリングされたテストによってブロックされている)にもかかわらず、レンダリングされたテストが再帰をブロックするのに十分ではないということですJSF ビルド フェーズ中に再帰が発生する可能性があります。


ところで、タイプの選択のサブセット内で、タイプにバインドされた特定の複合コンポーネントのみをレンダリングしたい場合、同様の問題に遭遇することがよくあります。「rendered」で型テストを実行するだけでは、型エラーを防ぐのに十分ではありません。以下で、「値」がアクティビティを含む多くの要素サブクラスの 1 つになる可能性があることを想像してください。ただし、アクティビティの次の複合コンポーネント部分のみを表示する必要があります。

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

( EL 式言語の instanceof チェックを参照してください。クラス文字列ベースの型テスト ソリューションはあまり柔軟ではないことに注意してください。サブクラスやインターフェイス テストでは機能しません。)

繰り返しますが、'rendered' による呼び出しをブロックする試みは十分ではありません。ビルド フェーズ中に型テストが既に失敗しているようです。再帰問題の解決策は、これに対する解決策も提供します。JSF2 での instanceof の (最終的な) 導入 (ここで投票してくださいhttp://java.net/jira/browse/JSP_SPEC_PUBLIC-113 ) でさえ、「レンダリング」でのみ使用される場合、ここでは役に立ちません。

4

2 に答える 2

4

さらなる研究と試行の後、自分自身の質問に答える。

まず、メリットンのおかげで、あなたの回答は私の質問に正確に答えるものではありませんでしたが、私を正しい道に導きました.

簡単な答えは、Mojarra 2.1.18 以降の @ViewScoped のビルド段階で c:if テストを使用して再帰を制御できるということです。私の場合、これは機能するようになりました:

        <c:if test="#{not cc.attrs.preventRecursionOnDialog}">

よくあることですが、次のような他の投稿への BalusC の貢献により、この回答 (Mojarra >= 2.1.18 へのアップグレードの必要性と @ViewScoped での JSTL タグライブラリの処理の改善に関する説明) にたどり着きました。

https://java.net/jira/browse/JAVASERVERFACES-1492

JSF2 Viewscope 検証の問題

http://balusc.blogspot.com.au/2010/06/benefits-and-pitfalls-of-viewscoped.html

JSF2 FaceletsのJSTL...理にかなっていますか?

Java Server Faces 2.0 の主な短所は何ですか?

この問題は、JSF を扱うすべての人にとって非常に重要だと思います。@ViewScoped では、何をレンダリングするかではなく、何をビルドするかを簡単に制御できるため、多くの問題が解決され、多くの可能性が開かれます。上記のリンク。

BalusC さん、これを読んだ場合は、あなたが JavaServer Faces と Enterprise Java の真のヒーローであることを知っておいてください。すべての JSF 愛好家を代表して、感謝します。

また、Ed Burns と Ted Goddard の報告と修正に感謝します: https://java.net/jira/browse/JAVASERVERFACES-1492これは JSF2 の大幅な改善です。

最後に、Mojarra 2.1.21 を NetBeans7.1+Glassfish3.1.1 にインストールするために (サードパーティの非互換性のために必要な) 汚いトリックを使用する必要がありました。サブ jsf-api.jar および jsf-impl.jar は失敗します)

これは私のプロジェクトにとって素晴らしい結果でした。Stackoverflowなしで何をしますか:)

于 2013-05-28T05:18:28.790 に答える
3

この質問は、私が JSF を嫌いな理由の完璧な例です。重要なこと (たとえば、あえぎ、大規模なコードの再利用など) を行うとすぐに、JSF 内部の知識が必要になります。

JSF はコンポーネント ツリーでビューを表します。そのツリーは、タグ ハンドラーによってビュー定義から構築され、ステートフルであり、ユーザーがビューを離れるまで存続します。複合コンポーネントの組み込みは、タグ ハンドラーによって行われます。c:if もタグ ハンドラによって実装されます。

コンポーネント ツリーは、リクエスト処理ライフサイクルのすべてのフェーズでトラバースされます。ただし、子を処理するかどうか (または何回処理するか) を決定するのは、個々のコンポーネント次第です。これが、 render 属性の実装方法です。すべてのフェーズで、コンポーネントはレンダリングされているかどうかを確認し、レンダリングされていない場合はそれ自体 (およびその子) の処理を​​スキップします。

JSF ビュー スコープは、コンポーネント ツリーのルート ノードである UIViewRoot 内に保持されます。したがって、タグ ハンドラの処理中は使用できません。それは多くの欠点の1つです:-)

それで、あなたは何ができますか?

  1. アクティビティ ツリーのすべてのアクティビティに複合コンポーネントを含めることができますが、その組み込みはビューのビルド時に行われるため、オンデマンドで行うことはできません。相互再帰の問題を脇に置いても、ユーザーが特定のダイアログを表示する可能性が低い場合に、サブアクティビティごとにダイアログを作成するのはおそらく無駄です。もちろん、c:if を使用して再帰を制限することもできます。必要なのは、ビューのビルド時に利用可能なスコープに情報を入れることだけです。

  2. または、コンポーネント ツリーに 1 つのダイアログを作成することもできますが、現在のアクティビティを、ターゲットを更新する EL 式にバインドすることで、別の時間に別のアクティビティを表示させることができます。もちろん、一度に表示されるダイアログは 1 つだけです。サブアクティビティのダイアログをスタックする必要がある場合は、ツリーの深さと同じ数のダイアログを作成できます。

  3. または、リクエスト処理ライフサイクルのすべてのフェーズで自身を再帰的に処理する単一のコンポーネントを作成できます。これは、既存のコンポーネント (ツリー コンポーネントなど) を適応させるか、ゼロから作成したコンポーネント (コンポーネント ツリーはステートフルであり、子コンポーネントの反復ごとに状態を保持および復元する必要があるため、それほど簡単ではありません) のいずれかです。 )。

于 2013-05-22T04:46:55.540 に答える