001 package de.deepamehta.plugins.webservice.provider;
002
003 import de.deepamehta.core.JSONEnabled;
004 import de.deepamehta.core.service.accesscontrol.AccessControlException;
005 import de.deepamehta.core.util.JavaUtils;
006
007 import org.codehaus.jettison.json.JSONArray;
008 import org.codehaus.jettison.json.JSONException;
009 import org.codehaus.jettison.json.JSONObject;
010
011 import javax.servlet.http.HttpServletRequest;
012
013 import javax.ws.rs.WebApplicationException;
014 import javax.ws.rs.core.Context;
015 import javax.ws.rs.core.MediaType;
016 import javax.ws.rs.core.Response;
017 import javax.ws.rs.core.Response.Status;
018 import javax.ws.rs.ext.ExceptionMapper;
019 import javax.ws.rs.ext.Provider;
020
021 // ### import java.io.PrintWriter;
022 // ### import java.io.StringWriter;
023 import java.util.logging.Level;
024 import java.util.logging.Logger;
025
026
027
028 /**
029 * Maps all Throwables but WebApplicationExceptions to a 500 (Internal Server Error) response.
030 * A WebApplicationException's response is returned directly.
031 * <p>
032 * We don't want Jersey to re-throw anything to the HTTP container as this would result in logging
033 * the exception twice and possibly to interspersed illegible stack traces (see #484).
034 */
035 @Provider
036 public class CatchAllExceptionMapper implements ExceptionMapper<Throwable> {
037
038 // ---------------------------------------------------------------------------------------------- Instance Variables
039
040 @Context
041 HttpServletRequest request;
042
043 private Logger logger = Logger.getLogger(getClass().getName());
044
045 // -------------------------------------------------------------------------------------------------- Public Methods
046
047 @Override
048 public Response toResponse(Throwable e) {
049 if (e instanceof WebApplicationException) {
050 return ((WebApplicationException) e).getResponse();
051 }
052 //
053 Status status = hasNestedAccessControlException(e) ? Status.UNAUTHORIZED : Status.INTERNAL_SERVER_ERROR;
054 //
055 logger.log(Level.SEVERE, errorMessage(status), e);
056 return Response.status(status).type(MediaType.APPLICATION_JSON).entity(new ExceptionInfo(e)).build();
057 }
058
059 // ------------------------------------------------------------------------------------------------- Private Methods
060
061 private boolean hasNestedAccessControlException(Throwable e) {
062 while (e != null) {
063 if (e instanceof AccessControlException) {
064 return true;
065 }
066 e = e.getCause();
067 }
068 return false;
069 }
070
071 private String errorMessage(Status status) {
072 return "Request \"" + JavaUtils.requestInfo(request) + "\" failed. Responding with " +
073 JavaUtils.responseInfo(status) + ". The original exception/error is:";
074 }
075
076 /* ### private String toJSON(Throwable e) {
077 try {
078 StringWriter out = new StringWriter();
079 e.printStackTrace(new PrintWriter(out));
080 return new JSONObject().put("exception", out).toString();
081 } catch (JSONException je) {
082 throw new RuntimeException("Generating exception info failed", je);
083 }
084 } */
085
086 // --------------------------------------------------------------------------------------------------- Private Class
087
088 private static class ExceptionInfo implements JSONEnabled {
089
090 private JSONObject json;
091
092 private ExceptionInfo(Throwable e) {
093 try {
094 json = createJSONObject(e);
095 } catch (JSONException je) {
096 throw new RuntimeException("Generating exception info failed", je);
097 }
098 }
099
100 private JSONObject createJSONObject(Throwable e) throws JSONException {
101 JSONObject json = new JSONObject()
102 .put("exception", e.getClass().getName())
103 .put("message", e.getMessage());
104 //
105 Throwable cause = e.getCause();
106 if (cause != null) {
107 json.put("cause", createJSONObject(cause));
108 }
109 //
110 return json;
111 }
112
113 @Override
114 public JSONObject toJSON() {
115 return json;
116 }
117 }
118 }