Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  // Copyright 2004, 2005 The Apache Software Foundation
  //
  // Licensed 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.tapestry.util.exception;
 
 import java.io.*;
 import java.util.List;

Analyzes an exception, creating one or more ExceptionDescriptions from it.

Author(s):
Howard Lewis Ship
 
 
 public class ExceptionAnalyzer
 {
     private static final int SKIP_LEADING_WHITESPACE = 0;
 
     private static final int SKIP_T = 1;
 
     private static final int SKIP_OTHER_WHITESPACE = 2;
     
     private final List exceptionDescriptions = new ArrayList();
 
     private final List propertyDescriptions = new ArrayList();
 
     private final CharArrayWriter writer = new CharArrayWriter();
 
     private boolean exhaustive = false;

    
If true, then stack trace is extracted for each exception. If false, the default, then stack trace is extracted for only the deepest exception.
 
 
     public boolean isExhaustive()
     {
         return ;
     }
 
     public void setExhaustive(boolean value)
     {
          = value;
     }

    
Analyzes the exceptions. This builds an ExceptionDescriptionfor the exception. It also looks for a non-null java.lang.Throwableproperty. If one exists, then a second ExceptionDescriptionis created. This continues until no more nested exceptions can be found.

The description includes a set of name/value properties (as ExceptionProperty) object. This list contains all non-null properties that are not, themselves, java.lang.Throwable.

The name is the display name (not the logical name) of the property. The value is the toString() value of the property. Only properties defined in subclasses of java.lang.Throwableare included.

A future enhancement will be to alphabetically sort the properties by name.

 
 
     public ExceptionDescription[] analyze(Throwable exception)
     {
         Throwable thrown = exception;
         try
         {
             while (thrown != null)
             {
                 thrown = buildDescription(thrown);
             }
 
             ExceptionDescription[] result = new ExceptionDescription[.size()];
 
             return (ExceptionDescription[]) .toArray(result);
         }
         finally
         {
             .clear();
             .clear();
 
             .reset();
        }
    }
    protected Throwable buildDescription(Throwable exception)
    {
        BeanInfo info;
        Class exceptionClass;
        ExceptionProperty property;
        PropertyDescriptor[] descriptors;
        PropertyDescriptor descriptor;
        Throwable next = null;
        int i;
        Object value;
        Method method;
        ExceptionProperty[] properties;
        ExceptionDescription description;
        String stringValue;
        String message;
        String[] stackTrace = null;
        .clear();
        message = exception.getMessage();
        exceptionClass = exception.getClass();
        // Get properties, ignoring those in Throwable and higher
        // (including the 'message' property).
        try
        {
            info = Introspector.getBeanInfo(exceptionClassThrowable.class);
        }
        catch (IntrospectionException e)
        {
            return null;
        }
        descriptors = info.getPropertyDescriptors();
        for (i = 0; i < descriptors.lengthi++)
        {
            descriptor = descriptors[i];
            method = descriptor.getReadMethod();
            if (method == null)
                continue;
            try
            {
                value = method.invoke(exceptionnull);
            }
            catch (Exception e)
            {
                continue;
            }
            if (value == null)
                continue;
            // Some annoying exceptions duplicate the message property
            // (I'm talking to YOU SAXParseException), so just edit that out.
            if (message != null && message.equals(value))
                continue;
            // Skip Throwables ... but the first non-null found is the next 
            // exception (unless it refers to the current one - some 3rd party
            // libaries do this). We kind of count on there being no more 
            // than one Throwable property per Exception.
            if (value instanceof Throwable)
            {
                if (next == null && value != exception)
                    next = (Throwablevalue;
                continue;
            }
            stringValue = value.toString().trim();
            if (stringValue.length() == 0)
                continue;
            property = new ExceptionProperty(descriptor.getDisplayName(), value);
            .add(property);
        }
        // If exhaustive, or in the deepest exception (where there's no next)
        // the extract the stack trace.
        if (next == null || )
            stackTrace = getStackTrace(exception);
        // Would be nice to sort the properties here.
        properties = new ExceptionProperty[.size()];
        ExceptionProperty[] propArray = (ExceptionProperty[]) .toArray(properties);
        description = new ExceptionDescription(exceptionClass.getName(), messagepropArraystackTrace);
        .add(description);
        return next;
    }

    
Gets the stack trace for the exception, and converts it into an array of strings.

This involves parsing the string generated indirectly from Throwable.printStackTrace(PrintWriter). This method can get confused if the message (presumably, the first line emitted by printStackTrace()) spans multiple lines.

Different JVMs format the exception in different ways.

A possible expansion would be more flexibility in defining the pattern used. Hopefully all 'mainstream' JVMs are close enough for this to continue working.

    protected String[] getStackTrace(Throwable exception)
    {
        .reset();
        PrintWriter printWriter = new PrintWriter();
        exception.printStackTrace(printWriter);
        printWriter.close();
        String fullTrace = .toString();
        .reset();
        // OK, the trick is to convert the full trace into an array of stack frames.
        StringReader stringReader = new StringReader(fullTrace);
        LineNumberReader lineReader = new LineNumberReader(stringReader);
        int lineNumber = 0;
        List frames = new ArrayList();
        try
        {
            while (true)
            {
                String line = lineReader.readLine();
                if (line == null)
                    break;
                // Always ignore the first line.
                if (++lineNumber == 1)
                    continue;
                frames.add(stripFrame(line));
            }
            lineReader.close();
        }
        catch (IOException ex)
        {
            // Not likely to happen with this particular set
            // of readers.
        }
        String[] result = new String[frames.size()];
        return (String[]) frames.toArray(result);
    }

    
Sun's JVM prefixes each line in the stack trace with " <tab>at</tab> ", other JVMs don't. This method looks for and strips such stuff.
    private String stripFrame(String frame)
    {
        char[] array = frame.toCharArray();
        int i = 0;
        int state = ;
        boolean more = true;
        while (more)
        {
            // Ran out of characters to skip? Return the empty string.
            if (i == array.length)
                return "";
            char ch = array[i];
            switch (state)
            {
                // Ignore whitespace at the start of the line.
                case :
                    if (Character.isWhitespace(ch))
                    {
                        i++;
                        continue;
                    }
                    if (ch == 'a')
                    {
                        state = ;
                        i++;
                        continue;
                    }
                    // Found non-whitespace, not 'a'
                    more = false;
                    break;
                // Skip over the 't' after an 'a'
                case :
                    if (ch == 't')
                    {
                        state = ;
                        i++;
                        continue;
                    }
                    // Back out the skipped-over 'a'
                    i--;
                    more = false;
                    break;
                // Skip whitespace between 'at' and the name of the class
                case :
                    if (Character.isWhitespace(ch))
                    {
                        i++;
                        continue;
                    }
                    // Not whitespace
                    more = false;
                    break;
            }
        }
        // Found nothing to strip out.
        if (i == 0)
            return frame;
        return frame.substring(i);
    }

    
Produces a text based exception report to the provided stream.
    public void reportException(Throwable exceptionPrintStream stream)
    {
        int i;
        int j;
        ExceptionDescription[] descriptions;
        ExceptionProperty[] properties;
        String[] stackTrace;
        String message;
        descriptions = analyze(exception);
        for (i = 0; i < descriptions.lengthi++)
        {
            message = descriptions[i].getMessage();
            if (message == null)
                stream.println(descriptions[i].getExceptionClassName());
            else
                stream.println(descriptions[i].getExceptionClassName() + ": "
                        + descriptions[i].getMessage());
            properties = descriptions[i].getProperties();
            for (j = 0; j < properties.lengthj++)
                stream.println("   " + properties[j].getName() + ": " + properties[j].getValue());
            // Just show the stack trace on the deepest exception.
            if (i + 1 == descriptions.length)
            {
                stackTrace = descriptions[i].getStackTrace();
                for (j = 0; j < stackTrace.lengthj++)
                    stream.println(stackTrace[j]);
            }
            else
            {
                stream.println();
            }
        }
    }
New to GrepCode? Check out our FAQ X