All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.lang3.exception.ExceptionUtils Maven / Gradle / Ivy

Go to download

Apache Commons Lang, a package of Java utility classes for the classes that are in java.lang's hierarchy, or are considered to be so standard as to justify existence in java.lang. for jdk 1.5

There is a newer version: 3.4
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.lang3.exception;

import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;

/**
 * 

Provides utilities for manipulating and examining * Throwable objects.

* * @since 1.0 * @version $Id: ExceptionUtils.java 1436770 2013-01-22 07:09:45Z ggregory $ */ public class ExceptionUtils { /** *

Used when printing stack frames to denote the start of a * wrapped exception.

* *

Package private for accessibility by test suite.

*/ static final String WRAPPED_MARKER = " [wrapped] "; /** *

The names of methods commonly used to access a wrapped exception.

*/ // TODO: Remove in Lang 4.0 private static final String[] CAUSE_METHOD_NAMES = { "getCause", "getNextException", "getTargetException", "getException", "getSourceException", "getRootCause", "getCausedByException", "getNested", "getLinkedException", "getNestedException", "getLinkedCause", "getThrowable", }; /** *

* Public constructor allows an instance of ExceptionUtils to be created, although that is not * normally necessary. *

*/ public ExceptionUtils() { super(); } //----------------------------------------------------------------------- /** *

Returns the default names used when searching for the cause of an exception.

* *

This may be modified and used in the overloaded getCause(Throwable, String[]) method.

* * @return cloned array of the default method names * @since 3.0 * @deprecated This feature will be removed in Lang 4.0 */ @Deprecated public static String[] getDefaultCauseMethodNames() { return ArrayUtils.clone(CAUSE_METHOD_NAMES); } //----------------------------------------------------------------------- /** *

Introspects the Throwable to obtain the cause.

* *

The method searches for methods with specific names that return a * Throwable object. This will pick up most wrapping exceptions, * including those from JDK 1.4. * *

The default list searched for are:

*
    *
  • getCause()
  • *
  • getNextException()
  • *
  • getTargetException()
  • *
  • getException()
  • *
  • getSourceException()
  • *
  • getRootCause()
  • *
  • getCausedByException()
  • *
  • getNested()
  • *
* *

If none of the above is found, returns null.

* * @param throwable the throwable to introspect for a cause, may be null * @return the cause of the Throwable, * null if none found or null throwable input * @since 1.0 * @deprecated This feature will be removed in Lang 4.0 */ @Deprecated public static Throwable getCause(final Throwable throwable) { return getCause(throwable, CAUSE_METHOD_NAMES); } /** *

Introspects the Throwable to obtain the cause.

* *

A null set of method names means use the default set. * A null in the set of method names will be ignored.

* * @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 Throwable, * null if none found or null throwable input * @since 1.0 * @deprecated This feature will be removed in Lang 4.0 */ @Deprecated public static Throwable getCause(final Throwable throwable, String[] methodNames) { if (throwable == null) { return null; } if (methodNames == null) { methodNames = CAUSE_METHOD_NAMES; } for (final String methodName : methodNames) { if (methodName != null) { final Throwable cause = getCauseUsingMethodName(throwable, methodName); if (cause != null) { return cause; } } } return null; } /** *

Introspects the Throwable to obtain the root cause.

* *

This method walks through the exception chain to the last element, * "root" of the tree, using {@link #getCause(Throwable)}, and * returns that exception.

* *

From version 2.2, this method handles recursive cause structures * that might otherwise cause infinite loops. If the throwable parameter * has a cause of itself, then null will be returned. If the throwable * parameter cause chain loops, the last element in the chain before the * loop is returned.

* * @param throwable the throwable to get the root cause for, may be null * @return the root cause of the Throwable, * null if none found or null throwable input */ public static Throwable getRootCause(final Throwable throwable) { final List list = getThrowableList(throwable); return list.size() < 2 ? null : (Throwable)list.get(list.size() - 1); } /** *

Finds a Throwable by method name.

* * @param throwable the exception to examine * @param methodName the name of the method to find and invoke * @return the wrapped exception, or null if not found */ // TODO: Remove in Lang 4.0 private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) { Method method = null; try { method = throwable.getClass().getMethod(methodName); } catch (final NoSuchMethodException ignored) { // NOPMD // exception ignored } catch (final SecurityException ignored) { // NOPMD // exception ignored } if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { try { return (Throwable) method.invoke(throwable); } catch (final IllegalAccessException ignored) { // NOPMD // exception ignored } catch (final IllegalArgumentException ignored) { // NOPMD // exception ignored } catch (final InvocationTargetException ignored) { // NOPMD // exception ignored } } return null; } //----------------------------------------------------------------------- /** *

Counts the number of Throwable objects in the * exception chain.

* *

A throwable without cause will return 1. * A throwable with one cause will return 2 and so on. * A null throwable will return 0.

* *

From version 2.2, this method handles recursive cause structures * that might otherwise cause infinite loops. The cause chain is * processed until the end is reached, or until the next item in the * chain is already in the result set.

* * @param throwable the throwable to inspect, may be null * @return the count of throwables, zero if null input */ public static int getThrowableCount(final Throwable throwable) { return getThrowableList(throwable).size(); } /** *

Returns the list of Throwable objects in the * exception chain.

* *

A throwable without cause will return an array containing * one element - the input throwable. * A throwable with one cause will return an array containing * two elements. - the input throwable and the cause throwable. * A null throwable will return an array of size zero.

* *

From version 2.2, this method handles recursive cause structures * that might otherwise cause infinite loops. The cause chain is * processed until the end is reached, or until the next item in the * chain is already in the result set.

* * @see #getThrowableList(Throwable) * @param throwable the throwable to inspect, may be null * @return the array of throwables, never null */ public static Throwable[] getThrowables(final Throwable throwable) { final List list = getThrowableList(throwable); return list.toArray(new Throwable[list.size()]); } /** *

Returns the list of Throwable objects in the * exception chain.

* *

A throwable without cause will return a list containing * one element - the input throwable. * A throwable with one cause will return a list containing * two elements. - the input throwable and the cause throwable. * A null throwable will return a list of size zero.

* *

This method handles recursive cause structures that might * otherwise cause infinite loops. The cause chain is processed until * the end is reached, or until the next item in the chain is already * in the result set.

* * @param throwable the throwable to inspect, may be null * @return the list of throwables, never null * @since Commons Lang 2.2 */ public static List getThrowableList(Throwable throwable) { final List list = new ArrayList(); while (throwable != null && list.contains(throwable) == false) { list.add(throwable); throwable = ExceptionUtils.getCause(throwable); } return list; } //----------------------------------------------------------------------- /** *

Returns the (zero based) index of the first Throwable * that matches the specified class (exactly) in the exception chain. * Subclasses of the specified class do not match - see * {@link #indexOfType(Throwable, Class)} for the opposite.

* *

A null throwable returns -1. * A null type returns -1. * No match in the chain returns -1.

* * @param throwable the throwable to inspect, may be null * @param clazz the class to search for, subclasses do not match, null returns -1 * @return the index into the throwable chain, -1 if no match or null input */ public static int indexOfThrowable(final Throwable throwable, final Class clazz) { return indexOf(throwable, clazz, 0, false); } /** *

Returns the (zero based) index of the first Throwable * that matches the specified type in the exception chain from * a specified index. * Subclasses of the specified class do not match - see * {@link #indexOfType(Throwable, Class, int)} for the opposite.

* *

A null throwable returns -1. * A null type returns -1. * No match in the chain returns -1. * A negative start index is treated as zero. * A start index greater than the number of throwables returns -1.

* * @param throwable the throwable to inspect, may be null * @param clazz the class to search for, subclasses do not match, null returns -1 * @param fromIndex the (zero based) index of the starting position, * negative treated as zero, larger than chain size returns -1 * @return the index into the throwable chain, -1 if no match or null input */ public static int indexOfThrowable(final Throwable throwable, final Class clazz, final int fromIndex) { return indexOf(throwable, clazz, fromIndex, false); } //----------------------------------------------------------------------- /** *

Returns the (zero based) index of the first Throwable * that matches the specified class or subclass in the exception chain. * Subclasses of the specified class do match - see * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

* *

A null throwable returns -1. * A null type returns -1. * No match in the chain returns -1.

* * @param throwable the throwable to inspect, may be null * @param type the type to search for, subclasses match, null returns -1 * @return the index into the throwable chain, -1 if no match or null input * @since 2.1 */ public static int indexOfType(final Throwable throwable, final Class type) { return indexOf(throwable, type, 0, true); } /** *

Returns the (zero based) index of the first Throwable * that matches the specified type in the exception chain from * a specified index. * Subclasses of the specified class do match - see * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

* *

A null throwable returns -1. * A null type returns -1. * No match in the chain returns -1. * A negative start index is treated as zero. * A start index greater than the number of throwables returns -1.

* * @param throwable the throwable to inspect, may be null * @param type the type to search for, subclasses match, null returns -1 * @param fromIndex the (zero based) index of the starting position, * negative treated as zero, larger than chain size returns -1 * @return the index into the throwable chain, -1 if no match or null input * @since 2.1 */ public static int indexOfType(final Throwable throwable, final Class type, final int fromIndex) { return indexOf(throwable, type, fromIndex, true); } /** *

Worker method for the indexOfType methods.

* * @param throwable the throwable to inspect, may be null * @param type the type to search for, subclasses match, null returns -1 * @param fromIndex the (zero based) index of the starting position, * negative treated as zero, larger than chain size returns -1 * @param subclass if true, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares * using references * @return index of the type within throwables nested within the specified throwable */ private static int indexOf(final Throwable throwable, final Class type, int fromIndex, final boolean subclass) { if (throwable == null || type == null) { return -1; } if (fromIndex < 0) { fromIndex = 0; } final Throwable[] throwables = ExceptionUtils.getThrowables(throwable); if (fromIndex >= throwables.length) { return -1; } if (subclass) { for (int i = fromIndex; i < throwables.length; i++) { if (type.isAssignableFrom(throwables[i].getClass())) { return i; } } } else { for (int i = fromIndex; i < throwables.length; i++) { if (type.equals(throwables[i].getClass())) { return i; } } } return -1; } //----------------------------------------------------------------------- /** *

Prints a compact stack trace for the root cause of a throwable * to System.err.

* *

The compact stack trace starts with the root cause and prints * stack frames up to the place where it was caught and wrapped. * Then it prints the wrapped exception and continues with stack frames * until the wrapper exception is caught and wrapped again, etc.

* *

The output of this method is consistent across JDK versions. * Note that this is the opposite order to the JDK1.4 display.

* *

The method is equivalent to printStackTrace for throwables * that don't have nested causes.

* * @param throwable the throwable to output * @since 2.0 */ public static void printRootCauseStackTrace(final Throwable throwable) { printRootCauseStackTrace(throwable, System.err); } /** *

Prints a compact stack trace for the root cause of a throwable.

* *

The compact stack trace starts with the root cause and prints * stack frames up to the place where it was caught and wrapped. * Then it prints the wrapped exception and continues with stack frames * until the wrapper exception is caught and wrapped again, etc.

* *

The output of this method is consistent across JDK versions. * Note that this is the opposite order to the JDK1.4 display.

* *

The method is equivalent to printStackTrace for throwables * that don't have nested causes.

* * @param throwable the throwable to output, may be null * @param stream the stream to output to, may not be null * @throws IllegalArgumentException if the stream is null * @since 2.0 */ public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream stream) { if (throwable == null) { return; } if (stream == null) { throw new IllegalArgumentException("The PrintStream must not be null"); } final String trace[] = getRootCauseStackTrace(throwable); for (final String element : trace) { stream.println(element); } stream.flush(); } /** *

Prints a compact stack trace for the root cause of a throwable.

* *

The compact stack trace starts with the root cause and prints * stack frames up to the place where it was caught and wrapped. * Then it prints the wrapped exception and continues with stack frames * until the wrapper exception is caught and wrapped again, etc.

* *

The output of this method is consistent across JDK versions. * Note that this is the opposite order to the JDK1.4 display.

* *

The method is equivalent to printStackTrace for throwables * that don't have nested causes.

* * @param throwable the throwable to output, may be null * @param writer the writer to output to, may not be null * @throws IllegalArgumentException if the writer is null * @since 2.0 */ public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter writer) { if (throwable == null) { return; } if (writer == null) { throw new IllegalArgumentException("The PrintWriter must not be null"); } final String trace[] = getRootCauseStackTrace(throwable); for (final String element : trace) { writer.println(element); } writer.flush(); } //----------------------------------------------------------------------- /** *

