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