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