Creates a compact stack trace for the root cause of the supplied * Throwable.

* *

The output of this method is consistent across JDK versions. * It consists of the root exception followed by each of its wrapping * exceptions separated by '[wrapped]'. Note that this is the opposite * order to the JDK1.4 display.

* * @param throwable the throwable to examine, may be null * @return an array of stack trace frames, never null * @since 2.0 */ public static String[] getRootCauseStackTrace(final Throwable throwable) { if (throwable == null) { return ArrayUtils.EMPTY_STRING_ARRAY; } final Throwable throwables[] = getThrowables(throwable); final int count = throwables.length; final List frames = new ArrayList(); List nextTrace = getStackFrameList(throwables[count - 1]); for (int i = count; --i >= 0;) { final List trace = nextTrace; if (i != 0) { nextTrace = getStackFrameList(throwables[i - 1]); removeCommonFrames(trace, nextTrace); } if (i == count - 1) { frames.add(throwables[i].toString()); } else { frames.add(WRAPPED_MARKER + throwables[i].toString()); } for (int j = 0; j < trace.size(); j++) { frames.add(trace.get(j)); } } return frames.toArray(new String[frames.size()]); } /** *

Removes common frames from the cause trace given the two stack traces.

* * @param causeFrames stack trace of a cause throwable * @param wrapperFrames stack trace of a wrapper throwable * @throws IllegalArgumentException if either argument is null * @since 2.0 */ public static void removeCommonFrames(final List causeFrames, final List wrapperFrames) { if (causeFrames == null || wrapperFrames == null) { throw new IllegalArgumentException("The List must not be null"); } int causeFrameIndex = causeFrames.size() - 1; int wrapperFrameIndex = wrapperFrames.size() - 1; while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { // Remove the frame from the cause trace if it is the same // as in the wrapper trace final String causeFrame = causeFrames.get(causeFrameIndex); final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); if (causeFrame.equals(wrapperFrame)) { causeFrames.remove(causeFrameIndex); } causeFrameIndex--; wrapperFrameIndex--; } } //----------------------------------------------------------------------- /** *

Gets the stack trace from a Throwable as a String.

* *

The result of this method vary by JDK version as this method * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. * On JDK1.3 and earlier, the cause exception will not be shown * unless the specified throwable alters printStackTrace.

* * @param throwable the Throwable to be examined * @return the stack trace as generated by the exception's * printStackTrace(PrintWriter) method */ public static String getStackTrace(final Throwable throwable) { final StringWriter sw = new StringWriter(); final PrintWriter pw = new PrintWriter(sw, true); throwable.printStackTrace(pw); return sw.getBuffer().toString(); } /** *

Captures the stack trace associated with the specified * Throwable object, decomposing it into a list of * stack frames.

* *

The result of this method vary by JDK version as this method * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. * On JDK1.3 and earlier, the cause exception will not be shown * unless the specified throwable alters printStackTrace.

* * @param throwable the Throwable to examine, may be null * @return an array of strings describing each stack frame, never null */ public static String[] getStackFrames(final Throwable throwable) { if (throwable == null) { return ArrayUtils.EMPTY_STRING_ARRAY; } return getStackFrames(getStackTrace(throwable)); } //----------------------------------------------------------------------- /** *

Returns an array where each element is a line from the argument.

* *

The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.

* * @param stackTrace a stack trace String * @return an array where each element is a line from the argument */ static String[] getStackFrames(final String stackTrace) { final String linebreak = SystemUtils.LINE_SEPARATOR; final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); final List list = new ArrayList(); while (frames.hasMoreTokens()) { list.add(frames.nextToken()); } return list.toArray(new String[list.size()]); } /** *

Produces a List of stack frames - the message * is not included. Only the trace of the specified exception is * returned, any caused by trace is stripped.

* *

This works in most cases - it will only fail if the exception * message contains a line that starts with: * "   at".

* * @param t is any throwable * @return List of stack frames */ static List getStackFrameList(final Throwable t) { final String stackTrace = getStackTrace(t); final String linebreak = SystemUtils.LINE_SEPARATOR; final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); final List list = new ArrayList(); boolean traceStarted = false; while (frames.hasMoreTokens()) { final String token = frames.nextToken(); // Determine if the line starts with at final int at = token.indexOf("at"); if (at != -1 && token.substring(0, at).trim().isEmpty()) { traceStarted = true; list.add(token); } else if (traceStarted) { break; } } return list; } //----------------------------------------------------------------------- /** * Gets a short message summarising the exception. *

* The message returned is of the form * {ClassNameWithoutPackage}: {ThrowableMessage} * * @param th the throwable to get a message for, null returns empty string * @return the message, non-null * @since Commons Lang 2.2 */ public static String getMessage(final Throwable th) { if (th == null) { return ""; } final String clsName = ClassUtils.getShortClassName(th, null); final String msg = th.getMessage(); return clsName + ": " + StringUtils.defaultString(msg); } //----------------------------------------------------------------------- /** * Gets a short message summarising the root cause exception. *

* The message returned is of the form * {ClassNameWithoutPackage}: {ThrowableMessage} * * @param th the throwable to get a message for, null returns empty string * @return the message, non-null * @since Commons Lang 2.2 */ public static String getRootCauseMessage(final Throwable th) { Throwable root = ExceptionUtils.getRootCause(th); root = root == null ? th : root; return getMessage(root); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy