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 */
019package org.apache.isis.viewer.restfulobjects.server;
020
021import java.util.List;
022
023import javax.ws.rs.core.Response;
024import javax.ws.rs.core.Response.ResponseBuilder;
025import javax.ws.rs.ext.ExceptionMapper;
026import javax.ws.rs.ext.Provider;
027
028import org.apache.commons.lang.exception.ExceptionUtils;
029import org.apache.isis.viewer.restfulobjects.applib.RestfulMediaType;
030import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse.HttpStatusCode;
031import org.apache.isis.viewer.restfulobjects.applib.util.JsonMapper;
032import org.jboss.resteasy.spi.Failure;
033
034import com.google.common.collect.Lists;
035
036@Provider
037public class RuntimeExceptionMapper implements ExceptionMapper<RuntimeException> {
038
039    @Override
040    public Response toResponse(final RuntimeException ex) {
041        HttpStatusCode statusCode = HttpStatusCode.INTERNAL_SERVER_ERROR;
042        if(ex instanceof Failure) {
043            Failure failure = (Failure) ex;
044            statusCode = HttpStatusCode.statusFor(failure.getErrorCode());
045        }
046        final ResponseBuilder builder = Response.status(statusCode.getJaxrsStatusType()).type(RestfulMediaType.APPLICATION_JSON_ERROR).entity(jsonFor(ex));
047        return builder.build();
048    }
049
050    private static class ExceptionPojo {
051
052        public static ExceptionPojo create(final Exception ex) {
053            return new ExceptionPojo(ex);
054        }
055
056        private static String format(final StackTraceElement stackTraceElement) {
057            return stackTraceElement.toString();
058        }
059
060        private final String message;
061        private final List<String> stackTrace = Lists.newArrayList();
062        private ExceptionPojo causedBy;
063
064        public ExceptionPojo(final Throwable ex) {
065            this.message = messageFor(ex);
066            final StackTraceElement[] stackTraceElements = ex.getStackTrace();
067            for (final StackTraceElement stackTraceElement : stackTraceElements) {
068                this.stackTrace.add(format(stackTraceElement));
069            }
070            final Throwable cause = ex.getCause();
071            if (cause != null && cause != ex) {
072                this.causedBy = new ExceptionPojo(cause);
073            }
074        }
075
076        private static String messageFor(final Throwable ex) {
077            final String message = ex.getMessage();
078            return message != null ? message : ex.getClass().getName();
079        }
080
081        @SuppressWarnings("unused")
082        public String getMessage() {
083            return message;
084        }
085
086        @SuppressWarnings("unused")
087        public List<String> getStackTrace() {
088            return stackTrace;
089        }
090
091        @SuppressWarnings("unused")
092        public ExceptionPojo getCausedBy() {
093            return causedBy;
094        }
095    }
096
097    static String jsonFor(final Exception ex) {
098        try {
099            return JsonMapper.instance().write(ExceptionPojo.create(ex));
100        } catch (final Exception e) {
101            // fallback
102            return "{ \"exception\": \"" + ExceptionUtils.getFullStackTrace(ex) + "\" }";
103        }
104    }
105}