001 /*
002 * Copyright (C) 2007 Google Inc.
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
017 package com.google.common.base;
018
019 import static com.google.common.base.Preconditions.checkNotNull;
020
021 import com.google.common.annotations.Beta;
022
023 import java.io.PrintWriter;
024 import java.io.StringWriter;
025 import java.util.ArrayList;
026 import java.util.Collections;
027 import java.util.List;
028
029 import javax.annotation.Nullable;
030
031 /**
032 * Static utility methods pertaining to instances of {@link Throwable}.
033 *
034 * @author Kevin Bourrillion
035 * @author Ben Yu
036 * @since 1
037 */
038 public final class Throwables {
039 private Throwables() {}
040
041 /**
042 * Propagates {@code throwable} exactly as-is, if and only if it is an
043 * instance of {@code declaredType}. Example usage:
044 * <pre>
045 * try {
046 * someMethodThatCouldThrowAnything();
047 * } catch (IKnowWhatToDoWithThisException e) {
048 * handle(e);
049 * } catch (Throwable t) {
050 * Throwables.propagateIfInstanceOf(t, IOException.class);
051 * Throwables.propagateIfInstanceOf(t, SQLException.class);
052 * throw Throwables.propagate(t);
053 * }
054 * </pre>
055 */
056 public static <X extends Throwable> void propagateIfInstanceOf(
057 @Nullable Throwable throwable, Class<X> declaredType) throws X {
058 if (declaredType.isInstance(throwable)) {
059 throw declaredType.cast(throwable);
060 }
061 }
062
063 /**
064 * Propagates {@code throwable} exactly as-is, if and only if it is an
065 * instance of {@link RuntimeException} or {@link Error}. Example usage:
066 * <pre>
067 * try {
068 * someMethodThatCouldThrowAnything();
069 * } catch (IKnowWhatToDoWithThisException e) {
070 * handle(e);
071 * } catch (Throwable t) {
072 * Throwables.propagateIfPossible(t);
073 * throw new RuntimeException("unexpected", t);
074 * }
075 * </pre>
076 */
077 public static void propagateIfPossible(@Nullable Throwable throwable) {
078 propagateIfInstanceOf(throwable, Error.class);
079 propagateIfInstanceOf(throwable, RuntimeException.class);
080 }
081
082 /**
083 * Propagates {@code throwable} exactly as-is, if and only if it is an
084 * instance of {@link RuntimeException}, {@link Error}, or
085 * {@code declaredType}. Example usage:
086 * <pre>
087 * try {
088 * someMethodThatCouldThrowAnything();
089 * } catch (IKnowWhatToDoWithThisException e) {
090 * handle(e);
091 * } catch (Throwable t) {
092 * Throwables.propagateIfPossible(t, OtherException.class);
093 * throw new RuntimeException("unexpected", t);
094 * }
095 * </pre>
096 *
097 * @param throwable the Throwable to possibly propagate
098 * @param declaredType the single checked exception type declared by the
099 * calling method
100 */
101 public static <X extends Throwable> void propagateIfPossible(
102 @Nullable Throwable throwable, Class<X> declaredType) throws X {
103 propagateIfInstanceOf(throwable, declaredType);
104 propagateIfPossible(throwable);
105 }
106
107 /**
108 * Propagates {@code throwable} exactly as-is, if and only if it is an
109 * instance of {@link RuntimeException}, {@link Error}, {@code declaredType1},
110 * or {@code declaredType2}. In the unlikely case that you have three or more
111 * declared checked exception types, you can handle them all by invoking these
112 * methods repeatedly. See usage example in {@link
113 * #propagateIfPossible(Throwable, Class)}.
114 *
115 * @param throwable the Throwable to possibly propagate
116 * @param declaredType1 any checked exception type declared by the calling
117 * method
118 * @param declaredType2 any other checked exception type declared by the
119 * calling method
120 */
121 public static <X1 extends Throwable, X2 extends Throwable>
122 void propagateIfPossible(@Nullable Throwable throwable,
123 Class<X1> declaredType1, Class<X2> declaredType2) throws X1, X2 {
124 checkNotNull(declaredType2);
125 propagateIfInstanceOf(throwable, declaredType1);
126 propagateIfPossible(throwable, declaredType2);
127 }
128
129 /**
130 * Propagates {@code throwable} as-is if it is an instance of
131 * {@link RuntimeException} or {@link Error}, or else as a last resort, wraps
132 * it in a {@code RuntimeException} then propagates.
133 * <p>
134 * This method always throws an exception. The {@code RuntimeException} return
135 * type is only for client code to make Java type system happy in case a
136 * return value is required by the enclosing method. Example usage:
137 * <pre>
138 * T doSomething() {
139 * try {
140 * return someMethodThatCouldThrowAnything();
141 * } catch (IKnowWhatToDoWithThisException e) {
142 * return handle(e);
143 * } catch (Throwable t) {
144 * throw Throwables.propagate(t);
145 * }
146 * }
147 * </pre>
148 *
149 * @param throwable the Throwable to propagate
150 * @return nothing will ever be returned; this return type is only for your
151 * convenience, as illustrated in the example above
152 */
153 public static RuntimeException propagate(Throwable throwable) {
154 propagateIfPossible(checkNotNull(throwable));
155 throw new RuntimeException(throwable);
156 }
157
158 /**
159 * Returns the innermost cause of {@code throwable}. The first throwable in a
160 * chain provides context from when the error or exception was initially
161 * detected. Example usage:
162 * <pre>
163 * assertEquals("Unable to assign a customer id",
164 * Throwables.getRootCause(e).getMessage());
165 * </pre>
166 */
167 public static Throwable getRootCause(Throwable throwable) {
168 Throwable cause;
169 while ((cause = throwable.getCause()) != null) {
170 throwable = cause;
171 }
172 return throwable;
173 }
174
175 /**
176 * Gets a {@code Throwable} cause chain as a list. The first entry in the
177 * list will be {@code throwable} followed by its cause hierarchy. Note
178 * that this is a snapshot of the cause chain and will not reflect
179 * any subsequent changes to the cause chain.
180 *
181 * <p>Here's an example of how it can be used to find specific types
182 * of exceptions in the cause chain:
183 *
184 * <pre>
185 * Iterables.filter(Throwables.getCausalChain(e), IOException.class));
186 * </pre>
187 *
188 * @param throwable the non-null {@code Throwable} to extract causes from
189 * @return an unmodifiable list containing the cause chain starting with
190 * {@code throwable}
191 */
192 @Beta // TODO: decide best return type
193 public static List<Throwable> getCausalChain(Throwable throwable) {
194 checkNotNull(throwable);
195 List<Throwable> causes = new ArrayList<Throwable>(4);
196 while (throwable != null) {
197 causes.add(throwable);
198 throwable = throwable.getCause();
199 }
200 return Collections.unmodifiableList(causes);
201 }
202
203 /**
204 * Returns a string containing the result of
205 * {@link Throwable#toString() toString()}, followed by the full, recursive
206 * stack trace of {@code throwable}. Note that you probably should not be
207 * parsing the resulting string; if you need programmatic access to the stack
208 * frames, you can call {@link Throwable#getStackTrace()}.
209 */
210 public static String getStackTraceAsString(Throwable throwable) {
211 StringWriter stringWriter = new StringWriter();
212 throwable.printStackTrace(new PrintWriter(stringWriter));
213 return stringWriter.toString();
214 }
215
216 /**
217 * Rethrows the cause exception of a given throwable, discarding the original
218 * throwable. Optionally, the stack frames of the cause and the outer
219 * exception are combined and the stack trace of the cause is set to this
220 * combined trace. If there is no cause the original exception is rethrown
221 * unchanged in all cases.
222 *
223 * @param exception the exception from which to extract the cause
224 * @param combineStackTraces if true the stack trace of the cause will be
225 * replaced by the concatenation of the trace from the exception and the
226 * trace from the cause.
227 */
228 @Beta
229 public static Exception throwCause(Exception exception,
230 boolean combineStackTraces) throws Exception {
231 Throwable cause = exception.getCause();
232 if (cause == null) {
233 throw exception;
234 }
235 if (combineStackTraces) {
236 StackTraceElement[] causeTrace = cause.getStackTrace();
237 StackTraceElement[] outerTrace = exception.getStackTrace();
238 StackTraceElement[] combined =
239 new StackTraceElement[causeTrace.length + outerTrace.length];
240 System.arraycopy(causeTrace, 0, combined, 0, causeTrace.length);
241 System.arraycopy(outerTrace, 0, combined,
242 causeTrace.length, outerTrace.length);
243 cause.setStackTrace(combined);
244 }
245 if (cause instanceof Exception) {
246 throw (Exception) cause;
247 }
248 if (cause instanceof Error) {
249 throw (Error) cause;
250 }
251 // The cause is a weird kind of Throwable, so throw the outer exception
252 throw exception;
253 }
254 }