gw.util.GosuExceptionUtil Maven / Gradle / Ivy
/*
* Copyright 2014 Guidewire Software, Inc.
*/
package gw.util;
import gw.util.concurrent.LockingLazyVar;
import java.io.StringWriter;
import java.io.PrintWriter;
import gw.config.CommonServices;
public class GosuExceptionUtil
{
private static final LockingLazyVar FORCE_THROWER = new LockingLazyVar() {
@Override
protected IForceThrower init() {
return CommonServices.getGosuIndustrialPark().getForceThrower();
}
};
/**
* Given an Exception, finds the root cause and returns it. This may end up
* returning the originally passed exception if that was the root cause.
*
* @param e the Throwable whose root cause should be located.
*
* @return the root Throwable, or e if it is the root.
*/
public static Throwable findExceptionCause( Throwable e )
{
Throwable error = e;
Throwable cause = e;
while( (error.getCause() != null) && (error.getCause() != error) )
{
cause = error.getCause();
error = cause;
}
return cause;
}
/**
* This method can be used to provide a more informative type-mismatch exception message than the standard java reflection
* does with its IllegalArgumentException.
*
* @param exceptionToWrap - exception to wrap
* @param featureName - feature being executed when the (e.g. "method" or "constructor")
* @param actualParameters - the actual parameter types expected
* @param args - the args passed to the feature
*/
public static void throwArgMismatchException( IllegalArgumentException exceptionToWrap, String featureName, Class[] actualParameters, Object[] args )
{
String argTypes = "(";
for( int i = 0; i < actualParameters.length; i++ )
{
Class aClass = actualParameters[i];
if( i > 0 )
{
argTypes += " ,";
}
argTypes += aClass.getName();
}
argTypes += ")";
String rttTypes = "(";
for( int i = 0; i < args.length; i++ )
{
if( i > 0 )
{
rttTypes += " ,";
}
if( args[i] != null )
{
rttTypes += args[i].getClass().getName();
}
else
{
rttTypes += "null";
}
}
rttTypes += ")";
throw new RuntimeException( "Tried to pass values of types: " + rttTypes + " into " + featureName + " that takes types " + argTypes, exceptionToWrap );
}
/**
* Given an Exception and an Exception type to look for, finds the exception of that type
* or returns null if none of that type exist.
*/
public static T findException(Class exceptionTypeToFind, Throwable t) {
Throwable cause = t;
while (cause != null)
{
if( exceptionTypeToFind.isAssignableFrom( cause.getClass() ) )
{
//noinspection unchecked
return (T)cause;
}
if( cause == cause.getCause() )
{
return null;
}
cause = cause.getCause();
}
return null;
}
public static RuntimeException convertToRuntimeException( Throwable t )
{
RuntimeException e;
if (t instanceof RuntimeException) {
e = (RuntimeException) t;
} else {
e = new RuntimeException(t);
}
return e;
}
public static String getStackTraceAsString(Throwable t) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
t.printStackTrace(pw);
return sw.toString();
}
/**
* This method will force the Throwable object that is passed in to be rethrown, regardless if it is a checked exception
* or not, allowing a developer to side step dealing with or wrapping checked exceptions in java.
*
*
This method "returns" a RuntimeException, so that you don't have to put spurious return statements after
* it is called, but the method never exits normally, so the return value is never used.
*
*
An example usage would be:
*
* throw GosuExceptionUtil.forceThrow( myCheckedException );
*
*
* @param t the throwable object to be rethrown.
*/
public static RuntimeException forceThrow( Throwable t )
{
return forceThrow( t, null );
}
public static RuntimeException forceThrow( Throwable t, String ctxInfo )
{
if( ctxInfo != null )
{
addContextInfoImpl( t, ctxInfo );
}
FORCE_THROWER.get().throwException( t );
return null;
}
public static T addContextInfo( T t, String ctxInfo )
{
return addContextInfoImpl( t, ctxInfo );
}
public static T addContextInfoImpl( T t, String ctxInfo )
{
StackTraceElement[] currentStack = new RuntimeException().getStackTrace();
StackTraceElement[] throwableStack = t.getStackTrace();
for( int i = 0; i < throwableStack.length && i < currentStack.length; i++ )
{
StackTraceElement currentElt = currentStack[currentStack.length - i - 1];
StackTraceElement throwableElt = throwableStack[throwableStack.length - i - 1];
if( !GosuObjectUtil.equals( throwableElt.getClassName(), currentElt.getClassName() ) &&
!areMethodsEquals( throwableElt, currentElt ) &&
!areFilesEquals( throwableElt, currentElt ) &&
throwableElt.getLineNumber() != currentElt.getLineNumber() )
{
break;
}
if( currentStack.length - i - 1 == 2 )
{
throwableElt = new StackTraceElement( throwableElt.getClassName(),
throwableElt.getMethodName(),
throwableElt.getFileName() + ":" + throwableElt.getLineNumber() + ") - (" + ctxInfo,
-1 );
throwableStack[throwableStack.length - i - 1] = throwableElt;
t.setStackTrace( throwableStack );
break;
}
}
return t;
}
private static boolean areMethodsEquals( StackTraceElement throwableElt, StackTraceElement currentElt )
{
if( throwableElt.getMethodName() == null )
{
return currentElt.getMethodName() == null;
}
String s = throwableElt.getMethodName();
int index = s.indexOf( ' ' );
if( index >= 0 )
{
s = s.substring( 0, index );
}
return s.equals( currentElt.getMethodName() );
}
private static boolean areFilesEquals( StackTraceElement throwableElt, StackTraceElement currentElt )
{
if( throwableElt.getFileName() == null )
{
return currentElt.getFileName() == null;
}
String s = throwableElt.getFileName();
int index = s.indexOf( ':' );
if( index >= 0 )
{
s = s.substring( 0, index );
}
return s.equals( currentElt.getFileName() );
}
public interface IForceThrower
{
public void throwException( Throwable t );
}
}