/** * Copyright (C) 2011 JTalks.org Team * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jtalks.jcommune.web.exception; import org.jtalks.jcommune.plugin.api.exceptions.NotFoundException; import org.springframework.beans.TypeMismatchException; import org.springframework.security.access.AccessDeniedException; import org.springframework.util.Assert; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.security.Principal; import java.util.Enumeration; import java.util.Properties; /** * Catches all the exceptions thrown in controllers, logs them and directs to the error pages. The standard * {@link SimpleMappingExceptionResolver} wasn't sufficient because it was logging all exceptions as warnings while * some of them are expected and should be logged as INFO (such as 404 topic not found). * * @author Vitaliy Kravchenko */ public class PrettyLogExceptionResolver extends SimpleMappingExceptionResolver { /** Template message for logging AccessDeniedException */ private static final String ACCESS_DENIED_MESSAGE = "Access was denied for user [%s] trying to %s %s"; /** Constant for anonymous user */ private static final String NOT_AUTHORIZED_USERNAME = "anonymousUser"; /** * {@inheritDoc} */ @Override protected void logException(Exception ex, HttpServletRequest request) { if (ex instanceof NotFoundException) { logger.info(getLogMessage(request, ex)); } else if (ex instanceof TypeMismatchException) { logger.info(getLogMessage(request, ex)); } else if (ex instanceof AccessDeniedException) { String url = request.getRequestURL().toString(); Principal principal = request.getUserPrincipal(); String user = principal != null ? principal.getName() : NOT_AUTHORIZED_USERNAME; String accessDeniedMessage = String.format(ACCESS_DENIED_MESSAGE, user, request.getMethod(), url); logger.info(accessDeniedMessage); } else { super.logException(ex, request); logger.info(getLogMessage(request, ex)); } } /** * Get info about occurred exception: request method, url, cookies and data. * @param request request * @param ex exception * @return log message */ private String getLogMessage(HttpServletRequest request, Exception ex) { String data = ""; String exceptionMessage = ex.getMessage(); try { Assert.notNull(request.getInputStream()); BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream())); String line; StringBuilder stringBuilder = new StringBuilder(); while ((line=reader.readLine()) != null ) { stringBuilder.append(line).append("\n"); } data = stringBuilder.append(exceptionMessage).toString(); } catch (IOException | IllegalArgumentException e) { logger.warn("Could not parse data from request"); } String queryString = request.getQueryString(); String url = request.getRequestURL().toString(); if (queryString != null && !queryString.isEmpty()) { url += "?" + queryString; } return String.format("[%s][%s][%s][%s]", request.getMethod(), url, request.getHeader("Cookie"), data); } /** * {@inheritDoc} */ @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { if (shouldApplyTo(request, handler)) { logException(ex, request); logger.debug(request,ex); prepareResponse(ex, response); return doResolveException(request, response, handler, ex); } else { return null; } } /** * {@inheritDoc} */ @Override protected ModelAndView getModelAndView(String viewName, Exception ex) { return new ModelAndView(viewName); } /** * {@inheritDoc} */ @Override protected String findMatchingViewName(Properties exceptionMappings, Exception ex) { String viewName = null; String dominantMapping = null; int deepest = Integer.MAX_VALUE; for (Enumeration<?> names = exceptionMappings.propertyNames(); names.hasMoreElements();) { String exceptionMapping = (String) names.nextElement(); int depth = getDepth(exceptionMapping, ex); if (depth >= 0 && (depth < deepest || (depth == deepest && dominantMapping != null && exceptionMapping.length() > dominantMapping.length()))) { deepest = depth; dominantMapping = exceptionMapping; viewName = exceptionMappings.getProperty(exceptionMapping); } } return viewName; } }