001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 * Contributors:
020 *     Florent Guillaume
021 */
022package org.nuxeo.ecm.core.opencmis.bindings;
023
024import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
025import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
026import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
027import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
028import static javax.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED;
029import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
030
031import java.io.IOException;
032
033import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
034import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
035import org.apache.chemistry.opencmis.commons.exceptions.CmisContentAlreadyExistsException;
036import org.apache.chemistry.opencmis.commons.exceptions.CmisFilterNotValidException;
037import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
038import org.apache.chemistry.opencmis.commons.exceptions.CmisNameConstraintViolationException;
039import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException;
040import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
041import org.apache.chemistry.opencmis.commons.exceptions.CmisPermissionDeniedException;
042import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
043import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
044import org.apache.chemistry.opencmis.commons.exceptions.CmisStreamNotSupportedException;
045import org.apache.chemistry.opencmis.commons.exceptions.CmisUpdateConflictException;
046import org.apache.chemistry.opencmis.commons.exceptions.CmisVersioningException;
047import org.apache.commons.lang.math.NumberUtils;
048import org.nuxeo.ecm.core.api.RecoverableClientException;
049import org.nuxeo.ecm.core.opencmis.bindings.NuxeoCmisErrorHelper.ErrorExtractor;
050import org.nuxeo.ecm.core.opencmis.bindings.NuxeoCmisErrorHelper.ErrorInfo;
051import org.slf4j.Logger;
052import org.slf4j.LoggerFactory;
053
054/**
055 * Helper to deal with HTTP errors.
056 *
057 * @since 7.1
058 */
059public class DefaultErrorExtractor implements ErrorExtractor {
060
061    private static final Logger LOG = LoggerFactory.getLogger(DefaultErrorExtractor.class);
062
063    // see CmisAtomPubServlet.printError
064    // see CmisBrowserBindingServlet.ErrorServiceCall.printError
065    @Override
066    public ErrorInfo extractError(Exception ex) {
067        int statusCode = SC_INTERNAL_SERVER_ERROR; // 500
068        String exceptionName = "runtime";
069
070        if (ex instanceof CmisRuntimeException) {
071            Throwable cause = ex.getCause();
072            if (cause instanceof RecoverableClientException) {
073                // don't log something harsh in that case
074                statusCode = getHttpStatus((RecoverableClientException) cause);
075            } else {
076                LOG.error(ex.getMessage(), ex);
077            }
078        } else if (ex instanceof CmisStorageException) {
079            LOG.error(ex.getMessage(), ex);
080            statusCode = getErrorCode((CmisStorageException) ex);
081            exceptionName = ((CmisStorageException) ex).getExceptionName();
082        } else if (ex instanceof CmisBaseException) {
083            statusCode = getErrorCode((CmisBaseException) ex);
084            exceptionName = ((CmisBaseException) ex).getExceptionName();
085        } else if (ex instanceof IOException) {
086            LOG.warn(ex.getMessage(), ex);
087        } else {
088            LOG.error(ex.getMessage(), ex);
089        }
090
091        String message = ex.getMessage();
092        if (!(ex instanceof CmisBaseException)) {
093            message = "An error occurred!";
094        }
095
096        return new ErrorInfo(statusCode, exceptionName, message);
097    }
098
099    /*
100     * A bit of a hack, we need a way to find the HTTP status from the exception. We use the last parameter of the
101     * localized message for that.
102     */
103    public int getHttpStatus(RecoverableClientException ex) {
104        String[] params = ex.geLocalizedMessageParams(); // urgh, typo
105        int len = params == null ? 0 : params.length;
106        String lastParam;
107        if (len > 0 && NumberUtils.isDigits(lastParam = params[len - 1])) {
108            try {
109                return Integer.parseInt(lastParam);
110            } catch (NumberFormatException e) {
111                // fall through
112            }
113        }
114        return SC_INTERNAL_SERVER_ERROR; // 500
115    }
116
117    // see CmisAtomPubServlet.getErrorCode
118    // see CmisBrowserBindingServlet.ErrorServiceCall.getErrorCode
119    public int getErrorCode(CmisBaseException ex) {
120        if (ex instanceof CmisConstraintException) {
121            return SC_CONFLICT; // 409
122        } else if (ex instanceof CmisContentAlreadyExistsException) {
123            return SC_CONFLICT; // 409
124        } else if (ex instanceof CmisFilterNotValidException) {
125            return SC_BAD_REQUEST; // 400
126        } else if (ex instanceof CmisInvalidArgumentException) {
127            return SC_BAD_REQUEST; // 400
128        } else if (ex instanceof CmisNameConstraintViolationException) {
129            return SC_CONFLICT; // 409
130        } else if (ex instanceof CmisNotSupportedException) {
131            return SC_METHOD_NOT_ALLOWED; // 405
132        } else if (ex instanceof CmisObjectNotFoundException) {
133            return SC_NOT_FOUND; // 404
134        } else if (ex instanceof CmisPermissionDeniedException) {
135            return SC_FORBIDDEN; // 403
136        } else if (ex instanceof CmisStorageException) {
137            return SC_INTERNAL_SERVER_ERROR; // 500
138        } else if (ex instanceof CmisStreamNotSupportedException) {
139            return SC_FORBIDDEN; // 403
140        } else if (ex instanceof CmisUpdateConflictException) {
141            return SC_CONFLICT; // 409
142        } else if (ex instanceof CmisVersioningException) {
143            return SC_CONFLICT; // 409
144        }
145        return SC_INTERNAL_SERVER_ERROR; // 500
146    }
147
148}