001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.rest; 014 015import static org.apache.juneau.internal.StringUtils.*; 016 017import java.lang.reflect.*; 018import java.text.*; 019 020import org.apache.juneau.*; 021import org.apache.juneau.rest.annotation.*; 022 023/** 024 * Exception thrown to trigger an error HTTP status. 025 * 026 * <p> 027 * REST methods on subclasses of {@link RestServlet} can throw this exception to trigger an HTTP status other than the 028 * automatically-generated <code>404</code>, <code>405</code>, and <code>500</code> statuses. 029 */ 030public class RestException extends FormattedRuntimeException { 031 032 private static final long serialVersionUID = 1L; 033 034 private final int status; 035 private int occurrence; 036 037 /** 038 * Constructor. 039 * 040 * @param status The HTTP status code. 041 * @param msg The status message. 042 * @param args Optional {@link MessageFormat}-style arguments. 043 */ 044 public RestException(int status, String msg, Object...args) { 045 super(msg, args); 046 this.status = status; 047 } 048 049 /** 050 * Constructor. 051 * 052 * @param status The HTTP status code. 053 * @param cause The root exception. 054 */ 055 public RestException(int status, Throwable cause) { 056 this(status, cause.getLocalizedMessage()); 057 initCause(cause); 058 } 059 060 061 /** 062 * Sets the inner cause for this exception. 063 * 064 * @param cause The inner cause. 065 * @return This object (for method chaining). 066 */ 067 @Override /* Throwable */ 068 public synchronized RestException initCause(Throwable cause) { 069 super.initCause(cause); 070 return this; 071 } 072 073 /** 074 * Returns the root cause of this exception. 075 * 076 * <p> 077 * The root cause is the first exception in the init-cause parent chain that's not one of the following: 078 * <ul> 079 * <li>{@link RestException} 080 * <li>{@link InvocationTargetException} 081 * </ul> 082 * 083 * @return The root cause of this exception, or <jk>null</jk> if no root cause was found. 084 */ 085 public Throwable getRootCause() { 086 Throwable t = this; 087 while(t != null) { 088 t = t.getCause(); 089 if (! (t instanceof RestException || t instanceof InvocationTargetException)) 090 return t; 091 } 092 return null; 093 } 094 095 /** 096 * Returns all error messages from all errors in this stack. 097 * 098 * <p> 099 * Typically useful if you want to render all the error messages in the stack, but don't want to render all the 100 * stack traces too. 101 * 102 * @param scrubForXssVulnerabilities 103 * If <jk>true</jk>, replaces <js>'<'</js>, <js>'>'</js>, and <js>'&'</js> characters with spaces. 104 * @return All error messages from all errors in this stack. 105 */ 106 public String getFullStackMessage(boolean scrubForXssVulnerabilities) { 107 String msg = getMessage(); 108 StringBuilder sb = new StringBuilder(); 109 if (msg != null) { 110 if (scrubForXssVulnerabilities) 111 msg = msg.replace('<', ' ').replace('>', ' ').replace('&', ' '); 112 sb.append(msg); 113 } 114 Throwable e = getCause(); 115 while (e != null) { 116 msg = e.getMessage(); 117 if (msg != null && scrubForXssVulnerabilities) 118 msg = msg.replace('<', ' ').replace('>', ' ').replace('&', ' '); 119 String cls = e.getClass().getSimpleName(); 120 if (msg == null) 121 sb.append(format("\nCaused by ({0})", cls)); 122 else 123 sb.append(format("\nCaused by ({0}): {1}", cls, msg)); 124 e = e.getCause(); 125 } 126 return sb.toString(); 127 } 128 129 @Override /* Object */ 130 public int hashCode() { 131 int i = 0; 132 Throwable t = this; 133 while (t != null) { 134 for (StackTraceElement e : t.getStackTrace()) 135 i ^= e.hashCode(); 136 t = t.getCause(); 137 } 138 return i; 139 } 140 141 void setOccurrence(int occurrence) { 142 this.occurrence = occurrence; 143 } 144 145 /** 146 * Returns the number of times this exception occurred on this servlet. 147 * 148 * @return 149 * The occurrence number if {@link RestResource#useStackTraceHashes() @RestResource.useStackTraceHashes()} is enabled, or <code>0</code> otherwise. 150 */ 151 public int getOccurrence() { 152 return occurrence; 153 } 154 155 /** 156 * Returns the HTTP status code. 157 * 158 * @return The HTTP status code. 159 */ 160 public int getStatus() { 161 return status; 162 } 163}