001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and contributors.
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 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 * $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $
020 */
021
022package org.nuxeo.common.utils;
023
024import java.io.InterruptedIOException;
025import java.lang.reflect.InvocationTargetException;
026
027/**
028 * Provides utility methods for manipulating and examining exceptions in a generic way.
029 *
030 * @author DM
031 */
032public final class ExceptionUtils {
033
034    // This is an utility class.
035    private ExceptionUtils() {
036    }
037
038    /**
039     * Gets the root cause of the given <code>Throwable</code>.
040     * <p>
041     * This method walks through the exception chain up to the root of the exceptions tree using
042     * {@link Throwable#getCause()}, and returns the root exception.
043     *
044     * @param throwable the throwable to get the root cause for, may be null - this is to avoid throwing other
045     *            un-interesting exception when handling a business-important exception
046     * @return the root cause of the <code>Throwable</code>, <code>null</code> if none found or null throwable input
047     */
048    public static Throwable getRootCause(Throwable throwable) {
049        Throwable cause = throwable;
050        if (throwable != null) {
051            cause = throwable.getCause();
052            while ((throwable = cause.getCause()) != null) {
053                cause = throwable;
054            }
055        }
056
057        return cause;
058    }
059
060    /**
061     * Throws a {@link RuntimeException} if the passed exception is an {@link InterruptedException} or
062     * {@link InterruptedIOException}, or if the current thread is marked interrupted.
063     *
064     * @param e the exception to check
065     * @throws RuntimeException if there was an interrupt
066     * @since 7.1
067     */
068    public static void checkInterrupt(Exception e) {
069        if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
070            // reset interrupted status
071            Thread.currentThread().interrupt();
072            // continue interrupt
073            throw new RuntimeException(e);
074        }
075        if (Thread.currentThread().isInterrupted()) {
076            // if an InterruptedException occurred earlier but was wrapped,
077            // continue interrupt
078            if (e instanceof RuntimeException) {
079                throw (RuntimeException) e;
080            } else {
081                throw new RuntimeException(e);
082            }
083        }
084    }
085
086    /**
087     * Unwraps the exception if it's an {@link InvocationTargetException}.
088     * <p>
089     * Also deals with interrupts by immediately throwing an exception.
090     *
091     * @param e the exception to unwrap
092     * @return the unwrapped exception
093     * @throws RuntimeException if there was an interrupt
094     * @since 7.1
095     */
096    public static Exception unwrapInvoke(Exception e) {
097        if (e instanceof InvocationTargetException) {
098            Throwable cause = e.getCause();
099            if (cause instanceof Error) {
100                // Error, throw immediately
101                throw (Error) cause;
102            } else if (cause instanceof Exception) {
103                e = (Exception) cause;
104            } else {
105                // Throwable direct subclass?!
106                e = new RuntimeException(cause);
107            }
108        }
109        checkInterrupt(e);
110        return e;
111    }
112
113    /**
114     * Wraps the exception into a {@link RuntimeException}, if needed, for re-throw.
115     * <p>
116     * Deals with {@link InvocationTargetException}, {@link InterruptedException} and {@link InterruptedIOException}.
117     *
118     * @param e the exception to wrap
119     * @return a {@link RuntimeException}
120     * @throws RuntimeException if there was an interrupt
121     * @since 7.1
122     */
123    public static RuntimeException runtimeException(Exception e) {
124        e = unwrapInvoke(e);
125        if (e instanceof RuntimeException) {
126            return (RuntimeException) e;
127        } else {
128            return new RuntimeException(e);
129        }
130    }
131
132}