1

セッションスコープのコンテナオブジェクトからの同じデータの一部を表示する2つのJSPページを含むJSFアプリケーションがあります。各ページはデータを異なる方法で表示し、それぞれがページ間で異なるデータテーブルに表示されます。これまでのところ、これはすべて適切に機能します。

私の問題は、バッキングBeanのアクションメソッドの内部からどのページが要求されたかをどのように把握するかについて少し浮気していることです。各ページで、データテーブルのバインディングを使用しました。

draftReport.jsp

<t:dataTable 
    border="1"
    id="reportDraftDataTable"
    binding="#{controller.reportDraftDataTable}"
    value="#{sessionData.reportDraftAdapterList}" 
    var="currentRow" 
    rowClasses="dataTableOddRow, dataTableEvenRow">

report.jsp

<t:dataTable 
    border="1"
    id="reportDataTable"
    binding="#{controller.reportDataTable}"
    value="#{sessionData.reportAdapterList}" 
    var="currentRow" 
    rowClasses="dataTableOddRow, dataTableEvenRow">

Controllerこれらのページのアクションメソッドのいくつかを備えたリクエストスコープのバッキングBean(名前付き)があります。バッキングBean(類似したJSPページごとに1つの類似したメソッド)でコードを複製するのではなく、レンダリングされているページを把握し、それを汎用ハンドラーメソッド(両方のページからのアクションを処理できる)のパラメーターとして使用したいと思いました。バッキングビーン。だから私はだましてこれをしました:

public class Controller {
    ...

    private HtmlDataTable preArrivalReportDataTable;
    private HtmlDataTable preArrivalReportDraftDataTable;
    private static enum ReportType {
        NON_DRAFT,
        DRAFT
    }
    ...

    private ReportType determineReportTypeFromControlBindings() {
        Validate.isTrue(this.preArrivalReportDataTable != null ^
                this.preArrivalReportDraftDataTable != null,
            "Either preArrivalReportDataTable XOR " +
            "preArrivalReportDraftDataTable must be null in " +
            "determineReportTypeFromControlBindings()");
        if (this.preArrivalReportDataTable != null) {
            return ReportType.NON_DRAFT;
        } else {
            return ReportType.DRAFT;
        }
    }
    ...

    public String actionOnReport() {
        ReportType reportType = null;
        reportType = determineReportTypeFromControlBindings();
        handleReportAction(reportType);
        return "REFRESH";
    }
    ...
}

これは、Controllerクラスのアクションメソッド内では問題なく機能しましたが、その後、ハッキーコードを最終的に壊す別のメソッドを追加する必要がありました。

    public String getStyleClass() {
        ReportType reportType = determineReportTypeFromControlBindings();
        switch (reportType) {
            case NON_DRAFT:
                return "styleA";
            case DRAFT:
                return "styleB";
            default:
                return null;
        }
    }

私のJSPでは、JSF-EL式は、バッキングBeanで使用しているデータテーブルのコントロールバインディングの上にあり、現在のページを判別しています。その時点でdetermineReportTypeFromControlBindings()、おそらくコントロールバインディングがまだ発生していないため、検証チェックで例外がスローされます。

私はこれが起こっていることに驚いていません。それはいつも間違った方法のように感じました。しかし、私の質問は次のとおりです。

リクエストスコープのバッキングBeanアクションメソッドから現在リクエストされているJSPページを判別する正しい方法は何ですか?

関連する場合は、MyFaces1.2Tomahawkタグライブラリを使用しています。

4

2 に答える 2

2

いくつかのアプローチを考えることができます。1 つはプロアクティブで、ページが Bean にビューが何であるかを伝えます。リアクティブは、Bean がビューを推測します。そして最後のものは、ドラフトと非ドラフトの具体的な実装を持つ抽象 Bean を持つことです。

私は最後のアプローチを好みます。これは Java に最も似ており、ハックが最も少ないと感じます。ただし、最初の 2 つを実行する方法に関するいくつかの基本的なアイデアを次に示します。

プロアクティブ: renderResponse フェーズ中にメソッドを呼び出して、レポート タイプを設定します。セッション スコープの Bean でのみこれを実行しました。リクエスト スコープでどれだけうまく機能するかわかりません。他のフェーズを確認するか、実際のフェーズに関係なく適用する必要があるかもしれません。

コントローラー.java

public void draftInitializer(PhaseEvent event) {
  if (event.getPhaseId().equals(PhaseId.RENDER_RESPONSE)) {
    reportType = DRAFT;
  }
} 

ドラフトレポート.jsp

<f:view beforePhase="#{controller.draftInitializer}">

リアクティブ:リクエストから URL を取得します。

コントローラー.java

  private String getRequestURL(){
    HttpServletRequest request = (HttpServletRequest)FacesContext.getExternalContext().getRequest();
    return request.getRequestURL();
  }

  private boolean isDraft() {
    return getRequestURL().contains(DRAFT_URL_IDENTIFIER);
  }
于 2010-10-04T22:41:57.113 に答える
0

そのため、リクエスト中にUIViewRootから取得したオブジェクトを調べることで、これを解決しました。読みやすいように見えたので 、列挙型を列挙型FacesContextに置き換えました。ReportTypeRequestedPage

public static enum RequestedPage {
    REPORT,
    REPORT_DRAFT,
    UNKNOWN
}

Controller次に、クラス 内にいくつかの文字列定数と新しいメソッドを作成しました。

private final static String REPORT_DRAFT_JSP_NAME = "draftReport.jsp";
private final static String REPORT_JSP_NAME = "report.jsp";

/**
 * This method should only be invoked from inside an action method.
 * An exception will be thrown if the method is called when either
 * the FacesContext or UIViewRoot are not available. This is normally
 * the case outside of an active request or before the RESTORE_VIEW
 * phase has been completed.
 * 
 * @return A non-null RequestedPage reference
 */
private RequestedPage determineRequestedPageFromViewId() {
    FacesContext facesContext = FacesContext.getCurrentInstance();
    Validate.notNull(facesContext);
    UIViewRoot uiViewRoot = facesContext.getViewRoot();
    Validate.notNull(uiViewRoot);
    String viewId = uiViewRoot.getViewId();
    logger.info("view id: " + viewId);
    RequestedPage requestedPage = null;
    if (viewId.contains(REPORT_DRAFT_JSP_NAME)) {
        requestedPage = RequestedPage.REPORT_DRAFT;
    } else if (viewId.contains(REPORT_JSP_NAME)) {
        requestedPage = RequestedPage.REPORT;
    } else {
        requestedPage = RequestedPage.UNKNOWN;
    }
    return requestedPage;
}

列挙型は、UNKNOWNアクション メソッドで ID を気にしないすべてのページをカバーすることを目的としています。メソッドの Javadoc コメントで述べた制約に従う限り、これは問題なく機能するようです。

このアプローチの唯一の残念な点は、クラスRequestedPageの初期化メソッド内で解決を行いたかったことです。Controller残念ながら、RESTORE_VIEWフェーズが開始する前に初期化子が呼び出され、UIViewRoot がまだ null であるため、これは機能しません。

動作しないコードは次のとおりです。

@PostConstruct
public void init() {
    logger.info("init() has been invoked");
    RequestedPage requestedPage = 
        determineRequestedPageFromViewId();
    // An exception is always thrown before I get here...
    this.theRequestedPage = requestedPage;
    logger.info("init() finished");
}

他の誰かが私のアプローチに代わる簡単な方法を持っていない限り、私はこれを受け入れることができます。

于 2010-10-05T14:25:17.753 に答える