JSF - Как определить текущую страницу JSP из метода действия базового компонента - PullRequest
1 голос
/ 04 октября 2010

У меня есть приложение JSF, содержащее две страницы JSP, которые обе отображают одни и те же данные из объекта контейнера в области сеанса.Каждая страница отображает данные по-разному, каждая в таблице данных, которая варьируется между страницами.Пока все работает правильно.

Моя проблема в том, что я немного обманывал то, как я выясняю, какая страница была запрошена изнутри в моих методах действия поддерживающего бина.На каждой странице я использовал привязку для своей таблицы данных.

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) с некоторыми методами действий для этих страниц.Вместо того, чтобы дублировать код на компоненте поддержки (один похожий метод для каждой похожей страницы 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, но затем мне нужно было добавить другой метод, который окончательно сломал мой хакерский код:

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

В моем JSP выражение JSF-EL расположено над привязкой элемента управления для таблицы данных, которую я использую в компоненте поддержки, чтобы определить, на какой странице я работаю.В этот момент determineReportTypeFromControlBindings() выдает исключение при проверке Validate, предположительно потому, что привязка элемента управления еще не произошла.

Я не удивлен, что это происходит.Это всегда было неправильно.Но мой вопрос:

Как правильно определить запрашиваемую в настоящий момент JSP-страницу из метода действия bean-объекта в области запроса?

В случае, если это уместно, Я использую библиотеку тегов Томагавк MyFaces 1.2.

Ответы [ 2 ]

2 голосов
/ 05 октября 2010

Я могу вспомнить несколько подходов, один из которых является упреждающим, когда страница сообщает бину, каков вид, реактивным, когда бин выводит представление. И последнее - иметь абстрактный bean-компонент с конкретными реализациями для черновика и не черновика.

Я предпочитаю последний подход, он кажется наиболее похожим на Java и наименее хакерским. Но вот несколько основных идей о том, как сделать первые два.

Упреждающий: Вызовите метод во время фазы renderResponse, чтобы установить тип отчета. Я делал это только в bean-объектах, определяемых сессией, не уверен, насколько хорошо это будет работать в области запроса, вам может потребоваться проверить другие фазы или просто применить их независимо от фактической фазы.

Controller.java

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

draftReport.jsp

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

Реактивный: Получить URL из запроса.

Controller.java

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

  private boolean isDraft() {
    return getRequestURL().contains(DRAFT_URL_IDENTIFIER);
  }
0 голосов
/ 05 октября 2010

Итак, я решил эту проблему, проверив объект 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");
}

Я могу жить с этим, если у кого-то еще нет простой альтернативы моему подходу.

...