1 /* 2 * Copyright (C) 2007 The Guava Authors 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 static com.google.common.base.Preconditions.checkNotNull; 20 21 import com.google.common.annotations.Beta; 22 23 import java.io.PrintWriter; 24 import java.io.StringWriter; 25 import java.util.ArrayList; 26 import java.util.Collections; 27 import java.util.List; 28 29 import javax.annotation.Nullable; 30 31 /** 32 * Static utility methods pertaining to instances of {@link Throwable}. 33 * 34 * <p>See the Guava User Guide entry on <a href= 35 * "http://code.google.com/p/guava-libraries/wiki/ThrowablesExplained"> 36 * Throwables</a>. 37 * 38 * @author Kevin Bourrillion 39 * @author Ben Yu 40 * @since 1.0 41 */ 42 public final class Throwables { Throwables()43 private Throwables() {} 44 45 /** 46 * Propagates {@code throwable} exactly as-is, if and only if it is an 47 * instance of {@code declaredType}. Example usage: 48 * <pre> 49 * try { 50 * someMethodThatCouldThrowAnything(); 51 * } catch (IKnowWhatToDoWithThisException e) { 52 * handle(e); 53 * } catch (Throwable t) { 54 * Throwables.propagateIfInstanceOf(t, IOException.class); 55 * Throwables.propagateIfInstanceOf(t, SQLException.class); 56 * throw Throwables.propagate(t); 57 * } 58 * </pre> 59 */ propagateIfInstanceOf( @ullable Throwable throwable, Class<X> declaredType)60 public static <X extends Throwable> void propagateIfInstanceOf( 61 @Nullable Throwable throwable, Class<X> declaredType) throws X { 62 // Check for null is needed to avoid frequent JNI calls to isInstance(). 63 if (throwable != null && declaredType.isInstance(throwable)) { 64 throw declaredType.cast(throwable); 65 } 66 } 67 68 /** 69 * Propagates {@code throwable} exactly as-is, if and only if it is an 70 * instance of {@link RuntimeException} or {@link Error}. Example usage: 71 * <pre> 72 * try { 73 * someMethodThatCouldThrowAnything(); 74 * } catch (IKnowWhatToDoWithThisException e) { 75 * handle(e); 76 * } catch (Throwable t) { 77 * Throwables.propagateIfPossible(t); 78 * throw new RuntimeException("unexpected", t); 79 * } 80 * </pre> 81 */ propagateIfPossible(@ullable Throwable throwable)82 public static void propagateIfPossible(@Nullable Throwable throwable) { 83 propagateIfInstanceOf(throwable, Error.class); 84 propagateIfInstanceOf(throwable, RuntimeException.class); 85 } 86 87 /** 88 * Propagates {@code throwable} exactly as-is, if and only if it is an 89 * instance of {@link RuntimeException}, {@link Error}, or 90 * {@code declaredType}. Example usage: 91 * <pre> 92 * try { 93 * someMethodThatCouldThrowAnything(); 94 * } catch (IKnowWhatToDoWithThisException e) { 95 * handle(e); 96 * } catch (Throwable t) { 97 * Throwables.propagateIfPossible(t, OtherException.class); 98 * throw new RuntimeException("unexpected", t); 99 * } 100 * </pre> 101 * 102 * @param throwable the Throwable to possibly propagate 103 * @param declaredType the single checked exception type declared by the 104 * calling method 105 */ propagateIfPossible( @ullable Throwable throwable, Class<X> declaredType)106 public static <X extends Throwable> void propagateIfPossible( 107 @Nullable Throwable throwable, Class<X> declaredType) throws X { 108 propagateIfInstanceOf(throwable, declaredType); 109 propagateIfPossible(throwable); 110 } 111 112 /** 113 * Propagates {@code throwable} exactly as-is, if and only if it is an 114 * instance of {@link RuntimeException}, {@link Error}, {@code declaredType1}, 115 * or {@code declaredType2}. In the unlikely case that you have three or more 116 * declared checked exception types, you can handle them all by invoking these 117 * methods repeatedly. See usage example in {@link 118 * #propagateIfPossible(Throwable, Class)}. 119 * 120 * @param throwable the Throwable to possibly propagate 121 * @param declaredType1 any checked exception type declared by the calling 122 * method 123 * @param declaredType2 any other checked exception type declared by the 124 * calling method 125 */ 126 public static <X1 extends Throwable, X2 extends Throwable> propagateIfPossible(@ullable Throwable throwable, Class<X1> declaredType1, Class<X2> declaredType2)127 void propagateIfPossible(@Nullable Throwable throwable, 128 Class<X1> declaredType1, Class<X2> declaredType2) throws X1, X2 { 129 checkNotNull(declaredType2); 130 propagateIfInstanceOf(throwable, declaredType1); 131 propagateIfPossible(throwable, declaredType2); 132 } 133 134 /** 135 * Propagates {@code throwable} as-is if it is an instance of 136 * {@link RuntimeException} or {@link Error}, or else as a last resort, wraps 137 * it in a {@code RuntimeException} then propagates. 138 * <p> 139 * This method always throws an exception. The {@code RuntimeException} return 140 * type is only for client code to make Java type system happy in case a 141 * return value is required by the enclosing method. Example usage: 142 * <pre> 143 * T doSomething() { 144 * try { 145 * return someMethodThatCouldThrowAnything(); 146 * } catch (IKnowWhatToDoWithThisException e) { 147 * return handle(e); 148 * } catch (Throwable t) { 149 * throw Throwables.propagate(t); 150 * } 151 * } 152 * </pre> 153 * 154 * @param throwable the Throwable to propagate 155 * @return nothing will ever be returned; this return type is only for your 156 * convenience, as illustrated in the example above 157 */ propagate(Throwable throwable)158 public static RuntimeException propagate(Throwable throwable) { 159 propagateIfPossible(checkNotNull(throwable)); 160 throw new RuntimeException(throwable); 161 } 162 163 /** 164 * Returns the innermost cause of {@code throwable}. The first throwable in a 165 * chain provides context from when the error or exception was initially 166 * detected. Example usage: 167 * <pre> 168 * assertEquals("Unable to assign a customer id", 169 * Throwables.getRootCause(e).getMessage()); 170 * </pre> 171 */ getRootCause(Throwable throwable)172 public static Throwable getRootCause(Throwable throwable) { 173 Throwable cause; 174 while ((cause = throwable.getCause()) != null) { 175 throwable = cause; 176 } 177 return throwable; 178 } 179 180 /** 181 * Gets a {@code Throwable} cause chain as a list. The first entry in the 182 * list will be {@code throwable} followed by its cause hierarchy. Note 183 * that this is a snapshot of the cause chain and will not reflect 184 * any subsequent changes to the cause chain. 185 * 186 * <p>Here's an example of how it can be used to find specific types 187 * of exceptions in the cause chain: 188 * 189 * <pre> 190 * Iterables.filter(Throwables.getCausalChain(e), IOException.class)); 191 * </pre> 192 * 193 * @param throwable the non-null {@code Throwable} to extract causes from 194 * @return an unmodifiable list containing the cause chain starting with 195 * {@code throwable} 196 */ 197 @Beta // TODO(kevinb): decide best return type getCausalChain(Throwable throwable)198 public static List<Throwable> getCausalChain(Throwable throwable) { 199 checkNotNull(throwable); 200 List<Throwable> causes = new ArrayList<Throwable>(4); 201 while (throwable != null) { 202 causes.add(throwable); 203 throwable = throwable.getCause(); 204 } 205 return Collections.unmodifiableList(causes); 206 } 207 208 /** 209 * Returns a string containing the result of 210 * {@link Throwable#toString() toString()}, followed by the full, recursive 211 * stack trace of {@code throwable}. Note that you probably should not be 212 * parsing the resulting string; if you need programmatic access to the stack 213 * frames, you can call {@link Throwable#getStackTrace()}. 214 */ getStackTraceAsString(Throwable throwable)215 public static String getStackTraceAsString(Throwable throwable) { 216 StringWriter stringWriter = new StringWriter(); 217 throwable.printStackTrace(new PrintWriter(stringWriter)); 218 return stringWriter.toString(); 219 } 220 } 221