A348 Error Handling

Modified

Overview

Studies analyzing the cause of downtime to online systems found that over 50% of the episodes were due to human operators, not programming errors or user errors. Even well designed and tested systems will fail, as software designers and implementers we should handle failures as gracefully as possible, minimizing the consequences, and providing the user with information that a failure occurred and their options.

Designers of Web-based systems practice in a dangerous environment where their systems are exposed to naive users and professional attacks.

There are several points to consider about system faults:

  1. Failures offer an opportunity to purposely or accidentally damage your system.
  2. Systems should not expose users to raw error messages from the operating system, language, etc., who would trust their money to a system that fails before their very eyes?
  3. Raw error messages are useful during system development, providing details of syntax errors, etc.,
  4. Fully testing any system for all possible combinations of possible conditions, particularly those originating from user input, is generally not feasible (e.g. what happens if a stock trader buys a negative number of stocks?). We should test boundary conditions at a minimum.
  5. Scripts may embed other languages (e.g. SQL) that is only executed under certain conditions (e.g. an empty string cannot be inserted by SQL), for which some conditions may cause failure.
  6. Reported errors can be caught and handled at the implementers discretion. Errors should be reported for correction as a means of system improvement.

Most modern languages handle detected failures generally as exceptions where an exception is some out of the ordinary condition that results in the execution of code reserved for dealing with the exceptional condition. Many languages implement exception handling with the try-catch-finally statement discussed below.

 

try...catch…finally Statement

Portions of the following description was extracted from the Microsoft Visual Studio Help

Arguments

tryStatements
Required. Statements where an error can occur.
exception
Required. Any variable name. The initial value of exception is the value of the thrown error.
catchStatements
Optional. Statements to handle errors occurring in the associated tryStatements.
finallyStatements
Optional. Statements that are unconditionally executed after all other error processing has occurred.

Remarks

The try...catch…finally statement provides a way to handle some or all of the possible errors that may occur in a given block of code, while still running code. If errors occur that the programmer has not handled, JScript simply provides its normal error message to a user, as if there was no error handling.

If the error cannot be handled in the catch associated with the try where the error occurred, use the throw statement to propagate, or rethrow, the error to a higher-level error handler.

After all statements in try have been executed and any error handling has occurred in catch, the statements in finally are unconditionally executed.

Notice that the code inside finally is executed even if a return statement occurs inside the try or catch blocks, or if the catch block re-throws the error. finally is guaranteed to always run, unless an unhandled error occurs (for example, causing a run-time error inside the catch block).

Example 1

print("1");
try {
        print("2");
        throw "an exception";
        print("3");
    }
    catch(e) {
        print("4 " + e);
    }
    finally {
        print("5");
    }  
}

function print(s){
   document.write(s);
}
This produces the following output:
1
2
4 an exception
5

 

Example 2

The following example illustrates JavaScript exception handling in a nested hierarchy. The general rules are:

  1. A throw inside a try will execute the companion catch.
  2. A throw inside a catch or finally will execute an enclosing catch.
try {
      // throw "first exception";
      print("1"); 

      try {
        print("2");
        throw "an exception 2";
        print("3");
      }
      catch(e) {
        print("4" + e);
        throw e + " re-thrown";
      }
      finally {
        print("5");
      }  
}
catch(e) {
    print("6 " + e);
}
finally {
  print("7");
}

function print(s){
   document.write(s);
}

This produces the following output:
1
2
4 an exception
5
6 an exception re-thrown
7

 

Exercise 1

What is the output after un-commenting the line in Example 2?

  • throw "first exception";
 

 

ASP Example using try/catch

The following example is from the project, Portfolio.asp. The essential approach is that any reported exception will be caught and somewhat gracefully reported to the user. By wrapping nearly the complete, normal execution script in a try block, all exceptions will be treated identically.

Portfolio.asp
<%@ Language=JScript%>
<%	
   if(Session("trader") == undefined)
	Response.Redirect("Login.xml");
	
try {
  conn = Server.CreateObject("ADODB.Connection"); 
  conn.Mode = 3;
  conn.Open ("DSN=Project");
   
  trader=Session("trader");
  rs = conn.Execute(
   "SELECT PORTFOLIO.SYMBOL, NAME, SHARES, PRICE,  " +
   "SHARES*PRICE as STOCKVALUE " +
   "FROM STOCK INNER JOIN PORTFOLIO ON " +
   "STOCK.SYMBOL = PORTFOLIO.SYMBOL WHERE ID='"+trader+"';");
     
  var xmlDoc = Server.CreateObject("Msxml2.DOMDocument");
  xmlDoc.async = false;
  rs.Save (xmlDoc, 1) ;    // Convert recordset to XML

  var xsl = Server.CreateObject("Msxml2.DOMDocument");
  xsl.async = false;           // Convert XML tree using XSL   
  xsl.load(Server.MapPath("PortfolioContent.htm"))
                                       // Write tree as HTML
  Response.Write(xmlDoc.transformNode(xsl)); 

  conn.Close();
}

catch(e) { 
%>

<html>
 <head>
  <title>Stock Trader - Technical Error</title>
 </head>
 <body>
  <h2>Stock Trader - Technical Error</h2>
  <p>Trading cannot be continued at this time. </p>
  <p>Please try again later.</p>
  <p>Contact 1-800-123-4567 to report this problem or send email to:
  <a href="mailto:">Online@StockTrader.com</a></p>
 </body>
</html>

<% } %>

