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.http.exception.*;
021import org.apache.juneau.rest.annotation.*;
022
023/**
024 * Exception thrown to trigger an error HTTP status.
025 *
026 * <div class='warn'>
027 *    <b>Deprecated</b> - Use {@link HttpException}
028 * </div>
029 *
030 * <p>
031 * REST methods on subclasses of {@link RestServlet} can throw this exception to trigger an HTTP status other than the
032 * automatically-generated <c>404</c>, <c>405</c>, and <c>500</c> statuses.
033 */
034@Deprecated
035public class RestException extends RuntimeException {
036
037   private static final long serialVersionUID = 1L;
038
039   private int status;
040   @Deprecated private int occurrence;
041
042   /**
043    * Constructor.
044    *
045    * @param cause The cause of this exception.
046    * @param status The HTTP status code.
047    * @param msg The status message.
048    * @param args Optional {@link MessageFormat}-style arguments.
049    */
050   public RestException(Throwable cause, int status, String msg, Object...args) {
051      super(message(cause, msg, args), cause);
052      this.status = status;
053   }
054
055   /**
056    * Constructor.
057    *
058    * @param msg The status message.
059    */
060   public RestException(String msg) {
061      super(msg, null);
062   }
063
064   private static String message(Throwable cause, String msg, Object...args) {
065      if (msg == null && cause != null)
066         return firstNonEmpty(cause.getLocalizedMessage(), cause.getClass().getName());
067      return format(msg, args);
068   }
069
070   /**
071    * Constructor.
072    * @param cause The root exception.
073    * @param status The HTTP status code.
074    */
075   public RestException(Throwable cause, int status) {
076      this(cause, status, null);
077   }
078
079   /**
080    * Constructor.
081    *
082    * @param status The HTTP status code.
083    * @param msg The status message.
084    * @param args Optional {@link MessageFormat}-style arguments.
085    */
086   public RestException(int status, String msg, Object...args) {
087      this(null, status, msg, args);
088   }
089
090   /**
091    * Returns the root cause of this exception.
092    *
093    * <p>
094    * The root cause is the first exception in the init-cause parent chain that's not one of the following:
095    * <ul>
096    *    <li>{@link RestException}
097    *    <li>{@link InvocationTargetException}
098    * </ul>
099    *
100    * @return The root cause of this exception, or <jk>null</jk> if no root cause was found.
101    */
102   public Throwable getRootCause() {
103      Throwable t = this;
104      while(t != null) {
105         t = t.getCause();
106         if (! (t instanceof RestException || t instanceof InvocationTargetException))
107            return t;
108      }
109      return null;
110   }
111
112   /**
113    * Returns all error messages from all errors in this stack.
114    *
115    * <p>
116    * Typically useful if you want to render all the error messages in the stack, but don't want to render all the
117    * stack traces too.
118    *
119    * @param scrubForXssVulnerabilities
120    *    If <jk>true</jk>, replaces <js>'&lt;'</js>, <js>'&gt;'</js>, and <js>'&amp;'</js> characters with spaces.
121    * @return All error messages from all errors in this stack.
122    */
123   public String getFullStackMessage(boolean scrubForXssVulnerabilities) {
124      String msg = getMessage();
125      StringBuilder sb = new StringBuilder();
126      if (msg != null) {
127         if (scrubForXssVulnerabilities)
128            msg = msg.replace('<', ' ').replace('>', ' ').replace('&', ' ');
129         sb.append(msg);
130      }
131      Throwable e = getCause();
132      while (e != null) {
133         msg = e.getMessage();
134         if (msg != null && scrubForXssVulnerabilities)
135            msg = msg.replace('<', ' ').replace('>', ' ').replace('&', ' ');
136         String cls = e.getClass().getSimpleName();
137         if (msg == null)
138            sb.append(format("\nCaused by ({0})", cls));
139         else
140            sb.append(format("\nCaused by ({0}): {1}", cls, msg));
141         e = e.getCause();
142      }
143      return sb.toString();
144   }
145
146   @Override /* Object */
147   public int hashCode() {
148      int i = 0;
149      Throwable t = this;
150      while (t != null) {
151         for (StackTraceElement e : t.getStackTrace())
152         i ^= e.hashCode();
153         t = t.getCause();
154      }
155      return i;
156   }
157
158   /**
159    * Set the occurrence count on this exception.
160    *
161    * @param occurrence The number of times this exception has occurred.
162    * @return This object (for method chaining).
163    */
164   @Deprecated
165   protected RestException setOccurrence(int occurrence) {
166      this.occurrence = occurrence;
167      return this;
168   }
169
170   /**
171    * Set the status code on this exception.
172    *
173    * @param status The status code.
174    * @return This object (for method chaining).
175    */
176   protected RestException setStatus(int status) {
177      this.status = status;
178      return this;
179   }
180
181   /**
182    * Returns the number of times this exception occurred on this servlet.
183    *
184    * @return
185    *    The occurrence number if {@link RestResource#useStackTraceHashes() @RestResource(useStackTraceHashes)} is enabled, or <c>0</c> otherwise.
186    */
187   @Deprecated
188   public int getOccurrence() {
189      return occurrence;
190   }
191
192   /**
193    * Returns the HTTP status code.
194    *
195    * @return The HTTP status code.
196    */
197   public int getStatus() {
198      return status;
199   }
200
201   // When serialized, just serialize the message itself.
202   @Override /* Object */
203   public String toString() {
204      return emptyIfNull(getLocalizedMessage());
205   }
206}