Обработчик исключений (явно украденный из здесь):
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>
partialViewContext.getRenderIds().clear()
работает для вас? (игнорируя то, как вы (авто) обновляете сообщения) - person BalusC   schedule 20.07.2015autoUpdate
, либо сделайте его таковым, либо впоследствии добавьте его идентификатор клиента вgetRenderIds()
. - person BalusC   schedule 20.07.2015facesContext.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.2015facesContext.getPartialViewContext().getRenderIds().remove("form:dataTable");
, прежде чем добавитьFacesMessage
. Это работает по мере необходимости, но тогда требуется уникальный фиксированный идентификатор таблицы данных,form:dataTable
везде. Другие таблицы данных с другим идентификатором в той же или другой контейнере страниц/имен не будут использовать эту вещь. - person Tiny   schedule 20.07.2015autoUpdate
здесь сработает. Видимо отсканировали и добавили слишком рано. - person BalusC   schedule 20.07.2015