001: /* ====================================================================
002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
003: *
004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by Jcorporate Ltd.
021: * (http://www.jcorporate.com/)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. "Jcorporate" and product names such as "Expresso" must
026: * not be used to endorse or promote products derived from this
027: * software without prior written permission. For written permission,
028: * please contact info@jcorporate.com.
029: *
030: * 5. Products derived from this software may not be called "Expresso",
031: * or other Jcorporate product names; nor may "Expresso" or other
032: * Jcorporate product names appear in their name, without prior
033: * written permission of Jcorporate Ltd.
034: *
035: * 6. No product derived from this software may compete in the same
036: * market space, i.e. framework, without prior written permission
037: * of Jcorporate Ltd. For written permission, please contact
038: * partners@jcorporate.com.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Jcorporate Ltd. Contributions back
056: * to the project(s) are encouraged when you make modifications.
057: * Please send them to support@jcorporate.com. For more information
058: * on Jcorporate Ltd. and its products, please see
059: * <http://www.jcorporate.com/>.
060: *
061: * Portions of this software are based upon other open source
062: * products and are subject to their respective licenses.
063: */
064:
065: package com.jcorporate.expresso.services.controller;
066:
067: import com.jcorporate.expresso.core.controller.ControllerException;
068: import com.jcorporate.expresso.core.controller.ControllerRequest;
069: import com.jcorporate.expresso.core.controller.ControllerResponse;
070: import com.jcorporate.expresso.core.controller.DBController;
071: import com.jcorporate.expresso.core.controller.NonHandleableException;
072: import com.jcorporate.expresso.core.controller.Output;
073: import com.jcorporate.expresso.core.controller.State;
074: import com.jcorporate.expresso.core.db.DBException;
075: import com.jcorporate.expresso.core.dbobj.SecuredDBObject;
076: import com.jcorporate.expresso.core.misc.ConfigManager;
077: import com.jcorporate.expresso.core.misc.ConfigurationException;
078: import com.jcorporate.expresso.core.misc.DateTime;
079: import com.jcorporate.expresso.core.misc.EMailSender;
080: import com.jcorporate.expresso.core.misc.EventHandler;
081: import com.jcorporate.expresso.core.misc.SerializableString;
082: import com.jcorporate.expresso.core.misc.StringUtil;
083: import com.jcorporate.expresso.core.security.User;
084: import com.jcorporate.expresso.kernel.exception.ChainedException;
085: import com.jcorporate.expresso.services.dbobj.DBMessage;
086: import com.jcorporate.expresso.services.dbobj.Setup;
087: import org.apache.log4j.Logger;
088:
089: import java.io.ByteArrayOutputStream;
090: import java.io.PrintStream;
091: import java.util.Iterator;
092: import java.util.StringTokenizer;
093:
094: /**
095: * This controller handles errors in whatever way is appropriate
096: * as specified in it's parameters.
097: *
098: * @deprecated This is not used anymore due to stability issues
099: */
100: public class ErrorHandler extends DBController {
101: private static Logger log = Logger.getLogger(ErrorHandler.class);
102:
103: /**
104: * Our constructor "declares" what states we handle
105: */
106: public ErrorHandler() {
107: super ();
108: State handle = new State("handle", "Handle an Error");
109: addState(handle);
110: setInitialState("handle");
111: this
112: .setSchema(com.jcorporate.expresso.core.ExpressoSchema.class);
113: } /* ErrorHandler() */
114:
115: private String getAttribString(String key, ControllerRequest params)
116: throws NonHandleableException {
117: StringUtil.assertNotBlank(key,
118: "Cannot request a blank or null key here");
119:
120: Object o = null;
121:
122: try {
123: o = params.getSession().getPersistentAttribute(key);
124: } catch (ControllerException ce) {
125: throw new NonHandleableException(ce);
126: }
127: if (o == null) {
128: return null;
129: }
130: if (o instanceof SerializableString) {
131: SerializableString s = (SerializableString) o;
132:
133: return StringUtil.notNull(s.toString());
134: }
135:
136: throw new NonHandleableException("Value in session key '" + key
137: + "' " + "was not a SerializableString - it was '"
138: + o.getClass().getName() + "'");
139: }
140:
141: private void handleState(ControllerResponse myResponse,
142: ControllerRequest params) throws ControllerException,
143: NonHandleableException {
144: log.debug("Handling error begins");
145:
146: Throwable t = null;
147: String callerType = "";
148:
149: try {
150: log.debug("Getting caller type");
151: callerType = StringUtil.notNull(getAttribString(
152: "callerType", params));
153: log.debug("Getting exception");
154:
155: Object o = params.getSession().getPersistentAttribute(
156: "exception");
157:
158: if (o == null) {
159: throw new NonHandleableException("No error in session");
160: }
161: if (o instanceof Throwable) {
162: t = (Throwable) o;
163: } else {
164: throw new NonHandleableException(
165: "The 'exception' object in the "
166: + "session was not a 'Throwable'. It was '"
167: + o.getClass().getName() + "'");
168: }
169: if (t == null) {
170: throw new NonHandleableException(
171: "There was no attribute called "
172: + "'exception' stored in the current session. Unable to handle error");
173: }
174:
175: /* Stash the stackTrace, if available, in an output */
176: boolean showStack = true;
177:
178: try {
179: showStack = ConfigManager.getContext(
180: params.getDataContext()).showStackTrace();
181: } catch (ConfigurationException ce) {
182: throw new NonHandleableException(ce);
183: }
184: if (showStack) {
185: if (t != null) {
186: ByteArrayOutputStream bos = new ByteArrayOutputStream();
187: t.printStackTrace(new PrintStream(bos));
188: myResponse.addOutput(new Output("stackTrace", bos
189: .toString()));
190: } else {
191: myResponse.addOutput(new Output("stackTrace",
192: "No stack trace was available"));
193: }
194: } else {
195: myResponse
196: .addOutput(new Output(
197: "stackTrace",
198: "Stack trace display is not "
199: + "enabled. To enable, add 'stackTrace=y' to the '"
200: + params.getDataContext()
201: + ".properties' file"));
202: }
203:
204: myResponse.addOutput(new Output("exceptionMessage", t
205: .getMessage()));
206:
207: if (t instanceof ChainedException) {
208: ChainedException c = (ChainedException) t;
209: Throwable nestedException = c.getNested();
210:
211: if (nestedException != null) {
212: myResponse.addOutput(new Output("nestedMessage",
213: StringUtil.notNull(c.getNested()
214: .getMessage())));
215: } else {
216: myResponse
217: .addOutput(new Output("nestedMessage", ""));
218: }
219: } else {
220: myResponse.addOutput(new Output("nestedMessage", ""));
221: }
222: } catch (Exception ex) {
223: log.error("Exception thrown after getting:", ex);
224: throw new NonHandleableException(
225: "Unable to handle exception", ex);
226: }
227:
228: reportError(t, myResponse, params);
229:
230: if (t instanceof SecurityException) {
231: displaySecurityException(myResponse, params);
232: } else if (t instanceof DBException) {
233: displayDBException((DBException) t, myResponse, params);
234: } else {
235: displayOtherException(myResponse, params);
236: }
237: if (callerType.equals("job")) {
238: try {
239: User u = new User();
240: u.setDataContext(params.getDataContext());
241: u.setLoginName(params.getUser());
242:
243: StringBuffer bigString = new StringBuffer(
244: getAttribString("message", params));
245: bigString.append("\n");
246:
247: String dbDescrip = StringUtil.notNull(ConfigManager
248: .getContext(params.getDataContext())
249: .getDescription());
250:
251: if (!dbDescrip.equals("")) {
252: dbDescrip = " (" + dbDescrip + ") ";
253: }
254:
255: bigString.append("A " + t.getClass().getName()
256: + "Error occurred in db/context '"
257: + params.getDataContext() + "' " + dbDescrip
258: + "\n");
259: bigString.append("Server:"
260: + Setup.getValueRequired(params
261: .getDataContext(), "HTTPServ") + "\n");
262: bigString.append("Stack Trace:\n");
263: bigString.append(myResponse.getOutput("stackTrace")
264: .getContent());
265:
266: if (u.find()) {
267: String oneRecipient = u.getEmail();
268:
269: if (log.isDebugEnabled()) {
270: log.debug("Notifying '" + oneRecipient
271: + "' of failure of " + "job");
272: }
273:
274: EMailSender ems = new EMailSender();
275: ems.setDBName(params.getDataContext());
276: ems.send(oneRecipient, getAttribString("message",
277: params), bigString.toString());
278: }
279: } catch (Exception ie) {
280: log.warn("Error sending mail", ie);
281: }
282: }
283: } /* handleState() */
284:
285: /**
286: * Display a security exception. If "next" is non-blank
287: * then generate a series of outputs showing the error. If it
288: * is blank, then display them directly to the user if possible
289: *
290: * @param params the ControllerRequest object
291: * @param myResponse the ControllerResponse object
292: */
293: private void displaySecurityException(
294: ControllerResponse myResponse, ControllerRequest params)
295: throws NonHandleableException {
296: log.debug("Displaying security exception");
297:
298: try {
299: myResponse.addOutput(new Output("errorType", "security"));
300: displayException(myResponse, params);
301: } catch (ControllerException ce) {
302: throw new NonHandleableException(ce);
303: }
304: } /* displaySecurityException() */
305:
306: /**
307: * Display a db exception. If "next" is non-blank
308: * then generate a series of outputs showing the error. If it
309: * is blank, then display them directly to the user if possible
310: *
311: * @param params the ControllerRequest object
312: * @param myResponse the ControllerResponse object
313: * @param de the database exception
314: */
315: private void displayDBException(DBException de,
316: ControllerResponse myResponse, ControllerRequest params)
317: throws NonHandleableException {
318: log.debug("Displaying db exception");
319:
320: try {
321: myResponse.addOutput(new Output("errorType", "db"));
322:
323: /* generate outputs */
324: genOutputErrorBody(getAttribString("message", params),
325: myResponse);
326: genOutputErrorBodyDB(de.getDBMessage(), myResponse);
327: } catch (ControllerException ce) {
328: throw new NonHandleableException(ce);
329: }
330: } /* displayDBException(String, DBException) */
331:
332: /**
333: * Display an exception which is not a security or db exception
334: * If "next" is non-blank
335: * then generate a series of outputs showing the error. If it
336: * is blank, then display them directly to the user if possible
337: *
338: * @param params the ControllerRequest object
339: * @param myResponse the ControllerResponse object
340: */
341: private void displayOtherException(ControllerResponse myResponse,
342: ControllerRequest params) throws NonHandleableException {
343: log.debug("Displaying other exception");
344:
345: try {
346: myResponse.addOutput(new Output("errorType", "other"));
347: displayException(myResponse, params);
348: } catch (ControllerException ce) {
349: throw new NonHandleableException(ce);
350: }
351: } /* displayOtherException(String) */
352:
353: /**
354: * Handle the common function of displaying the exception - either
355: * to the user directly or via outputs as specified by the "next"
356: * parameter
357: *
358: * @param params the ControllerRequest object
359: * @param myResponse the ControllerResponse object
360: */
361: private void displayException(ControllerResponse myResponse,
362: ControllerRequest params) throws NonHandleableException {
363: try {
364:
365: /* generate outputs */
366: genOutputErrorBody(getAttribString("message", params),
367: myResponse);
368: } catch (ControllerException ce) {
369: throw new NonHandleableException(ce);
370: }
371: } /* displayOtherException(String) */
372:
373: /**
374: * Return the title of this Controller
375: *
376: * @return java.lang.String The Title of the controller
377: */
378: public String getTitle() {
379: return ("Error Handling");
380: } /* getTitle() */
381:
382: /**
383: * Transition to a new state
384: *
385: * @param newState The new state to transition into
386: * @param params the ControllerRequest object
387: * @return a ControllerResponse object
388: * @throws ControllerException upon error
389: * @throws NonHandleableException upon fatal error
390: */
391: public ControllerResponse newState(String newState,
392: ControllerRequest params) throws ControllerException,
393: NonHandleableException {
394: newState = "handle"; /* no matter what we were asked for! */
395:
396: ControllerResponse myResponse = null;
397:
398: try {
399: myResponse = super .newState(newState, params);
400: handleState(myResponse, params);
401: } catch (Exception ee) {
402: throw new NonHandleableException(ee);
403: }
404:
405: log.debug("Error controller returning from newstate");
406:
407: return myResponse;
408: } /* newState() */
409:
410: /**
411: * Override the normal stateAllowed method to always allow
412: * access to this controller
413: *
414: * @param newState the state to execute
415: * @param params the ControllerRequest object
416: * @return true for this controller for all states
417: * @throws ControllerException upon error
418: */
419: public synchronized boolean stateAllowed(String newState,
420: ControllerRequest params) throws ControllerException {
421: return true;
422: } /* stateAllowed(String) */
423:
424: /**
425: * Method called for any kind of error to provide event notifications for
426: * errors.
427: *
428: * @param t the Throwable object
429: * @param params the ControllerRequest object
430: * @param myResponse the ControllerResponse object
431: * @throws NonHandleableException on fatal error
432: */
433: protected void reportError(Throwable t,
434: ControllerResponse myResponse, ControllerRequest params)
435: throws NonHandleableException {
436: try {
437: StringBuffer theMessage = new StringBuffer();
438: theMessage.append("Error:\n");
439: theMessage.append("\tA "
440: + t.getClass().getName()
441: + " Error occurred at "
442: + DateTime
443: .getDateTimeForDB(params.getDataContext())
444: + " in database/context '"
445: + params.getDataContext() + "' to user '"
446: + params.getUser() + "'\n");
447: theMessage.append("\tThe requested URL was '"
448: + getAttribString("RequestedURL", params) + "'\n");
449:
450: if (getAttribString("QueryString", params) != null) {
451: theMessage.append("?"
452: + getAttribString("QueryString", params)
453: + "'\n");
454: }
455:
456: String errorMessage = getAttribString("message", params);
457:
458: if (errorMessage != null) {
459: theMessage.append("\t" + errorMessage);
460: } else {
461: theMessage.append("No error message was available");
462: }
463:
464: theMessage.append(myResponse.getOutput("stackTrace")
465: .getContent());
466: log.error(theMessage.toString(), t);
467:
468: try {
469: String servletEventFlag = StringUtil.notNull(Setup
470: .getValue(params.getDataContext(),
471: "ServletEvent"));
472:
473: if (servletEventFlag.equalsIgnoreCase("Y")) {
474: EventHandler.Event(params.getDataContext(),
475: "SYSERROR", theMessage.toString(), false);
476: } else if (servletEventFlag.equalsIgnoreCase("E")) {
477: if (!(t instanceof SecurityException)) {
478: EventHandler.Event(params.getDataContext(),
479: "SYSERROR", theMessage.toString(),
480: false);
481: }
482: } else {
483: log
484: .warn("Sending of errors via email is not enabled in "
485: + "db/context '"
486: + params.getDataContext() + "'");
487: }
488: } catch (Exception e) {
489: log.error("Unable to log error correctly:"
490: + theMessage.toString(), e);
491: }
492: } catch (DBException dbe) {
493: throw new NonHandleableException(dbe);
494: } catch (ControllerException ce) {
495: throw new NonHandleableException(ce);
496: }
497: } /* reportError */
498:
499: /**
500: * Generate outputs containing the various components
501: * of this error message for use by a custom display page,
502: * such as showerror.jsp
503: *
504: * @param errorMessage the error message
505: * @param myResponse the ControllerResponse object
506: * @throws ControllerException upon error
507: */
508: private void genOutputErrorBody(String errorMessage,
509: ControllerResponse myResponse) throws ControllerException {
510: if (errorMessage == null) {
511: log.error("Error message was null");
512: errorMessage = ("Error message was null");
513: }
514:
515: myResponse.addOutput(new Output("originalError", errorMessage));
516:
517: StringTokenizer stk = new StringTokenizer(errorMessage, ":");
518: String objectName = null;
519:
520: if (stk.hasMoreTokens()) {
521: objectName = stk.nextToken();
522: } else {
523: objectName = ("NONE");
524: }
525:
526: String message = ("");
527: String errorText = ("");
528: String messageNumber = ("");
529:
530: if (stk.hasMoreTokens()) {
531: while (stk.hasMoreTokens()) {
532: message = message + stk.nextToken() + " ";
533: }
534: if (message.startsWith("[")) {
535:
536: /* message has a message number in it */
537: StringTokenizer stk2 = new StringTokenizer(message, "]");
538: messageNumber = stk2.nextToken().substring(1);
539:
540: while (stk2.hasMoreTokens()) {
541: errorText = errorText + stk2.nextToken();
542: }
543: } else { /* no number */
544: errorText = message;
545: messageNumber = ("None Assigned");
546: }
547: } else { /* if there is anything after the initial colon */
548: errorText = errorMessage;
549: objectName = ("Unknown");
550: messageNumber = ("None Assigned");
551: }
552:
553: myResponse.addOutput(new Output("errorText", errorText));
554: myResponse
555: .addOutput(new Output("messageNumber", messageNumber));
556: myResponse.addOutput(new Output("objectName", objectName));
557: } /* genOutputErrorBody(String) */
558:
559: protected void genOutputErrorBodyDB(String dbErrorMessage,
560: ControllerResponse myResponse) throws ControllerException {
561: String newMessage = dbErrorMessage;
562:
563: try {
564: if (!StringUtil.notNull(dbErrorMessage).equals("")) {
565: DBMessage dm = new DBMessage(
566: SecuredDBObject.SYSTEM_ACCOUNT);
567: DBMessage oneDm = null;
568:
569: for (Iterator dml = dm.searchAndRetrieveList()
570: .iterator(); dml.hasNext();) {
571: oneDm = (DBMessage) dml.next();
572:
573: if (dbErrorMessage.indexOf(oneDm
574: .getField("InputString")) != -1) {
575: newMessage = oneDm.getField("OutputString");
576: break;
577: }
578: } /* for each message mapping registered */
579:
580: }
581:
582: myResponse.addOutput(new Output("dbErrorMessage",
583: newMessage));
584: } catch (DBException de) {
585: log.error("Unable to lookup error message mappings", de);
586: }
587: } /* genOutputErrorBodyDB(String, String) */
588:
589: } /* ErrorHandler */
|