001/* 002 * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * arussel 016 */ 017package org.nuxeo.ecm.platform.web.common.exceptionhandling; 018 019import static org.nuxeo.ecm.platform.ui.web.auth.NXAuthConstants.DISABLE_REDIRECT_REQUEST_KEY; 020 021import java.io.IOException; 022import java.io.PrintWriter; 023import java.io.StringWriter; 024import java.util.Locale; 025import java.util.ResourceBundle; 026 027import javax.faces.context.FacesContext; 028import javax.servlet.RequestDispatcher; 029import javax.servlet.ServletException; 030import javax.servlet.http.HttpServletRequest; 031import javax.servlet.http.HttpServletResponse; 032 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035import org.nuxeo.common.utils.i18n.I18NUtils; 036import org.nuxeo.ecm.core.api.NuxeoException; 037import org.nuxeo.ecm.core.api.WrappedException; 038import org.nuxeo.ecm.platform.web.common.exceptionhandling.descriptor.ErrorHandler; 039 040/** 041 * @author arussel 042 */ 043public class DefaultNuxeoExceptionHandler implements NuxeoExceptionHandler { 044 045 private static final Log log = LogFactory.getLog(DefaultNuxeoExceptionHandler.class); 046 047 protected NuxeoExceptionHandlerParameters parameters; 048 049 public void setParameters(NuxeoExceptionHandlerParameters parameters) { 050 this.parameters = parameters; 051 } 052 053 /** 054 * Puts a marker in request to avoid looping over the exception handling mechanism 055 * 056 * @throws ServletException if request has already been marked as handled. The initial exception is then wrapped. 057 */ 058 059 protected void startHandlingException(HttpServletRequest request, HttpServletResponse response, Throwable t) 060 throws ServletException { 061 if (request.getAttribute(EXCEPTION_HANDLER_MARKER) == null) { 062 if (log.isDebugEnabled()) { 063 log.debug("Initial exception", t); 064 } 065 // mark request as already processed by this mechanism to avoid 066 // looping over it 067 request.setAttribute(EXCEPTION_HANDLER_MARKER, true); 068 // disable further redirect by nuxeo url system 069 request.setAttribute(DISABLE_REDIRECT_REQUEST_KEY, true); 070 } else { 071 // avoid looping over exception mechanism 072 throw new ServletException(t); 073 } 074 } 075 076 public void handleException(HttpServletRequest request, HttpServletResponse response, Throwable t) 077 throws IOException, ServletException { 078 startHandlingException(request, response, t); 079 try { 080 ErrorHandler handler = getHandler(t); 081 Integer code = handler.getCode(); 082 boolean is404 = Integer.valueOf(404).equals(code); 083 parameters.getListener().startHandling(t, request, response); 084 085 Throwable unwrappedException = unwrapException(t); 086 StringWriter swriter = new StringWriter(); 087 PrintWriter pwriter = new PrintWriter(swriter); 088 t.printStackTrace(pwriter); 089 String stackTrace = swriter.getBuffer().toString(); 090 if (is404) { 091 log.debug(t.getMessage()); 092 } else { 093 log.error(stackTrace); 094 parameters.getLogger().error(stackTrace); 095 } 096 097 parameters.getListener().beforeSetErrorPageAttribute(unwrappedException, request, response); 098 request.setAttribute("exception_message", unwrappedException.getLocalizedMessage()); 099 request.setAttribute("user_message", getUserMessage(handler.getMessage(), request.getLocale())); 100 request.setAttribute("stackTrace", stackTrace); 101 request.setAttribute("securityError", ExceptionHelper.isSecurityError(unwrappedException)); 102 request.setAttribute("messageBundle", ResourceBundle.getBundle(parameters.getBundleName(), 103 request.getLocale(), Thread.currentThread().getContextClassLoader())); 104 String dumpedRequest = parameters.getRequestDumper().getDump(request); 105 if (!is404) { 106 parameters.getLogger().error(dumpedRequest); 107 } 108 request.setAttribute("request_dump", dumpedRequest); 109 110 parameters.getListener().beforeForwardToErrorPage(unwrappedException, request, response); 111 if (!response.isCommitted()) { 112 if (code != null) { 113 response.setStatus(code); 114 } 115 String errorPage = handler.getPage(); 116 errorPage = (errorPage == null) ? parameters.getDefaultErrorPage() : errorPage; 117 RequestDispatcher requestDispatcher = request.getRequestDispatcher(errorPage); 118 if (requestDispatcher != null) { 119 requestDispatcher.forward(request, response); 120 } else { 121 log.error("Cannot forward to error page, " + "no RequestDispatcher found for errorPage=" 122 + errorPage + " handler=" + handler); 123 } 124 FacesContext fContext = FacesContext.getCurrentInstance(); 125 if (fContext != null) { 126 fContext.responseComplete(); 127 } else { 128 log.error("Cannot set response complete: faces context is null"); 129 } 130 } else { 131 // do not throw an error, just log it: afterDispatch needs to 132 // be called, and sometimes the initial error is a 133 // ClientAbortException 134 log.error("Cannot forward to error page: " + "response is already committed"); 135 } 136 parameters.getListener().afterDispatch(unwrappedException, request, response); 137 } catch (ServletException e) { 138 throw e; 139 } catch (RuntimeException | IOException e) { 140 throw new ServletException(e); 141 } 142 } 143 144 protected ErrorHandler getHandler(Throwable t) { 145 Throwable throwable = unwrapException(t); 146 String className = null; 147 if (throwable instanceof WrappedException) { 148 WrappedException wrappedException = (WrappedException) throwable; 149 className = wrappedException.getClassName(); 150 } else { 151 className = throwable.getClass().getName(); 152 } 153 for (ErrorHandler handler : parameters.getHandlers()) { 154 if (handler.getError() != null && className.matches(handler.getError())) { 155 return handler; 156 } 157 } 158 throw new NuxeoException("No error handler set."); 159 } 160 161 protected Object getUserMessage(String messageKey, Locale locale) { 162 return I18NUtils.getMessageString(parameters.getBundleName(), messageKey, null, locale); 163 } 164 165 /** 166 * @deprecated use {@link ExceptionHelper#unwrapException(Throwable)} 167 */ 168 @Deprecated 169 public static Throwable unwrapException(Throwable t) { 170 return ExceptionHelper.unwrapException(t); 171 } 172 173}