Stock Trader - Technical Error

Trading cannot be continued at this time.

Please try again later.

Contact 1-800-123-4567 to report this problem or send email to: Online@StockTrader.com

 

 

Exercise 2

  1. Copy, paste and save onto a virtual drive as Exercise2.asp
  2. Load SessionTrader.asp to initialize the stock trader.
  3. Load Exercise2.asp into browser to verify operation.
  4. Change:

      xsl.load(Server.MapPath("PortfolioContent.htm"))

     to a non-existent file.
     

    1. Reload Exercise2.asp
    2. An error message should be displayed.
  5. Add a second try/catch with a try only on the statements:

      conn = Server.CreateObject("ADODB.Connection");
      conn.Mode = 3;
      conn.Open ("DSN=Project");

    and some simple HTML that states that a database error occurred.
     

  6. Load into browser to verify operation. The same error message should be displayed.
  7. Change DSN=Project to another name to induce the database error.
  8. Two error messages may have been displayed. What would be a reasonable measure to display only one error message?

 

Logging Errors

Rather than displaying an error message and depending upon the user to report the error, more useful for the developer is to try individual elements that throw exceptions and write details to a system log for later analysis.

The following example has separate try/catch statements for each source of an exception (i.e. the operations that throw exceptions to the caller). By handling each exception, specific error information can be logged to a file. The user sees the same error message in any failure case.

<%@ Language=JScript%>
<%	
if(Session("trader") == undefined)
	Response.Redirect("Login.xml");

var errorstr =" [Portfolio.asp] ";
var error = false;

  conn = Server.CreateObject("ADODB.Connection"); 
  conn.Mode = 3;
  conn.Open ("DSN=Project");
   
if(!error) try {
  trader=Session("trader");
  rs = conn.Execute(
   "SELECT PORTFOLIO.SYMBOL, NAME, SHARES, PRICE,  " +
   "SHARES*PRICE as STOCKVALUE " +
   "FROM STOCK INNER JOIN PORTFOLIO ON " +
   "STOCK.SYMBOL = PORTFOLIO.SYMBOL WHERE ID='"+trader+"';");
} catch(e) { 
  error = true;
  errorstr = errorstr + " SELECT " + e;
  }
     
if(!error) try {
  var xmlDoc = Server.CreateObject("Msxml2.DOMDocument");
  xmlDoc.async = false;
  rs.Save (xmlDoc, 1) ;        // Convert recordset to XML
} catch(e) { 
  error = true;
  errorstr = errorstr + " XML " + e; 
  }
                                    
if(!error) try {
  var xsl = Server.CreateObject("Msxml2.DOMDocument");
  xsl.async = false;           // Convert XML tree using XSL   
  xsl.load(Server.MapPath("PortfolioContent.htm"))
} catch(e) { 
  error = true ;
  errorstr = errorstr + " XSL LOAD " + e; 
  }

if(!error) try {
  Response.Write(xmlDoc.transformNode(xsl)); 
  conn.Close();
} catch(e) { 
  error = true ;
  errorstr = errorstr + " XSL TRANSFORM " + e; 
  }
if(error) {
  try {
    var d = new Date();              
    var ForAppending = 8;
    fs = new ActiveXObject("Scripting.FileSystemObject");
    a = fs.OpenTextFile("c:\\Project\\errorlog.txt", ForAppending, true);
    a.Write("\r\n" + d + errorstr);
    a.Close;
  } catch(e) { }
%>

<html>
 <head>
  <title>Stock Trader - Technical Error</title>
 </head>
 <body>
  <h2>Stock Trader - Technical Error</h2>
  <p>Trading cannot be continued at this time. </p>
  <p>Please try again later.</p>
  <p>Contact 1-800-123-4567 to speak to technical support or send email to:
  <a href="mailto:">Online@StockTrader.com</a></p>
 </body>
</html>

<% } %>

Stock Trader - Technical Error

Trading cannot be continued at this time.

Please try again later.

Contact 1-800-123-4567 to speak to technical support or send email to: Online@StockTrader.com


Error in XSL of PortfolioContent.htm transformation
from line:

Response.Write(xmlDoc.transformNode(xsl));

 

Produces contents in errorlog.txt of:

Thu Jun 19 05:32:50 EDT 2005
 Portfolio.asp XSL TRANSFORM [object Error]

 

Note - Our script also handles exceptions when attempting to write the error log by executing within a try/catch statement. A cleaner solution than including the HTML in each ASP script would be to place in a separate, error notification file and after logging the error, redirect to that file.

Exercise 3

  1. Make a minor error in the PortfolioContent.htm file.
  2. Copy, paste and save as Exercise3.asp
  3. Modify the HTML to write the Session("trader") variable to the error log file.
  4. Load SessionTrader.asp to initialize the stock trader.
  5. Load Exercise3.asp into browser to verify operation. An error message should be displayed.
  6. Open C:\Project\errorlog.txt.

 

  1. Change DSN=Project to another name to induce the database error and reload Exercise3.asp to observe results.
  2. Add error handling to the following consistent with Exercise3.asp

      conn = Server.CreateObject("ADODB.Connection");
      conn.Mode = 3;
      conn.Open ("DSN=Project");

  3. Reload Exercise3.asp to verify operation.
  4. Reopen C:\Project\errorlog.txt