001/* 002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nuxeo - initial API and implementation 018 * 019 */ 020 021package org.nuxeo.ecm.webengine; 022 023import java.io.PrintWriter; 024import java.io.StringWriter; 025 026import javax.servlet.http.HttpServletResponse; 027import javax.ws.rs.WebApplicationException; 028import javax.ws.rs.core.Response; 029 030import org.apache.commons.logging.Log; 031import org.apache.commons.logging.LogFactory; 032import org.nuxeo.common.utils.ExceptionUtils; 033import org.nuxeo.ecm.core.api.DocumentNotFoundException; 034import org.nuxeo.ecm.core.api.DocumentSecurityException; 035import org.nuxeo.ecm.webengine.model.ModuleResource; 036import org.nuxeo.ecm.webengine.model.WebContext; 037import org.nuxeo.ecm.webengine.model.exceptions.WebResourceNotFoundException; 038import org.nuxeo.ecm.webengine.model.exceptions.WebSecurityException; 039 040/** 041 * @deprecated since 9.3. Use {@code NuxeoException} instead. 042 */ 043@Deprecated 044public class WebException extends WebApplicationException { 045 046 protected static final Log log = LogFactory.getLog(WebException.class); 047 048 private static final long serialVersionUID = 176876876786L; 049 050 protected String type; 051 052 protected Throwable cause; 053 054 protected String message; 055 056 protected int status; 057 058 public WebException() { 059 } 060 061 public WebException(Response response) { 062 super(response); 063 } 064 065 public WebException(int status) { 066 super(status); 067 this.status = status; 068 } 069 070 public WebException(Response.Status status) { 071 super(status); 072 this.status = status.getStatusCode(); 073 } 074 075 /** 076 * Use WebException.wrap() and not the constructor. 077 */ 078 protected WebException(Throwable cause, Response.Status status) { 079 super(cause, status); 080 this.cause = cause; 081 this.status = status.getStatusCode(); 082 } 083 084 protected WebException(Throwable cause, int status) { 085 super(cause, status); 086 this.cause = cause; 087 this.status = status; 088 } 089 090 protected WebException(Throwable cause) { 091 super(cause); 092 this.cause = cause; 093 } 094 095 public WebException(String message) { 096 this.message = message; 097 } 098 099 public WebException(String message, int code) { 100 super(code); 101 this.message = message; 102 this.status = code; 103 } 104 105 public WebException(String message, Throwable cause, int status) { 106 if (cause == null) { 107 throw new IllegalArgumentException("the cause parameter cannot be null"); 108 } 109 this.status = status == -1 ? getStatus(cause) : status; 110 this.cause = cause; 111 this.message = message == null ? cause.getMessage() : message; 112 this.type = cause.getClass().getName(); 113 } 114 115 public WebException(String message, Throwable cause) { 116 if (cause == null) { 117 throw new IllegalArgumentException("the cause parameter cannot be null"); 118 } 119 this.status = getStatus(cause); 120 this.cause = cause; 121 this.message = message == null ? cause.getMessage() : message; 122 this.type = cause.getClass().getName(); 123 } 124 125 public static WebException newException(String message, Throwable cause) { 126 return newException(message, cause, -1); 127 } 128 129 public static WebException newException(Throwable cause) { 130 return newException(null, cause); 131 } 132 133 public static WebException newException(String message, Throwable cause, int status) { 134 if (cause == null) { 135 throw new IllegalArgumentException("the cause parameter cannot be null"); 136 } 137 return new WebException(message, cause, status); 138 } 139 140 public static String toString(Throwable t) { 141 StringWriter sw = new StringWriter(); 142 PrintWriter pw = new PrintWriter(sw); 143 t.printStackTrace(pw); 144 pw.close(); 145 return sw.toString(); 146 } 147 148 public static WebException wrap(Throwable e) { 149 return wrap(null, e); 150 } 151 152 public static WebException wrap(String message, Throwable exception) { 153 if (exception instanceof Exception) { 154 ExceptionUtils.checkInterrupt((Exception) exception); 155 } 156 if (exception instanceof DocumentSecurityException) { 157 return new WebException(message, exception, Response.Status.FORBIDDEN.getStatusCode()); 158 } else if (exception instanceof WebException) { 159 return (WebException) exception; 160 } else if (exception instanceof DocumentNotFoundException) { 161 return new WebException(message, exception, Response.Status.NOT_FOUND.getStatusCode()); 162 } else { 163 return new WebException(message, exception); 164 } 165 } 166 167 public static Object handleError(WebApplicationException e) { 168 StringWriter sw = new StringWriter(); 169 PrintWriter pw = new PrintWriter(sw); 170 e.printStackTrace(pw); 171 pw.close(); 172 return Response.status(500).entity(sw.toString()).build(); 173 } 174 175 /** 176 * Tries to find the best matching HTTP status for the given exception. 177 */ 178 public static int getStatus(Throwable cause) { 179 // use a max depth of 8 to avoid infinite loops for broken exceptions 180 // which are referencing themselves as the cause 181 return getStatus(cause, 8); 182 } 183 184 public static int getStatus(Throwable cause, int depth) { 185 if (depth == 0) { 186 log.warn("Possible infinite loop! Check the exception wrapping."); 187 return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; 188 } 189 if ((cause instanceof DocumentSecurityException) || (cause instanceof SecurityException) 190 || "javax.ejb.EJBAccessException".equals(cause.getClass().getName())) { 191 return HttpServletResponse.SC_FORBIDDEN; 192 } else if (cause instanceof DocumentNotFoundException || cause instanceof WebResourceNotFoundException) { 193 return HttpServletResponse.SC_NOT_FOUND; 194 } else if (cause instanceof WebSecurityException) { 195 return HttpServletResponse.SC_UNAUTHORIZED; 196 } 197 Throwable parent = cause.getCause(); 198 if (parent == cause) { 199 log.warn("Infinite loop detected! Check the exception wrapping."); 200 return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; 201 } 202 if (parent != null) { 203 return getStatus(parent, depth - 1); 204 } 205 if (cause.getMessage() != null && cause.getMessage().contains(DocumentNotFoundException.class.getName())) { 206 log.warn("Badly wrapped exception: found a DocumentNotFoundException" 207 + " message but no DocumentNotFoundException", cause); 208 return HttpServletResponse.SC_NOT_FOUND; 209 } 210 return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; 211 } 212 213 public static boolean isSecurityError(Throwable t) { 214 return getStatus(t) == HttpServletResponse.SC_FORBIDDEN; 215 } 216 217 /** 218 * For compatibility only. 219 */ 220 @Deprecated 221 public static Response toResponse(Throwable t) { 222 return new WebException(t).toResponse(); 223 } 224 225 @Override 226 public String getMessage() { 227 return message; 228 } 229 230 public int getStatusCode() { 231 return status; 232 } 233 234 public String getStackTraceString() { 235 StringWriter sw = new StringWriter(); 236 PrintWriter pw = new PrintWriter(sw); 237 printStackTrace(pw); 238 pw.close(); 239 return sw.toString(); 240 } 241 242 /** 243 * For compatibility only. 244 */ 245 @Deprecated 246 public int getReturnCode() { 247 return super.getResponse().getStatus(); 248 } 249 250 /** 251 * Handle if needed custom error webengine module handler. 252 */ 253 public Response toResponse() { 254 WebContext ctx = WebEngine.getActiveContext(); 255 if (ctx != null && ctx.head() instanceof ModuleResource) { 256 return toWebModuleResponse(ctx); 257 } 258 return Response.status(status).entity(this).build(); 259 } 260 261 /** 262 * Ask Webengine module for custom error handling and response. 263 */ 264 protected Response toWebModuleResponse(WebContext ctx) { 265 ModuleResource mr = (ModuleResource) ctx.head(); 266 Object result = mr.handleError((WebApplicationException) this.getCause()); 267 if (result instanceof Response) { 268 return (Response) result; 269 } 270 if (result instanceof WebException) { 271 status = ((WebException) result).getStatus(); 272 } 273 return Response.fromResponse(getResponse()).status(status).entity(result).build(); 274 } 275 276 public int getStatus() { 277 return status; 278 } 279 280 public void setStatus(int status) { 281 this.status = status; 282 } 283 284 public String getType() { 285 return type; 286 } 287 288 public Throwable getCause() { 289 return cause; 290 } 291 292 public String getRequestId() { 293 return ""; 294 } 295 296 public String getHelpUrl() { 297 return ""; 298 } 299 300}