ExceptionUtils.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.lang3.exception;

  18. import java.io.PrintStream;
  19. import java.io.PrintWriter;
  20. import java.io.StringWriter;
  21. import java.lang.reflect.Method;
  22. import java.lang.reflect.UndeclaredThrowableException;
  23. import java.util.ArrayList;
  24. import java.util.Collections;
  25. import java.util.List;
  26. import java.util.Objects;
  27. import java.util.StringTokenizer;
  28. import java.util.function.Consumer;
  29. import java.util.stream.Stream;

  30. import org.apache.commons.lang3.ArrayUtils;
  31. import org.apache.commons.lang3.ClassUtils;
  32. import org.apache.commons.lang3.StringUtils;
  33. import org.apache.commons.lang3.reflect.MethodUtils;
  34. import org.apache.commons.lang3.util.IterableStringTokenizer;

  35. /**
  36.  * Provides utilities for manipulating and examining
  37.  * {@link Throwable} objects.
  38.  *
  39.  * @since 1.0
  40.  */
  41. public class ExceptionUtils {

  42.     /**
  43.      * The names of methods commonly used to access a wrapped exception.
  44.      */
  45.     // TODO: Remove in Lang 4
  46.     private static final String[] CAUSE_METHOD_NAMES = {
  47.         "getCause",
  48.         "getNextException",
  49.         "getTargetException",
  50.         "getException",
  51.         "getSourceException",
  52.         "getRootCause",
  53.         "getCausedByException",
  54.         "getNested",
  55.         "getLinkedException",
  56.         "getNestedException",
  57.         "getLinkedCause",
  58.         "getThrowable",
  59.     };

  60.     private static final int NOT_FOUND = -1;

  61.     /**
  62.      * Used when printing stack frames to denote the start of a
  63.      * wrapped exception.
  64.      *
  65.      * <p>Package private for accessibility by test suite.</p>
  66.      */
  67.     static final String WRAPPED_MARKER = " [wrapped] ";

  68.     /**
  69.      * Throws the given (usually checked) exception without adding the exception to the throws
  70.      * clause of the calling method. This method prevents throws clause
  71.      * inflation and reduces the clutter of "Caused by" exceptions in the
  72.      * stack trace.
  73.      * <p>
  74.      * The use of this technique may be controversial, but useful.
  75.      * </p>
  76.      * <pre>
  77.      *  // There is no throws clause in the method signature.
  78.      *  public int propagateExample {
  79.      *      try {
  80.      *          // Throws IOException
  81.      *          invocation();
  82.      *      } catch (Exception e) {
  83.      *          // Propagates a checked exception.
  84.      *          throw ExceptionUtils.asRuntimeException(e);
  85.      *      }
  86.      *      // more processing
  87.      *      ...
  88.      *      return value;
  89.      *  }
  90.      * </pre>
  91.      * <p>
  92.      * This is an alternative to the more conservative approach of wrapping the
  93.      * checked exception in a RuntimeException:
  94.      * </p>
  95.      * <pre>
  96.      *  // There is no throws clause in the method signature.
  97.      *  public int wrapExample() {
  98.      *      try {
  99.      *          // throws IOException.
  100.      *          invocation();
  101.      *      } catch (Error e) {
  102.      *          throw e;
  103.      *      } catch (RuntimeException e) {
  104.      *          // Throws an unchecked exception.
  105.      *          throw e;
  106.      *      } catch (Exception e) {
  107.      *          // Wraps a checked exception.
  108.      *          throw new UndeclaredThrowableException(e);
  109.      *      }
  110.      *      // more processing
  111.      *      ...
  112.      *      return value;
  113.      *  }
  114.      * </pre>
  115.      * <p>
  116.      * One downside to using this approach is that the Java compiler will not
  117.      * allow invoking code to specify a checked exception in a catch clause
  118.      * unless there is some code path within the try block that has invoked a
  119.      * method declared with that checked exception. If the invoking site wishes
  120.      * to catch the shaded checked exception, it must either invoke the shaded
  121.      * code through a method re-declaring the desired checked exception, or
  122.      * catch Exception and use the {@code instanceof} operator. Either of these
  123.      * techniques are required when interacting with non-Java JVM code such as
  124.      * Jython, Scala, or Groovy, since these languages do not consider any
  125.      * exceptions as checked.
  126.      * </p>
  127.      *
  128.      * @param throwable
  129.      *            The throwable to rethrow.
  130.      * @param <T> The type of the returned value.
  131.      * @return Never actually returned, this generic type matches any type
  132.      *         which the calling site requires. "Returning" the results of this
  133.      *         method, as done in the propagateExample above, will satisfy the
  134.      *         Java compiler requirement that all code paths return a value.
  135.      * @since 3.14.0
  136.      * @see #wrapAndThrow(Throwable)
  137.      */
  138.     public static <T extends RuntimeException> T asRuntimeException(final Throwable throwable) {
  139.         // claim that the typeErasure invocation throws a RuntimeException
  140.         return ExceptionUtils.<T, RuntimeException>eraseType(throwable);
  141.     }

  142.     /**
  143.      * Claims a Throwable is another Throwable type using type erasure. This
  144.      * hides a checked exception from the Java compiler, allowing a checked
  145.      * exception to be thrown without having the exception in the method's throw
  146.      * clause.
  147.      */
  148.     @SuppressWarnings("unchecked")
  149.     private static <R, T extends Throwable> R eraseType(final Throwable throwable) throws T {
  150.         throw (T) throwable;
  151.     }

  152.     /**
  153.      * Performs an action for each Throwable causes of the given Throwable.
  154.      * <p>
  155.      * A throwable without cause will return a stream containing one element - the input throwable. A throwable with one cause
  156.      * will return a stream containing two elements. - the input throwable and the cause throwable. A {@code null} throwable
  157.      * will return a stream of count zero.
  158.      * </p>
  159.      *
  160.      * <p>
  161.      * This method handles recursive cause structures that might otherwise cause infinite loops. The cause chain is
  162.      * processed until the end is reached, or until the next item in the chain is already in the result set.
  163.      * </p>
  164.      * @param throwable The Throwable to traverse.
  165.      * @param consumer a non-interfering action to perform on the elements.
  166.      * @since 3.13.0
  167.      */
  168.     public static void forEach(final Throwable throwable, final Consumer<Throwable> consumer) {
  169.         stream(throwable).forEach(consumer);
  170.     }

  171.     /**
  172.      * Introspects the {@link Throwable} to obtain the cause.
  173.      *
  174.      * <p>The method searches for methods with specific names that return a
  175.      * {@link Throwable} object. This will pick up most wrapping exceptions,
  176.      * including those from JDK 1.4.
  177.      * </p>
  178.      *
  179.      * <p>The default list searched for are:</p>
  180.      * <ul>
  181.      *  <li>{@code getCause()}</li>
  182.      *  <li>{@code getNextException()}</li>
  183.      *  <li>{@code getTargetException()}</li>
  184.      *  <li>{@code getException()}</li>
  185.      *  <li>{@code getSourceException()}</li>
  186.      *  <li>{@code getRootCause()}</li>
  187.      *  <li>{@code getCausedByException()}</li>
  188.      *  <li>{@code getNested()}</li>
  189.      * </ul>
  190.      *
  191.      * <p>If none of the above is found, returns {@code null}.</p>
  192.      *
  193.      * @param throwable  the throwable to introspect for a cause, may be null
  194.      * @return the cause of the {@link Throwable},
  195.      *  {@code null} if none found or null throwable input
  196.      * @since 1.0
  197.      * @deprecated This feature will be removed in Lang 4, use {@link Throwable#getCause} instead
  198.      */
  199.     @Deprecated
  200.     public static Throwable getCause(final Throwable throwable) {
  201.         return getCause(throwable, null);
  202.     }

  203.     /**
  204.      * Introspects the {@link Throwable} to obtain the cause.
  205.      *
  206.      * <p>A {@code null} set of method names means use the default set.
  207.      * A {@code null} in the set of method names will be ignored.</p>
  208.      *
  209.      * @param throwable  the throwable to introspect for a cause, may be null
  210.      * @param methodNames  the method names, null treated as default set
  211.      * @return the cause of the {@link Throwable},
  212.      *  {@code null} if none found or null throwable input
  213.      * @since 1.0
  214.      * @deprecated This feature will be removed in Lang 4, use {@link Throwable#getCause} instead
  215.      */
  216.     @Deprecated
  217.     public static Throwable getCause(final Throwable throwable, String[] methodNames) {
  218.         if (throwable == null) {
  219.             return null;
  220.         }
  221.         if (methodNames == null) {
  222.             final Throwable cause = throwable.getCause();
  223.             if (cause != null) {
  224.                 return cause;
  225.             }
  226.             methodNames = CAUSE_METHOD_NAMES;
  227.         }
  228.         return Stream.of(methodNames).map(m -> getCauseUsingMethodName(throwable, m)).filter(Objects::nonNull).findFirst().orElse(null);
  229.     }

  230.     /**
  231.      * Gets a {@link Throwable} by method name.
  232.      *
  233.      * @param throwable  the exception to examine
  234.      * @param methodName  the name of the method to find and invoke
  235.      * @return the wrapped exception, or {@code null} if not found
  236.      */
  237.     // TODO: Remove in Lang 4
  238.     private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) {
  239.         if (methodName != null) {
  240.             final Method method = MethodUtils.getMethodObject(throwable.getClass(), methodName);
  241.             if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) {
  242.                 try {
  243.                     return (Throwable) method.invoke(throwable);
  244.                 } catch (final ReflectiveOperationException ignored) {
  245.                     // exception ignored
  246.                 }
  247.             }
  248.         }
  249.         return null;
  250.     }

  251.     /**
  252.      * Gets the default names used when searching for the cause of an exception.
  253.      *
  254.      * <p>This may be modified and used in the overloaded getCause(Throwable, String[]) method.</p>
  255.      *
  256.      * @return cloned array of the default method names
  257.      * @since 3.0
  258.      * @deprecated This feature will be removed in Lang 4
  259.      */
  260.     @Deprecated
  261.     public static String[] getDefaultCauseMethodNames() {
  262.         return ArrayUtils.clone(CAUSE_METHOD_NAMES);
  263.     }

  264.     /**
  265.      * Gets a short message summarizing the exception.
  266.      * <p>
  267.      * The message returned is of the form
  268.      * {ClassNameWithoutPackage}: {ThrowableMessage}
  269.      * </p>
  270.      *
  271.      * @param th  the throwable to get a message for, null returns empty string
  272.      * @return the message, non-null
  273.      * @since 2.2
  274.      */
  275.     public static String getMessage(final Throwable th) {
  276.         if (th == null) {
  277.             return StringUtils.EMPTY;
  278.         }
  279.         final String clsName = ClassUtils.getShortClassName(th, null);
  280.         return clsName + ": " + StringUtils.defaultString(th.getMessage());
  281.     }

  282.     /**
  283.      * Walks the {@link Throwable} to obtain its root cause.
  284.      *
  285.      * <p>This method walks through the exception chain until the last element,
  286.      * the root cause of the chain, using {@link Throwable#getCause()}, and
  287.      * returns that exception.</p>
  288.      *
  289.      * <p>This method handles recursive cause chains that might
  290.      * otherwise cause infinite loops. The cause chain is processed until
  291.      * the end, or until the next item in the chain is already
  292.      * processed. If we detect a loop, then return the element before the loop.</p>

  293.      *
  294.      * @param throwable  the throwable to get the root cause for, may be null
  295.      * @return the root cause of the {@link Throwable},
  296.      *  {@code null} if null throwable input
  297.      */
  298.     public static Throwable getRootCause(final Throwable throwable) {
  299.         final List<Throwable> list = getThrowableList(throwable);
  300.         return list.isEmpty() ? null : list.get(list.size() - 1);
  301.     }

  302.     /**
  303.      * Gets a short message summarizing the root cause exception.
  304.      * <p>
  305.      * The message returned is of the form
  306.      * {ClassNameWithoutPackage}: {ThrowableMessage}
  307.      * </p>
  308.      *
  309.      * @param throwable  the throwable to get a message for, null returns empty string
  310.      * @return the message, non-null
  311.      * @since 2.2
  312.      */
  313.     public static String getRootCauseMessage(final Throwable throwable) {
  314.         final Throwable root = getRootCause(throwable);
  315.         return getMessage(root == null ? throwable : root);
  316.     }

  317.     /**
  318.      * Gets a compact stack trace for the root cause of the supplied
  319.      * {@link Throwable}.
  320.      *
  321.      * <p>The output of this method is consistent across JDK versions.
  322.      * It consists of the root exception followed by each of its wrapping
  323.      * exceptions separated by '[wrapped]'. Note that this is the opposite
  324.      * order to the JDK1.4 display.</p>
  325.      *
  326.      * @param throwable  the throwable to examine, may be null
  327.      * @return an array of stack trace frames, never null
  328.      * @since 2.0
  329.      */
  330.     public static String[] getRootCauseStackTrace(final Throwable throwable) {
  331.         return getRootCauseStackTraceList(throwable).toArray(ArrayUtils.EMPTY_STRING_ARRAY);
  332.     }

  333.     /**
  334.      * Gets a compact stack trace for the root cause of the supplied {@link Throwable}.
  335.      *
  336.      * <p>
  337.      * The output of this method is consistent across JDK versions. It consists of the root exception followed by each of
  338.      * its wrapping exceptions separated by '[wrapped]'. Note that this is the opposite order to the JDK1.4 display.
  339.      * </p>
  340.      *
  341.      * @param throwable the throwable to examine, may be null
  342.      * @return a list of stack trace frames, never null
  343.      * @since 3.13.0
  344.      */
  345.     public static List<String> getRootCauseStackTraceList(final Throwable throwable) {
  346.         if (throwable == null) {
  347.             return Collections.emptyList();
  348.         }
  349.         final Throwable[] throwables = getThrowables(throwable);
  350.         final int count = throwables.length;
  351.         final List<String> frames = new ArrayList<>();
  352.         List<String> nextTrace = getStackFrameList(throwables[count - 1]);
  353.         for (int i = count; --i >= 0;) {
  354.             final List<String> trace = nextTrace;
  355.             if (i != 0) {
  356.                 nextTrace = getStackFrameList(throwables[i - 1]);
  357.                 removeCommonFrames(trace, nextTrace);
  358.             }
  359.             if (i == count - 1) {
  360.                 frames.add(throwables[i].toString());
  361.             } else {
  362.                 frames.add(WRAPPED_MARKER + throwables[i].toString());
  363.             }
  364.             frames.addAll(trace);
  365.         }
  366.         return frames;
  367.     }

  368.     /**
  369.      * Gets a {@link List} of stack frames, the message
  370.      * is not included. Only the trace of the specified exception is
  371.      * returned, any caused by trace is stripped.
  372.      *
  373.      * <p>This works in most cases and will only fail if the exception
  374.      * message contains a line that starts with: {@code "<whitespace>at"}.</p>
  375.      *
  376.      * @param throwable is any throwable
  377.      * @return List of stack frames
  378.      */
  379.     static List<String> getStackFrameList(final Throwable throwable) {
  380.         final String stackTrace = getStackTrace(throwable);
  381.         final String linebreak = System.lineSeparator();
  382.         final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
  383.         final List<String> list = new ArrayList<>();
  384.         boolean traceStarted = false;
  385.         while (frames.hasMoreTokens()) {
  386.             final String token = frames.nextToken();
  387.             // Determine if the line starts with "<whitespace>at"
  388.             final int at = token.indexOf("at");
  389.             if (at != NOT_FOUND && token.substring(0, at).trim().isEmpty()) {
  390.                 traceStarted = true;
  391.                 list.add(token);
  392.             } else if (traceStarted) {
  393.                 break;
  394.             }
  395.         }
  396.         return list;
  397.     }

  398.     /**
  399.      * Gets an array where each element is a line from the argument.
  400.      *
  401.      * <p>The end of line is determined by the value of {@link System#lineSeparator()}.</p>
  402.      *
  403.      * @param stackTrace  a stack trace String
  404.      * @return an array where each element is a line from the argument
  405.      */
  406.     static String[] getStackFrames(final String stackTrace) {
  407.         return new IterableStringTokenizer(stackTrace, System.lineSeparator()).toArray();
  408.     }

  409.     /**
  410.      * Gets the stack trace associated with the specified
  411.      * {@link Throwable} object, decomposing it into a list of
  412.      * stack frames.
  413.      *
  414.      * <p>
  415.      * The result of this method vary by JDK version as this method
  416.      * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
  417.      * </p>
  418.      *
  419.      * @param throwable  the {@link Throwable} to examine, may be null
  420.      * @return an array of strings describing each stack frame, never null
  421.      */
  422.     public static String[] getStackFrames(final Throwable throwable) {
  423.         if (throwable == null) {
  424.             return ArrayUtils.EMPTY_STRING_ARRAY;
  425.         }
  426.         return getStackFrames(getStackTrace(throwable));
  427.     }

  428.     /**
  429.      * Gets the stack trace from a Throwable as a String, including suppressed and cause exceptions.
  430.      *
  431.      * <p>
  432.      * The result of this method vary by JDK version as this method
  433.      * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
  434.      * </p>
  435.      *
  436.      * @param throwable  the {@link Throwable} to be examined, may be null
  437.      * @return the stack trace as generated by the exception's
  438.      * {@code printStackTrace(PrintWriter)} method, or an empty String if {@code null} input
  439.      */
  440.     public static String getStackTrace(final Throwable throwable) {
  441.         if (throwable == null) {
  442.             return StringUtils.EMPTY;
  443.         }
  444.         final StringWriter sw = new StringWriter();
  445.         throwable.printStackTrace(new PrintWriter(sw, true));
  446.         return sw.toString();
  447.     }

  448.     /**
  449.      * Gets a count of the number of {@link Throwable} objects in the
  450.      * exception chain.
  451.      *
  452.      * <p>A throwable without cause will return {@code 1}.
  453.      * A throwable with one cause will return {@code 2} and so on.
  454.      * A {@code null} throwable will return {@code 0}.</p>
  455.      *
  456.      * <p>This method handles recursive cause chains
  457.      * that might otherwise cause infinite loops. The cause chain is
  458.      * processed until the end, or until the next item in the
  459.      * chain is already in the result.</p>
  460.      *
  461.      * @param throwable  the throwable to inspect, may be null
  462.      * @return the count of throwables, zero on null input
  463.      */
  464.     public static int getThrowableCount(final Throwable throwable) {
  465.         return getThrowableList(throwable).size();
  466.     }

  467.     /**
  468.      * Gets the list of {@link Throwable} objects in the
  469.      * exception chain.
  470.      *
  471.      * <p>A throwable without cause will return a list containing
  472.      * one element - the input throwable.
  473.      * A throwable with one cause will return a list containing
  474.      * two elements. - the input throwable and the cause throwable.
  475.      * A {@code null} throwable will return a list of size zero.</p>
  476.      *
  477.      * <p>This method handles recursive cause chains that might
  478.      * otherwise cause infinite loops. The cause chain is processed until
  479.      * the end, or until the next item in the chain is already
  480.      * in the result list.</p>
  481.      *
  482.      * @param throwable  the throwable to inspect, may be null
  483.      * @return the list of throwables, never null
  484.      * @since 2.2
  485.      */
  486.     public static List<Throwable> getThrowableList(Throwable throwable) {
  487.         final List<Throwable> list = new ArrayList<>();
  488.         while (throwable != null && !list.contains(throwable)) {
  489.             list.add(throwable);
  490.             throwable = throwable.getCause();
  491.         }
  492.         return list;
  493.     }

  494.     /**
  495.      * Gets the list of {@link Throwable} objects in the
  496.      * exception chain.
  497.      *
  498.      * <p>A throwable without cause will return an array containing
  499.      * one element - the input throwable.
  500.      * A throwable with one cause will return an array containing
  501.      * two elements. - the input throwable and the cause throwable.
  502.      * A {@code null} throwable will return an array of size zero.</p>
  503.      *
  504.      * <p>This method handles recursive cause chains
  505.      * that might otherwise cause infinite loops. The cause chain is
  506.      * processed until the end, or until the next item in the
  507.      * chain is already in the result array.</p>
  508.      *
  509.      * @see #getThrowableList(Throwable)
  510.      * @param throwable  the throwable to inspect, may be null
  511.      * @return the array of throwables, never null
  512.      */
  513.     public static Throwable[] getThrowables(final Throwable throwable) {
  514.         return getThrowableList(throwable).toArray(ArrayUtils.EMPTY_THROWABLE_ARRAY);
  515.     }

  516.     /**
  517.      * Tests if the throwable's causal chain have an immediate or wrapped exception
  518.      * of the given type?
  519.      *
  520.      * @param chain
  521.      *            The root of a Throwable causal chain.
  522.      * @param type
  523.      *            The exception type to test.
  524.      * @return true, if chain is an instance of type or is an
  525.      *         UndeclaredThrowableException wrapping a cause.
  526.      * @since 3.5
  527.      * @see #wrapAndThrow(Throwable)
  528.      */
  529.     public static boolean hasCause(Throwable chain,
  530.             final Class<? extends Throwable> type) {
  531.         if (chain instanceof UndeclaredThrowableException) {
  532.             chain = chain.getCause();
  533.         }
  534.         return type.isInstance(chain);
  535.     }

  536.     /**
  537.      * Worker method for the {@code indexOfType} methods.
  538.      *
  539.      * @param throwable  the throwable to inspect, may be null
  540.      * @param type  the type to search for, subclasses match, null returns -1
  541.      * @param fromIndex  the (zero-based) index of the starting position,
  542.      *  negative treated as zero, larger than chain size returns -1
  543.      * @param subclass if {@code true}, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
  544.      * using references
  545.      * @return index of the {@code type} within throwables nested within the specified {@code throwable}
  546.      */
  547.     private static int indexOf(final Throwable throwable, final Class<? extends Throwable> type, int fromIndex, final boolean subclass) {
  548.         if (throwable == null || type == null) {
  549.             return NOT_FOUND;
  550.         }
  551.         if (fromIndex < 0) {
  552.             fromIndex = 0;
  553.         }
  554.         final Throwable[] throwables = getThrowables(throwable);
  555.         if (fromIndex >= throwables.length) {
  556.             return NOT_FOUND;
  557.         }
  558.         if (subclass) {
  559.             for (int i = fromIndex; i < throwables.length; i++) {
  560.                 if (type.isAssignableFrom(throwables[i].getClass())) {
  561.                     return i;
  562.                 }
  563.             }
  564.         } else {
  565.             for (int i = fromIndex; i < throwables.length; i++) {
  566.                 if (type.equals(throwables[i].getClass())) {
  567.                     return i;
  568.                 }
  569.             }
  570.         }
  571.         return NOT_FOUND;
  572.     }

  573.     /**
  574.      * Returns the (zero-based) index of the first {@link Throwable}
  575.      * that matches the specified class (exactly) in the exception chain.
  576.      * Subclasses of the specified class do not match - see
  577.      * {@link #indexOfType(Throwable, Class)} for the opposite.
  578.      *
  579.      * <p>A {@code null} throwable returns {@code -1}.
  580.      * A {@code null} type returns {@code -1}.
  581.      * No match in the chain returns {@code -1}.</p>
  582.      *
  583.      * @param throwable  the throwable to inspect, may be null
  584.      * @param clazz  the class to search for, subclasses do not match, null returns -1
  585.      * @return the index into the throwable chain, -1 if no match or null input
  586.      */
  587.     public static int indexOfThrowable(final Throwable throwable, final Class<? extends Throwable> clazz) {
  588.         return indexOf(throwable, clazz, 0, false);
  589.     }

  590.     /**
  591.      * Returns the (zero-based) index of the first {@link Throwable}
  592.      * that matches the specified type in the exception chain from
  593.      * a specified index.
  594.      * Subclasses of the specified class do not match - see
  595.      * {@link #indexOfType(Throwable, Class, int)} for the opposite.
  596.      *
  597.      * <p>A {@code null} throwable returns {@code -1}.
  598.      * A {@code null} type returns {@code -1}.
  599.      * No match in the chain returns {@code -1}.
  600.      * A negative start index is treated as zero.
  601.      * A start index greater than the number of throwables returns {@code -1}.</p>
  602.      *
  603.      * @param throwable  the throwable to inspect, may be null
  604.      * @param clazz  the class to search for, subclasses do not match, null returns -1
  605.      * @param fromIndex  the (zero-based) index of the starting position,
  606.      *  negative treated as zero, larger than chain size returns -1
  607.      * @return the index into the throwable chain, -1 if no match or null input
  608.      */
  609.     public static int indexOfThrowable(final Throwable throwable, final Class<? extends Throwable> clazz, final int fromIndex) {
  610.         return indexOf(throwable, clazz, fromIndex, false);
  611.     }

  612.     /**
  613.      * Returns the (zero-based) index of the first {@link Throwable}
  614.      * that matches the specified class or subclass in the exception chain.
  615.      * Subclasses of the specified class do match - see
  616.      * {@link #indexOfThrowable(Throwable, Class)} for the opposite.
  617.      *
  618.      * <p>A {@code null} throwable returns {@code -1}.
  619.      * A {@code null} type returns {@code -1}.
  620.      * No match in the chain returns {@code -1}.</p>
  621.      *
  622.      * @param throwable  the throwable to inspect, may be null
  623.      * @param type  the type to search for, subclasses match, null returns -1
  624.      * @return the index into the throwable chain, -1 if no match or null input
  625.      * @since 2.1
  626.      */
  627.     public static int indexOfType(final Throwable throwable, final Class<? extends Throwable> type) {
  628.         return indexOf(throwable, type, 0, true);
  629.     }

  630.     /**
  631.      * Returns the (zero-based) index of the first {@link Throwable}
  632.      * that matches the specified type in the exception chain from
  633.      * a specified index.
  634.      * Subclasses of the specified class do match - see
  635.      * {@link #indexOfThrowable(Throwable, Class)} for the opposite.
  636.      *
  637.      * <p>A {@code null} throwable returns {@code -1}.
  638.      * A {@code null} type returns {@code -1}.
  639.      * No match in the chain returns {@code -1}.
  640.      * A negative start index is treated as zero.
  641.      * A start index greater than the number of throwables returns {@code -1}.</p>
  642.      *
  643.      * @param throwable  the throwable to inspect, may be null
  644.      * @param type  the type to search for, subclasses match, null returns -1
  645.      * @param fromIndex  the (zero-based) index of the starting position,
  646.      *  negative treated as zero, larger than chain size returns -1
  647.      * @return the index into the throwable chain, -1 if no match or null input
  648.      * @since 2.1
  649.      */
  650.     public static int indexOfType(final Throwable throwable, final Class<? extends Throwable> type, final int fromIndex) {
  651.         return indexOf(throwable, type, fromIndex, true);
  652.     }

  653.     /**
  654.      * Checks if a throwable represents a checked exception
  655.      *
  656.      * @param throwable
  657.      *            The throwable to check.
  658.      * @return True if the given Throwable is a checked exception.
  659.      * @since 3.13.0
  660.      */
  661.     public static boolean isChecked(final Throwable throwable) {
  662.         return throwable != null && !(throwable instanceof Error) && !(throwable instanceof RuntimeException);
  663.     }

  664.     /**
  665.      * Checks if a throwable represents an unchecked exception
  666.      *
  667.      * @param throwable
  668.      *            The throwable to check.
  669.      * @return True if the given Throwable is an unchecked exception.
  670.      * @since 3.13.0
  671.      */
  672.     public static boolean isUnchecked(final Throwable throwable) {
  673.         return throwable != null && (throwable instanceof Error || throwable instanceof RuntimeException);
  674.     }

  675.     /**
  676.      * Prints a compact stack trace for the root cause of a throwable
  677.      * to {@code System.err}.
  678.      * <p>
  679.      * The compact stack trace starts with the root cause and prints
  680.      * stack frames up to the place where it was caught and wrapped.
  681.      * Then it prints the wrapped exception and continues with stack frames
  682.      * until the wrapper exception is caught and wrapped again, etc.
  683.      * </p>
  684.      * <p>
  685.      * The output of this method is consistent across JDK versions.
  686.      * </p>
  687.      * <p>
  688.      * The method is equivalent to {@code printStackTrace} for throwables
  689.      * that don't have nested causes.
  690.      * </p>
  691.      *
  692.      * @param throwable  the throwable to output
  693.      * @since 2.0
  694.      */
  695.     public static void printRootCauseStackTrace(final Throwable throwable) {
  696.         printRootCauseStackTrace(throwable, System.err);
  697.     }

  698.     /**
  699.      * Prints a compact stack trace for the root cause of a throwable.
  700.      *
  701.      * <p>The compact stack trace starts with the root cause and prints
  702.      * stack frames up to the place where it was caught and wrapped.
  703.      * Then it prints the wrapped exception and continues with stack frames
  704.      * until the wrapper exception is caught and wrapped again, etc.</p>
  705.      *
  706.      * <p>The output of this method is consistent across JDK versions.
  707.      * Note that this is the opposite order to the JDK1.4 display.</p>
  708.      *
  709.      * <p>The method is equivalent to {@code printStackTrace} for throwables
  710.      * that don't have nested causes.</p>
  711.      *
  712.      * @param throwable  the throwable to output, may be null
  713.      * @param printStream  the stream to output to, may not be null
  714.      * @throws NullPointerException if the printStream is {@code null}
  715.      * @since 2.0
  716.      */
  717.     @SuppressWarnings("resource")
  718.     public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream printStream) {
  719.         if (throwable == null) {
  720.             return;
  721.         }
  722.         Objects.requireNonNull(printStream, "printStream");
  723.         getRootCauseStackTraceList(throwable).forEach(printStream::println);
  724.         printStream.flush();
  725.     }

  726.     /**
  727.      * Prints a compact stack trace for the root cause of a throwable.
  728.      *
  729.      * <p>The compact stack trace starts with the root cause and prints
  730.      * stack frames up to the place where it was caught and wrapped.
  731.      * Then it prints the wrapped exception and continues with stack frames
  732.      * until the wrapper exception is caught and wrapped again, etc.</p>
  733.      *
  734.      * <p>The output of this method is consistent across JDK versions.
  735.      * Note that this is the opposite order to the JDK1.4 display.</p>
  736.      *
  737.      * <p>The method is equivalent to {@code printStackTrace} for throwables
  738.      * that don't have nested causes.</p>
  739.      *
  740.      * @param throwable  the throwable to output, may be null
  741.      * @param printWriter  the writer to output to, may not be null
  742.      * @throws NullPointerException if the printWriter is {@code null}
  743.      * @since 2.0
  744.      */
  745.     @SuppressWarnings("resource")
  746.     public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter printWriter) {
  747.         if (throwable == null) {
  748.             return;
  749.         }
  750.         Objects.requireNonNull(printWriter, "printWriter");
  751.         getRootCauseStackTraceList(throwable).forEach(printWriter::println);
  752.         printWriter.flush();
  753.     }

  754.     /**
  755.      * Removes common frames from the cause trace given the two stack traces.
  756.      *
  757.      * @param causeFrames  stack trace of a cause throwable
  758.      * @param wrapperFrames  stack trace of a wrapper throwable
  759.      * @throws NullPointerException if either argument is null
  760.      * @since 2.0
  761.      */
  762.     public static void removeCommonFrames(final List<String> causeFrames, final List<String> wrapperFrames) {
  763.         Objects.requireNonNull(causeFrames, "causeFrames");
  764.         Objects.requireNonNull(wrapperFrames, "wrapperFrames");
  765.         int causeFrameIndex = causeFrames.size() - 1;
  766.         int wrapperFrameIndex = wrapperFrames.size() - 1;
  767.         while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) {
  768.             // Remove the frame from the cause trace if it is the same
  769.             // as in the wrapper trace
  770.             final String causeFrame = causeFrames.get(causeFrameIndex);
  771.             final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex);
  772.             if (causeFrame.equals(wrapperFrame)) {
  773.                 causeFrames.remove(causeFrameIndex);
  774.             }
  775.             causeFrameIndex--;
  776.             wrapperFrameIndex--;
  777.         }
  778.     }

  779.     /**
  780.      * Throws the given (usually checked) exception without adding the exception to the throws
  781.      * clause of the calling method. This method prevents throws clause
  782.      * inflation and reduces the clutter of "Caused by" exceptions in the
  783.      * stack trace.
  784.      * <p>
  785.      * The use of this technique may be controversial, but useful.
  786.      * </p>
  787.      * <pre>
  788.      *  // There is no throws clause in the method signature.
  789.      *  public int propagateExample() {
  790.      *      try {
  791.      *          // throws SomeCheckedException.
  792.      *          return invocation();
  793.      *      } catch (SomeCheckedException e) {
  794.      *          // Propagates a checked exception and compiles to return an int.
  795.      *          return ExceptionUtils.rethrow(e);
  796.      *      }
  797.      *  }
  798.      * </pre>
  799.      * <p>
  800.      * This is an alternative to the more conservative approach of wrapping the
  801.      * checked exception in a RuntimeException:
  802.      * </p>
  803.      * <pre>
  804.      *  // There is no throws clause in the method signature.
  805.      *  public int wrapExample() {
  806.      *      try {
  807.      *          // throws IOException.
  808.      *          return invocation();
  809.      *      } catch (Error e) {
  810.      *          throw e;
  811.      *      } catch (RuntimeException e) {
  812.      *          // Throws an unchecked exception.
  813.      *          throw e;
  814.      *      } catch (Exception e) {
  815.      *          // wraps a checked exception.
  816.      *          throw new UndeclaredThrowableException(e);
  817.      *      }
  818.      *  }
  819.      * </pre>
  820.      * <p>
  821.      * One downside to using this approach is that the Java compiler will not
  822.      * allow invoking code to specify a checked exception in a catch clause
  823.      * unless there is some code path within the try block that has invoked a
  824.      * method declared with that checked exception. If the invoking site wishes
  825.      * to catch the shaded checked exception, it must either invoke the shaded
  826.      * code through a method re-declaring the desired checked exception, or
  827.      * catch Exception and use the {@code instanceof} operator. Either of these
  828.      * techniques are required when interacting with non-Java JVM code such as
  829.      * Jython, Scala, or Groovy, since these languages do not consider any
  830.      * exceptions as checked.
  831.      * </p>
  832.      *
  833.      * @param throwable
  834.      *            The throwable to rethrow.
  835.      * @param <T> The type of the return value.
  836.      * @return Never actually returns, this generic type matches any type
  837.      *         which the calling site requires. "Returning" the results of this
  838.      *         method, as done in the propagateExample above, will satisfy the
  839.      *         Java compiler requirement that all code paths return a value.
  840.      * @since 3.5
  841.      * @see #wrapAndThrow(Throwable)
  842.      */
  843.     public static <T> T rethrow(final Throwable throwable) {
  844.         // claim that the typeErasure invocation throws a RuntimeException
  845.         return ExceptionUtils.<T, RuntimeException>eraseType(throwable);
  846.     }

  847.     /**
  848.      * Streams causes of a Throwable.
  849.      * <p>
  850.      * A throwable without cause will return a stream containing one element - the input throwable. A throwable with one cause
  851.      * will return a stream containing two elements. - the input throwable and the cause throwable. A {@code null} throwable
  852.      * will return a stream of count zero.
  853.      * </p>
  854.      *
  855.      * <p>
  856.      * This method handles recursive cause chains that might otherwise cause infinite loops. The cause chain is
  857.      * processed until the end, or until the next item in the chain is already in the result.
  858.      * </p>
  859.      *
  860.      * @param throwable The Throwable to traverse
  861.      * @return A new Stream of Throwable causes.
  862.      * @since 3.13.0
  863.      */
  864.     public static Stream<Throwable> stream(final Throwable throwable) {
  865.         // No point building a custom Iterable as it would keep track of visited elements to avoid infinite loops
  866.         return getThrowableList(throwable).stream();
  867.     }

  868.     /**
  869.      * Worker method for the {@code throwableOfType} methods.
  870.      *
  871.      * @param <T> the type of Throwable you are searching.
  872.      * @param throwable  the throwable to inspect, may be null
  873.      * @param type  the type to search, subclasses match, null returns null
  874.      * @param fromIndex  the (zero-based) index of the starting position,
  875.      *  negative treated as zero, larger than chain size returns null
  876.      * @param subclass if {@code true}, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
  877.      * using references
  878.      * @return throwable of the {@code type} within throwables nested within the specified {@code throwable}
  879.      */
  880.     private static <T extends Throwable> T throwableOf(final Throwable throwable, final Class<T> type, int fromIndex, final boolean subclass) {
  881.         if (throwable == null || type == null) {
  882.             return null;
  883.         }
  884.         if (fromIndex < 0) {
  885.             fromIndex = 0;
  886.         }
  887.         final Throwable[] throwables = getThrowables(throwable);
  888.         if (fromIndex >= throwables.length) {
  889.             return null;
  890.         }
  891.         if (subclass) {
  892.             for (int i = fromIndex; i < throwables.length; i++) {
  893.                 if (type.isAssignableFrom(throwables[i].getClass())) {
  894.                     return type.cast(throwables[i]);
  895.                 }
  896.             }
  897.         } else {
  898.             for (int i = fromIndex; i < throwables.length; i++) {
  899.                 if (type.equals(throwables[i].getClass())) {
  900.                     return type.cast(throwables[i]);
  901.                 }
  902.             }
  903.         }
  904.         return null;
  905.     }

  906.     /**
  907.      * Returns the first {@link Throwable}
  908.      * that matches the specified class (exactly) in the exception chain.
  909.      * Subclasses of the specified class do not match - see
  910.      * {@link #throwableOfType(Throwable, Class)} for the opposite.
  911.      *
  912.      * <p>A {@code null} throwable returns {@code null}.
  913.      * A {@code null} type returns {@code null}.
  914.      * No match in the chain returns {@code null}.</p>
  915.      *
  916.      * @param <T> the type of Throwable you are searching.
  917.      * @param throwable  the throwable to inspect, may be null
  918.      * @param clazz  the class to search for, subclasses do not match, null returns null
  919.      * @return the first matching throwable from the throwable chain, null if no match or null input
  920.      * @since 3.10
  921.      */
  922.     public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz) {
  923.         return throwableOf(throwable, clazz, 0, false);
  924.     }

  925.     /**
  926.      * Returns the first {@link Throwable}
  927.      * that matches the specified type in the exception chain from
  928.      * a specified index.
  929.      * Subclasses of the specified class do not match - see
  930.      * {@link #throwableOfType(Throwable, Class, int)} for the opposite.
  931.      *
  932.      * <p>A {@code null} throwable returns {@code null}.
  933.      * A {@code null} type returns {@code null}.
  934.      * No match in the chain returns {@code null}.
  935.      * A negative start index is treated as zero.
  936.      * A start index greater than the number of throwables returns {@code null}.</p>
  937.      *
  938.      * @param <T> the type of Throwable you are searching.
  939.      * @param throwable  the throwable to inspect, may be null
  940.      * @param clazz  the class to search for, subclasses do not match, null returns null
  941.      * @param fromIndex  the (zero-based) index of the starting position,
  942.      *  negative treated as zero, larger than chain size returns null
  943.      * @return the first matching throwable from the throwable chain, null if no match or null input
  944.      * @since 3.10
  945.      */
  946.     public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz, final int fromIndex) {
  947.         return throwableOf(throwable, clazz, fromIndex, false);
  948.     }

  949.     /**
  950.      * Returns the throwable of the first {@link Throwable}
  951.      * that matches the specified class or subclass in the exception chain.
  952.      * Subclasses of the specified class do match - see
  953.      * {@link #throwableOfThrowable(Throwable, Class)} for the opposite.
  954.      *
  955.      * <p>A {@code null} throwable returns {@code null}.
  956.      * A {@code null} type returns {@code null}.
  957.      * No match in the chain returns {@code null}.</p>
  958.      *
  959.      * @param <T> the type of Throwable you are searching.
  960.      * @param throwable  the throwable to inspect, may be null
  961.      * @param type  the type to search for, subclasses match, null returns null
  962.      * @return the first matching throwable from the throwable chain, null if no match or null input
  963.      * @since 3.10
  964.      */
  965.     public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type) {
  966.         return throwableOf(throwable, type, 0, true);
  967.     }

  968.     /**
  969.      * Returns the first {@link Throwable}
  970.      * that matches the specified type in the exception chain from
  971.      * a specified index.
  972.      * Subclasses of the specified class do match - see
  973.      * {@link #throwableOfThrowable(Throwable, Class)} for the opposite.
  974.      *
  975.      * <p>A {@code null} throwable returns {@code null}.
  976.      * A {@code null} type returns {@code null}.
  977.      * No match in the chain returns {@code null}.
  978.      * A negative start index is treated as zero.
  979.      * A start index greater than the number of throwables returns {@code null}.</p>
  980.      *
  981.      * @param <T> the type of Throwable you are searching.
  982.      * @param throwable  the throwable to inspect, may be null
  983.      * @param type  the type to search for, subclasses match, null returns null
  984.      * @param fromIndex  the (zero-based) index of the starting position,
  985.      *  negative treated as zero, larger than chain size returns null
  986.      * @return the first matching throwable from the throwable chain, null if no match or null input
  987.      * @since 3.10
  988.      */
  989.     public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type, final int fromIndex) {
  990.         return throwableOf(throwable, type, fromIndex, true);
  991.     }

  992.     /**
  993.      * Tests whether the specified {@link Throwable} is unchecked and throws it if so.
  994.      *
  995.      * @param <T> The Throwable type.
  996.      * @param throwable the throwable to test and throw or return.
  997.      * @return the given throwable.
  998.      * @since 3.13.0
  999.      * @deprecated Use {@link #throwUnchecked(Throwable)}.
  1000.      */
  1001.     @Deprecated
  1002.     public static <T> T throwUnchecked(final T throwable) {
  1003.         if (throwable instanceof RuntimeException) {
  1004.             throw (RuntimeException) throwable;
  1005.         }
  1006.         if (throwable instanceof Error) {
  1007.             throw (Error) throwable;
  1008.         }
  1009.         return throwable;
  1010.     }

  1011.     /**
  1012.      * Tests whether the specified {@link Throwable} is unchecked and throws it if so.
  1013.      *
  1014.      * @param <T> The Throwable type.
  1015.      * @param throwable the throwable to test and throw or return.
  1016.      * @return the given throwable.
  1017.      * @since 3.14.0
  1018.      */
  1019.     public static <T extends Throwable> T throwUnchecked(final T throwable) {
  1020.         if (isUnchecked(throwable)) {
  1021.             throw asRuntimeException(throwable);
  1022.         }
  1023.         return throwable;
  1024.     }

  1025.     /**
  1026.      * Throws a checked exception without adding the exception to the throws
  1027.      * clause of the calling method. For checked exceptions, this method throws
  1028.      * an UndeclaredThrowableException wrapping the checked exception. For
  1029.      * Errors and RuntimeExceptions, the original exception is rethrown.
  1030.      * <p>
  1031.      * The downside to using this approach is that invoking code which needs to
  1032.      * handle specific checked exceptions must sniff up the exception chain to
  1033.      * determine if the caught exception was caused by the checked exception.
  1034.      * </p>
  1035.      *
  1036.      * @param throwable
  1037.      *            The throwable to rethrow.
  1038.      * @param <R> The type of the returned value.
  1039.      * @return Never actually returned, this generic type matches any type
  1040.      *         which the calling site requires. "Returning" the results of this
  1041.      *         method will satisfy the Java compiler requirement that all code
  1042.      *         paths return a value.
  1043.      * @since 3.5
  1044.      * @see #asRuntimeException(Throwable)
  1045.      * @see #hasCause(Throwable, Class)
  1046.      */
  1047.     public static <R> R wrapAndThrow(final Throwable throwable) {
  1048.         throw new UndeclaredThrowableException(throwUnchecked(throwable));
  1049.     }

  1050.     /**
  1051.      * Public constructor allows an instance of {@link ExceptionUtils} to be created, although that is not
  1052.      * normally necessary.
  1053.      *
  1054.      * @deprecated TODO Make private in 4.0.
  1055.      */
  1056.     @Deprecated
  1057.     public ExceptionUtils() {
  1058.         // empty
  1059.     }
  1060. }