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}