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