001: /* Copyright 2001, 2004 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal;
007:
008: import java.io.PrintWriter;
009: import java.util.ArrayList;
010: import java.util.Iterator;
011: import java.util.List;
012: import javax.servlet.http.HttpServletResponse;
013:
014: import org.apache.commons.logging.Log;
015: import org.apache.commons.logging.LogFactory;
016:
017: public class ExceptionHelper {
018:
019: private static final Log log = LogFactory
020: .getLog(ExceptionHelper.class);
021:
022: // List of strings to match in traceback for various containers
023: // This array must be expanded as additional Application Server
024: // containers become better known
025: private static final String boundaries[] = { "at javax.servlet.http.HttpServlet." };
026:
027: /**
028: * Generate traceback only to the Servlet-container interface.
029: *
030: * @param ex any throwable exception
031: * @return stack trace string without container layers
032: */
033: public static String shortStackTrace(Throwable ex) {
034: if (ex == null)
035: return "";
036: java.io.StringWriter sw = new java.io.StringWriter();
037: ex.printStackTrace(new java.io.PrintWriter(sw));
038: sw.flush();
039: String stktr = sw.toString();
040: return trimStackTrace(stktr);
041: }
042:
043: /**
044: * Trims a String representation of a Stack Trace to remove
045: * the portion of the trace that is in the servlet container layer.
046: * @param stackTrace - String result of printStackTrace
047: * @return the stack trace with portions of the trace that dive into the container
048: * layer removed.
049: */
050: static String trimStackTrace(String stackTrace) {
051:
052: StringBuffer trimmedTrace = new StringBuffer();
053:
054: // a List of Strings to be trimmed and appended to the buffer
055: // these represent elements in the causal chain
056: List fragments = new ArrayList();
057:
058: int causeCut = (stackTrace.indexOf("Caused by"));
059:
060: if (causeCut > 0) {
061: // there are one or more Caused by fragments to consider
062: // we traverse stackTrace, parsing out fragments for later processing
063: // and updating stackTrace to contain the remaining unparsed portion
064: // as we go
065:
066: while (stackTrace.length() > 0) {
067:
068: if (stackTrace.startsWith("Caused by")) {
069: // don't count the "Caused by" leading the stackTrace
070: causeCut = stackTrace.substring(9).indexOf(
071: "Caused by");
072: if (causeCut > 0)
073: causeCut += 9;
074: } else {
075: causeCut = stackTrace.indexOf("Caused by");
076: }
077:
078: if (causeCut > -1) {
079: // stackTrace currently includes multiple fragments
080: // parse out the first and leave the rest for next iteration
081:
082: fragments.add(stackTrace.substring(0, causeCut));
083: stackTrace = stackTrace.substring(causeCut);
084: } else {
085: // stackTrace currently is a bare fragment
086: // grab it
087: fragments.add(stackTrace);
088: stackTrace = "";
089: }
090: }
091: } else {
092: // there's ony a single Throwable in the chain
093: fragments.add(stackTrace);
094: }
095:
096: // now that we have fragments to consider
097:
098: for (Iterator iter = fragments.iterator(); iter.hasNext();) {
099: String consideredFragment = (String) iter.next();
100:
101: // flag to indicate that a trimmed form of this fragment has been appended
102: // to the trimmed stack trace buffer
103: boolean appended = false;
104: for (int i = 0; i < boundaries.length; i++) {
105: int cut = consideredFragment.indexOf(boundaries[i]);
106: if (cut > 0) {
107: // stack trace includes a trace through our container
108: // in which we are not interested: trim it.
109: // grab the desired portion up to the boundary
110: trimmedTrace.append(consideredFragment.substring(0,
111: cut).trim());
112: trimmedTrace.append("\n");
113: appended = true;
114: break;
115: }
116: }
117:
118: if (!appended) {
119: // a trimmed version of this fragment was not appended
120: // because it doesn't need to be trimmed -- append the whole thing.
121: trimmedTrace.append(consideredFragment.trim());
122: trimmedTrace.append("\n");
123: }
124: }
125:
126: return trimmedTrace.toString();
127: }
128:
129: /**
130: * Generic Exception Handler called from catch clause
131: *
132: * @param eid the ErrorID (as seen from catch)
133: * @param parm
134: * @param ex the Exception caught
135: * @throws PortalException
136: */
137: public static void genericHandler(ErrorID eid, String parm,
138: Throwable ex) throws PortalException {
139:
140: // *** Handle PortalExceptions ***
141:
142: // Log it if logging was deferred in .signal() call
143: // Rethrow it
144: if (ex instanceof PortalException) {
145: if (((PortalException) ex).isLogPending())
146: traceToLog(eid, parm, ex);
147: throw (PortalException) ex;
148: }
149:
150: // *** Handle all other Exceptions ***
151:
152: // Log the message and traceback
153: traceToLog(eid, parm, ex);
154:
155: // Create a derived PortalException chained to this
156: PortalException nex = new PortalException(eid, ex);
157:
158: nex.setLogPending(false);
159: ProblemsTable.store(nex);
160: throw nex;
161: }
162:
163: public static void genericHandler(ErrorID eid, Throwable ex)
164: throws PortalException {
165: genericHandler(eid, null, ex);
166: }
167:
168: /**
169: * Create PortalException from ErrorID and throw it. Maybe trace it.
170: * @param eid ErrorId
171: * @param parm Additional error information
172: * @param tracenow Trace now or defer till first catch.
173: * @throws PortalException
174: */
175: public static void signal(ErrorID eid, String parm, boolean tracenow)
176: throws PortalException {
177: PortalException nex = new PortalException(eid);
178: nex.setParameter(parm);
179: signal(nex, tracenow);
180: }
181:
182: /**
183: * Create PortalException from Errorid, trace, and throw it.
184: * @param eid ErrorID to use to generate PortalException
185: * @param parm Additional error information
186: * @throws PortalException
187: */
188: public static void signal(ErrorID eid, String parm)
189: throws PortalException {
190: signal(eid, parm, true);
191: }
192:
193: /**
194: * Throw PortalException provided by caller, maybe trace it.
195: * @param nex Exception provided by caller
196: * @param tracenow Trace now, or later after first catch.
197: * @throws PortalException
198: */
199: private static void signal(PortalException nex, boolean tracenow)
200: throws PortalException {
201: if (tracenow) {
202: traceToLog(nex.getErrorID(), nex.getParameter(), nex);
203: ProblemsTable.store(nex);
204: }
205: throw nex;
206: }
207:
208: /**
209: * Generate, trace, and throw Portal Exception given ErrorID.
210: * @param eid ErrorID
211: * @throws PortalException
212: */
213: public static void signal(ErrorID eid) throws PortalException {
214: signal(eid, null, true);
215: }
216:
217: /**
218: * Common logic for generating log entry of errors
219: * @param eid ErrorID with initial message
220: * @param parm Parameter string to append to eid msg
221: * @param ex Old exception
222: */
223: private static void traceToLog(ErrorID eid, String parm,
224: Throwable ex) {
225:
226: if (ex != null && ex instanceof PortalException) {
227: if (!((PortalException) ex).isLogPending())
228: return; // This PortalException was already logged.
229: else
230: ((PortalException) ex).setLogPending(false);
231: }
232:
233: String logmsg = errorInfo(eid, parm, ex);
234: log.error(logmsg);
235: }
236:
237: /**
238: * Generate error string for logging or /problems online display
239: * @param eid Error ID
240: * @param parm Parameter string
241: * @param ex Exception
242: * @return Multiline text with message and traceback
243: */
244: public static String errorInfo(ErrorID eid, String parm,
245: Throwable ex) {
246: StringBuffer errorinfobuffer = new StringBuffer(1000);
247:
248: if (eid != Errors.legacy)
249: errorinfobuffer.append(eid.getMessage()); // Error ID message
250: else
251: errorinfobuffer.append(ex.getMessage());
252:
253: if (parm != null) { // Parameter data
254: errorinfobuffer.append("\n [specific data: ");
255: errorinfobuffer.append(parm);
256: errorinfobuffer.append("] ");
257: }
258:
259: errorinfobuffer.append("\n");
260:
261: if (ex != null)
262: errorinfobuffer.append(shortStackTrace(ex)); // Stack trace
263: return errorinfobuffer.toString();
264: }
265:
266: /**
267: * Generic Top-Level Exception Handler caled from catch clause
268: * (doesn't rethrow exception)
269: * @param eid Error ID
270: * @param parm Parameter string
271: * @param t Exception caught
272: */
273: public static void genericTopHandler(ErrorID eid, String parm,
274: Throwable t) {
275:
276: // If this is an already logged Portal Exception, we are done
277: if (t instanceof PortalException
278: && !((PortalException) t).isLogPending()) {
279: return;
280: }
281:
282: traceToLog(eid, parm, t);
283:
284: if (t instanceof PortalException) // already in the table
285: return;
286:
287: // Create a derived PortalException (just for Problems Table)
288: PortalException nex = null;
289: nex = new PortalException(eid, t);
290: ProblemsTable.store(nex);
291: }
292:
293: public static void genericTopHandler(ErrorID eid, Throwable ex) {
294: genericTopHandler(eid, null, ex);
295: }
296:
297: /**
298: * Generate HTML page to send to end user after fatal error
299: * @param resp Servlet response object
300: * @param e PortalException received at Servlet code.
301: */
302: public static void generateErrorPage(HttpServletResponse resp,
303: Exception e) {
304: resp.setContentType("text/html");
305: resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
306: try {
307: PrintWriter out = resp.getWriter();
308: out.println("<h1>uPortal Error</h1>");
309: out
310: .println("<p>Sorry, but uPortal encountered an error that is preventing it from rendering. "
311: + "The error must be corrected by system administrators. Try again later.</p>");
312: //out.println("<p><a href='http://www.yale.edu/portal'>Click here to display the static Yaleinfo page.</a></p>");
313: out.println("<!--");
314: ErrorID eid = Errors.bug;
315: String parm = "";
316: if (e instanceof PortalException) {
317: PortalException pe = (PortalException) e;
318: if (pe.errorID != null)
319: eid = pe.errorID;
320: if (pe.parameter != null)
321: parm = pe.parameter;
322: }
323: out.println(errorInfo(eid, parm, e));
324: out.println("-->");
325: out.flush();
326: } catch (Exception ex) {
327: ;
328: }
329: }
330: }
|