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 * <p>See the Guava User Guide entry on <a href= 035 * "http://code.google.com/p/guava-libraries/wiki/ThrowablesExplained"> 036 * Throwables</a>. 037 * 038 * @author Kevin Bourrillion 039 * @author Ben Yu 040 * @since 1.0 041 */ 042 public final class Throwables { 043 private Throwables() {} 044 045 /** 046 * Propagates {@code throwable} exactly as-is, if and only if it is an 047 * instance of {@code declaredType}. Example usage: 048 * <pre> 049 * try { 050 * someMethodThatCouldThrowAnything(); 051 * } catch (IKnowWhatToDoWithThisException e) { 052 * handle(e); 053 * } catch (Throwable t) { 054 * Throwables.propagateIfInstanceOf(t, IOException.class); 055 * Throwables.propagateIfInstanceOf(t, SQLException.class); 056 * throw Throwables.propagate(t); 057 * } 058 * </pre> 059 */ 060 public static <X extends Throwable> void propagateIfInstanceOf( 061 @Nullable Throwable throwable, Class<X> declaredType) throws X { 062 // Check for null is needed to avoid frequent JNI calls to isInstance(). 063 if (throwable != null && declaredType.isInstance(throwable)) { 064 throw declaredType.cast(throwable); 065 } 066 } 067 068 /** 069 * Propagates {@code throwable} exactly as-is, if and only if it is an 070 * instance of {@link RuntimeException} or {@link Error}. Example usage: 071 * <pre> 072 * try { 073 * someMethodThatCouldThrowAnything(); 074 * } catch (IKnowWhatToDoWithThisException e) { 075 * handle(e); 076 * } catch (Throwable t) { 077 * Throwables.propagateIfPossible(t); 078 * throw new RuntimeException("unexpected", t); 079 * } 080 * </pre> 081 */ 082 public static void propagateIfPossible(@Nullable Throwable throwable) { 083 propagateIfInstanceOf(throwable, Error.class); 084 propagateIfInstanceOf(throwable, RuntimeException.class); 085 } 086 087 /** 088 * Propagates {@code throwable} exactly as-is, if and only if it is an 089 * instance of {@link RuntimeException}, {@link Error}, or 090 * {@code declaredType}. Example usage: 091 * <pre> 092 * try { 093 * someMethodThatCouldThrowAnything(); 094 * } catch (IKnowWhatToDoWithThisException e) { 095 * handle(e); 096 * } catch (Throwable t) { 097 * Throwables.propagateIfPossible(t, OtherException.class); 098 * throw new RuntimeException("unexpected", t); 099 * } 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 */ 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> 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 */ 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 */ 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 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 */ 215 public static String getStackTraceAsString(Throwable throwable) { 216 StringWriter stringWriter = new StringWriter(); 217 throwable.printStackTrace(new PrintWriter(stringWriter)); 218 return stringWriter.toString(); 219 } 220 }