/** * *************************************************************************** * Copyright (c) 2010 Qcadoo Limited * Project: Qcadoo Framework * Version: 1.4 * * This file is part of Qcadoo. * * Qcadoo is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************** */ package com.qcadoo.view.internal.exceptionresolver; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.web.multipart.MaxUploadSizeExceededException; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; import com.qcadoo.localization.api.TranslationService; import com.qcadoo.model.api.CopyException; import com.qcadoo.model.api.validators.ErrorMessage; import com.qcadoo.view.api.exception.ClassDrivenExceptionResolver; import com.qcadoo.view.api.exception.ExceptionInfo; import com.qcadoo.view.api.exception.ExceptionInfoResolver; import com.qcadoo.view.internal.controllers.ErrorController; public final class DefaultExceptionResolver extends SimpleMappingExceptionResolver implements ClassDrivenExceptionResolver { private static final Logger LOG = LoggerFactory.getLogger(DefaultExceptionResolver.class); public static final String DEFAULT_EXCEPTION_MESSAGE_ATTRIBUTE = "exceptionMessage"; private final Map<String, String> messageTranslations = new HashMap<String, String>(); private final Map<Class<?>, String> exceptionTranslations = new HashMap<Class<?>, String>(); @Autowired private ErrorController errorController; @Autowired private TranslationService translationService; @Value("${maxUploadSize:2000000}") private int maxUploadSize; private Map<Class<? extends Exception>, ExceptionInfoResolver<? extends Exception>> classResolversMap = new HashMap<Class<? extends Exception>, ExceptionInfoResolver<? extends Exception>>(); @PostConstruct public void init() { exceptionTranslations.put(DataIntegrityViolationException.class, "dataIntegrityViolationException.objectInUse"); exceptionTranslations.put(MaxUploadSizeExceededException.class, "uploadException.maxSizeExceeded"); exceptionTranslations.put(CopyException.class, "copyException"); messageTranslations.put("Entity.* is in use", "dataIntegrityViolationException.objectInUse"); messageTranslations.put("Entity.* cannot be found", "illegalStateException.entityNotFound"); messageTranslations.put("PrintError:DocumentNotGenerated", "illegalStateException.printErrorDocumentNotGenerated"); } @Override protected ModelAndView doResolveException(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception exception) { ModelAndView mv = super.doResolveException(request, response, handler, exception); if (mv == null) { return mv; } String codeStr = mv.getViewName(); int code = Integer.parseInt(codeStr); CustomTranslationMessage customExceptionMessage = getCustomExceptionMessage(exception); if (LOG.isDebugEnabled()) { LOG.debug("Adding exception message to view: " + customExceptionMessage); } String customExceptionMessageHeader = null; String customExceptionMessageExplanation = null; @SuppressWarnings("rawtypes") ExceptionInfoResolver exceptionInfoResolver = classResolversMap.get(exception.getClass()); if (exceptionInfoResolver != null) { @SuppressWarnings("unchecked") ExceptionInfo info = exceptionInfoResolver.getExceptionInfo(exception); customExceptionMessageHeader = translationService.translate(info.getMessageHeader(), LocaleContextHolder.getLocale()); customExceptionMessageExplanation = translationService.translate(info.getMessageExplanation(), LocaleContextHolder.getLocale(), info.getMessageExplanationArgs()); } else if (customExceptionMessage != null) { customExceptionMessageHeader = translationService.translate( "qcadooView.errorPage.error." + customExceptionMessage.getMessage() + ".header", LocaleContextHolder.getLocale()); if (customExceptionMessage.getEntityIdentifier() == null) { if("uploadException.maxSizeExceeded".equals(customExceptionMessage.getMessage())){ customExceptionMessageExplanation = translationService.translate("qcadooView.errorPage.error." + customExceptionMessage.getMessage() + ".explanation", LocaleContextHolder.getLocale(), "" + maxUploadSize/1000000); } else { customExceptionMessageExplanation = translationService.translate("qcadooView.errorPage.error." + customExceptionMessage.getMessage() + ".explanation", LocaleContextHolder.getLocale()); } } else { customExceptionMessageExplanation = translationService.translate("qcadooView.errorPage.error." + customExceptionMessage.getMessage() + ".explanation", LocaleContextHolder.getLocale(), customExceptionMessage.getEntityIdentifier()); } Throwable rootException = getRootException(exception); if (rootException instanceof CopyException) { String copyExplanation = getAdditionalMessageForCopyException((CopyException) rootException, LocaleContextHolder.getLocale()); if (copyExplanation != null) { customExceptionMessageExplanation = copyExplanation; } } } return errorController.getAccessDeniedPageView(code, exception, customExceptionMessageHeader, customExceptionMessageExplanation, LocaleContextHolder.getLocale()); } private Throwable getRootException(final Throwable throwable) { if (throwable.getCause() != null) { return getRootException(throwable.getCause()); } if (throwable instanceof InvocationTargetException) { return getRootException(((InvocationTargetException) throwable).getTargetException()); } return throwable; } private String getAdditionalMessageForCopyException(final CopyException exception, final Locale locale) { for (Map.Entry<String, ErrorMessage> error : exception.getEntity().getErrors().entrySet()) { String field = translationService.translate(exception.getEntity().getDataDefinition().getPluginIdentifier() + "." + exception.getEntity().getDataDefinition().getName() + "." + error.getKey() + ".label", locale); return field + " - " + translationService.translate(error.getValue().getMessage(), locale, error.getValue().getVars()); } for (ErrorMessage error : exception.getEntity().getGlobalErrors()) { return translationService.translate(error.getMessage(), locale, error.getVars()); } return null; } private CustomTranslationMessage getCustomExceptionMessage(final Throwable throwable) { CustomTranslationMessage exceptionMessage = getCustomExceptionMessageForClass(throwable); if (exceptionMessage != null) { return exceptionMessage; } String exceptionMessageString = throwable.getMessage(); if (exceptionMessageString == null) { return null; } for (Map.Entry<String, String> translation : messageTranslations.entrySet()) { if (exceptionMessageString.matches(translation.getKey())) { String translationValue = translation.getValue(); Pattern p = Pattern.compile("\\[ENTITY\\..+\\]"); Matcher m = p.matcher(exceptionMessageString); if (m.find()) { String entityIdentifier = exceptionMessageString.substring(m.start(0) + 8, m.end(0) - 1); return new CustomTranslationMessage(translationValue, entityIdentifier); } return new CustomTranslationMessage(translationValue); } } return null; } private CustomTranslationMessage getCustomExceptionMessageForClass(final Throwable throwable) { for (Map.Entry<Class<?>, String> translation : exceptionTranslations.entrySet()) { if (translation.getKey().isInstance(throwable)) { return new CustomTranslationMessage(translation.getValue()); } } if (throwable instanceof InvocationTargetException) { return getCustomExceptionMessage(((InvocationTargetException) throwable).getTargetException()); } if (throwable.getCause() == null) { return null; } return getCustomExceptionMessage(throwable.getCause()); } private class CustomTranslationMessage { private final String message; private final String entityIdentifier; public CustomTranslationMessage(final String message) { this(message, null); } public CustomTranslationMessage(final String message, final String entityIdentifier) { this.message = message; this.entityIdentifier = entityIdentifier; } public String getMessage() { return message; } public String getEntityIdentifier() { return entityIdentifier; } } @Override public <T extends Exception> void addExceptionInfoResolver(final Class<T> exceptionClass, final ExceptionInfoResolver<T> exceptionInfoResolver) { classResolversMap.put(exceptionClass, exceptionInfoResolver); } @Override public <T extends Exception> void removeExceptionInfoResolver(final Class<T> exceptionClass) { classResolversMap.remove(exceptionClass); } }