1

我有一个 JSF 应用程序,其中包含两个 JSP 页面,它们都显示来自会话范围的容器对象的一些相同数据。每个页面都以不同的方式显示数据,每个页面都在不同页面之间变化的数据表中。到目前为止,这一切都正常工作。

我的问题是我一直在欺骗我如何弄清楚从我的支持 bean 操作方法中请求的页面。在每一页上,我都为我的数据表使用了绑定。

草稿报告.jsp

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

报告.jsp

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

我有一个请求范围的支持 bean(名为Controller),其中包含这些页面的一些操作方法。我不想在支持 bean 上重复代码(每个类似的 JSP 页面使用一个类似的方法),而是想弄清楚正在呈现的页面并将其用作通用处理程序方法(可以处理来自两个页面的操作)的参数后备豆。所以我作弊并这样做了:

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 类的操作方法中运行良好,但后来我需要添加另一个方法,最终破坏了我的 hacky 代码:

    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 页面的正确方法是什么?

如果它是相关的,我使用的是 MyFaces 1.2 Tomahawk 标记库。

4

2 回答 2

2

我可以想到几种方法,一种是主动的,页面告诉 bean 视图是什么,一种是被动的,bean 推断视图。最后一个是有一个抽象 bean,其中包含草稿和非草稿的具体实现。

我更喜欢最后一种方法,它感觉最像 Java,而且最不hacky。但这里有一些关于如何做前两个的基本想法。

Proactive:在 renderResponse 阶段调用方法来设置报表类型。我只在会话范围的 bean 中完成了此操作,不确定它在请求范围内的效果如何,您可能需要检查其他阶段,或者可能只是应用它而不管实际阶段如何。

控制器.java

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

草稿报告.jsp

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

Reactive:从请求中获取 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。我用ReportType枚举替换了我的枚举,RequestedPage因为它看起来更具可读性。

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枚举旨在涵盖我在操作方法中不关心其身份的所有页面。只要我遵守我在方法 Javadoc 注释中提到的约束,这似乎可以正常工作。

这种方法唯一令人失望的是我想在我的类RequestedPage的初始化方法中进行解析。Controller不幸的是,这不起作用,因为在RESTORE_VIEW阶段开始之前调用了初始化程序,因此 UIViewRoot 仍然为空。

这是不起作用的代码

@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 回答