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}