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 * @author Kevin Bourrillion 35 * @author Ben Yu 36 * @since 1.0 37 */ 38 public final class Throwables { Throwables()39 private Throwables() {} 40 41 /** 42 * Propagates {@code throwable} exactly as-is, if and only if it is an 43 * instance of {@code declaredType}. Example usage: 44 * <pre> 45 * try { 46 * someMethodThatCouldThrowAnything(); 47 * } catch (IKnowWhatToDoWithThisException e) { 48 * handle(e); 49 * } catch (Throwable t) { 50 * Throwables.propagateIfInstanceOf(t, IOException.class); 51 * Throwables.propagateIfInstanceOf(t, SQLException.class); 52 * throw Throwables.propagate(t); 53 * } 54 * </pre> 55 */ propagateIfInstanceOf( @ullable Throwable throwable, Class<X> declaredType)56 public static <X extends Throwable> void propagateIfInstanceOf( 57 @Nullable Throwable throwable, Class<X> declaredType) throws X { 58 // Check for null is needed to avoid frequent JNI calls to isInstance(). 59 if (throwable != null && declaredType.isInstance(throwable)) { 60 throw declaredType.cast(throwable); 61 } 62 } 63 64 /** 65 * Propagates {@code throwable} exactly as-is, if and only if it is an 66 * instance of {@link RuntimeException} or {@link Error}. Example usage: 67 * <pre> 68 * try { 69 * someMethodThatCouldThrowAnything(); 70 * } catch (IKnowWhatToDoWithThisException e) { 71 * handle(e); 72 * } catch (Throwable t) { 73 * Throwables.propagateIfPossible(t); 74 * throw new RuntimeException("unexpected", t); 75 * } 76 * </pre> 77 */ propagateIfPossible(@ullable Throwable throwable)78 public static void propagateIfPossible(@Nullable Throwable throwable) { 79 propagateIfInstanceOf(throwable, Error.class); 80 propagateIfInstanceOf(throwable, RuntimeException.class); 81 } 82 83 /** 84 * Propagates {@code throwable} exactly as-is, if and only if it is an 85 * instance of {@link RuntimeException}, {@link Error}, or 86 * {@code declaredType}. Example usage: 87 * <pre> 88 * try { 89 * someMethodThatCouldThrowAnything(); 90 * } catch (IKnowWhatToDoWithThisException e) { 91 * handle(e); 92 * } catch (Throwable t) { 93 * Throwables.propagateIfPossible(t, OtherException.class); 94 * throw new RuntimeException("unexpected", t); 95 * } 96 * </pre> 97 * 98 * @param throwable the Throwable to possibly propagate 99 * @param declaredType the single checked exception type declared by the 100 * calling method 101 */ propagateIfPossible( @ullable Throwable throwable, Class<X> declaredType)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> propagateIfPossible(@ullable Throwable throwable, Class<X1> declaredType1, Class<X2> declaredType2)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 */ propagate(Throwable throwable)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 */ getRootCause(Throwable throwable)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 getCausalChain(Throwable throwable)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 */ getStackTraceAsString(Throwable throwable)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