001 /* 002 * Copyright (C) 2007 Google Inc. 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 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 if (declaredType.isInstance(throwable)) { 059 throw declaredType.cast(throwable); 060 } 061 } 062 063 /** 064 * Propagates {@code throwable} exactly as-is, if and only if it is an 065 * instance of {@link RuntimeException} or {@link Error}. Example usage: 066 * <pre> 067 * try { 068 * someMethodThatCouldThrowAnything(); 069 * } catch (IKnowWhatToDoWithThisException e) { 070 * handle(e); 071 * } catch (Throwable t) { 072 * Throwables.propagateIfPossible(t); 073 * throw new RuntimeException("unexpected", t); 074 * } 075 * </pre> 076 */ 077 public static void propagateIfPossible(@Nullable Throwable throwable) { 078 propagateIfInstanceOf(throwable, Error.class); 079 propagateIfInstanceOf(throwable, RuntimeException.class); 080 } 081 082 /** 083 * Propagates {@code throwable} exactly as-is, if and only if it is an 084 * instance of {@link RuntimeException}, {@link Error}, or 085 * {@code declaredType}. Example usage: 086 * <pre> 087 * try { 088 * someMethodThatCouldThrowAnything(); 089 * } catch (IKnowWhatToDoWithThisException e) { 090 * handle(e); 091 * } catch (Throwable t) { 092 * Throwables.propagateIfPossible(t, OtherException.class); 093 * throw new RuntimeException("unexpected", t); 094 * } 095 * </pre> 096 * 097 * @param throwable the Throwable to possibly propagate 098 * @param declaredType the single checked exception type declared by the 099 * calling method 100 */ 101 public static <X extends Throwable> void propagateIfPossible( 102 @Nullable Throwable throwable, Class<X> declaredType) throws X { 103 propagateIfInstanceOf(throwable, declaredType); 104 propagateIfPossible(throwable); 105 } 106 107 /** 108 * Propagates {@code throwable} exactly as-is, if and only if it is an 109 * instance of {@link RuntimeException}, {@link Error}, {@code declaredType1}, 110 * or {@code declaredType2}. In the unlikely case that you have three or more 111 * declared checked exception types, you can handle them all by invoking these 112 * methods repeatedly. See usage example in {@link 113 * #propagateIfPossible(Throwable, Class)}. 114 * 115 * @param throwable the Throwable to possibly propagate 116 * @param declaredType1 any checked exception type declared by the calling 117 * method 118 * @param declaredType2 any other checked exception type declared by the 119 * calling method 120 */ 121 public static <X1 extends Throwable, X2 extends Throwable> 122 void propagateIfPossible(@Nullable Throwable throwable, 123 Class<X1> declaredType1, Class<X2> declaredType2) throws X1, X2 { 124 checkNotNull(declaredType2); 125 propagateIfInstanceOf(throwable, declaredType1); 126 propagateIfPossible(throwable, declaredType2); 127 } 128 129 /** 130 * Propagates {@code throwable} as-is if it is an instance of 131 * {@link RuntimeException} or {@link Error}, or else as a last resort, wraps 132 * it in a {@code RuntimeException} then propagates. 133 * <p> 134 * This method always throws an exception. The {@code RuntimeException} return 135 * type is only for client code to make Java type system happy in case a 136 * return value is required by the enclosing method. Example usage: 137 * <pre> 138 * T doSomething() { 139 * try { 140 * return someMethodThatCouldThrowAnything(); 141 * } catch (IKnowWhatToDoWithThisException e) { 142 * return handle(e); 143 * } catch (Throwable t) { 144 * throw Throwables.propagate(t); 145 * } 146 * } 147 * </pre> 148 * 149 * @param throwable the Throwable to propagate 150 * @return nothing will ever be returned; this return type is only for your 151 * convenience, as illustrated in the example above 152 */ 153 public static RuntimeException propagate(Throwable throwable) { 154 propagateIfPossible(checkNotNull(throwable)); 155 throw new RuntimeException(throwable); 156 } 157 158 /** 159 * Returns the innermost cause of {@code throwable}. The first throwable in a 160 * chain provides context from when the error or exception was initially 161 * detected. Example usage: 162 * <pre> 163 * assertEquals("Unable to assign a customer id", 164 * Throwables.getRootCause(e).getMessage()); 165 * </pre> 166 */ 167 public static Throwable getRootCause(Throwable throwable) { 168 Throwable cause; 169 while ((cause = throwable.getCause()) != null) { 170 throwable = cause; 171 } 172 return throwable; 173 } 174 175 /** 176 * Gets a {@code Throwable} cause chain as a list. The first entry in the 177 * list will be {@code throwable} followed by its cause hierarchy. Note 178 * that this is a snapshot of the cause chain and will not reflect 179 * any subsequent changes to the cause chain. 180 * 181 * <p>Here's an example of how it can be used to find specific types 182 * of exceptions in the cause chain: 183 * 184 * <pre> 185 * Iterables.filter(Throwables.getCausalChain(e), IOException.class)); 186 * </pre> 187 * 188 * @param throwable the non-null {@code Throwable} to extract causes from 189 * @return an unmodifiable list containing the cause chain starting with 190 * {@code throwable} 191 */ 192 @Beta // TODO: decide best return type 193 public static List<Throwable> getCausalChain(Throwable throwable) { 194 checkNotNull(throwable); 195 List<Throwable> causes = new ArrayList<Throwable>(4); 196 while (throwable != null) { 197 causes.add(throwable); 198 throwable = throwable.getCause(); 199 } 200 return Collections.unmodifiableList(causes); 201 } 202 203 /** 204 * Returns a string containing the result of 205 * {@link Throwable#toString() toString()}, followed by the full, recursive 206 * stack trace of {@code throwable}. Note that you probably should not be 207 * parsing the resulting string; if you need programmatic access to the stack 208 * frames, you can call {@link Throwable#getStackTrace()}. 209 */ 210 public static String getStackTraceAsString(Throwable throwable) { 211 StringWriter stringWriter = new StringWriter(); 212 throwable.printStackTrace(new PrintWriter(stringWriter)); 213 return stringWriter.toString(); 214 } 215 216 /** 217 * Rethrows the cause exception of a given throwable, discarding the original 218 * throwable. Optionally, the stack frames of the cause and the outer 219 * exception are combined and the stack trace of the cause is set to this 220 * combined trace. If there is no cause the original exception is rethrown 221 * unchanged in all cases. 222 * 223 * @param exception the exception from which to extract the cause 224 * @param combineStackTraces if true the stack trace of the cause will be 225 * replaced by the concatenation of the trace from the exception and the 226 * trace from the cause. 227 */ 228 @Beta 229 public static Exception throwCause(Exception exception, 230 boolean combineStackTraces) throws Exception { 231 Throwable cause = exception.getCause(); 232 if (cause == null) { 233 throw exception; 234 } 235 if (combineStackTraces) { 236 StackTraceElement[] causeTrace = cause.getStackTrace(); 237 StackTraceElement[] outerTrace = exception.getStackTrace(); 238 StackTraceElement[] combined = 239 new StackTraceElement[causeTrace.length + outerTrace.length]; 240 System.arraycopy(causeTrace, 0, combined, 0, causeTrace.length); 241 System.arraycopy(outerTrace, 0, combined, 242 causeTrace.length, outerTrace.length); 243 cause.setStackTrace(combined); 244 } 245 if (cause instanceof Exception) { 246 throw (Exception) cause; 247 } 248 if (cause instanceof Error) { 249 throw (Error) cause; 250 } 251 // The cause is a weird kind of Throwable, so throw the outer exception 252 throw exception; 253 } 254 }