001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 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; 026import java.nio.channels.ClosedByInterruptException; 027import java.util.ArrayList; 028import java.util.List; 029 030/** 031 * Provides utility methods for manipulating and examining exceptions in a generic way. 032 * 033 * @author DM 034 */ 035public final class ExceptionUtils { 036 037 // This is an utility class. 038 private ExceptionUtils() { 039 } 040 041 /** 042 * Gets the root cause of the given <code>Throwable</code>. 043 * <p> 044 * This method walks through the exception chain up to the root of the exceptions tree using 045 * {@link Throwable#getCause()}, and returns the root exception. 046 * 047 * @param throwable the throwable to get the root cause for, may be null - this is to avoid throwing other 048 * un-interesting exception when handling a business-important exception 049 * @return the root cause of the <code>Throwable</code>, <code>null</code> if none found or null throwable input 050 */ 051 public static Throwable getRootCause(Throwable throwable) { 052 // This code is taken from Apache commons utils org.apache.commons.lang3.exception.ExceptionUtils 053 final List<Throwable> list = getThrowableList(throwable); 054 return list.size() < 2 ? null : (Throwable) list.get(list.size() - 1); 055 } 056 057 public static List<Throwable> getThrowableList(Throwable throwable) { 058 final List<Throwable> list = new ArrayList<>(); 059 while (throwable != null && list.contains(throwable) == false) { 060 list.add(throwable); 061 throwable = getCause(throwable); 062 } 063 return list; 064 } 065 066 protected static Throwable getCause(Throwable throwable) { 067 if (throwable != null) { 068 return throwable.getCause(); 069 } 070 return null; 071 } 072 073 /** 074 * Throws a {@link RuntimeException} if the passed exception is an {@link InterruptedException} or 075 * {@link InterruptedIOException}, or if the current thread is marked interrupted. 076 * 077 * @param e the exception to check 078 * @throws RuntimeException if there was an interrupt 079 * @since 7.1 080 */ 081 public static void checkInterrupt(Exception e) { 082 if (isInterrupted(e)) { 083 // reset interrupted status 084 Thread.currentThread().interrupt(); 085 // continue interrupt 086 throw new RuntimeException(e); 087 } 088 if (Thread.currentThread().isInterrupted()) { 089 // if an InterruptedException occurred earlier but was wrapped, 090 // continue interrupt 091 if (e instanceof RuntimeException) { 092 throw (RuntimeException) e; 093 } else { 094 throw new RuntimeException(e); 095 } 096 } 097 } 098 099 /** 100 * Unwraps the exception if it's an {@link InvocationTargetException}. 101 * <p> 102 * Also deals with interrupts by immediately throwing an exception. 103 * 104 * @param e the exception to unwrap 105 * @return the unwrapped exception 106 * @throws RuntimeException if there was an interrupt 107 * @since 7.1 108 */ 109 public static Exception unwrapInvoke(Exception e) { 110 if (e instanceof InvocationTargetException) { 111 Throwable cause = e.getCause(); 112 if (cause instanceof Error) { 113 // Error, throw immediately 114 throw (Error) cause; 115 } else if (cause instanceof Exception) { 116 e = (Exception) cause; 117 } else { 118 // Throwable direct subclass?! 119 e = new RuntimeException(cause); 120 } 121 } 122 checkInterrupt(e); 123 return e; 124 } 125 126 /** 127 * Wraps the exception into a {@link RuntimeException}, if needed, for re-throw. 128 * <p> 129 * Deals with {@link InvocationTargetException}, {@link InterruptedException} and {@link InterruptedIOException}. 130 * 131 * @param e the exception to wrap 132 * @return a {@link RuntimeException} 133 * @throws RuntimeException if there was an interrupt 134 * @since 7.1 135 */ 136 public static RuntimeException runtimeException(Exception e) { 137 e = unwrapInvoke(e); 138 if (e instanceof RuntimeException) { 139 return (RuntimeException) e; 140 } else { 141 return new RuntimeException(e); 142 } 143 } 144 145 /** 146 * DON'T USE THIS METHOD - INTERNAL API. 147 * <p /> 148 * This helper method is used to detect if an exception is caused by an {@link InterruptedException} or something 149 * equivalent (for example {@link ClosedByInterruptException}. This is a temporary method, we should rely on the 150 * {@link Thread#isInterrupted()} status in the future. 151 * 152 * @since 9.3 153 */ 154 public static boolean hasInterruptedCause(Throwable e) { 155 Throwable t = e; 156 while (t != null) { 157 if (isInterrupted(t)) { 158 return true; 159 } 160 t = t.getCause(); 161 } 162 return false; 163 } 164 165 /** 166 * @since 9.3 167 */ 168 public static boolean isInterrupted(Throwable t) { 169 return t instanceof InterruptedException || t instanceof InterruptedIOException 170 || t instanceof ClosedByInterruptException; 171 } 172 173}