001 /* 002 * Copyright (C) 2007 The Guava Authors 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.0 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 // Check for null is needed to avoid frequent JNI calls to isInstance(). 059 if (throwable != null && declaredType.isInstance(throwable)) { 060 throw declaredType.cast(throwable); 061 } 062 } 063 064 /** 065 * Propagates {@code throwable} exactly as-is, if and only if it is an 066 * instance of {@link RuntimeException} or {@link Error}. Example usage: 067 * <pre> 068 * try { 069 * someMethodThatCouldThrowAnything(); 070 * } catch (IKnowWhatToDoWithThisException e) { 071 * handle(e); 072 * } catch (Throwable t) { 073 * Throwables.propagateIfPossible(t); 074 * throw new RuntimeException("unexpected", t); 075 * } 076 * </pre> 077 */ 078 public static void propagateIfPossible(@Nullable Throwable throwable) { 079 propagateIfInstanceOf(throwable, Error.class); 080 propagateIfInstanceOf(throwable, RuntimeException.class); 081 } 082 083 /** 084 * Propagates {@code throwable} exactly as-is, if and only if it is an 085 * instance of {@link RuntimeException}, {@link Error}, or 086 * {@code declaredType}. Example usage: 087 * <pre> 088 * try { 089 * someMethodThatCouldThrowAnything(); 090 * } catch (IKnowWhatToDoWithThisException e) { 091 * handle(e); 092 * } catch (Throwable t) { 093 * Throwables.propagateIfPossible(t, OtherException.class); 094 * throw new RuntimeException("unexpected", t); 095 * } 096 * </pre> 097 * 098 * @param throwable the Throwable to possibly propagate 099 * @param declaredType the single checked exception type declared by the 100 * calling method 101 */ 102 public static <X extends Throwable> void propagateIfPossible( 103 @Nullable Throwable throwable, Class<X> declaredType) throws X { 104 propagateIfInstanceOf(throwable, declaredType); 105 propagateIfPossible(throwable); 106 } 107 108 /** 109 * Propagates {@code throwable} exactly as-is, if and only if it is an 110 * instance of {@link RuntimeException}, {@link Error}, {@code declaredType1}, 111 * or {@code declaredType2}. In the unlikely case that you have three or more 112 * declared checked exception types, you can handle them all by invoking these 113 * methods repeatedly. See usage example in {@link 114 * #propagateIfPossible(Throwable, Class)}. 115 * 116 * @param throwable the Throwable to possibly propagate 117 * @param declaredType1 any checked exception type declared by the calling 118 * method 119 * @param declaredType2 any other checked exception type declared by the 120 * calling method 121 */ 122 public static <X1 extends Throwable, X2 extends Throwable> 123 void propagateIfPossible(@Nullable Throwable throwable, 124 Class<X1> declaredType1, Class<X2> declaredType2) throws X1, X2 { 125 checkNotNull(declaredType2); 126 propagateIfInstanceOf(throwable, declaredType1); 127 propagateIfPossible(throwable, declaredType2); 128 } 129 130 /** 131 * Propagates {@code throwable} as-is if it is an instance of 132 * {@link RuntimeException} or {@link Error}, or else as a last resort, wraps 133 * it in a {@code RuntimeException} then propagates. 134 * <p> 135 * This method always throws an exception. The {@code RuntimeException} return 136 * type is only for client code to make Java type system happy in case a 137 * return value is required by the enclosing method. Example usage: 138 * <pre> 139 * T doSomething() { 140 * try { 141 * return someMethodThatCouldThrowAnything(); 142 * } catch (IKnowWhatToDoWithThisException e) { 143 * return handle(e); 144 * } catch (Throwable t) { 145 * throw Throwables.propagate(t); 146 * } 147 * } 148 * </pre> 149 * 150 * @param throwable the Throwable to propagate 151 * @return nothing will ever be returned; this return type is only for your 152 * convenience, as illustrated in the example above 153 */ 154 public static RuntimeException propagate(Throwable throwable) { 155 propagateIfPossible(checkNotNull(throwable)); 156 throw new RuntimeException(throwable); 157 } 158 159 /** 160 * Returns the innermost cause of {@code throwable}. The first throwable in a 161 * chain provides context from when the error or exception was initially 162 * detected. Example usage: 163 * <pre> 164 * assertEquals("Unable to assign a customer id", 165 * Throwables.getRootCause(e).getMessage()); 166 * </pre> 167 */ 168 public static Throwable getRootCause(Throwable throwable) { 169 Throwable cause; 170 while ((cause = throwable.getCause()) != null) { 171 throwable = cause; 172 } 173 return throwable; 174 } 175 176 /** 177 * Gets a {@code Throwable} cause chain as a list. The first entry in the 178 * list will be {@code throwable} followed by its cause hierarchy. Note 179 * that this is a snapshot of the cause chain and will not reflect 180 * any subsequent changes to the cause chain. 181 * 182 * <p>Here's an example of how it can be used to find specific types 183 * of exceptions in the cause chain: 184 * 185 * <pre> 186 * Iterables.filter(Throwables.getCausalChain(e), IOException.class)); 187 * </pre> 188 * 189 * @param throwable the non-null {@code Throwable} to extract causes from 190 * @return an unmodifiable list containing the cause chain starting with 191 * {@code throwable} 192 */ 193 @Beta // TODO(kevinb): decide best return type 194 public static List<Throwable> getCausalChain(Throwable throwable) { 195 checkNotNull(throwable); 196 List<Throwable> causes = new ArrayList<Throwable>(4); 197 while (throwable != null) { 198 causes.add(throwable); 199 throwable = throwable.getCause(); 200 } 201 return Collections.unmodifiableList(causes); 202 } 203 204 /** 205 * Returns a string containing the result of 206 * {@link Throwable#toString() toString()}, followed by the full, recursive 207 * stack trace of {@code throwable}. Note that you probably should not be 208 * parsing the resulting string; if you need programmatic access to the stack 209 * frames, you can call {@link Throwable#getStackTrace()}. 210 */ 211 public static String getStackTraceAsString(Throwable throwable) { 212 StringWriter stringWriter = new StringWriter(); 213 throwable.printStackTrace(new PrintWriter(stringWriter)); 214 return stringWriter.toString(); 215 } 216 217 /** 218 * Rethrows the cause exception of a given throwable, discarding the original 219 * throwable. Optionally, the stack frames of the cause and the outer 220 * exception are combined and the stack trace of the cause is set to this 221 * combined trace. If there is no cause the original exception is rethrown 222 * unchanged in all cases. 223 * 224 * @param exception the exception from which to extract the cause 225 * @param combineStackTraces if true the stack trace of the cause will be 226 * replaced by the concatenation of the trace from the exception and the 227 * trace from the cause. 228 * @deprecated For {@code throwCause(e, false)}, use {@code 229 * propagateIfPossible(e.getCause(), Exception.class); throw e;}. For 230 * {@code throwCause(e, true)}, there is no exact equivalent. Callers are 231 * encouraged to consider whether the cause contains all useful stack 232 * trace context. (It usually does if it comes from within the thread, 233 * e.g., for an {@code InvocationTargetException}, and it usually does not 234 * otherwise, e.g., for an {@code ExecutionException}.) If the cause 235 * contains all useful context, consider throwing it directly if your 236 * method signature permits or wrapping it in an appropriate exception if 237 * not. If the cause does not contain all useful context, consider 238 * propagating the wrapper exception or rewrapping the cause in an 239 * appropriate exception. For the particular case of exceptions thrown by 240 * {@code Future.get}, consider {@link 241 * com.google.common.util.concurrent.Futures#get(java.util.concurrent.Future, 242 * Class)}. This method will be removed from Guava in Guava release 11.0. 243 */ 244 @Beta 245 @Deprecated 246 public static Exception throwCause(Exception exception, 247 boolean combineStackTraces) throws Exception { 248 Throwable cause = exception.getCause(); 249 if (cause == null) { 250 throw exception; 251 } 252 if (combineStackTraces) { 253 StackTraceElement[] causeTrace = cause.getStackTrace(); 254 StackTraceElement[] outerTrace = exception.getStackTrace(); 255 StackTraceElement[] combined = 256 new StackTraceElement[causeTrace.length + outerTrace.length]; 257 System.arraycopy(causeTrace, 0, combined, 0, causeTrace.length); 258 System.arraycopy(outerTrace, 0, combined, 259 causeTrace.length, outerTrace.length); 260 cause.setStackTrace(combined); 261 } 262 if (cause instanceof Exception) { 263 throw (Exception) cause; 264 } 265 if (cause instanceof Error) { 266 throw (Error) cause; 267 } 268 // The cause is a weird kind of Throwable, so throw the outer exception 269 throw exception; 270 } 271 }