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 */
016 package com.googlecode.catchexception.throwable;
017
018 import org.assertj.core.internal.cglib.proxy.MethodInterceptor;
019
020 import com.googlecode.catchexception.throwable.internal.DelegatingInterceptor;
021 import com.googlecode.catchexception.throwable.internal.InterfaceOnlyProxyFactory;
022 import com.googlecode.catchexception.throwable.internal.SubclassProxyFactory;
023 import com.googlecode.catchexception.throwable.internal.ThrowableHolder;
024 import com.googlecode.catchexception.throwable.internal.ThrowableProcessingInterceptor;
025
026 /**
027 *
028 * @author rwoo
029 * @since 1.2.0
030 *
031 */
032 public 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.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
055 assert "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
079 assert "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
109 if (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
135 if (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 }