📄 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 org.hibernate.exception;
import org.hibernate.util.ArrayHelper;
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;
/**
* <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
* @version $Id: ExceptionUtils.java 4782 2004-11-21 00:11:27Z pgmjsd $
* @since 1.0
*/
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/>
* <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/>
* <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/>
* <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/>
* <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/>
* <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>
* <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/>
* <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/>
* <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/>
* <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/>
* <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/>
* <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();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -