Зарегистрировать исключения как нарушение преобразования/валидации в JSF

Обработчик исключений (явно украденный из здесь):

public final class ApplicationExceptionHandler extends ExceptionHandlerWrapper {

    private final ExceptionHandler wrapped;

    public ApplicationExceptionHandler(ExceptionHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public void handle() throws FacesException {
        FacesContext facesContext = FacesContext.getCurrentInstance();

        for (Iterator<ExceptionQueuedEvent> iter = getUnhandledExceptionQueuedEvents().iterator(); iter.hasNext();) {
            Throwable exception = Exceptions.unwrap(iter.next().getContext().getException());

            if (Exceptions.is(exception, EntityNotFoundException.class)) {
                FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_FATAL, "Summary", "Message");
                facesContext.addMessage(null, facesMessage);
            } else if (Exceptions.is(exception, DatabaseConstraintViolationException.class)) {
                FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_FATAL, "Summary", "Message");
                facesContext.addMessage(null, facesMessage);
            } else if (Exceptions.is(exception, DuplicateEntityException.class)) {
                FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_FATAL, "Summary", "Message");
                facesContext.addMessage(null, facesMessage);
            } else if (Exceptions.is(exception, EntityAlreadyModifiedException.class)) { // OptimisticLockException
                FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_FATAL, "Summary", "Message");
                facesContext.addMessage(null, facesMessage);
            } else if (Exceptions.is(exception, DatabaseException.class)) {
                FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_FATAL, "Summary", "Message");
                facesContext.addMessage(null, facesMessage);
            } else {
                // Render a global error page, if any other exceptions occur.
                String errorPageLocation = "/WEB-INF/error_pages/GeneralError.xhtml";
                facesContext.setViewRoot(facesContext.getApplication().getViewHandler().createView(facesContext, errorPageLocation));
                facesContext.getPartialViewContext().setRenderAll(true);
                facesContext.renderResponse();
            }
        }

        // getWrapped().handle();
    }

    @Override
    public ExceptionHandler getWrapped() {
        return wrapped;
    }
}

Фабрика зарегистрирована в faces-config.xml (думаю, все остальное на стороне EJB/JPA здесь не нужно). Exceptions.is() является служебным методом OmniFaces.

Когда возникает какое-либо исключение, упомянутое в лестнице, оно должно быть зарегистрировано как нарушение проверки, т. е. значения модели не должны обновляться, а методы действия (слушателя), если они задействованы, должны выполняться/запускаться, как если бы какое-то преобразование/проверка не удалась.


Где это требуется:

Это по существу требуется при работе с оптимистической блокировкой на уровне постоянства. Например, если предпринимается попытка удалить строку/строки в <p/h:dataTable> (путем нажатия командной кнопки или ссылки Ajaxical), которые уже изменены другим пользователем в другом сеансе за спиной, javax.persistence.OptimisticLockException должно быть выброшено, что происходит правильно с использованием этого исключения. механизм обработчика.

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

Это окажется верным только для самой первой попытки удалить устаревшую строку/строки. При следующей попытке строки с устаревшими значениями могут быть удалены, потому что после того, как это исключение будет выдано и сообщение будет отображено с самой первой попытки, таблица данных также будет обновлена ​​последней обновленной версией строки. . Следовательно, в следующем запросе клиент будет отправлять строки с последней обновленной версией строки в каждой строке, которые не будут обнаружены как одновременные изменения поставщиком постоянства, что очевидно. Это совершенно законно для поставщика постоянства, чтобы удалить эти строки. Это может вызвать у конечных пользователей неприятный опыт — по крайней мере, не такой хороший, как хотелось бы.

Как этого можно достичь с помощью этого механизма обработки исключений - когда любое исключение, указанное обработчиком исключений, как указано выше, должно отображаться удобное для пользователя сообщение (что происходит правильно), и ни значения модели, ни методы действия (слушателя) не должны запускаться как если преобразование или проверка нарушены (т. е. целевой <p:dataTable> или любой другой компонент пользовательского интерфейса, содержащий строки, не должен обновляться)?


ИЗМЕНИТЬ:

Управляемый компонент (просмотр области):

