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    }