/* * Copyright (c) 2002-2012 Alibaba Group Holding Limited. * All rights reserved. * * 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 com.alibaba.toolkit.util.exception; import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; /** * 异常的辅助类. * * @author Michael Zhou * @version $Id: ExceptionHelper.java,v 1.1 2003/07/03 07:26:22 baobao Exp $ */ public class ExceptionHelper { private static final String STRING_EXCEPTION_MESSAGE = ": "; private static final String STRING_CAUSED_BY = "Caused by: "; private static final String STRING_MORE_PREFIX = "\t... "; private static final String STRING_MORE_SUFFIX = " more"; private static final String STRING_STACK_TRACE_PREFIX = "\tat "; private static final String STRING_CR = "\r"; private static final String STRING_LF = "\n"; private static final String GET_STACK_TRACE_METHOD_NAME = "getStackTrace"; private static Method GET_STACK_TRACE_METHOD; static { // JDK1.4支持Throwable.getStackTrace()方法 try { GET_STACK_TRACE_METHOD = Throwable.class.getMethod(GET_STACK_TRACE_METHOD_NAME, new Class[0]); } catch (NoSuchMethodException e) { } } /** * 从<code>ChainedThrowable</code>实例中取得<code>Throwable</code>对象. * * @param throwable <code>ChainedThrowable</code>实例 * @return <code>Throwable</code>对象 */ private static Throwable getThrowable(ChainedThrowable throwable) { if (throwable instanceof ChainedThrowableDelegate) { return ((ChainedThrowableDelegate) throwable).delegatedThrowable; } else { return (Throwable) throwable; } } /** * 将<code>Throwable</code>转换成<code>ChainedThrowable</code>. 如果已经是 * <code>ChainedThrowable</code>了, 则直接返回, 否则将它包装在 * <code>ChainedThrowableDelegate</code>中返回. * * @param throwable <code>Throwable</code>对象 * @return <code>ChainedThrowable</code>对象 */ public static ChainedThrowable getChainedThrowable(Throwable throwable) { if (throwable != null && !(throwable instanceof ChainedThrowable)) { return new ChainedThrowableDelegate(throwable); } return (ChainedThrowable) throwable; } /** * 取得被代理的异常的起因, 如果起因不是<code>ChainedThrowable</code>, 则用 * <code>ChainedThrowableDelegate</code>包装并返回. * * @param throwable 异常 * @return 异常的起因 */ public static ChainedThrowable getChainedThrowableCause(ChainedThrowable throwable) { return getChainedThrowable(throwable.getCause()); } /** * 打印调用栈到标准错误. * * @param throwable 异常 */ public static void printStackTrace(ChainedThrowable throwable) { printStackTrace(throwable, System.err); } /** * 打印调用栈到指定输出流. * * @param throwable 异常 * @param stream 输出字节流 */ public static void printStackTrace(ChainedThrowable throwable, PrintStream stream) { printStackTrace(throwable, new PrintWriter(stream)); } /** * 打印调用栈到指定输出流. * * @param throwable 异常 * @param writer 输出字符流 */ public static void printStackTrace(ChainedThrowable throwable, PrintWriter writer) { synchronized (writer) { String[] currentStack = analyzeStackTrace(throwable); printThrowableMessage(throwable, writer, false); for (String element : currentStack) { writer.println(element); } printStackTraceRecursive(throwable, writer, currentStack); writer.flush(); } } /** * 递归地打印所有异常链的调用栈. * * @param throwable 异常 * @param writer 输出流 * @param currentStack 当前的堆栈 */ private static void printStackTraceRecursive(ChainedThrowable throwable, PrintWriter writer, String[] currentStack) { ChainedThrowable cause = getChainedThrowableCause(throwable); if (cause != null) { String[] causeStack = analyzeStackTrace(cause); int i = currentStack.length - 1; int j = causeStack.length - 1; for (; i >= 0 && j >= 0; i--, j--) { if (!currentStack[i].equals(causeStack[j])) { break; } } printThrowableMessage(cause, writer, true); for (i = 0; i <= j; i++) { writer.println(causeStack[i]); } if (j < causeStack.length - 1) { writer.println(STRING_MORE_PREFIX + (causeStack.length - j - 1) + STRING_MORE_SUFFIX); } printStackTraceRecursive(cause, writer, causeStack); } } /** * 打印异常的message. * * @param throwable 异常 * @param writer 输出流 * @param cause 是否是起因异常 */ private static void printThrowableMessage(ChainedThrowable throwable, PrintWriter writer, boolean cause) { StringBuffer buffer = new StringBuffer(); if (cause) { buffer.append(STRING_CAUSED_BY); } Throwable t = getThrowable(throwable); buffer.append(t.getClass().getName()); String message = t.getMessage(); if (message != null) { buffer.append(STRING_EXCEPTION_MESSAGE).append(message); } writer.println(buffer.toString()); } /** * 分析异常的调用栈, 取得当前异常的信息, 不包括起因异常的信息. * * @param throwable 取得指定异常的调用栈 * @return 调用栈数组 */ private static String[] analyzeStackTrace(ChainedThrowable throwable) { if (GET_STACK_TRACE_METHOD != null) { Throwable t = getThrowable(throwable); try { Object[] stackTrace = (Object[]) GET_STACK_TRACE_METHOD.invoke(t, new Object[0]); String[] list = new String[stackTrace.length]; for (int i = 0; i < stackTrace.length; i++) { list[i] = STRING_STACK_TRACE_PREFIX + stackTrace[i]; } return list; } catch (IllegalAccessException e) { } catch (IllegalArgumentException e) { } catch (InvocationTargetException e) { } } return new StackTraceAnalyzer(throwable).getLines(); } /** 分析stack trace的辅助类. */ private static class StackTraceAnalyzer { private Throwable throwable; private String message; private StackTraceEntry currentEntry = new StackTraceEntry(); private StackTraceEntry selectedEntry = currentEntry; private StackTraceEntry entry; StackTraceAnalyzer(ChainedThrowable throwable) { this.throwable = getThrowable(throwable); this.message = this.throwable.getMessage(); // 取得stack trace字符串. StringWriter writer = new StringWriter(); PrintWriter pw = new PrintWriter(writer); throwable.printCurrentStackTrace(pw); String stackTraceDump = writer.toString(); // 分割字符串, 按行分割, 但不能割开message字串 int p = 0; int i = -1; int j = -1; int k = -1; while (p < stackTraceDump.length()) { boolean includesMessage = false; int s = p; if (i == -1 && message != null) { i = stackTraceDump.indexOf(message, p); } if (j == -1) { j = stackTraceDump.indexOf(STRING_CR, p); } if (k == -1) { k = stackTraceDump.indexOf(STRING_LF, p); } // 如果找到message if (i != -1 && (j == -1 || i <= j) && (k == -1 || i <= k)) { includesMessage = true; p = i + message.length(); i = -1; if (j < p) { j = -1; } if (k < p) { k = -1; } // 继续直到换行 } // 如果找到换行CR或CRLF if (j != -1 && (k == -1 || j < k)) { p = j + 1; if (p < stackTraceDump.length() && stackTraceDump.charAt(p) == STRING_LF.charAt(0)) { p++; // CRLF } addLine(stackTraceDump.substring(s, j), includesMessage); j = -1; if (k < p) { k = -1; } continue; } // 如果找到LF if (k != -1) { int q = k + 1; addLine(stackTraceDump.substring(s, k), includesMessage); p = q; k = -1; continue; } // 如果都没找到, 说明到了最后一行 int q = stackTraceDump.length(); if (p + 1 < q) { addLine(stackTraceDump.substring(s), includesMessage); p = q; } } // 选择"较小"的entry if (currentEntry.compareTo(selectedEntry) < 0) { selectedEntry = currentEntry; } } private void addLine(String line, boolean includesMessage) { StackTraceEntry nextEntry = currentEntry.accept(line, includesMessage); if (nextEntry != null) { // 选择"较小"的entry if (currentEntry.compareTo(selectedEntry) < 0) { selectedEntry = currentEntry; } currentEntry = nextEntry; } } String[] getLines() { return (String[]) selectedEntry.lines.toArray(new String[selectedEntry.lines.size()]); } private class StackTraceEntry implements Comparable { private List lines = new ArrayList(10); private int includesMessage = 0; private int includesThrowable = 0; private int count = 0; StackTraceEntry accept(String line, boolean includesMessage) { // 如果是...at XXX.java(Line...), 则加入到lines列表中. // 否则创建并返回新的entry. if (line.startsWith(STRING_STACK_TRACE_PREFIX)) { lines.add(line); count++; return null; } else if (count > 0) { StackTraceEntry newEntry = new StackTraceEntry(); newEntry.accept(line, includesMessage); return newEntry; } // 设置权重 if (includesMessage) { this.includesMessage = 1; } if (line.indexOf(throwable.getClass().getName()) != -1) { this.includesThrowable = 1; } return null; } public int compareTo(Object o) { StackTraceEntry otherEntry = (StackTraceEntry) o; int thisWeight = includesMessage + includesThrowable; int otherWeight = otherEntry.includesMessage + otherEntry.includesThrowable; // weight大的排在前, 如果weight相同, 则count小的排在前 if (thisWeight == otherWeight) { return count - otherEntry.count; } else { return otherWeight - thisWeight; } } } } }