@Named
@ViewScoped
public class Bean extends LazyDataModel<Entity> implements Serializable {

    @Inject
    private Service service;
    private List<Entity> selectedValues; // Getter & setter.
    
    private static final long serialVersionUID = 1L;

    @Override
    public List<Entity> load(int first, int pageSize, List<SortMeta> multiSortMeta, Map<String, Object> filters) {

        setRowCount(service.rowCount());
        // Other logic.
        return service.getList(first, pageSize, map, filters);
    }

    public void delete(ActionEvent event) {
        if (service.delete(selectedValues)) { // EJB call to delete selected rows in <p:dataTable>.
            // FacesMeaage - delete succeeded.
        } else {
            // FacesMeaage - delete failed.
        }
    }

    // This method is invoked before delete() that
    // just warns the user about deletion of rows using <p:confirmDialog>.
    public void confirmDelete(ActionEvent event) {
        if (selectedValues != null && !selectedValues.isEmpty()) {
            // FacesMessage - rows are selected/checked in <p:dataTable>.
        } else {
            // FacesMessage - rows are not selected/checked in <p:dataTable>.
        }
    }
}

Таблица данных:

<p:dataTable id="dataTable"
             var="row"
             value="#{bean}"
             lazy="true"
             sortMode="multiple"
             selection="#{bean.selectedValues}"
             rows="10">

    <p:column selectionMode="multiple">
        <f:facet name="footer">

            <p:commandButton oncomplete="PF('confirmDelete').show()"
                             update=":form:confirmDialog"
                             process=":form:dataTable"
                             actionListener="#{bean.confirmDelete}"/> <!---->
        </f:facet>
    </p:column>

    ...
    ...
    ...

    <p:column headerText="Edit">
        <p:rowEditor/>
    </p:column>
</p:dataTable>

<p:confirmDialog id="confirmDialog"
                 widgetVar="confirmDelete"
                 message="Message from bean">

    <p:commandButton id="confirmDelete"
                     value="Yes"
                     process="@this"
                     update="dataTable messages"
                     oncomplete="PF('confirmDelete').hide()"
                     actionListener="#{bean.delete}"/> <!---->

    <p:commandButton id="declineDelete"
                     value="No" 
                     onclick="PF('confirmDelete').hide()"
                     type="button"/>
    </p:confirmDialog>

person Tiny    schedule 19.07.2015    source источник
comment
т.е. значения модели не должны обновляться, ни методы действия (слушателя) Но я понимаю, что эти исключения возникают на этапе вызова приложения?   -  person BalusC    schedule 20.07.2015
comment
Когда возникает одно из упомянутых исключений, компонент пользовательского интерфейса, содержащий данные, не должен обновляться каким-либо возможным способом, если это невозможно.   -  person Tiny    schedule 20.07.2015
comment
Да, я понял эту часть и могу ответить на нее, но я пытаюсь сделать шаг назад, так как это звучит несколько подозрительно. Например, ограничен ли запрос bean-компонента, а не вид? Или модель обновляется на этапе ответа на визуализацию, а не на этапе вызова приложения сразу после вызова службы?   -  person BalusC    schedule 20.07.2015
comment
Обновите вопрос. Бины обычно имеют область просмотра.   -  person Tiny    schedule 20.07.2015
comment
О, это ленивая модель данных. Что касается конкретной проблемы, partialViewContext.getRenderIds().clear() работает для вас? (игнорируя то, как вы (авто) обновляете сообщения)   -  person BalusC    schedule 20.07.2015
comment
Куда поставить эту строчку?   -  person Tiny    schedule 20.07.2015
comment
В обработчике исключений, когда нужно показать сообщение. Если компонент целевого сообщения не имеет autoUpdate, либо сделайте его таковым, либо впоследствии добавьте его идентификатор клиента в getRenderIds().   -  person BalusC    schedule 20.07.2015
comment
Я добавил facesContext.getPartialViewContext().getRenderIds().clear();, facesContext.getPartialViewContext().getRenderIds().add("messages"); и facesContext.addMessage("messages", FacesMessage); в том же порядке в одну из лестниц if-else if-else вместе с <p:messages id="messages" globalOnly="true" redisplay="false" autoUpdate="true"/>. Это предотвращает обновление таблицы, но сообщение не отображается на <p:messages>.   -  person Tiny    schedule 20.07.2015
comment
Я удалил только таблицу данных, используя facesContext.getPartialViewContext().getRenderIds().remove("form:dataTable");, прежде чем добавить FacesMessage. Это работает по мере необходимости, но тогда требуется уникальный фиксированный идентификатор таблицы данных, form:dataTable везде. Другие таблицы данных с другим идентификатором в той же или другой контейнере страниц/имен не будут использовать эту вещь.   -  person Tiny    schedule 20.07.2015
comment
Я ожидаю, что autoUpdate здесь сработает. Видимо отсканировали и добавили слишком рано.   -  person BalusC    schedule 20.07.2015
comment
::fore-palm:: Извините, я пытался использовать другой файл XHTML. Предлагаемый подход работает по мере необходимости. Пожалуйста, добавьте это в качестве ответа. Спасибо.   -  person Tiny    schedule 20.07.2015


