blob: 595077ad63a224ce696a96931bee958554f53253 [file] [log] [blame]
/*
* 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 flex.messaging.util;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
/**
*
*/
public class ExceptionUtil
{
/**
* List of no-arg methods that are known to return a wrapped throwable.
**/
public static String[] unwrapMethods = { "getRootCause", "getTargetException",
"getTargetError", "getException",
"getCausedByException", "getLinkedException" };
/**
* Get the wrapped Exception object from the Throwable object.
* @param t the Throwable object
* @return Throwable the wrapped exception object if any
*/
public static Throwable wrappedException(Throwable t)
{
// Handle these statically since they are core to Java
return (t instanceof InvocationTargetException)?
((InvocationTargetException)t).getTargetException() : getRootCauseWithReflection(t);
}
/**
* Get to the base exception (if any).
* @param t the Throwable object
* @return the base Exception object
*/
public static Throwable baseException(Throwable t)
{
Throwable wrapped = wrappedException(t);
return wrapped != null? baseException(wrapped) : t;
}
/**
* Return the stack trace in a String.
* @param t the Throwable object
* @return String the String presentation of the Throwable object
*/
public static String toString(Throwable t)
{
StringWriter strWrt = new StringWriter();
t.printStackTrace(new PrintWriter(strWrt));
return strWrt.toString();
}
/**
* Return the stack trace up to the first line that starts with prefix.
*
* <p>Example: ExceptionUtil.getStackTraceUpTo(exception, "jrunx.");</p>
* @param t the Throwable object
* @param prefix the prefix message that we are looking for
* @return String the String of stack trace lines till the prefix message is located
*/
public static String getStackTraceUpTo(Throwable t, String prefix)
{
StringTokenizer tokens = new StringTokenizer(toString(t), "\n\r");
StringBuffer trace = new StringBuffer();
boolean done = false;
String lookingFor = "at " + prefix;
while (!done && tokens.hasMoreElements())
{
String token = tokens.nextToken();
if (token.indexOf(lookingFor) == -1)
trace.append(token);
else
done = true;
trace.append(StringUtils.NEWLINE);
}
return trace.toString();
}
/**
* return the top n lines of this stack trace.
*
* <p>Example: ExceptionUtil.getStackTraceLines(exception, 10);</p>
* @param t the Throwable object
* @param numLines number of lines we should trace down
* @return String the String of stack trace lines
*/
public static String getStackTraceLines(Throwable t, int numLines)
{
StringTokenizer tokens = new StringTokenizer(toString(t), "\n\r");
StringBuffer trace = new StringBuffer();
for (int i=0; i<numLines; i++)
{
String token = tokens.nextToken();
trace.append(token);
trace.append(StringUtils.NEWLINE);
}
return trace.toString();
}
/**
* Return the "nth" method call from the stack trace of "t", where 0 is
* the top.
* @param t the Throwable object
* @param nth the line number of the message should we skip
* @return String the callAt String
*/
public static String getCallAt(Throwable t, int nth)
{
StringTokenizer tokens = new StringTokenizer(toString(t), "\n\r");
try
{
// Skip the first line - the exception message
for(int i = 0; i <= nth; ++i)
tokens.nextToken();
// get the method name from the next token
String token = tokens.nextToken();
int index1 = token.indexOf(' ');
int index2 = token.indexOf('(');
StringBuffer call = new StringBuffer();
call.append(token.substring(index1 < 0 ? 0 : index1 + 1, index2 < 0 ? call.length() : index2));
int index3 = token.indexOf(':', index2 < 0 ? 0 : index2);
if(index3 >= 0)
{
int index4 = token.indexOf(')', index3);
call.append(token.substring(index3, index4 < 0 ? token.length() : index4));
}
return call.toString();
}
catch(NoSuchElementException e) {}
return "unknown";
}
/**
* Utility method for converting an exception into a string. This
* method unwinds all wrapped exceptions
* @param t The throwable exception
* @return The printable exception
*/
public static String exceptionToString(Throwable t)
{
StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw);
//print out the exception stack.
printExceptionStack(t, out, 0);
return sw.toString();
}
/**
* Utility method for converting an exception and all chained root causes into a
* string. Unlike <code>exceptionToString(Throwable)</code> which prints the chain
* from most nested root cause down to the top-level exception, this method prints
* from the top-level exception down to the most nested root cause.
*
* @param t The throwable exception.
* @return The printable exception.
*/
public static String exceptionFollowedByRootCausesToString(Throwable t)
{
StringBuffer output = new StringBuffer();
Throwable root = t;
while (root != null)
{
output.append((root == t) ? ((root instanceof Exception) ? " Exception: " : " Error: ") : " Root cause: ");
output.append(ExceptionUtil.toString(root));
// Do not recurse if the root cause has already been printed; this will have happened if the root cause has
// been assigned to the current Throwable via initCause() or as a constructor argument.
Throwable cause = root.getCause();
root = ExceptionUtil.wrappedException(root);
if (cause == root)
break;
}
return output.toString();
}
/**
* Recursively prints out a stack of wrapped exceptions.
*
* @param th the Throwable object
* @param out the output writer to print to
* @param depth the number of levels the stack should be printed
*/
protected static void printExceptionStack(Throwable th, PrintWriter out, int depth){
//only print the stack depth if the depth is greater than 0
boolean printStackDepth = depth>0;
Throwable wrappedException = ExceptionUtil.wrappedException(th);
if (wrappedException != null)
{
printStackDepth = true;
printExceptionStack(wrappedException, out, depth + 1);
}
if(printStackDepth){
out.write("[" + depth + "]");
}
th.printStackTrace(out);
}
private static Throwable getRootCauseWithReflection(Throwable t)
{
for(int i = 0; i < unwrapMethods.length; i++)
{
Method m = null;
try
{
m = t.getClass().getMethod(unwrapMethods[i], (Class[])null);
return (Throwable) m.invoke(t, (Object[])null);
}
catch(Exception nsme)
{
// ignore
}
}
return null;
}
}