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 018import org.assertj.core.internal.cglib.proxy.MethodInterceptor; 019 020import com.googlecode.catchexception.throwable.internal.DelegatingInterceptor; 021import com.googlecode.catchexception.throwable.internal.InterfaceOnlyProxyFactory; 022import com.googlecode.catchexception.throwable.internal.SubclassProxyFactory; 023import com.googlecode.catchexception.throwable.internal.ThrowableHolder; 024import com.googlecode.catchexception.throwable.internal.ThrowableProcessingInterceptor; 025 026/** 027 * 028 * @author rwoo 029 * @since 1.2.0 030 * 031 */ 032public class CatchThrowable { 033 034 /** 035 * Returns the throwable caught during the last call on the proxied object in the current thread. 036 * 037 * @param <E> 038 * This type parameter makes some type casts redundant. 039 * @return Returns the throwable caught during the last call on the proxied object in the current thread - if the 040 * call was made through a proxy that has been created via {@link #verifyThrowable(Object, Class) 041 * verifyThrowable()} or {@link #catchThrowable(Object, Class) catchThrowable()}. Returns null the proxy has 042 * not caught an throwable. Returns null if the caught throwable belongs to a class that is no longer 043 * {@link ClassLoader loaded}. 044 */ 045 public static <E extends Throwable> E caughtThrowable() { 046 return ThrowableHolder.<E> get(); 047 } 048 049 /** 050 * Use it to verify that an throwable is thrown and to get access to the thrown throwable (for further 051 * verifications). 052 * <p> 053 * The following example verifies that obj.doX() throws a Throwable: 054 * <code><pre class="prettyprint lang-java">verifyThrowable(obj).doX(); // catch and verify 055assert "foobar".equals(caughtThrowable().getMessage()); // further analysis 056</pre></code> 057 * <p> 058 * If <code>doX()</code> does not throw a <code>Throwable</code>, then a {@link ThrowableNotThrownAssertionError} is 059 * thrown. Otherwise the thrown throwable can be retrieved via {@link #caughtThrowable()}. 060 * <p> 061 * 062 * @param <T> 063 * The type of the given <code>obj</code>. 064 * 065 * @param obj 066 * The instance that shall be proxied. Must not be <code>null</code>. 067 * @return Returns an object that verifies that each invocation on the underlying object throws an throwable. 068 */ 069 public static <T> T verifyThrowable(T obj) { 070 return verifyThrowable(obj, Throwable.class); 071 } 072 073 /** 074 * Use it to verify that an throwable of specific type is thrown and to get access to the thrown throwable (for 075 * further verifications). 076 * <p> 077 * The following example verifies that obj.doX() throws a MyThrowable: 078 * <code><pre class="prettyprint lang-java">verifyThrowable(obj, MyThrowable.class).doX(); // catch and verify 079assert "foobar".equals(caughtThrowable().getMessage()); // further analysis 080</pre></code> 081 * <p> 082 * If <code>doX()</code> does not throw a <code>MyThrowable</code>, then a {@link ThrowableNotThrownAssertionError} 083 * is thrown. Otherwise the thrown throwable can be retrieved via {@link #caughtThrowable()}. 084 * <p> 085 * 086 * @param <T> 087 * The type of the given <code>obj</code>. 088 * 089 * @param <E> 090 * The type of the throwable that shall be caught. 091 * @param obj 092 * The instance that shall be proxied. Must not be <code>null</code>. 093 * @param clazz 094 * The type of the throwable that shall be thrown by the underlying object. Must not be <code>null</code> 095 * . 096 * @return Returns an object that verifies that each invocation on the underlying object throws an throwable of the 097 * given type. 098 */ 099 public static <T, E extends Throwable> T verifyThrowable(T obj, Class<E> clazz) { 100 101 return processThrowable(obj, clazz, true); 102 } 103 104 /** 105 * Use it to catch an throwable and to get access to the thrown throwable (for further verifications). 106 * <p> 107 * In the following example you catch throwables that are thrown by obj.doX(): 108 * <code><pre class="prettyprint lang-java">catchThrowable(obj).doX(); // catch 109if (caughtThrowable() != null) { 110 assert "foobar".equals(caughtThrowable().getMessage()); // further analysis 111}</pre></code> If <code>doX()</code> 112 * throws a throwable, then {@link #caughtThrowable()} will return the caught throwable. If <code>doX()</code> does 113 * not throw a throwable, then {@link #caughtThrowable()} will return <code>null</code>. 114 * <p> 115 * 116 * @param <T> 117 * The type of the given <code>obj</code>. 118 * 119 * @param obj 120 * The instance that shall be proxied. Must not be <code>null</code>. 121 * @return Returns a proxy for the given object. The proxy catches throwables of the given type when a method on the 122 * proxy is called. 123 */ 124 public static <T> T catchThrowable(T obj) { 125 126 return processThrowable(obj, Throwable.class, false); 127 } 128 129 /** 130 * Use it to catch an throwable of a specific type and to get access to the thrown throwable (for further 131 * verifications). 132 * <p> 133 * In the following example you catch throwables of type MyThrowable that are thrown by obj.doX(): 134 * <code><pre class="prettyprint lang-java">catchThrowable(obj, MyThrowable.class).doX(); // catch 135if (caughtThrowable() != null) { 136 assert "foobar".equals(caughtThrowable().getMessage()); // further analysis 137}</pre></code> If <code>doX()</code> 138 * throws a <code>MyThrowable</code>, then {@link #caughtThrowable()} will return the caught throwable. If 139 * <code>doX()</code> does not throw a <code>MyThrowable</code>, then {@link #caughtThrowable()} will return 140 * <code>null</code>. If <code>doX()</code> throws an throwable of another type, i.e. not a subclass but another 141 * class, then this throwable is not thrown and {@link #caughtThrowable()} will return <code>null</code>. 142 * <p> 143 * 144 * @param <T> 145 * The type of the given <code>obj</code>. 146 * 147 * @param <E> 148 * The type of the throwable that shall be caught. 149 * @param obj 150 * The instance that shall be proxied. Must not be <code>null</code>. 151 * @param clazz 152 * The type of the throwable that shall be caught. Must not be <code>null</code>. 153 * @return Returns a proxy for the given object. The proxy catches throwables of the given type when a method on the 154 * proxy is called. 155 */ 156 public static <T, E extends Throwable> T catchThrowable(T obj, Class<E> clazz) { 157 158 return processThrowable(obj, clazz, false); 159 } 160 161 /** 162 * Creates a proxy that processes throwables thrown by the underlying object. 163 * <p> 164 * Delegates to {@link SubclassProxyFactory#createProxy(Class, MethodInterceptor)} which itself might delegate to 165 * {@link InterfaceOnlyProxyFactory#createProxy(Class, MethodInterceptor)}. 166 */ 167 @SuppressWarnings("javadoc") 168 private static <T, E extends Throwable> T processThrowable(T obj, Class<E> throwableClazz, boolean assertThrowable) { 169 170 if (obj == null) { 171 throw new IllegalArgumentException("obj must not be null"); 172 } 173 174 return new SubclassProxyFactory().<T> createProxy(obj.getClass(), new ThrowableProcessingInterceptor<E>(obj, 175 throwableClazz, assertThrowable)); 176 177 } 178 179 /** 180 * Returns a proxy that implements all interfaces of the underlying object. 181 * 182 * @param <T> 183 * must be an interface the object implements 184 * @param obj 185 * the object that created proxy will delegate all calls to 186 * @return Returns a proxy that implements all interfaces of the underlying object and delegates all calls to that 187 * underlying object. 188 */ 189 public static <T> T interfaces(T obj) { 190 191 if (obj == null) { 192 throw new IllegalArgumentException("obj must not be null"); 193 } 194 195 return new InterfaceOnlyProxyFactory().<T> createProxy(obj.getClass(), new DelegatingInterceptor(obj)); 196 } 197 198 /** 199 * Sets the {@link #caughtThrowable() caught throwable} to null. This does not affect throwables saved at threads 200 * other than the current one. 201 * <p> 202 * Actually you probably never need to call this method because each method call on a proxied object in the current 203 * thread resets the caught throwable. But if you want to improve test isolation or if you want to 'clean up' after 204 * testing (to avoid memory leaks), call the method before or after testing. 205 */ 206 public static void resetCaughtThrowable() { 207 ThrowableHolder.set(null); 208 } 209 210}