001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.util;
020:
021: import java.io.PrintWriter;
022: import java.io.StringWriter;
023: import javax.servlet.jsp.JspException;
024: import javax.servlet.jsp.el.ELException;
025: import javax.servlet.ServletException;
026:
027: import org.apache.beehive.netui.util.internal.InternalStringBuilder;
028:
029: /**
030: * Format a {@link java.lang.Throwable} so that it displays well in HTML.
031: */
032: public class HtmlExceptionFormatter {
033:
034: /**
035: * An XHTML line break
036: */
037: private static final String HTML_LINE_BREAK = "<br/>";
038:
039: /**
040: * The end of line character to replace with an HTML line break
041: */
042: private static final String LINE_BREAK = "\n";
043:
044: private static final String CAUSED_BY = "caused by ";
045:
046: /**
047: * Format an exception into XHTML.
048: * <p/>
049: * Optionally include a message and the stack trace.
050: * <p/>
051: * The formatted exception will have line breaks replaced with XHTML line breaks for display
052: * in HTML. The String message of the cause will be included, and the stack trace of the
053: * cause is optionally included given the value of <code>includeStackTrace</code>
054: *
055: * @param message the message to include with the formatted exception. This is in addition to the message in the stack trace.
056: * @param t a Throwable exception to format
057: * @param includeStackTrace a boolean that determines whether to include the stack trace in the formatted output
058: * @return the formatted error message, optionally including the stack trace.
059: */
060: public static String format(String message, Throwable t,
061: boolean includeStackTrace) {
062: InternalStringBuilder buf = new InternalStringBuilder();
063:
064: if (message != null) {
065: buf.append(message);
066:
067: Throwable cause = discoverRootCause(t);
068: if (cause != null) {
069: buf.append(HTML_LINE_BREAK);
070: buf.append(CAUSED_BY);
071: buf.append(": ");
072: buf.append(cause.toString());
073: }
074: }
075:
076: if (includeStackTrace) {
077: if (message != null)
078: buf.append(HTML_LINE_BREAK);
079:
080: String st = addStackTrace(t);
081: buf.append(st);
082:
083: Throwable rootCause = null;
084: Throwable tmp = t;
085: while (hasRootCause(tmp)
086: && (rootCause = discoverRootCause(tmp)) != null) {
087: st = addStackTrace(rootCause);
088: buf.append(HTML_LINE_BREAK);
089: buf.append(st);
090: tmp = rootCause;
091: }
092: }
093:
094: return buf.toString().replaceAll(LINE_BREAK, HTML_LINE_BREAK);
095: }
096:
097: private static String addStackTrace(Throwable t) {
098: InternalStringBuilder buf = new InternalStringBuilder();
099: StringWriter sw = new StringWriter();
100: PrintWriter pw = new PrintWriter(sw);
101: t.printStackTrace(pw);
102: pw.flush();
103: pw.close();
104:
105: String error = sw.toString();
106: int pos = error.indexOf(LINE_BREAK);
107: if (pos != -1) {
108: String lineOne = error.substring(0, pos);
109: String rest = error.substring(pos);
110:
111: buf.append("<span class='pfErrorLineOne'>");
112: buf.append(lineOne);
113: buf.append("</span>");
114: buf.append(rest);
115: } else {
116: buf.append(sw.toString());
117: }
118:
119: return buf.toString();
120: }
121:
122: private static boolean hasRootCause(Throwable t) {
123: return t.getCause() == null
124: && (t instanceof JspException
125: || t instanceof ServletException || t instanceof ELException);
126: }
127:
128: private static Throwable discoverRootCause(Throwable t) {
129: Throwable cause = null;
130: if (t instanceof JspException)
131: cause = ((JspException) t).getRootCause();
132: else if (t instanceof ServletException)
133: cause = ((ServletException) t).getRootCause();
134: else if (t instanceof ELException)
135: cause = ((ELException) t).getRootCause();
136: else
137: cause = t.getCause();
138:
139: return cause;
140: }
141: }
|