Ответы (1)


и ни значения модели, ни методы action(Listener) не должны запускаться

Во-первых, было бы странно, если бы перечисленные исключения бизнес-службы генерировались на другом этапе, а не на этапе вызова приложения. Эти исключения на самом деле генерируются на этапе вызова приложения, верно? Было бы слишком поздно пропускать значения модели обновления и вызывать фазы приложения.

Обычный поток в методе действия:

  • при необходимости: предварительно обработать данные, например. подготовить аргументы метода службы.
  • вызвать метод службы (который может вызвать любое из этих исключений).
  • при необходимости: данные постобработки, например. обновление свойств компонента, когда не выполняется PRG.

Таким образом, если из метода службы выдается исключение, то шаг постобработки уже будет пропущен, а компонент (с областью видимости) по-прежнему будет содержать старые свойства. Конечный пользователь сможет повторно выполнить тот же запрос только тогда, когда компонент находится в области запроса или когда свойства компонента обновляются во время ответа рендеринга.

Таким образом, то, что вы столкнулись с этой проблемой, предполагает, что bean-компонент находится в области запроса или что вы обновляете свойства bean-компонента во время ответа на рендеринг, например. (в) непосредственно в методе получения. Согласно комментариям и обновленному вопросу, LazyDataModel делает это. Возможно, в большинстве случаев разумно, но точно не в вашем случае.


т.е. целевой <p:dataTable> или любой другой компонент пользовательского интерфейса, содержащий строки, не должен обновляться

Все, что вы укажете в <p:ajax update> или <f:ajax render>, находится в API, доступном по адресу PartialViewContext#getRenderIds(). Это возвращает изменяемую коллекцию, поэтому вы можете просто очистить ее.

facesContext.getPartialViewContext().getRenderIds().clear();

Сделайте это в тот же момент, когда вы обрабатываете исключение и добавляете сообщение о лицах.

person BalusC    schedule 20.07.2015
comment
Исключения из сервисного уровня не могут быть выброшены без служебного вызова EJB, который в этом случае должен быть сделан действием (слушателем), в свою очередь. Я забыл рассмотреть эту интуитивную вещь, которая даже не имеет прямого отношения к JSF. Мне стало очень плохо, когда я понял реальную историю. Я думал только в одном направлении. В противном случае формат вопроса мог быть совершенно другим. - person Tiny; 21.07.2015
comment
Как бы вы иначе задали вопрос? - person BalusC; 21.07.2015
comment
Сразу к делу с таблицей данных и фрагментами кода управляемого компонента заранее и с вопросом, например, как предотвратить обновление компонента пользовательского интерфейса (например, <p:dataTable>) Ajaxial, когда выдается исключение (или лучше одно из предопределенных исключений) из сервисного уровня? (упоминается, как приложение обрабатывает исключения с помощью фабрики обработчиков исключений), опуская раздел Где это требуется полностью или, возможно, сократив его до одной или двух строк (особенно запутанной части, Значения модели не должны обновляться, ни методы действия (слушателя)) - person Tiny; 21.07.2015