1 /* 2 * Copyright (C) 2007 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.base; 18 19 import java.io.PrintWriter; 20 import java.io.StringWriter; 21 import java.util.List; 22 import java.util.ArrayList; 23 import java.util.Collections; 24 25 /** 26 * Static utility methods pertaining to instances of {@link Throwable}. 27 * 28 * @author Kevin Bourrillion 29 * @author Ben Yu 30 * @since 2009.09.15 <b>tentative</b> 31 */ 32 public final class Throwables { Throwables()33 private Throwables() {} 34 35 /** 36 * Propagates {@code throwable} exactly as-is, if and only if it is an 37 * instance of {@code declaredType}. Example usage: 38 * <pre> 39 * try { 40 * someMethodThatCouldThrowAnything(); 41 * } catch (IKnowWhatToDoWithThisException e) { 42 * handle(e); 43 * } catch (Throwable t) { 44 * Throwables.propagateIfInstanceOf(t, IOException.class); 45 * Throwables.propagateIfInstanceOf(t, SQLException.class); 46 * throw Throwables.propagate(t); 47 * } 48 * </pre> 49 */ propagateIfInstanceOf( Throwable throwable, Class<X> declaredType)50 public static <X extends Throwable> void propagateIfInstanceOf( 51 Throwable throwable, Class<X> declaredType) throws X { 52 if (declaredType.isInstance(throwable)) { 53 throw declaredType.cast(throwable); 54 } 55 } 56 57 /** 58 * Propagates {@code throwable} exactly as-is, if and only if it is an 59 * instance of {@link RuntimeException} or {@link Error}. Example usage: 60 * <pre> 61 * try { 62 * someMethodThatCouldThrowAnything(); 63 * } catch (IKnowWhatToDoWithThisException e) { 64 * handle(e); 65 * } catch (Throwable t) { 66 * Throwables.propagateIfPossible(t); 67 * throw new RuntimeException("unexpected", t); 68 * } 69 * </pre> 70 */ propagateIfPossible(Throwable throwable)71 public static void propagateIfPossible(Throwable throwable) { 72 propagateIfInstanceOf(throwable, Error.class); 73 propagateIfInstanceOf(throwable, RuntimeException.class); 74 } 75 76 /** 77 * Propagates {@code throwable} exactly as-is, if and only if it is an 78 * instance of {@link RuntimeException}, {@link Error}, or 79 * {@code declaredType}. Example usage: 80 * <pre> 81 * try { 82 * someMethodThatCouldThrowAnything(); 83 * } catch (IKnowWhatToDoWithThisException e) { 84 * handle(e); 85 * } catch (Throwable t) { 86 * Throwables.propagateIfPossible(t, OtherException.class); 87 * throw new RuntimeException("unexpected", t); 88 * } 89 * </pre> 90 * 91 * @param throwable the Throwable to possibly propagate 92 * @param declaredType the single checked exception type declared by the 93 * calling method 94 */ propagateIfPossible( Throwable throwable, Class<X> declaredType)95 public static <X extends Throwable> void propagateIfPossible( 96 Throwable throwable, Class<X> declaredType) throws X { 97 propagateIfInstanceOf(throwable, declaredType); 98 propagateIfPossible(throwable); 99 } 100 101 /** 102 * Propagates {@code throwable} exactly as-is, if and only if it is an 103 * instance of {@link RuntimeException}, {@link Error}, {@code aDeclaredType}, 104 * or {@code anotherDeclaredType}. In the unlikely case that you have three 105 * or more declared checked exception types, you can handle them all by 106 * invoking these methods repeatedly. See usage example in 107 * {@link #propagateIfPossible(Throwable, Class)}. 108 * 109 * @param throwable the Throwable to possibly propagate 110 * @param aDeclaredType any checked exception type declared by the calling 111 * method 112 * @param anotherDeclaredType any other checked exception type declared by the 113 * calling method 114 */ 115 public static <X1 extends Throwable, X2 extends Throwable> void propagateIfPossible(Throwable throwable, Class<X1> aDeclaredType, Class<X2> anotherDeclaredType)116 propagateIfPossible(Throwable throwable, Class<X1> aDeclaredType, 117 Class<X2> anotherDeclaredType) throws X1, X2 { 118 propagateIfInstanceOf(throwable, aDeclaredType); 119 propagateIfPossible(throwable, anotherDeclaredType); 120 } 121 122 /** 123 * Propagates {@code throwable} as-is if it is an instance of 124 * {@link RuntimeException} or {@link Error}, or else as a last resort, wraps 125 * it in a {@code RuntimeException} then propagates. 126 * <p> 127 * This method always throws an exception. The {@code RuntimeException} return 128 * type is only for client code to make Java type system happy in case a 129 * return value is required by the enclosing method. Example usage: 130 * <pre> 131 * T doSomething() { 132 * try { 133 * return someMethodThatCouldThrowAnything(); 134 * } catch (IKnowWhatToDoWithThisException e) { 135 * return handle(e); 136 * } catch (Throwable t) { 137 * throw Throwables.propagate(t); 138 * } 139 * } 140 * </pre> 141 * 142 * @param throwable the Throwable to propagate 143 * @return nothing will ever be returned 144 */ propagate(Throwable throwable)145 public static RuntimeException propagate(Throwable throwable) { 146 propagateIfPossible(throwable); 147 throw new RuntimeException(throwable); 148 } 149 150 /** 151 * Returns the innermost cause of {@code throwable}. The first throwable in a 152 * chain provides context from when the error or exception was initially 153 * detected. Example usage: 154 * <pre> 155 * assertEquals("Unable to assign a customer id", 156 * Throwables.getRootCause(e).getMessage()); 157 * </pre> 158 */ getRootCause(Throwable throwable)159 public static Throwable getRootCause(Throwable throwable) { 160 Throwable cause; 161 while ((cause = throwable.getCause()) != null) { 162 throwable = cause; 163 } 164 return throwable; 165 } 166 167 /** 168 * Gets a {@code Throwable} cause chain as a list. The first entry in the 169 * list will be {@code throwable} followed by its cause hierarchy. Note 170 * that this is a snapshot of the cause chain and will not reflect 171 * any subsequent changes to the cause chain. 172 * 173 * <p>Here's an example of how it can be used to find specific types 174 * of exceptions in the cause chain: 175 * 176 * <pre> 177 * Iterables.filter(Throwables.getCausalChain(e), IOException.class)); 178 * </pre> 179 * 180 * @param throwable the non-null {@code Throwable} to extract causes from 181 * @return an unmodifiable list containing the cause chain starting with 182 * {@code throwable} 183 */ getCausalChain(Throwable throwable)184 public static List<Throwable> getCausalChain(Throwable throwable) { 185 Preconditions.checkNotNull(throwable); 186 List<Throwable> causes = new ArrayList<Throwable>(4); 187 while (throwable != null) { 188 causes.add(throwable); 189 throwable = throwable.getCause(); 190 } 191 return Collections.unmodifiableList(causes); 192 } 193 194 /** 195 * Returns a string containing the result of 196 * {@link Throwable#toString() toString()}, followed by the full, recursive 197 * stack trace of {@code throwable}. Note that you probably should not be 198 * parsing the resulting string; if you need programmatic access to the stack 199 * frames, you can call {@link Throwable#getStackTrace()}. 200 */ getStackTraceAsString(Throwable throwable)201 public static String getStackTraceAsString(Throwable throwable) { 202 StringWriter stringWriter = new StringWriter(); 203 throwable.printStackTrace(new PrintWriter(stringWriter)); 204 return stringWriter.toString(); 205 } 206 207 /** 208 * Rethrows the cause exception of a given throwable, discarding the original 209 * throwable. Optionally, the stack frames of the cause and the outer 210 * exception are combined and the stack trace of the cause is set to this 211 * combined trace. If there is no cause the original exception is rethrown 212 * unchanged in all cases. 213 * 214 * @param exception the exception from which to extract the cause 215 * @param combineStackTraces if true the stack trace of the cause will be 216 * replaced by the concatenation of the trace from the exception and the 217 * trace from the cause. 218 */ throwCause(Exception exception, boolean combineStackTraces)219 public static Exception throwCause(Exception exception, boolean combineStackTraces) 220 throws Exception { 221 Throwable cause = exception.getCause(); 222 if (cause == null) { 223 throw exception; 224 } 225 if (combineStackTraces) { 226 StackTraceElement[] causeTrace = cause.getStackTrace(); 227 StackTraceElement[] outerTrace = exception.getStackTrace(); 228 StackTraceElement[] combined = new StackTraceElement[causeTrace.length + outerTrace.length]; 229 System.arraycopy(causeTrace, 0, combined, 0, causeTrace.length); 230 System.arraycopy(outerTrace, 0, combined, causeTrace.length, outerTrace.length); 231 cause.setStackTrace(combined); 232 } 233 if (cause instanceof Exception) { 234 throw (Exception) cause; 235 } 236 if (cause instanceof Error) { 237 throw (Error) cause; 238 } 239 // The cause is a weird kind of Throwable, so throw the outer exception 240 throw exception; 241 } 242 } 243