| Utils.java |
/*
* $Id: Utils.java,v 1.53 2011/02/12 08:22:46 agoubard Exp $
*
* See the COPYRIGHT file for redistribution and use restrictions.
*/
package org.xins.common;
import java.lang.reflect.Method;
import org.xins.common.text.TextUtils;
/**
* General utility functions.
*
* @version $Revision: 1.53 $ $Date: 2011/02/12 08:22:46 $
* @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
*
* @since XINS 1.1.0
*/
public final class Utils {
/**
* Constructs a new <code>Utils</code> object.
*/
private Utils() {
// empty
}
/**
* Retrieves the name of the calling class at the specified level. The level
* <code>0</code> indicates the direct caller, while <code>1</code>
* indicates the caller of the caller.
*
* <p>If it cannot be determined, then <code>"<unknown>"</code> is returned.
*
* @param level
* the level of the caller, must be >= 0.
*
* @param methodMode
* <code>true</code> if the information wanted is the method name,
* <code>false</code> if the information wanted is the class name.
*
* @return
* the class name of method name of the caller of the caller of this method, at the
* specified level, never an empty string and never <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>level < 0</code>.
*
* @since XINS 2.0
*/
private static String getCallingTrace(int level, boolean methodMode)
throws IllegalArgumentException {
// Check preconditions
if (level < 0) {
throw new IllegalArgumentException("level (" + level + ") < 0");
}
int depth = level + 3;
// Create an exception in order to have a stack trace
Throwable exception = new Throwable();
// Analyze the stack trace
StackTraceElement[] trace = exception.getStackTrace();
if (trace != null) {
for (int pos = 0, i = 0; i < trace.length; i++) {
// Skip all non-authentic methods
String method = trace[pos].getMethodName();
while (method.startsWith("access$")) {
method = trace[++pos].getMethodName();
}
// If we are at the right depth, then return the method name
if (i == depth) {
if (methodMode) {
return method;
} else {
return trace[pos].getClassName();
}
// Otherwise go deeper
} else {
pos++;
}
}
}
// Fallback
return "<unknown>";
}
/**
* Retrieves the name of the calling class at the specified level. The level
* <code>0</code> indicates the direct caller, while <code>1</code>
* indicates the caller of the caller.
*
* <p>If it cannot be determined, then <code>"<unknown>"</code> is
* returned.
*
* @param level
* the level of the caller, must be >= 0.
*
* @return
* the class name of the caller of the caller of this method, at the
* specified level, never an empty string and never <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>level < 0</code>.
*
* @since XINS 1.3.0
*/
public static String getCallingClass(int level)
throws IllegalArgumentException {
return getCallingTrace(level, false);
}
/**
* Retrieves the name of the calling class. If it cannot be determined,
* then a special string (e.g. <code>"<unknown>"</code>) is returned.
*
* @return
* the class name of the caller of the caller of this method, never an
* empty string and never <code>null</code>.
*/
public static String getCallingClass() {
Throwable exception = new Throwable();
StackTraceElement[] trace = exception.getStackTrace();
if (trace != null && trace.length >= 3) {
StackTraceElement caller = trace[2];
if (caller != null) {
String callingClass = caller.getClassName();
if (! TextUtils.isEmpty(callingClass)) {
return callingClass;
}
}
}
// Fallback
return "<unknown>";
}
/**
* Retrieves the name of the calling method at the specified level. The
* level <code>0</code> indicates the direct caller, while <code>1</code>
* indicates the caller of the caller.
*
* <p>If it cannot be determined, then <code>"<unknown>"</code> is
* returned.
*
* @param level
* the level of the caller, must be >= 0.
*
* @return
* the method name of the caller of the caller of this method, at the
* specified level, never an empty string and never <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>level < 0</code>.
*
* @since XINS 1.3.0
*/
public static String getCallingMethod(int level)
throws IllegalArgumentException {
return getCallingTrace(level, true);
}
/**
* Retrieves the name of the calling method. If it cannot be determined,
* then a special string (e.g. <code>"<unknown>"</code>) is returned.
*
* @return
* the method name of the caller of the caller of this method, never an
* empty string and never <code>null</code>.
*/
public static String getCallingMethod() {
Throwable exception = new Throwable();
StackTraceElement[] trace = exception.getStackTrace();
if (trace != null && trace.length >= 3) {
StackTraceElement caller = trace[2];
if (caller != null) {
String callingMethod = caller.getMethodName();
if (! TextUtils.isEmpty(callingMethod)) {
return callingMethod;
}
}
}
// Fallback
return "<unknown>";
}
/**
* Logs an exception that will be ignored, with the specified detail
* message.
*
* @param detectingClass
* the name of the class that caught the exception, cannot be
* <code>null</code>.
*
* @param detectingMethod
* the name of the method within the <code>detectingClass</code> that
* caught the exception, cannot be <code>null</code>.
*
* @param subjectClass
* the name of the class which threw the exception, cannot be
* <code>null</code>.
*
* @param subjectMethod
* the name of the method (within the <code>subjectClass</code>) which
* threw the exception, cannot be <code>null</code>.
*
* @param detail
* detail message, can be <code>null</code>.
*
* @param exception
* the exception to log, cannot be <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>detectingClass == null || detectingMethod == null
* || subjectClass == null || subjectMethod == null
* || exception == null</code>.
*
* @since XINS 1.3.0
*/
private static void logIgnoredException(String detectingClass,
String detectingMethod,
String subjectClass,
String subjectMethod,
String detail,
Throwable exception)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("detectingClass", detectingClass,
"detectingMethod", detectingMethod,
"subjectClass", subjectClass,
"subjectMethod", subjectMethod);
MandatoryArgumentChecker.check("exception", exception);
// Perform the actual logging
Log.log_1051(exception.getClass().getName(), exception.getMessage(),
detectingClass, detectingMethod,
subjectClass, subjectMethod,
detail);
}
/**
* Logs an exception that will be ignored.
*
* @param exception
* the exception to log, cannot be <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>exception == null</code>.
*
* @since XINS 1.5.0
*/
public static void logIgnoredException(Throwable exception)
throws IllegalArgumentException {
// Determine detecting class and method
String detectingClass = getCallingClass();
String detectingMethod = getCallingMethod();
String sourceClass = null;
String sourceMethod = null;
try {
// Determine the source of the exception
StackTraceElement[] trace = exception.getStackTrace();
for (int i = 1; i < trace.length && sourceClass == null; i++) {
StackTraceElement stackTraceElement = trace[i];
if (stackTraceElement.getClassName().equals(detectingClass) &&
stackTraceElement.getMethodName().equals(detectingMethod)) {
// Go one level up the stack trace to know which method threw the exception
StackTraceElement source = trace[i - 1];
sourceClass = source.getClassName();
sourceMethod = source.getMethodName();
}
}
if (sourceClass == null) {
sourceClass = "<unknown>";
sourceMethod = "<unknown>";
}
// If there's any exception, then fallback to default values
} catch (Throwable t) {
sourceClass = "<unknown>";
sourceMethod = "<unknown>";
}
// Call alternative method with detail set to null
logIgnoredException(detectingClass, detectingMethod,
sourceClass, sourceMethod,
null, exception);
}
/**
* Logs an exception that will be ignored.
*
* @param detectingClass
* the name of the class that caught the exception, cannot be
* <code>null</code>.
*
* @param detectingMethod
* the name of the method within the <code>detectingClass</code> that
* caught the exception, cannot be <code>null</code>.
*
* @param subjectClass
* the name of the class which threw the exception, cannot be
* <code>null</code>.
*
* @param subjectMethod
* the name of the method (within the <code>subjectClass</code>) which
* threw the exception, cannot be <code>null</code>.
*
* @param exception
* the exception to log, cannot be <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>detectingClass == null || detectingMethod == null
* || subjectClass == null || subjectMethod == null
* || exception == null</code>.
*
* @since XINS 1.3.0
*
* @deprecated Since XINS 2.0, use {@link #logProgrammingError(Throwable exception)}
*/
public static void logIgnoredException(String detectingClass,
String detectingMethod,
String subjectClass,
String subjectMethod,
Throwable exception)
throws IllegalArgumentException {
// Call alternative method with detail set to null
logIgnoredException(detectingClass, detectingMethod,
subjectClass, subjectMethod,
null, exception);
}
/**
* Logs a programming error with an optional cause exception, and returns a
* <code>ProgrammingException</code> object for it.
*
* <p>The calling class/method are considered the detecting class and the
* caller of those (one level up) is considered the subject class/method
* for the programming error.
*
* @param detail
* the detail message, can be <code>null</code>.
*
* @return
* an appropriate {@link ProgrammingException} that can be thrown by the
* calling method, never <code>null</code>.
*/
public static ProgrammingException logProgrammingError(String detail) {
return logProgrammingError(getCallingClass(0), getCallingMethod(0),
getCallingClass(1), getCallingMethod(1),
detail);
}
/**
* Logs a programming error with an optional cause exception, and returns a
* <code>ProgrammingException</code> object for it.
*
* @param cause
* the cause exception, cannot be <code>null</code>.
*
* @return
* an appropriate {@link ProgrammingException} that can be thrown by the
* calling method, never <code>null</code>.
*/
public static ProgrammingException logProgrammingError(Throwable cause) {
return logProgrammingError(null, cause);
}
/**
* Logs a programming error with an optional cause exception and an optional
* message, and returns a <code>ProgrammingException</code> object for it.
*
* @param detail
* the detail message, can be <code>null</code>.
*
* @param cause
* the cause exception, cannot be <code>null</code>.
*
* @return
* an appropriate {@link ProgrammingException} that can be thrown by the
* calling method, never <code>null</code>.
*
* @since XINS 2.0.
*/
public static ProgrammingException logProgrammingError(String detail, Throwable cause) {
// Determine detecting class and method
String detectingClass = getCallingClass();
String detectingMethod = getCallingMethod();
String sourceClass = null;
String sourceMethod = null;
try {
// Determine the source of the exception
StackTraceElement[] trace = cause.getStackTrace();
for (int i = 1; i < trace.length && sourceClass == null; i++) {
StackTraceElement stackTraceElement = trace[i];
if (stackTraceElement.getClassName().equals(detectingClass) &&
stackTraceElement.getMethodName().equals(detectingMethod)) {
// Go one level up the stack trace to know which method threw the exception
StackTraceElement source = trace[i - 1];
sourceClass = source.getClassName();
sourceMethod = source.getMethodName();
}
}
if (sourceClass == null) {
sourceClass = "<unknown>";
sourceMethod = "<unknown>";
}
// If there's any exception, then fallback to default values
} catch (Throwable t) {
sourceClass = "<unknown>";
sourceMethod = "<unknown>";
}
// Log the programming error
return logProgrammingError(detectingClass, detectingMethod,
sourceClass, sourceMethod,
detail, cause);
}
/**
* }
* Logs a programming error with an optional cause exception, and returns a
* <code>ProgrammingException</code> object for it.
*
* @param detectingClass
* the name of the class that detected the problem, or
* <code>null</code> if unknown.
*
* @param detectingMethod
* the name of the method within the <code>detectingClass</code> that
* detected the problem, or <code>null</code> if unknown.
*
* @param subjectClass
* the name of the class which exposes the programming error, or
* <code>null</code> if unknown.
*
* @param subjectMethod
* the name of the method (within the <code>subjectClass</code>) which
* exposes the programming error, or <code>null</code> if unknown.
*
* @param detail
* the detail message, can be <code>null</code>.
*
* @param cause
* the cause exception, can be <code>null</code>.
*
* @return
* an appropriate {@link ProgrammingException} that can be thrown by the
* calling method, never <code>null</code>.
*
* @deprecated Since XINS 2.0, use {@link #logProgrammingError(String detail, Throwable cause)}
*/
public static ProgrammingException
logProgrammingError(String detectingClass,
String detectingMethod,
String subjectClass,
String subjectMethod,
String detail,
Throwable cause) {
// Log programming error (not due to exception)
if (cause == null) {
Log.log_1050(detectingClass, detectingMethod,
subjectClass, subjectMethod,
detail);
// Log programming error (due to exception)
} else {
Log.log_1052(cause,
detectingClass, detectingMethod,
subjectClass, subjectMethod,
detail);
}
// Construct and return ProgrammingException object
return new ProgrammingException(detectingClass, detectingMethod,
subjectClass, subjectMethod,
detail, cause);
}
/**
* Logs a programming error with no cause exception, and returns a
* <code>ProgrammingException</code> object for it.
*
* @param detectingClass
* the name of the class that detected the problem, or
* <code>null</code> if unknown.
*
* @param detectingMethod
* the name of the method within the <code>detectingClass</code> that
* detected the problem, or <code>null</code> if unknown.
*
* @param subjectClass
* the name of the class which exposes the programming error, or
* <code>null</code> if unknown.
*
* @param subjectMethod
* the name of the method (within the <code>subjectClass</code>) which
* exposes the programming error, or <code>null</code> if unknown.
*
* @param detail
* the detail message, can be <code>null</code>.
*
* @return
* an appropriate {@link ProgrammingException} that can be thrown by the
* calling method, never <code>null</code>.
*
* @deprecated Since XINS 2.0, use {@link #logProgrammingError(String detail)}
*/
public static ProgrammingException
logProgrammingError(String detectingClass,
String detectingMethod,
String subjectClass,
String subjectMethod,
String detail) {
return logProgrammingError(detectingClass, detectingMethod,
subjectClass, subjectMethod,
detail, null);
}
/**
* Determines the name of the specified class.
*
* @param c
* the class to determine the name for, not <code>null</code>.
*
* @return
* the name of the class, never <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>c == null</code>.
*
* @since XINS 1.2.0
*/
public static String getNameOfClass(Class c)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("c", c);
// Handle arrays
if (c.isArray()) {
Class comp = c.getComponentType();
String name = getNameOfClass(comp);
if (c.getName().charAt(0) == '[') {
name += "[]";
}
return name;
// Handle non-arrays (primitives and classes)
} else {
return c.getName();
}
}
/**
* Determines the name of the class of the specified object.
*
* @param object
* the object to determine the name of the class for, not
* <code>null</code>.
*
* @return
* the name of the class, never <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>object == null</code>.
*
* @since XINS 1.2.0
*/
public static String getClassName(Object object)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("object", object);
return getNameOfClass(object.getClass());
}
/**
* Determines the context class loader of the current thread if available.
* If not available the ClassLoader used to load this class is returned.
*
* @return
* the context ClassLoader of the current thread, never <code>null</code>.
*
* @since XINS 2.2
*/
public static ClassLoader getContextClassLoader() {
ClassLoader loader = null;
Thread thread = Thread.currentThread();
try {
Method loaderMethod = thread.getClass().getMethod("getContextClassLoader", null);
loader = (ClassLoader) loaderMethod.invoke(thread, null);
} catch (Throwable ex) {
loader = Utils.class.getClassLoader();
}
return loader;
}
}