📄 exceptionutils.java
字号:
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowledgement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgement may appear in the software itself,
* if and wherever such third-party acknowledgements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package net.sf.hibernate.exception;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import net.sf.hibernate.util.ArrayHelper;
/**
* <p>Provides utilities for manipulating and examining
* <code>Throwable</code> objects.</p>
*
* @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
* @author Dmitri Plotnikov
* @author Stephen Colebourne
* @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a>
* @author Pete Gieser
* @since 1.0
* @version $Id: ExceptionUtils.java,v 1.1.2.4 2003/10/28 22:10:02 oneovthafew Exp $
*/
public final class ExceptionUtils {
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
/**
* <p>Used when printing stack frames to denote the start of a
* wrapped exception.</p>
*
* <p>Package private for accessibility by test suite.</p>
*/
static final String WRAPPED_MARKER = " [wrapped] ";
/**
* <p>The names of methods commonly used to access a wrapped exception.</p>
*/
private static final String[] CAUSE_METHOD_NAMES = {
"getCause",
"getNextException",
"getTargetException",
"getException",
"getSourceException",
"getRootCause",
"getCausedByException",
"getNested"
};
/**
* <p>The Method object for JDK1.4 getCause.</p>
*/
private static final Method THROWABLE_CAUSE_METHOD;
static {
Method getCauseMethod;
try {
getCauseMethod = Throwable.class.getMethod("getCause", null);
}
catch (Exception e) {
getCauseMethod = null;
}
THROWABLE_CAUSE_METHOD = getCauseMethod;
}
private ExceptionUtils() {
}
//-----------------------------------------------------------------------
/**
* <p>Adds to the list of method names used in the search for <code>Throwable</code>
* objects.</p>
*
* @param methodName the methodName to add to the list, <code>null</code>
* and empty strings are ignored
* @since 2.0
*/
/*public static void addCauseMethodName(String methodName) {
if ( StringHelper.isNotEmpty(methodName) ) {
List list = new ArrayList( Arrays.asList(CAUSE_METHOD_NAMES );
list.add(methodName);
CAUSE_METHOD_NAMES = (String[]) list.toArray(new String[list.size()]);
}
}*/
/**
* <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
*
* <p>The method searches for methods with specific names that return a
* <code>Throwable</code> object. This will pick up most wrapping exceptions,
* including those from JDK 1.4, and
* {@link org.apache.commons.lang.exception.NestableException NestableException}.
* The method names can be added to using {@link #addCauseMethodName(String)}.</p>
*
* <p>The default list searched for are:</p>
* <ul>
* <li><code>getCause()</code></li>
* <li><code>getNextException()</code></li>
* <li><code>getTargetException()</code></li>
* <li><code>getException()</code></li>
* <li><code>getSourceException()</code></li>
* <li><code>getRootCause()</code></li>
* <li><code>getCausedByException()</code></li>
* <li><code>getNested()</code></li>
* </ul>
*
* <p>In the absence of any such method, the object is inspected for a
* <code>detail</code> field assignable to a <code>Throwable</code>.</p>
*
* <p>If none of the above is found, returns <code>null</code>.</p>
*
* @param throwable the throwable to introspect for a cause, may be null
* @return the cause of the <code>Throwable</code>,
* <code>null</code> if none found or null throwable input
*/
public static Throwable getCause(Throwable throwable) {
return getCause(throwable, CAUSE_METHOD_NAMES);
}
/**
* <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
*
* <ol>
* <li>Try known exception types.</li>
* <li>Try the supplied array of method names.</li>
* <li>Try the field 'detail'.</li>
* </ol>
*
* <p>A <code>null</code> set of method names means use the default set.
* A <code>null</code> in the set of method names will be ignored.</p>
*
* @param throwable the throwable to introspect for a cause, may be null
* @param methodNames the method names, null treated as default set
* @return the cause of the <code>Throwable</code>,
* <code>null</code> if none found or null throwable input
*/
public static Throwable getCause(Throwable throwable, String[] methodNames) {
if (throwable == null) {
return null;
}
Throwable cause = getCauseUsingWellKnownTypes(throwable);
if (cause == null) {
if (methodNames == null) {
methodNames = CAUSE_METHOD_NAMES;
}
for (int i = 0; i < methodNames.length; i++) {
String methodName = methodNames[i];
if (methodName != null) {
cause = getCauseUsingMethodName(throwable, methodName);
if (cause != null) {
break;
}
}
}
if (cause == null) {
cause = getCauseUsingFieldName(throwable, "detail");
}
}
return cause;
}
/**
* <p>Introspects the <code>Throwable</code> to obtain the root cause.</p>
*
* <p>This method walks through the exception chain to the last element,
* "root" of the tree, using {@link #getCause(Throwable)}, and
* returns that exception.</p>
*
* @param throwable the throwable to get the root cause for, may be null
* @return the root cause of the <code>Throwable</code>,
* <code>null</code> if none found or null throwable input
*/
public static Throwable getRootCause(Throwable throwable) {
Throwable cause = getCause(throwable);
if (cause != null) {
throwable = cause;
while ( ( throwable = getCause(throwable) ) != null ) {
cause = throwable;
}
}
return cause;
}
/**
* <p>Finds a <code>Throwable</code> for known types.</p>
*
* <p>Uses <code>instanceof</code> checks to examine the exception,
* looking for well known types which could contain chained or
* wrapped exceptions.</p>
*
* @param throwable the exception to examine
* @return the wrapped exception, or <code>null</code> if not found
*/
private static Throwable getCauseUsingWellKnownTypes(Throwable throwable) {
if (throwable instanceof Nestable) {
return ( (Nestable) throwable ).getCause();
}
else if (throwable instanceof SQLException) {
return ( (SQLException) throwable ).getNextException();
}
else if (throwable instanceof InvocationTargetException) {
return ( (InvocationTargetException) throwable ).getTargetException();
}
else {
return null;
}
}
/**
* <p>Finds a <code>Throwable</code> by method name.</p>
*
* @param throwable the exception to examine
* @param methodName the name of the method to find and invoke
* @return the wrapped exception, or <code>null</code> if not found
*/
private static Throwable getCauseUsingMethodName(Throwable throwable, String methodName) {
Method method = null;
try {
method = throwable.getClass().getMethod(methodName, null);
}
catch (NoSuchMethodException ignored) {}
catch (SecurityException ignored) {}
if ( method != null && Throwable.class.isAssignableFrom( method.getReturnType() ) ) {
try {
return (Throwable) method.invoke(throwable, ArrayHelper.EMPTY_OBJECT_ARRAY);
}
catch (IllegalAccessException ignored) {}
catch (IllegalArgumentException ignored) {}
catch (InvocationTargetException ignored) {}
}
return null;
}
/**
* <p>Finds a <code>Throwable</code> by field name.</p>
*
* @param throwable the exception to examine
* @param fieldName the name of the attribute to examine
* @return the wrapped exception, or <code>null</code> if not found
*/
private static Throwable getCauseUsingFieldName(Throwable throwable, String fieldName) {
Field field = null;
try {
field = throwable.getClass().getField(fieldName);
}
catch (NoSuchFieldException ignored) {}
catch (SecurityException ignored) {}
if ( field != null && Throwable.class.isAssignableFrom( field.getType() ) ) {
try {
return (Throwable) field.get(throwable);
}
catch (IllegalAccessException ignored) {}
catch (IllegalArgumentException ignored) {}
}
return null;
}
//-----------------------------------------------------------------------
/**
* <p>Checks if the Throwable class has a <code>getCause</code> method.</p>
*
* <p>This is true for JDK 1.4 and above.</p>
*
* @return true if Throwable is nestable
* @since 2.0
*/
public static boolean isThrowableNested() {
return (THROWABLE_CAUSE_METHOD != null);
}
/**
* <p>Checks whether this <code>Throwable</code> class can store a cause.</p>
*
* <p>This method does <b>not</b> check whether it actually does store a cause.<p>
*
* @param throwable the <code>Throwable</code> to examine, may be null
* @return boolean <code>true</code> if nested otherwise <code>false</code>
* @since 2.0
*/
public static boolean isNestedThrowable(Throwable throwable) {
if (throwable == null) {
return false;
}
if (throwable instanceof Nestable) {
return true;
}
else if (throwable instanceof SQLException) {
return true;
}
else if (throwable instanceof InvocationTargetException) {
return true;
}
else if ( isThrowableNested() ) {
return true;
}
Class cls = throwable.getClass();
for (int i = 0, isize = CAUSE_METHOD_NAMES.length; i < isize; i++) {
try {
Method method = cls.getMethod(CAUSE_METHOD_NAMES[i], null);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -