001/*
002 * (C) Copyright 2006-2010 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 *     bstefanescu
018 */
019package org.nuxeo.ecm.webengine.app;
020
021import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
022import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
023import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
024import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
025
026import javax.ws.rs.WebApplicationException;
027import javax.ws.rs.core.Context;
028import javax.ws.rs.core.HttpHeaders;
029import javax.ws.rs.core.Response;
030import javax.ws.rs.ext.ExceptionMapper;
031import javax.ws.rs.ext.Provider;
032
033import org.apache.logging.log4j.LogManager;
034import org.apache.logging.log4j.Logger;
035import org.nuxeo.ecm.core.api.NuxeoException;
036import org.nuxeo.ecm.core.api.validation.DocumentValidationException;
037import org.nuxeo.ecm.webengine.WebEngine;
038import org.nuxeo.ecm.webengine.WebException;
039import org.nuxeo.ecm.webengine.model.ModuleResource;
040import org.nuxeo.ecm.webengine.model.WebContext;
041import org.nuxeo.runtime.api.Framework;
042import org.nuxeo.runtime.transaction.TransactionHelper;
043
044/**
045 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
046 */
047@Provider
048public class WebEngineExceptionMapper implements ExceptionMapper<Throwable> {
049
050    @Context
051    HttpHeaders headers;
052
053    private static final Logger log = LogManager.getLogger(WebEngineExceptionMapper.class);
054
055    @Override
056    public Response toResponse(Throwable cause) {
057        TransactionHelper.setTransactionRollbackOnly();
058        if (headers.getAcceptableMediaTypes().contains(APPLICATION_JSON_TYPE)) {
059            if (cause instanceof DocumentValidationException) {
060                DocumentValidationException dve = (DocumentValidationException) cause;
061                return Response.status(dve.getStatusCode()).entity(dve.getReport()).build();
062            }
063        }
064
065        // backward compatibility
066        if (cause instanceof WebException) {
067            return ((WebException) cause).toResponse();
068        }
069
070        // webengine custom error handling, if any
071        Object result = handleErrorOnWebModule(cause);
072        if (result instanceof Throwable) {
073            cause = (Throwable) result;
074        } else if (result instanceof Response) {
075            return (Response) result;
076        } else if (result != null) {
077            return Response.status(SC_INTERNAL_SERVER_ERROR).entity(result).build();
078        }
079
080        int statusCode = getStatusCode(cause);
081        if (statusCode >= SC_INTERNAL_SERVER_ERROR) {
082            log.error(cause, cause);
083        } else if (Framework.isDevModeSet()) {
084            log.warn(cause, cause);
085        } else {
086            log.debug(cause, cause);
087        }
088        // make sure we have a NuxeoException
089        return Response.status(statusCode)
090                       .entity(cause instanceof NuxeoException ? cause : new NuxeoException(cause, statusCode))
091                       .build();
092    }
093
094    protected static int getStatusCode(Throwable t) {
095        if (t instanceof WebException) {
096            WebException webException = (WebException) t;
097            return webException.getStatusCode();
098        } else if (t instanceof WebApplicationException) {
099            WebApplicationException e = (WebApplicationException) t;
100            return e.getResponse().getStatus();
101        } else if (t instanceof NuxeoException) {
102            NuxeoException e = (NuxeoException) t;
103            return e.getStatusCode();
104        } else if (t instanceof SecurityException) {
105            return SC_FORBIDDEN;
106        } else if (t instanceof IllegalArgumentException) {
107            return SC_BAD_REQUEST;
108        }
109
110        Throwable cause = t.getCause();
111        if (cause == null || t == cause) {
112            return SC_INTERNAL_SERVER_ERROR;
113        }
114        return getStatusCode(cause);
115    }
116
117    protected static Object handleErrorOnWebModule(Throwable t) {
118        WebContext ctx = WebEngine.getActiveContext();
119        if (ctx != null && ctx.head() instanceof ModuleResource) {
120            ModuleResource mr = (ModuleResource) ctx.head();
121            return mr.handleError(t);
122        }
123        return null;
124    }
125
126}