blob: 3af471e82b0fc922d55be62fc51b0887cc13883c [file] [log] [blame]
/*
* Copyright 1999,2004 The Apache Software Foundation.
*
* Licensed 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.spi;
import java.io.PrintWriter;
import java.io.Writer;
import java.lang.reflect.Method;
import java.util.Vector;
import org.apache.log4j.helpers.PlatformInfo;
/**
* ThrowableInformation is log4j's internal representation of throwables. It
* essentially consists of a string array, called 'rep', where the first
* element, that is rep[0], represents the string representation of the
* throwable (i.e. the value you get when you do throwable.toString()) and
* subsequent elements correspond the stack trace with the top most entry of the
* stack corresponding to the second entry of the 'rep' array that is rep[1].
*
* Note that ThrowableInformation does not store the throwable it represents.
*
* @author Ceki Gülcü
*
*/
public class ThrowableInformation implements java.io.Serializable {
static final long serialVersionUID = -4748765566864322735L;
//private transient Throwable throwable;
private String[] rep;
public ThrowableInformation(Throwable throwable) {
VectorWriter vw = new VectorWriter();
extractStringRep(throwable, vw);
rep = vw.toStringArray();
}
public ThrowableInformation(String[] rep) {
this.rep = rep;
}
// public Throwable getThrowable() {
// return throwable;
//}
public void extractStringRep(Throwable t, VectorWriter vw) {
t.printStackTrace(vw);
// Check if the Throwable t has a nested Throwable. If so, invoke
// extractStringRep recursively.
// Note that the Throwable.getCause was added in JDK 1.4. The printStackTrace
// method was modified in JDK 1.4 to handle the nested throwable returned
// by Throwable.getCause.
try {
Class tC = t.getClass();
Method[] mA = tC.getMethods();
Method nextThrowableMethod = null;
for (int i = 0; i < mA.length; i++) {
if (("getCause".equals(mA[i].getName()) && !PlatformInfo.isJDK14OrLater())
|| "getRootCause".equals(mA[i].getName())
|| "getNextException".equals(mA[i].getName())
|| "getException".equals(mA[i].getName())) {
// check param types
Class[] params = mA[i].getParameterTypes();
if ((params == null) || (params.length == 0)) {
// just found the getter for the nested throwable
nextThrowableMethod = mA[i];
break; // no need to search further
}
}
}
if (nextThrowableMethod != null) {
// get the nested throwable and log it
Throwable nextT =
(Throwable) nextThrowableMethod.invoke(t, new Object[0]);
if (nextT != null) {
vw.print("Root cause follows.");
extractStringRep(nextT, vw);
}
}
} catch (Exception e) {
// do nothing
}
}
/**
* Retun a clone of the string representation of the exceptopn (throwable)
* that this object represents.
*/
public String[] getThrowableStrRep() {
return (String[]) rep.clone();
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ThrowableInformation)) {
return false;
}
ThrowableInformation r = (ThrowableInformation) o;
if (rep == null) {
if (r.rep != null) {
return false;
} else {
return true;
}
}
// at this point we know that both rep and r.rep are non-null.
if (rep.length != r.rep.length) {
return false;
}
int len = rep.length;
for (int i = 0; i < len; i++) {
if (!rep[i].equals(r.rep[i])) {
return false;
}
}
return true;
}
}
/**
* VectorWriter is a seemingly trivial implemtantion of PrintWriter. The
* throwable instance that we are trying to represnt is asked to print itself to
* a VectorWriter.
*
* By our design choice, r string representation of the throwable does not
* contain any line separators. It follows that println() methods of
* VectorWriter ignore the 'ln' part.
*/
class VectorWriter extends PrintWriter {
private Vector v;
VectorWriter() {
super(new NullWriter());
v = new Vector();
}
public void print(Object o) {
v.addElement(o.toString());
}
public void print(char[] chars) {
v.addElement(new String(chars));
}
public void print(String s) {
v.addElement(s);
}
public void println(Object o) {
v.addElement(o.toString());
}
// JDK 1.1.x apprenly uses this form of println while in
// printStackTrace()
public void println(char[] chars) {
v.addElement(new String(chars));
}
public void println(String s) {
v.addElement(s);
}
public void write(char[] chars) {
v.addElement(new String(chars));
}
public void write(char[] chars, int off, int len) {
v.addElement(new String(chars, off, len));
}
public void write(String s, int off, int len) {
v.addElement(s.substring(off, off + len));
}
public void write(String s) {
v.addElement(s);
}
public String[] toStringArray() {
int len = v.size();
String[] sa = new String[len];
for (int i = 0; i < len; i++) {
sa[i] = (String) v.elementAt(i);
}
return sa;
}
}
class NullWriter extends Writer {
public void close() {
// blank
}
public void flush() {
// blank
}
public void write(char[] cbuf, int off, int len) {
// blank
}
}