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.Path; 024import javax.ws.rs.core.MediaType; 025import javax.ws.rs.core.Response; 026import javax.ws.rs.core.Response.ResponseBuilder; 027import javax.ws.rs.ext.ExceptionMapper; 028import javax.ws.rs.ext.Provider; 029 030import org.apache.commons.lang.exception.ExceptionUtils; 031import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; 032import org.apache.isis.viewer.restfulobjects.applib.RestfulMediaType; 033import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse; 034import org.apache.isis.viewer.restfulobjects.applib.util.JsonMapper; 035 036import com.google.common.collect.Lists; 037 038//@Path("/") // FIXME: workaround for TomEE ... but breaks the RestEasy TCK tests so commented out:-( 039@Provider 040public class RestfulObjectsApplicationExceptionMapper implements ExceptionMapper<RestfulObjectsApplicationException> { 041 042 @Override 043 public Response toResponse(final RestfulObjectsApplicationException ex) { 044 final ResponseBuilder builder = Response.status(ex.getHttpStatusCode().getJaxrsStatusType()); 045 046 // body and content-type 047 final JsonRepresentation bodyRepr = ex.getBody(); 048 final Throwable cause = ex.getCause(); 049 if (bodyRepr != null) { 050 final String body = bodyRepr.toString(); 051 builder.entity(body); 052 builder.type(MediaType.APPLICATION_JSON); // generic; the spec doesn't define what the media type should be 053 } else if(cause == null) { 054 builder.type(MediaType.APPLICATION_JSON); // generic; the spec doesn't define what the media type should be 055 } else { 056 String body; 057 try { 058 body = JsonMapper.instance().write(ExceptionPojo.create(cause)); 059 } catch (final Exception e) { 060 // fallback 061 body = "{ \"exception\": \"" + ExceptionUtils.getFullStackTrace(cause) + "\" }"; 062 } 063 builder.entity(body); 064 builder.type(RestfulMediaType.APPLICATION_JSON_ERROR); 065 } 066 067 final String message = ex.getMessage(); 068 if (message != null) { 069 builder.header(RestfulResponse.Header.WARNING.getName(), RestfulResponse.Header.WARNING.render(message)); 070 } 071 return builder.build(); 072 } 073 074 private static class ExceptionPojo { 075 076 public static ExceptionPojo create(final Throwable ex) { 077 return new ExceptionPojo(ex); 078 } 079 080 private static String format(final StackTraceElement stackTraceElement) { 081 return stackTraceElement.toString(); 082 } 083 084 private final int httpStatusCode; 085 private final String message; 086 private final List<String> stackTrace = Lists.newArrayList(); 087 private ExceptionPojo causedBy; 088 089 public ExceptionPojo(final Throwable ex) { 090 httpStatusCode = getHttpStatusCodeIfAny(ex); 091 this.message = ex.getMessage(); 092 final StackTraceElement[] stackTraceElements = ex.getStackTrace(); 093 for (final StackTraceElement stackTraceElement : stackTraceElements) { 094 this.stackTrace.add(format(stackTraceElement)); 095 } 096 final Throwable cause = ex.getCause(); 097 if (cause != null && cause != ex) { 098 this.causedBy = new ExceptionPojo(cause); 099 } 100 } 101 102 private int getHttpStatusCodeIfAny(final Throwable ex) { 103 if (!(ex instanceof HasHttpStatusCode)) { 104 return 0; 105 } 106 final HasHttpStatusCode hasHttpStatusCode = (HasHttpStatusCode) ex; 107 return hasHttpStatusCode.getHttpStatusCode().getStatusCode(); 108 } 109 110 @SuppressWarnings("unused") 111 public int getHttpStatusCode() { 112 return httpStatusCode; 113 } 114 115 @SuppressWarnings("unused") 116 public String getMessage() { 117 return message; 118 } 119 120 @SuppressWarnings("unused") 121 public List<String> getStackTrace() { 122 return stackTrace; 123 } 124 125 @SuppressWarnings("unused") 126 public ExceptionPojo getCausedBy() { 127 return causedBy; 128 } 129 130 } 131 132 133}