/*
* 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 org.apache.log4j;
import org.apache.log4j.spi.
ThrowableRenderer;
import java.io.
File;
import java.lang.reflect.
Method;
import java.net.
URL;
import java.security.
CodeSource;
import java.util.
HashMap;
import java.util.
Map;
/**
* Enhanced implementation of ThrowableRenderer. Uses Throwable.getStackTrace
* if running on JDK 1.4 or later and delegates to DefaultThrowableRenderer.render
* on earlier virtual machines.
*
* @since 1.2.16
*/
public final class
EnhancedThrowableRenderer implements
ThrowableRenderer {
/**
* Throwable.getStackTrace() method.
*/
private
Method getStackTraceMethod;
/**
* StackTraceElement.getClassName() method.
*/
private
Method getClassNameMethod;
/**
* Construct new instance.
*/
public
EnhancedThrowableRenderer() {
try {
Class[]
noArgs = null;
getStackTraceMethod =
Throwable.class.
getMethod("getStackTrace",
noArgs);
Class ste =
Class.
forName("java.lang.StackTraceElement");
getClassNameMethod =
ste.
getMethod("getClassName",
noArgs);
} catch(
Exception ex) {
}
}
/**
* {@inheritDoc}
*/
public
String[]
doRender(final
Throwable throwable) {
if (
getStackTraceMethod != null) {
try {
Object[]
noArgs = null;
Object[]
elements = (
Object[])
getStackTraceMethod.
invoke(
throwable,
noArgs);
String[]
lines = new
String[
elements.length + 1];
lines[0] =
throwable.
toString();
Map classMap = new
HashMap();
for(int
i = 0;
i <
elements.length;
i++) {
lines[
i+1] =
formatElement(
elements[
i],
classMap);
}
return
lines;
} catch(
Exception ex) {
}
}
return
DefaultThrowableRenderer.
render(
throwable);
}
/**
* Format one element from stack trace.
* @param element element, may not be null.
* @param classMap map of class name to location.
* @return string representation of element.
*/
private
String formatElement(final
Object element, final
Map classMap) {
StringBuffer buf = new
StringBuffer("\tat ");
buf.
append(
element);
try {
String className =
getClassNameMethod.
invoke(
element, (
Object[]) null).
toString();
Object classDetails =
classMap.
get(
className);
if (
classDetails != null) {
buf.
append(
classDetails);
} else {
Class cls =
findClass(
className);
int
detailStart =
buf.
length();
buf.
append('[');
try {
CodeSource source =
cls.
getProtectionDomain().
getCodeSource();
if (
source != null) {
URL locationURL =
source.
getLocation();
if (
locationURL != null) {
//
// if a file: URL
//
if ("file".
equals(
locationURL.
getProtocol())) {
String path =
locationURL.
getPath();
if (
path != null) {
//
// find the last file separator character
//
int
lastSlash =
path.
lastIndexOf('/');
int
lastBack =
path.
lastIndexOf(
File.
separatorChar);
if (
lastBack >
lastSlash) {
lastSlash =
lastBack;
}
//
// if no separator or ends with separator (a directory)
// then output the URL, otherwise just the file name.
//
if (
lastSlash <= 0 ||
lastSlash ==
path.
length() - 1) {
buf.
append(
locationURL);
} else {
buf.
append(
path.
substring(
lastSlash + 1));
}
}
} else {
buf.
append(
locationURL);
}
}
}
} catch(
SecurityException ex) {
}
buf.
append(':');
Package pkg =
cls.
getPackage();
if (
pkg != null) {
String implVersion =
pkg.
getImplementationVersion();
if (
implVersion != null) {
buf.
append(
implVersion);
}
}
buf.
append(']');
classMap.
put(
className,
buf.
substring(
detailStart));
}
} catch(
Exception ex) {
}
return
buf.
toString();
}
/**
* Find class given class name.
* @param className class name, may not be null.
* @return class, will not be null.
* @throws ClassNotFoundException thrown if class can not be found.
*/
private
Class findClass(final
String className) throws
ClassNotFoundException {
try {
return
Thread.
currentThread().
getContextClassLoader().
loadClass(
className);
} catch (
ClassNotFoundException e) {
try {
return
Class.
forName(
className);
} catch (
ClassNotFoundException e1) {
return
getClass().
getClassLoader().
loadClass(
className);
}
}
}
}