package org.webcat.exceptiondoctor.runtime;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import org.webcat.exceptiondoctor.AbstractExceptionHandler;
import org.webcat.exceptiondoctor.ExceptionHandlerInterface;
public class ExceptionMap
{
// Singleton
private static ExceptionMap singletonMap = new ExceptionMap();
// The map that stores the Exception names and handlers that handle them.
private HashMap<String, ExceptionHandlerInterface> exMap;
/**
* Create a new exception map, this initializes the hashmap.
*
*/
private ExceptionMap()
{
exMap = new HashMap<String, ExceptionHandlerInterface>();
}
/**
* Exception map getter
*
* @return this gets the exception map singleton
*/
public synchronized static ExceptionMap getExceptionMap()
{
return singletonMap;
}
/**
* This method adds a mapping from an exception to a handler
*
* @param exceptionName
* Exception to be mapped
* @param handlerClassRef
* Handler to be mapped
*/
private synchronized void addMapping(String exceptionName,
ExceptionHandlerInterface handlerClassRef)
{
exMap.put(exceptionName, handlerClassRef);
}
/**
* The method looks for a maping from the exception to an appropriate
* handler. If there is no mapping it will create one (Only if there is a
* handler for the specific exception.
*
* @param exception
* The exception to find a handler for.
* @return A handler for the class. This will never return null because All
* exceptions are inherited from Throwable.
*/
public synchronized ExceptionHandlerInterface getHandler(Throwable exception)
{
ExceptionHandlerInterface classRef = null;
/*
* This tracks if the method is at the first level of recursion. It
* makes sure that no mapping is created between a throwable and a
* derived cause.
*/
boolean topLevel = true;
classRef = getHandler(exception, topLevel);
return classRef;
}
/**
* This method creates an instance of a handler from a class name.
*
* @param classRef
* The handler class to be initiated.
* @return an instance of the class or null if it could not be instanciated.
*/
private AbstractExceptionHandler getHandlerInstance(Class<?> classRef)
throws ClassNotFoundException
{
AbstractExceptionHandler ex = null;
// All exception handlers have a no argument constructor.
Class<?>[] args = {};
Constructor<?> maker;
// Try to create an instance of the handler. If not then
// return null.
try
{
maker = classRef.getConstructor(args);
Object[] instArgs = {};
ex = (AbstractExceptionHandler) maker.newInstance(instArgs);
}
catch (Throwable e)
{
throw new ClassNotFoundException();
}
return ex;
}
/**
* This method gets the handler for a corresponding exception.
*
* @param exception
* The exception we are looking for a handler for
* @param topLevel
* The level of recursion
* @return An instance of the corresponding handler
*/
private ExceptionHandlerInterface getHandler(Throwable exception,
boolean topLevel)
{
// Look for an existing mapping
String exceptionName = getExceptionName(exception);
ExceptionHandlerInterface classRef = exMap.get(exceptionName);
// If mapping doesnt exist try to make one.
if (classRef == null)
{
classRef = buildMapEntry(exception, topLevel);
}
return classRef;
}
/**
* This is a utility to get the simple name for an exception.
*
* @param exception
* the named exception
* @return The simple name for the exception
*/
private String getExceptionName(Throwable exception)
{
return exception.getClass().getSimpleName();
}
/**
* This builds a map entry for the exception if you are at the top level of
* recursion, otherwise it simply returns the appropriate handler
*
* @param exception
* the exception to find a mapping for
* @param topLevel
* Level of recursion
* @return either the appropriate handle or null.
*/
private ExceptionHandlerInterface buildMapEntry(Throwable exception,
boolean topLevel)
{
Class<?> classRef = null;
ExceptionHandlerInterface handle;
String exName = getExceptionName(exception);
// Try to find a mapping in the classpath.
try
{
try
{
// First, try the default package
classRef = Class.forName(exName + "Handler");
}
catch (ClassNotFoundException e)
{
try
{
// Next, try the built-in location
classRef = Class.forName(
"org.webcat.exceptiondoctor.handlers."
+ exName
+ "Handler");
}
catch (ClassNotFoundException ee)
{
// Finally, look in the older, legacy location
classRef = Class.forName("handlers." + exName + "Handler");
}
}
handle = getHandlerInstance(classRef);
if (topLevel)
{
addMapping(exName, handle);
}
}
catch (ClassNotFoundException e)
{
topLevel = false;
if (exception.getCause() != null)
{
// Recursive call on the cause of the exception.
handle = getHandler(exception.getCause(), topLevel);
}
else
{
return null;
}
}
return handle;
}
}