UI error handling refactoring (#1106)
* refactored HawkbitUIErrorHandler to delegate error details extraction to external extractor beans * refactored ui error handling, allowed ui error details extractors to return a list of error details * added license headers, restructured package structure * adapted javadocs * fixed sonar findings * fixed license header * added tests for HawkbitUIErrorHandler * refactored ConstraintViolationErrorExtractor, added test for extractors * changed UI tests feature to Management UI * fixed the parent/child error type resolution by ui error details extractor, added test Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io>
This commit is contained in:
@@ -8,8 +8,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui;
|
||||
|
||||
import org.eclipse.hawkbit.ui.components.HawkbitUIErrorHandler;
|
||||
import org.eclipse.hawkbit.ui.components.NotificationUnreadButton;
|
||||
import org.eclipse.hawkbit.ui.error.ErrorView;
|
||||
import org.eclipse.hawkbit.ui.menu.DashboardEvent.PostViewChangeEvent;
|
||||
import org.eclipse.hawkbit.ui.menu.DashboardMenu;
|
||||
import org.eclipse.hawkbit.ui.menu.DashboardMenuItem;
|
||||
@@ -34,6 +34,7 @@ import com.vaadin.navigator.View;
|
||||
import com.vaadin.navigator.ViewChangeListener;
|
||||
import com.vaadin.navigator.ViewProvider;
|
||||
import com.vaadin.server.ClientConnector.DetachListener;
|
||||
import com.vaadin.server.ErrorHandler;
|
||||
import com.vaadin.server.Responsive;
|
||||
import com.vaadin.server.VaadinRequest;
|
||||
import com.vaadin.spring.navigator.SpringViewProvider;
|
||||
@@ -73,6 +74,7 @@ public abstract class AbstractHawkbitUI extends UI implements DetachListener {
|
||||
private final SpringViewProvider viewProvider;
|
||||
private final transient ApplicationContext context;
|
||||
private final transient EventPushStrategy pushStrategy;
|
||||
private final transient ErrorHandler uiErrorHandler;
|
||||
|
||||
private final transient HawkbitEntityEventListener entityEventsListener;
|
||||
|
||||
@@ -80,7 +82,7 @@ public abstract class AbstractHawkbitUI extends UI implements DetachListener {
|
||||
final UIEventProvider eventProvider, final SpringViewProvider viewProvider,
|
||||
final ApplicationContext context, final DashboardMenu dashboardMenu, final ErrorView errorview,
|
||||
final NotificationUnreadButton notificationUnreadButton, final UiProperties uiProperties,
|
||||
final VaadinMessageSource i18n) {
|
||||
final VaadinMessageSource i18n, final ErrorHandler uiErrorHandler) {
|
||||
this.pushStrategy = pushStrategy;
|
||||
this.viewProvider = viewProvider;
|
||||
this.context = context;
|
||||
@@ -89,6 +91,7 @@ public abstract class AbstractHawkbitUI extends UI implements DetachListener {
|
||||
this.notificationUnreadButton = notificationUnreadButton;
|
||||
this.uiProperties = uiProperties;
|
||||
this.i18n = i18n;
|
||||
this.uiErrorHandler = uiErrorHandler;
|
||||
|
||||
this.entityEventsListener = new HawkbitEntityEventListener(eventBus, eventProvider, notificationUnreadButton);
|
||||
}
|
||||
@@ -184,7 +187,7 @@ public abstract class AbstractHawkbitUI extends UI implements DetachListener {
|
||||
setNavigator(navigator);
|
||||
|
||||
if (UI.getCurrent().getErrorHandler() == null) {
|
||||
UI.getCurrent().setErrorHandler(new HawkbitUIErrorHandler());
|
||||
UI.getCurrent().setErrorHandler(uiErrorHandler);
|
||||
}
|
||||
|
||||
LOG.debug("Current locale of the application is : {}", getLocale());
|
||||
|
||||
@@ -8,7 +8,13 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.PermissionService;
|
||||
import org.eclipse.hawkbit.ui.error.HawkbitUIErrorHandler;
|
||||
import org.eclipse.hawkbit.ui.error.extractors.ConstraintViolationErrorExtractor;
|
||||
import org.eclipse.hawkbit.ui.error.extractors.UiErrorDetailsExtractor;
|
||||
import org.eclipse.hawkbit.ui.error.extractors.UploadErrorExtractor;
|
||||
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -19,6 +25,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.vaadin.spring.servlet.Vaadin4SpringServlet;
|
||||
|
||||
import com.vaadin.server.ErrorHandler;
|
||||
import com.vaadin.server.SystemMessagesProvider;
|
||||
import com.vaadin.server.VaadinServlet;
|
||||
|
||||
@@ -76,6 +83,28 @@ public class MgmtUiConfiguration {
|
||||
return new LocalizedSystemMessagesProvider(uiProperties, i18n);
|
||||
}
|
||||
|
||||
/**
|
||||
* UI Error handler bean.
|
||||
*
|
||||
* @return UI Error handler
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ErrorHandler uiErrorHandler(final VaadinMessageSource i18n,
|
||||
final List<UiErrorDetailsExtractor> uiErrorDetailsExtractor) {
|
||||
return new HawkbitUIErrorHandler(i18n, uiErrorDetailsExtractor);
|
||||
}
|
||||
|
||||
@Bean
|
||||
UiErrorDetailsExtractor uploadErrorExtractor() {
|
||||
return new UploadErrorExtractor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
UiErrorDetailsExtractor constraintViolationErrorExtractor(final VaadinMessageSource i18n) {
|
||||
return new ConstraintViolationErrorExtractor(i18n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vaadin4Spring servlet bean.
|
||||
*
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.components;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
|
||||
import org.eclipse.hawkbit.ui.common.notification.ParallelNotification;
|
||||
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
|
||||
import org.eclipse.hawkbit.ui.utils.SpringContextHolder;
|
||||
import org.eclipse.hawkbit.ui.utils.UINotification;
|
||||
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.vaadin.icons.VaadinIcons;
|
||||
import com.vaadin.server.ClientConnector.ConnectorErrorEvent;
|
||||
import com.vaadin.server.DefaultErrorHandler;
|
||||
import com.vaadin.server.ErrorEvent;
|
||||
import com.vaadin.server.Page;
|
||||
import com.vaadin.server.UploadException;
|
||||
import com.vaadin.shared.Connector;
|
||||
import com.vaadin.ui.Component;
|
||||
import com.vaadin.ui.Notification;
|
||||
import com.vaadin.ui.UI;
|
||||
|
||||
/**
|
||||
* Default handler for Hawkbit UI.
|
||||
*/
|
||||
public class HawkbitUIErrorHandler extends DefaultErrorHandler {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HawkbitUIErrorHandler.class);
|
||||
|
||||
@Override
|
||||
public void error(final ErrorEvent event) {
|
||||
|
||||
// filter upload exceptions
|
||||
if (event.getThrowable() instanceof UploadException) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Notification notification = buildNotification(getRootExceptionFrom(event));
|
||||
if (event instanceof ConnectorErrorEvent) {
|
||||
final Connector connector = ((ConnectorErrorEvent) event).getConnector();
|
||||
if (connector instanceof UI) {
|
||||
final UI uiInstance = (UI) connector;
|
||||
uiInstance.access(() -> notification.show(uiInstance.getPage()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Optional<Page> originError = getPageOriginError(event);
|
||||
if (originError.isPresent()) {
|
||||
notification.show(originError.get());
|
||||
return;
|
||||
}
|
||||
|
||||
notification.show(Page.getCurrent());
|
||||
}
|
||||
|
||||
private static Throwable getRootExceptionFrom(final ErrorEvent event) {
|
||||
return getRootCauseOf(event.getThrowable());
|
||||
}
|
||||
|
||||
private static Throwable getRootCauseOf(final Throwable ex) {
|
||||
|
||||
if (ex.getCause() != null) {
|
||||
return getRootCauseOf(ex.getCause());
|
||||
}
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
private static Optional<Page> getPageOriginError(final ErrorEvent event) {
|
||||
|
||||
final Component errorOrigin = findAbstractComponent(event);
|
||||
|
||||
if (errorOrigin != null && errorOrigin.getUI() != null) {
|
||||
return Optional.ofNullable(errorOrigin.getUI().getPage());
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to build a notification based on an exception.
|
||||
*
|
||||
* @param ex
|
||||
* the throwable
|
||||
* @return a hawkbit error notification message
|
||||
*/
|
||||
protected ParallelNotification buildNotification(final Throwable ex) {
|
||||
|
||||
LOG.error("Error in UI: ", ex);
|
||||
|
||||
final String errorMessage = extractMessageFrom(ex);
|
||||
final VaadinMessageSource i18n = SpringContextHolder.getInstance().getBean(VaadinMessageSource.class);
|
||||
|
||||
return buildErrorNotification(i18n.getMessage("caption.error"), errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to build a error notification based on caption and description.
|
||||
*
|
||||
* @param caption
|
||||
* Caption
|
||||
* @param description
|
||||
* Description
|
||||
* @return a hawkbit error notification message
|
||||
*/
|
||||
protected static ParallelNotification buildErrorNotification(final String caption, final String description) {
|
||||
return UINotification.buildNotification(SPUIStyleDefinitions.SP_NOTIFICATION_ERROR_MESSAGE_STYLE, caption,
|
||||
description, VaadinIcons.EXCLAMATION_CIRCLE, true);
|
||||
}
|
||||
|
||||
private static String extractMessageFrom(final Throwable ex) {
|
||||
|
||||
if (!(ex instanceof ConstraintViolationException)) {
|
||||
if (!StringUtils.isEmpty(ex.getMessage())) {
|
||||
return ex.getMessage();
|
||||
}
|
||||
return ex.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
final Set<ConstraintViolation<?>> violations = ((ConstraintViolationException) ex).getConstraintViolations();
|
||||
|
||||
if (violations == null) {
|
||||
return ex.getClass().getSimpleName();
|
||||
} else {
|
||||
return violations.stream().map(violation -> violation.getPropertyPath() + " " + violation.getMessage())
|
||||
.collect(Collectors.joining(System.lineSeparator()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui;
|
||||
package org.eclipse.hawkbit.ui.error;
|
||||
|
||||
import org.eclipse.hawkbit.ui.menu.DashboardMenu;
|
||||
import org.eclipse.hawkbit.ui.menu.DashboardMenuItem;
|
||||
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.error;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.ui.common.notification.ParallelNotification;
|
||||
import org.eclipse.hawkbit.ui.error.extractors.UiErrorDetailsExtractor;
|
||||
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
|
||||
import org.eclipse.hawkbit.ui.utils.UINotification;
|
||||
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.vaadin.icons.VaadinIcons;
|
||||
import com.vaadin.server.ClientConnector.ConnectorErrorEvent;
|
||||
import com.vaadin.server.DefaultErrorHandler;
|
||||
import com.vaadin.server.ErrorEvent;
|
||||
import com.vaadin.server.ErrorHandler;
|
||||
import com.vaadin.server.Page;
|
||||
import com.vaadin.shared.Connector;
|
||||
import com.vaadin.ui.Component;
|
||||
import com.vaadin.ui.Notification;
|
||||
import com.vaadin.ui.UI;
|
||||
|
||||
/**
|
||||
* Default handler for Hawkbit UI.
|
||||
*/
|
||||
public class HawkbitUIErrorHandler implements ErrorHandler {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HawkbitUIErrorHandler.class);
|
||||
|
||||
private final VaadinMessageSource i18n;
|
||||
private final transient List<UiErrorDetailsExtractor> uiErrorDetailsExtractors;
|
||||
|
||||
/**
|
||||
* Constructor for HawkbitUIErrorHandler.
|
||||
*
|
||||
* @param i18n
|
||||
* Message source used for localization
|
||||
* @param uiErrorDetailsExtractors
|
||||
* ui error details extractors
|
||||
*/
|
||||
public HawkbitUIErrorHandler(final VaadinMessageSource i18n,
|
||||
final List<UiErrorDetailsExtractor> uiErrorDetailsExtractors) {
|
||||
this.i18n = i18n;
|
||||
this.uiErrorDetailsExtractors = uiErrorDetailsExtractors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(final ErrorEvent event) {
|
||||
final Page currentPage = getPageFrom(event);
|
||||
final List<UiErrorDetails> errorDetails = extractErrorDetails(event);
|
||||
|
||||
if (errorDetails.isEmpty()) {
|
||||
showGenericErrorNotification(currentPage, event);
|
||||
return;
|
||||
}
|
||||
|
||||
errorDetails.stream().filter(UiErrorDetails::isPresent)
|
||||
.forEach(details -> showSpecificErrorNotification(currentPage, details));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to find the {@link Page} to show notification on.
|
||||
*
|
||||
* @param event
|
||||
* error event
|
||||
* @return current {@link Page} for error notification
|
||||
*/
|
||||
protected Page getPageFrom(final ErrorEvent event) {
|
||||
if (event instanceof ConnectorErrorEvent) {
|
||||
final Connector connector = ((ConnectorErrorEvent) event).getConnector();
|
||||
if (connector instanceof UI) {
|
||||
final UI uiInstance = (UI) connector;
|
||||
return uiInstance.getPage();
|
||||
}
|
||||
}
|
||||
|
||||
final Optional<Page> errorOriginPage = getErrorOriginPage(event);
|
||||
if (errorOriginPage.isPresent()) {
|
||||
return errorOriginPage.get();
|
||||
}
|
||||
|
||||
return Page.getCurrent();
|
||||
}
|
||||
|
||||
private static Optional<Page> getErrorOriginPage(final ErrorEvent event) {
|
||||
final Component errorOrigin = DefaultErrorHandler.findAbstractComponent(event);
|
||||
|
||||
if (errorOrigin != null && errorOrigin.getUI() != null) {
|
||||
return Optional.ofNullable(errorOrigin.getUI().getPage());
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private List<UiErrorDetails> extractErrorDetails(final ErrorEvent event) {
|
||||
return uiErrorDetailsExtractors.stream()
|
||||
.map(extractor -> extractor.extractErrorDetailsFrom(event.getThrowable())).flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void showGenericErrorNotification(final Page page, final ErrorEvent event) {
|
||||
LOG.error("Unexpected Ui error occured", event.getThrowable());
|
||||
|
||||
final Notification notification = buildErrorNotification(i18n.getMessage("caption.error"),
|
||||
i18n.getMessage("message.error"));
|
||||
showErrorNotification(page, notification);
|
||||
}
|
||||
|
||||
private void showSpecificErrorNotification(final Page page, final UiErrorDetails details) {
|
||||
final Notification notification = buildErrorNotification(details.getCaption(), details.getDescription());
|
||||
showErrorNotification(page, notification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to build an error notification based on caption and description.
|
||||
*
|
||||
* @param caption
|
||||
* notification caption
|
||||
* @param description
|
||||
* notification description
|
||||
* @return a hawkbit error notification message
|
||||
*/
|
||||
protected static ParallelNotification buildErrorNotification(final String caption, final String description) {
|
||||
return UINotification.buildNotification(SPUIStyleDefinitions.SP_NOTIFICATION_ERROR_MESSAGE_STYLE, caption,
|
||||
description, VaadinIcons.EXCLAMATION_CIRCLE, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to show notification on the given page.
|
||||
*
|
||||
* @param page
|
||||
* page to show notification on
|
||||
* @param notification
|
||||
* notification to show
|
||||
*/
|
||||
protected void showErrorNotification(final Page page, final Notification notification) {
|
||||
page.getUI().access(() -> notification.show(page));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.error;
|
||||
|
||||
/**
|
||||
* Details of UI errors for building the error notification.
|
||||
*/
|
||||
public final class UiErrorDetails {
|
||||
private static final UiErrorDetails EMPTY = new UiErrorDetails();
|
||||
|
||||
private final String caption;
|
||||
private final String description;
|
||||
|
||||
private UiErrorDetails() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
private UiErrorDetails(final String caption, final String description) {
|
||||
this.caption = caption;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCaption() {
|
||||
return caption;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if error details are not empty.
|
||||
*
|
||||
* @return if error details are populated
|
||||
*/
|
||||
public boolean isPresent() {
|
||||
return caption != null && description != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates empty error details that should be ignored by error handler.
|
||||
*
|
||||
* @return empty error details
|
||||
*/
|
||||
public static UiErrorDetails empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates error details that should be processed by error handler.
|
||||
*
|
||||
* @param caption
|
||||
* error details caption
|
||||
* @param description
|
||||
* error details description
|
||||
* @return error details
|
||||
*/
|
||||
public static UiErrorDetails create(final String caption, final String description) {
|
||||
return new UiErrorDetails(caption, description);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.error.extractors;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.hawkbit.ui.error.UiErrorDetails;
|
||||
|
||||
/**
|
||||
* Base class for single UI error details extractors.
|
||||
*/
|
||||
public abstract class AbstractSingleUiErrorDetailsExtractor implements UiErrorDetailsExtractor {
|
||||
|
||||
@Override
|
||||
public List<UiErrorDetails> extractErrorDetailsFrom(final Throwable error) {
|
||||
return findDetails(error).map(Collections::singletonList).orElseGet(Collections::emptyList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts single ui error details from given error.
|
||||
*
|
||||
* @param error
|
||||
* error to extract details from
|
||||
* @return ui error details if found
|
||||
*/
|
||||
protected abstract Optional<UiErrorDetails> findDetails(Throwable error);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.error.extractors;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.hawkbit.ui.error.UiErrorDetails;
|
||||
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* UI error details extractor for {@link ConstraintViolationException}.
|
||||
*/
|
||||
public class ConstraintViolationErrorExtractor extends AbstractSingleUiErrorDetailsExtractor {
|
||||
private final VaadinMessageSource i18n;
|
||||
|
||||
/**
|
||||
* Constructor for ConstraintViolationErrorExtractor.
|
||||
*
|
||||
* @param i18n
|
||||
* Message source used for localization
|
||||
*/
|
||||
public ConstraintViolationErrorExtractor(final VaadinMessageSource i18n) {
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<UiErrorDetails> findDetails(final Throwable error) {
|
||||
return findExceptionOf(error, ConstraintViolationException.class).map(ex -> {
|
||||
final StringBuilder descriptionBuilder = new StringBuilder(getBasicDescription(ex, error));
|
||||
getViolationsDescription(ex).ifPresent(violationsDescription -> descriptionBuilder.append(":")
|
||||
.append(System.lineSeparator()).append(violationsDescription));
|
||||
|
||||
return UiErrorDetails.create(i18n.getMessage("caption.error"), descriptionBuilder.toString());
|
||||
});
|
||||
}
|
||||
|
||||
private static String getBasicDescription(final ConstraintViolationException ex, final Throwable error) {
|
||||
return StringUtils.isEmpty(ex.getMessage()) ? error.getClass().getSimpleName() : ex.getMessage();
|
||||
}
|
||||
|
||||
private static Optional<String> getViolationsDescription(final ConstraintViolationException ex) {
|
||||
final Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
|
||||
if (!CollectionUtils.isEmpty(violations)) {
|
||||
return Optional.of(formatViolations(violations));
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static String formatViolations(final Set<ConstraintViolation<?>> violations) {
|
||||
return violations.stream().map(violation -> violation.getPropertyPath() + " " + violation.getMessage())
|
||||
.collect(Collectors.joining(System.lineSeparator()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.error.extractors;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.hawkbit.ui.error.UiErrorDetails;
|
||||
|
||||
/**
|
||||
* Base interface for extracting ui error details from given error.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface UiErrorDetailsExtractor {
|
||||
|
||||
/**
|
||||
* Extracts ui error details from given error.
|
||||
*
|
||||
* @param error
|
||||
* error to extract details from
|
||||
* @return ui error details
|
||||
*/
|
||||
List<UiErrorDetails> extractErrorDetailsFrom(final Throwable error);
|
||||
|
||||
/**
|
||||
* Tries to find out if error matches the given exception type.
|
||||
*
|
||||
* @param error
|
||||
* error to match
|
||||
* @param exceptionType
|
||||
* the type of exception to match
|
||||
* @return casted error if matched
|
||||
*/
|
||||
default <T> Optional<T> findExceptionOf(final Throwable error, final Class<T> exceptionType) {
|
||||
if (exceptionType.isAssignableFrom(error.getClass())) {
|
||||
return Optional.of((T) error);
|
||||
}
|
||||
|
||||
if (error.getCause() != null) {
|
||||
return findExceptionOf(error.getCause(), exceptionType);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.error.extractors;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.hawkbit.ui.error.UiErrorDetails;
|
||||
|
||||
import com.vaadin.server.UploadException;
|
||||
|
||||
/**
|
||||
* UI error details extractor for {@link UploadException}.
|
||||
*/
|
||||
public class UploadErrorExtractor extends AbstractSingleUiErrorDetailsExtractor {
|
||||
|
||||
@Override
|
||||
protected Optional<UiErrorDetails> findDetails(final Throwable error) {
|
||||
// UploadException is ignored
|
||||
return findExceptionOf(error, UploadException.class).map(ex -> UiErrorDetails.empty());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user