001/** 002 * Copyright (C) 2011 rwoo@gmx.de 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 */ 016package com.googlecode.catchexception.throwable; 017 018/** 019 * @author rwoo 020 * @since 1.2.0 021 */ 022public class CatchThrowable { 023 024 /** 025 * Returns the throwable caught during the last call on the proxied object in the current thread. 026 * 027 * @return Returns the throwable caught during the last call on the proxied object in the current thread - if the 028 * call was made through a proxy that has been created via {@link #verifyThrowable(ThrowingCallable, Class)} 029 * verifyThrowable()} or {@link #catchThrowable(ThrowingCallable)}. Returns null the proxy has 030 * not caught an throwable. Returns null if the caught throwable belongs to a class that is no longer 031 * {@link ClassLoader loaded}. 032 * 033 * @param <T> throwable caught during the last call on the proxied object 034 */ 035 public static <T extends Throwable> T caughtThrowable() { 036 return ThrowableHolder.get(); 037 } 038 039 public static <T extends Throwable> T caughtThrowable(Class<T> caughtThrowableType) { 040 return ThrowableHolder.get(); 041 } 042 043 /** 044 * Use it to verify that an throwable is thrown and to get access to the thrown throwable (for further 045 * verifications). 046 * 047 * The following example verifies that obj.doX() throws a Throwable: 048 * <code>verifyThrowable(obj).doX(); // catch and verify 049 * assert "foobar".equals(caughtThrowable().getMessage()); // further analysis 050 * </code> 051 * 052 * If <code>doX()</code> does not throw a <code>Throwable</code>, then a {@link ThrowableNotThrownAssertionError} is 053 * thrown. Otherwise the thrown throwable can be retrieved via {@link #caughtThrowable()}. 054 * 055 * 056 * @param actor The instance that shall be proxied. Must not be <code>null</code>. 057 */ 058 public static void verifyThrowable(ThrowingCallable actor) { 059 verifyThrowable(actor, Throwable.class); 060 } 061 062 /** 063 * Use it to verify that an throwable of specific type is thrown and to get access to the thrown throwable (for 064 * further verifications). 065 * 066 * The following example verifies that obj.doX() throws a MyThrowable: 067 * <code>verifyThrowable(obj, MyThrowable.class).doX(); // catch and verify 068 * assert "foobar".equals(caughtThrowable().getMessage()); // further analysis 069 * </code> 070 * 071 * If <code>doX()</code> does not throw a <code>MyThrowable</code>, then a {@link ThrowableNotThrownAssertionError} 072 * is thrown. Otherwise the thrown throwable can be retrieved via {@link #caughtThrowable()}. 073 * 074 * 075 * @param actor The instance that shall be proxied. Must not be <code>null</code>. 076 * @param clazz The type of the throwable that shall be thrown by the underlying object. Must not be 077 * <code>null</code> 078 */ 079 public static void verifyThrowable(ThrowingCallable actor, Class<? extends Throwable> clazz) { 080 validateArguments(actor, clazz); 081 catchThrowable(actor, clazz, true); 082 } 083 084 /** 085 * Use it to catch an throwable and to get access to the thrown throwable (for further verifications). 086 * 087 * In the following example you catch throwables that are thrown by obj.doX(): 088 * <code>catchThrowable(obj).doX(); // catch 089 * if (caughtThrowable() != null) { 090 * assert "foobar".equals(caughtThrowable().getMessage()); // further analysis 091 * }</code> If <code>doX()</code> 092 * throws a throwable, then {@link #caughtThrowable()} will return the caught throwable. If <code>doX()</code> does 093 * not throw a throwable, then {@link #caughtThrowable()} will return <code>null</code>. 094 * 095 * 096 * @param actor The instance that shall be proxied. Must not be <code>null</code>. 097 */ 098 public static void catchThrowable(ThrowingCallable actor) { 099 validateArguments(actor, Throwable.class); 100 catchThrowable(actor, Throwable.class, false); 101 } 102 103 /** 104 * Use it to catch an throwable of a specific type and to get access to the thrown throwable (for further 105 * verifications). 106 * 107 * In the following example you catch throwables of type MyThrowable that are thrown by obj.doX(): 108 * <code>catchThrowable(obj, MyThrowable.class).doX(); // catch 109 * if (caughtThrowable() != null) { 110 * assert "foobar".equals(caughtThrowable().getMessage()); // further analysis 111 * }</code> If <code>doX()</code> 112 * throws a <code>MyThrowable</code>, then {@link #caughtThrowable()} will return the caught throwable. If 113 * <code>doX()</code> does not throw a <code>MyThrowable</code>, then {@link #caughtThrowable()} will return 114 * <code>null</code>. If <code>doX()</code> throws an throwable of another type, i.e. not a subclass but another 115 * class, then this throwable is not thrown and {@link #caughtThrowable()} will return <code>null</code>. 116 * 117 * 118 * @param actor The instance that shall be proxied. Must not be <code>null</code>. 119 * @param clazz The type of the throwable that shall be caught. Must not be <code>null</code>. 120 */ 121 public static void catchThrowable(ThrowingCallable actor, Class<? extends Throwable> clazz) { 122 validateArguments(actor, clazz); 123 catchThrowable(actor, clazz, false); 124 } 125 126 private static void catchThrowable(ThrowingCallable actor, 127 Class<? extends Throwable> clazz, boolean assertException) { 128 resetCaughtThrowable(); 129 Throwable throwable = ThrowableCaptor.captureThrowable(actor); 130 if (throwable == null) { 131 if (!assertException) { 132 return; 133 } else { 134 throw new ThrowableNotThrownAssertionError(clazz); 135 } 136 } 137 // is the thrown exception of the expected type? 138 if (clazz.isAssignableFrom(throwable.getClass())) { 139 ThrowableHolder.set(throwable); 140 } else { 141 if (assertException) { 142 throw new ThrowableNotThrownAssertionError(clazz, throwable); 143 } else { 144 ExceptionUtil.sneakyThrow(throwable); 145 } 146 } 147 } 148 149 private static void validateArguments(ThrowingCallable actor, Class<? extends Throwable> clazz) { 150 if (actor == null) throw new IllegalArgumentException("obj must not be null"); 151 if (clazz == null) throw new IllegalArgumentException("throwableClazz must not be null"); 152 } 153 154 /** 155 * Sets the {@link #caughtThrowable() caught throwable} to null. This does not affect throwables saved at threads 156 * other than the current one. 157 * 158 * Actually you probably never need to call this method because each method call on a proxied object in the current 159 * thread resets the caught throwable. But if you want to improve test isolation or if you want to 'clean up' after 160 * testing (to avoid memory leaks), call the method before or after testing. 161 */ 162 public static void resetCaughtThrowable() { 163 ThrowableHolder.set(null); 164 } 165 166}