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}