/* * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Nuxeo - initial API and implementation * */ package org.nuxeo.ecm.webengine; import java.io.PrintWriter; import java.io.StringWriter; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.common.utils.ExceptionUtils; import org.nuxeo.ecm.core.api.DocumentNotFoundException; import org.nuxeo.ecm.core.api.DocumentSecurityException; import org.nuxeo.ecm.webengine.model.ModuleResource; import org.nuxeo.ecm.webengine.model.WebContext; import org.nuxeo.ecm.webengine.model.exceptions.WebResourceNotFoundException; import org.nuxeo.ecm.webengine.model.exceptions.WebSecurityException; public class WebException extends WebApplicationException { protected static final Log log = LogFactory.getLog(WebException.class); private static final long serialVersionUID = 176876876786L; protected String type; protected Throwable cause; protected String message; protected int status; public WebException() { } public WebException(Response response) { super(response); } public WebException(int status) { super(status); this.status = status; } public WebException(Response.Status status) { super(status); this.status = status.getStatusCode(); } /** * Use WebException.wrap() and not the constructor. */ protected WebException(Throwable cause, Response.Status status) { super(cause, status); this.cause = cause; this.status = status.getStatusCode(); } protected WebException(Throwable cause, int status) { super(cause, status); this.cause = cause; this.status = status; } protected WebException(Throwable cause) { super(cause); this.cause = cause; } public WebException(String message) { this.message = message; } public WebException(String message, int code) { super(code); this.message = message; this.status = code; } public WebException(String message, Throwable cause, int status) { if (cause == null) { throw new IllegalArgumentException("the cause parameter cannot be null"); } this.status = status == -1 ? getStatus(cause) : status; this.cause = cause; this.message = message == null ? cause.getMessage() : message; this.type = cause.getClass().getName(); } public WebException(String message, Throwable cause) { if (cause == null) { throw new IllegalArgumentException("the cause parameter cannot be null"); } this.status = getStatus(cause); this.cause = cause; this.message = message == null ? cause.getMessage() : message; this.type = cause.getClass().getName(); } public static WebException newException(String message, Throwable cause) { return newException(message, cause, -1); } public static WebException newException(Throwable cause) { return newException(null, cause); } public static WebException newException(String message, Throwable cause, int status) { if (cause == null) { throw new IllegalArgumentException("the cause parameter cannot be null"); } return new WebException(message, cause, status); } public static String toString(Throwable t) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); t.printStackTrace(pw); pw.close(); return sw.toString(); } public static WebException wrap(Throwable e) { return wrap(null, e); } public static WebException wrap(String message, Throwable exception) { if (exception instanceof Exception) { ExceptionUtils.checkInterrupt((Exception) exception); } if (exception instanceof DocumentSecurityException) { return new WebSecurityException(message, exception); } else if (exception instanceof WebException) { return (WebException) exception; } else if (exception instanceof DocumentNotFoundException) { return new WebResourceNotFoundException(message, exception); } else { return new WebException(message, exception); } } public static Object handleError(WebApplicationException e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); pw.close(); return Response.status(500).entity(sw.toString()).build(); } /** * Tries to find the best matching HTTP status for the given exception. */ public static int getStatus(Throwable cause) { // use a max depth of 8 to avoid infinite loops for broken exceptions // which are referencing themselves as the cause return getStatus(cause, 8); } public static int getStatus(Throwable cause, int depth) { if (depth == 0) { log.warn("Possible infinite loop! Check the exception wrapping."); return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; } if ((cause instanceof DocumentSecurityException) || (cause instanceof SecurityException) || "javax.ejb.EJBAccessException".equals(cause.getClass().getName())) { return HttpServletResponse.SC_FORBIDDEN; } else if (cause instanceof DocumentNotFoundException || cause instanceof WebResourceNotFoundException) { return HttpServletResponse.SC_NOT_FOUND; } else if (cause instanceof WebSecurityException) { return HttpServletResponse.SC_UNAUTHORIZED; } Throwable parent = cause.getCause(); if (parent == cause) { log.warn("Infinite loop detected! Check the exception wrapping."); return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; } if (parent != null) { return getStatus(parent, depth - 1); } if (cause.getMessage() != null && cause.getMessage().contains(DocumentNotFoundException.class.getName())) { log.warn("Badly wrapped exception: found a DocumentNotFoundException" + " message but no DocumentNotFoundException", cause); return HttpServletResponse.SC_NOT_FOUND; } return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; } public static boolean isSecurityError(Throwable t) { return getStatus(t) == HttpServletResponse.SC_FORBIDDEN; } /** * For compatibility only. */ @Deprecated public static Response toResponse(Throwable t) { return new WebException(t).toResponse(); } @Override public String getMessage() { return message; } public int getStatusCode() { return status; } public String getStackTraceString() { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); printStackTrace(pw); pw.close(); return sw.toString(); } /** * For compatibility only. */ @Deprecated public int getReturnCode() { return super.getResponse().getStatus(); } /** * Handle if needed custom error webengine module handler. */ public Response toResponse() { WebContext ctx = WebEngine.getActiveContext(); if (ctx != null && ctx.head() instanceof ModuleResource) { return toWebModuleResponse(ctx); } return Response.status(status).entity(this).build(); } /** * Ask Webengine module for custom error handling and response. */ protected Response toWebModuleResponse(WebContext ctx) { ModuleResource mr = (ModuleResource) ctx.head(); Object result = mr.handleError((WebApplicationException) this.getCause()); if (result instanceof Response) { return (Response) result; } if (result instanceof WebException) { status = ((WebException) result).getStatus(); } return Response.fromResponse(getResponse()).status(status).entity(result).build(); } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getType() { return type; } public Throwable getCause() { return cause; } public String getRequestId() { return ""; } public String getHelpUrl() { return ""; } }