/*
 * 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.lang.exception;

import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

A shared implementation of the nestable exception functionality.

The code is shared between NestableError, NestableException and NestableRuntimeException.

Author:Apache Software Foundation, Rafal Krzewski, Daniel L. Rall, Kasper Nielsen, Steven Caswell, Sean C. Sullivan
Since:1.0
Version:$Id: NestableDelegate.java 905636 2010-02-02 14:03:32Z niallp $
/** * <p>A shared implementation of the nestable exception functionality.</p> * <p> * The code is shared between * {@link org.apache.commons.lang.exception.NestableError NestableError}, * {@link org.apache.commons.lang.exception.NestableException NestableException} and * {@link org.apache.commons.lang.exception.NestableRuntimeException NestableRuntimeException}. * </p> * * @author Apache Software Foundation * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a> * @author Daniel L. Rall * @author <a href="mailto:knielsen@apache.org">Kasper Nielsen</a> * @author <a href="mailto:steven@caswell.name">Steven Caswell</a> * @author Sean C. Sullivan * @since 1.0 * @version $Id: NestableDelegate.java 905636 2010-02-02 14:03:32Z niallp $ */
public class NestableDelegate implements Serializable {
Required for serialization support.
See Also:
  • Serializable
/** * Required for serialization support. * * @see java.io.Serializable */
private static final long serialVersionUID = 1L;
Constructor error message.
/** * Constructor error message. */
private transient static final String MUST_BE_THROWABLE = "The Nestable implementation passed to the NestableDelegate(Nestable) " + "constructor must extend java.lang.Throwable";
Holds the reference to the exception or error that we're wrapping (which must be a Nestable implementation).
/** * Holds the reference to the exception or error that we're * wrapping (which must be a {@link * org.apache.commons.lang.exception.Nestable} implementation). */
private Throwable nestable = null;
Whether to print the stack trace top-down. This public flag may be set by calling code, typically in initialisation. This exists for backwards compatability, setting it to false will return the library to v1.0 behaviour (but will affect all users of the library in the classloader).
Since:2.0
/** * Whether to print the stack trace top-down. * This public flag may be set by calling code, typically in initialisation. * This exists for backwards compatability, setting it to false will return * the library to v1.0 behaviour (but will affect all users of the library * in the classloader). * @since 2.0 */
public static boolean topDown = true;
Whether to trim the repeated stack trace. This public flag may be set by calling code, typically in initialisation. This exists for backwards compatability, setting it to false will return the library to v1.0 behaviour (but will affect all users of the library in the classloader).
Since:2.0
/** * Whether to trim the repeated stack trace. * This public flag may be set by calling code, typically in initialisation. * This exists for backwards compatability, setting it to false will return * the library to v1.0 behaviour (but will affect all users of the library * in the classloader). * @since 2.0 */
public static boolean trimStackFrames = true;
Whether to match subclasses via indexOf. This public flag may be set by calling code, typically in initialisation. This exists for backwards compatability, setting it to false will return the library to v2.0 behaviour (but will affect all users of the library in the classloader).
Since:2.1
/** * Whether to match subclasses via indexOf. * This public flag may be set by calling code, typically in initialisation. * This exists for backwards compatability, setting it to false will return * the library to v2.0 behaviour (but will affect all users of the library * in the classloader). * @since 2.1 */
public static boolean matchSubclasses = true;
Constructs a new NestableDelegate instance to manage the specified Nestable.
Params:
  • nestable – the Nestable implementation (must extend Throwable)
Since:2.0
/** * Constructs a new <code>NestableDelegate</code> instance to manage the * specified <code>Nestable</code>. * * @param nestable the Nestable implementation (<i>must</i> extend * {@link java.lang.Throwable}) * @since 2.0 */
public NestableDelegate(Nestable nestable) { if (nestable instanceof Throwable) { this.nestable = (Throwable) nestable; } else { throw new IllegalArgumentException(MUST_BE_THROWABLE); } }
Returns the error message of the Throwable in the chain of Throwables at the specified index, numbered from 0.
Params:
  • index – the index of the Throwable in the chain of Throwables
Throws:
Returns:the error message, or null if the Throwable at the specified index in the chain does not contain a message
Since:2.0
/** * Returns the error message of the <code>Throwable</code> in the chain of <code>Throwable</code>s at the * specified index, numbered from 0. * * @param index * the index of the <code>Throwable</code> in the chain of <code>Throwable</code>s * @return the error message, or null if the <code>Throwable</code> at the specified index in the chain does not * contain a message * @throws IndexOutOfBoundsException * if the <code>index</code> argument is negative or not less than the count of <code>Throwable</code>s * in the chain * @since 2.0 */
public String getMessage(int index) { Throwable t = this.getThrowable(index); if (Nestable.class.isInstance(t)) { return ((Nestable) t).getMessage(0); } return t.getMessage(); }
Returns the full message contained by the Nestable and any nested Throwables.
Params:
  • baseMsg – the base message to use when creating the full message. Should be generally be called via nestableHelper.getMessage(super.getMessage()), where super is an instance of Throwable.
Returns:The concatenated message for this and all nested Throwables
Since:2.0
/** * Returns the full message contained by the <code>Nestable</code> and any nested <code>Throwable</code>s. * * @param baseMsg * the base message to use when creating the full message. Should be generally be called via * <code>nestableHelper.getMessage(super.getMessage())</code>, where <code>super</code> is an * instance of {@link java.lang.Throwable}. * @return The concatenated message for this and all nested <code>Throwable</code>s * @since 2.0 */
public String getMessage(String baseMsg) { Throwable nestedCause = ExceptionUtils.getCause(this.nestable); String causeMsg = nestedCause == null ? null : nestedCause.getMessage(); if (nestedCause == null || causeMsg == null) { return baseMsg; // may be null, which is a valid result } if (baseMsg == null) { return causeMsg; } return baseMsg + ": " + causeMsg; }
Returns the error message of this and any nested Throwables in an array of Strings, one element for each message. Any Throwable not containing a message is represented in the array by a null. This has the effect of cause the length of the returned array to be equal to the result of the getThrowableCount() operation.
Returns:the error messages
Since:2.0
/** * Returns the error message of this and any nested <code>Throwable</code>s in an array of Strings, one element * for each message. Any <code>Throwable</code> not containing a message is represented in the array by a null. * This has the effect of cause the length of the returned array to be equal to the result of the * {@link #getThrowableCount()} operation. * * @return the error messages * @since 2.0 */
public String[] getMessages() { Throwable[] throwables = this.getThrowables(); String[] msgs = new String[throwables.length]; for (int i = 0; i < throwables.length; i++) { msgs[i] = (Nestable.class.isInstance(throwables[i]) ? ((Nestable) throwables[i]).getMessage(0) : throwables[i].getMessage()); } return msgs; }
Returns the Throwable in the chain of Throwables at the specified index, numbered from 0.
Params:
  • index – the index, numbered from 0, of the Throwable in the chain of Throwables
Throws:
Returns:the Throwable
Since:2.0
/** * Returns the <code>Throwable</code> in the chain of * <code>Throwable</code>s at the specified index, numbered from 0. * * @param index the index, numbered from 0, of the <code>Throwable</code> in * the chain of <code>Throwable</code>s * @return the <code>Throwable</code> * @throws IndexOutOfBoundsException if the <code>index</code> argument is * negative or not less than the count of <code>Throwable</code>s in the * chain * @since 2.0 */
public Throwable getThrowable(int index) { if (index == 0) { return this.nestable; } Throwable[] throwables = this.getThrowables(); return throwables[index]; }
Returns the number of Throwables contained in the Nestable contained by this delegate.
Returns:the throwable count
Since:2.0
/** * Returns the number of <code>Throwable</code>s contained in the * <code>Nestable</code> contained by this delegate. * * @return the throwable count * @since 2.0 */
public int getThrowableCount() { return ExceptionUtils.getThrowableCount(this.nestable); }
Returns this delegate's Nestable and any nested Throwables in an array of Throwables, one element for each Throwable.
Returns:the Throwables
Since:2.0
/** * Returns this delegate's <code>Nestable</code> and any nested * <code>Throwable</code>s in an array of <code>Throwable</code>s, one * element for each <code>Throwable</code>. * * @return the <code>Throwable</code>s * @since 2.0 */
public Throwable[] getThrowables() { return ExceptionUtils.getThrowables(this.nestable); }
Returns the index, numbered from 0, of the first Throwable that matches the specified type, or a subclass, in the chain of Throwables with an index greater than or equal to the specified index. The method returns -1 if the specified type is not found in the chain.

NOTE: From v2.1, we have clarified the Nestable interface such that this method matches subclasses. If you want to NOT match subclasses, please use ExceptionUtils.indexOfThrowable(Throwable, Class, int) (which is avaiable in all versions of lang). An alternative is to use the public static flag matchSubclasses on NestableDelegate, however this is not recommended.

Params:
  • type – the type to find, subclasses match, null returns -1
  • fromIndex – the index, numbered from 0, of the starting position in the chain to be searched
Throws:
Returns:index of the first occurrence of the type in the chain, or -1 if the type is not found
Since:2.0
/** * Returns the index, numbered from 0, of the first <code>Throwable</code> * that matches the specified type, or a subclass, in the chain of <code>Throwable</code>s * with an index greater than or equal to the specified index. * The method returns -1 if the specified type is not found in the chain. * <p> * NOTE: From v2.1, we have clarified the <code>Nestable</code> interface * such that this method matches subclasses. * If you want to NOT match subclasses, please use * {@link ExceptionUtils#indexOfThrowable(Throwable, Class, int)} * (which is avaiable in all versions of lang). * An alternative is to use the public static flag {@link #matchSubclasses} * on <code>NestableDelegate</code>, however this is not recommended. * * @param type the type to find, subclasses match, null returns -1 * @param fromIndex the index, numbered from 0, of the starting position in * the chain to be searched * @return index of the first occurrence of the type in the chain, or -1 if * the type is not found * @throws IndexOutOfBoundsException if the <code>fromIndex</code> argument * is negative or not less than the count of <code>Throwable</code>s in the * chain * @since 2.0 */
public int indexOfThrowable(Class type, int fromIndex) { if (type == null) { return -1; } if (fromIndex < 0) { throw new IndexOutOfBoundsException("The start index was out of bounds: " + fromIndex); } Throwable[] throwables = ExceptionUtils.getThrowables(this.nestable); if (fromIndex >= throwables.length) { throw new IndexOutOfBoundsException("The start index was out of bounds: " + fromIndex + " >= " + throwables.length); } if (matchSubclasses) { 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 the stack trace of this exception the the standar error stream.
/** * Prints the stack trace of this exception the the standar error * stream. */
public void printStackTrace() { printStackTrace(System.err); }
Prints the stack trace of this exception to the specified stream.
Params:
  • out – PrintStream to use for output.
See Also:
/** * Prints the stack trace of this exception to the specified * stream. * * @param out <code>PrintStream</code> to use for output. * @see #printStackTrace(PrintWriter) */
public void printStackTrace(PrintStream out) { synchronized (out) { PrintWriter pw = new PrintWriter(out, false); printStackTrace(pw); // Flush the PrintWriter before it's GC'ed. pw.flush(); } }
Prints the stack trace of this exception to the specified writer. If the Throwable class has a getCause method (i.e. running on jre1.4 or higher), this method just uses Throwable's printStackTrace() method. Otherwise, generates the stack-trace, by taking into account the 'topDown' and 'trimStackFrames' parameters. The topDown and trimStackFrames are set to 'true' by default (produces jre1.4-like stack trace).
Params:
  • out – PrintWriter to use for output.
/** * Prints the stack trace of this exception to the specified * writer. If the Throwable class has a <code>getCause</code> * method (i.e. running on jre1.4 or higher), this method just * uses Throwable's printStackTrace() method. Otherwise, generates * the stack-trace, by taking into account the 'topDown' and * 'trimStackFrames' parameters. The topDown and trimStackFrames * are set to 'true' by default (produces jre1.4-like stack trace). * * @param out <code>PrintWriter</code> to use for output. */
public void printStackTrace(PrintWriter out) { Throwable throwable = this.nestable; // if running on jre1.4 or higher, use default printStackTrace if (ExceptionUtils.isThrowableNested()) { if (throwable instanceof Nestable) { ((Nestable)throwable).printPartialStackTrace(out); } else { throwable.printStackTrace(out); } return; } // generating the nested stack trace List stacks = new ArrayList(); while (throwable != null) { String[] st = getStackFrames(throwable); stacks.add(st); throwable = ExceptionUtils.getCause(throwable); } // If NOT topDown, reverse the stack String separatorLine = "Caused by: "; if (!topDown) { separatorLine = "Rethrown as: "; Collections.reverse(stacks); } // Remove the repeated lines in the stack if (trimStackFrames) { trimStackFrames(stacks); } synchronized (out) { for (Iterator iter=stacks.iterator(); iter.hasNext();) { String[] st = (String[]) iter.next(); for (int i=0, len=st.length; i < len; i++) { out.println(st[i]); } if (iter.hasNext()) { out.print(separatorLine); } } } }
Captures the stack trace associated with the specified Throwable object, decomposing it into a list of stack frames.
Params:
  • t – The Throwable.
Returns: An array of strings describing each stack frame.
Since:2.0
/** * Captures the stack trace associated with the specified * <code>Throwable</code> object, decomposing it into a list of * stack frames. * * @param t The <code>Throwable</code>. * @return An array of strings describing each stack frame. * @since 2.0 */
protected String[] getStackFrames(Throwable t) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw, true); // Avoid infinite loop between decompose() and printStackTrace(). if (t instanceof Nestable) { ((Nestable) t).printPartialStackTrace(pw); } else { t.printStackTrace(pw); } return ExceptionUtils.getStackFrames(sw.getBuffer().toString()); }
Trims the stack frames. The first set is left untouched. The rest of the frames are truncated from the bottom by comparing with one just on top.
Params:
  • stacks – The list containing String[] elements
Since:2.0
/** * Trims the stack frames. The first set is left untouched. The rest * of the frames are truncated from the bottom by comparing with * one just on top. * * @param stacks The list containing String[] elements * @since 2.0 */
protected void trimStackFrames(List stacks) { for (int size=stacks.size(), i=size-1; i > 0; i--) { String[] curr = (String[]) stacks.get(i); String[] next = (String[]) stacks.get(i-1); List currList = new ArrayList(Arrays.asList(curr)); List nextList = new ArrayList(Arrays.asList(next)); ExceptionUtils.removeCommonFrames(currList, nextList); int trimmed = curr.length - currList.size(); if (trimmed > 0) { currList.add("\t... "+trimmed+" more"); stacks.set( i, currList.toArray(new String[currList.size()]) ); } } } }