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 }