Difference between revisions of "Java Error Handling"

From AgileApps Support Wiki
imported>Aeric
imported>Aeric
 
(58 intermediate revisions by the same user not shown)
Line 1: Line 1:
The [[Java Class Template]] embodies the error handling principles explained here. To do so, it uses the following tools:
The goal of error handling is identify the error that occurred, where it happened, and (ideally) what data was present at the time. The ideas presented in this section can help to achieve those goals.
 
====Error Handling Tools====
The [[Java Class Template]] embodies the error handling principles explained below. To do so, it uses the following tools:
:* [[Logger.info]] - Put a text message into the [[Debug Log]]. Add <tt>"/n"</tt> (newline) to create a line break.
:* [[Logger.info]] - Put a text message into the [[Debug Log]]. Add <tt>"/n"</tt> (newline) to create a line break.
:*: None of the other tools put an entry into the log. This is the only one that does.
:* [[Functions.showMessage]] - Display an HTML message onscreen. Add <tt><nowiki>"<br>"</nowiki></tt> to create a line break.
:* [[Functions.showMessage]] - Display an HTML message onscreen. Add <tt><nowiki>"<br>"</nowiki></tt> to create a line break.
:: '''Note:''' Only one message is displayed, when the code returns to the platform. Multiple calls are concatenated.
:*: Multiple calls to showMessage() are concatenated in the message buffer--but only if no Exceptions occurred.
:* [[Functions.throwError]] - Raise an exception to discontinue processing and roll back the current transaction.
:*: The contents of the message buffer are displayed when the code returns to the platform.  
:* [[Functions.throwError]] - Raise an exception to discontinue processing, display a message, and roll back the current transaction.
:*: The required message argument (<tt>Functions.throwError("some message")</tt> is displayed to the user.
:*: That call is equivalent to <tt>throw new Exception("some message")</tt>.
:*: Those calls ''overwrite'' the message buffer, replacing any previous calls and any stored text from calls to <tt>showMessage</tt>. so only the last call is seen by the user.
:*: Whenever such a message is displayed, the [[Debug Log]] should contain detailed information on the cause. Follow the steps in the next section to be sure it does.
 
:''Learn more:''
:To test various error conditions and see the results, use this class: [[ErrorHandlingTest Class]].
 
{{Tip|<br>By all means, take advantage of the [[Unit Test Framework]] to identify and fix bugs before your users see them.}}


The goal of error handling is identify which error occurred, where it happened, and (ideally) what data was present at the time. The principles elucidated below help to achieve those goals. (You can call [[Functions.throwError]] to get a stack trace, but it generally doesn't help very much, because the trace is almost entirely devoted to the sequence of platform calls that got to your code. You're more interested in the steps your program followed. Following these steps gives you that information.)
====Error Handling Principles====
# ''Errors are ignored unless you throw them.'' So:
#:a. All calls to platform functions and standard Java functions should be in a try-catch block.
#:b. Any code that sees an error (whether inside the try or catch) should call [[Functions.throwError]].
# Nothing goes into the log unless you put there. Be sure to capture the information you need for debugging.
# A standard Java stack trace is of little value, since it is almost entirely the sequence of platform calls that got to your code. You're more interested in the steps your program followed. To get that information, catch every exception and add the name of the current method to the log, along with the exception's class name:
#:a. Call [[Logger.info]]. Use the class name as the "category" label (2nd parameter).
#:b. Include the method name in the message.
#:c. Include the exception's class name, using <tt>e.getClass().getName()</tt>.
#:: (For a standard Java exception like ArrayIndexOutOfBoundsException, the class name will generally tell you what went wrong.)


;Error-Handling Principles:
====Error Handling Snippets====
# Use the class name as "category" label when calling [[Logger.info]] (to ).
The following code fragments embody those principles:  
# In a logged error message, include the method name (to find the message rapidly).
# For normal code in a try-block (for example, when you get back an unexpected value from a call), generate an exception to interrupt processing and roll back the current transaction:
# When catching an unexpected exception, display the exception's class name.<br>That's generally more indicative than the message embedded in the exception
#:<syntaxhighlight lang="java" enclose="div">
# All calls to platform functions need to be in a try-catch block.
// Result.getCode() >= 0 on success, -1 on failure
# All calls to methods that invoke a platform function need to be in a try-catch block.
Result r = somePlatformAPI();
# In a method that is invoked directly from the platform, the rule is to LOG, SHOW, and THROW:
if (r.getCode() < 0) {
#::<syntaxhighlight lang="java" enclose="div">
  // THROW THE ERROR
// LOG, SHOW, and THROW
  String msg = "Error <doing something>:\n"+ r.getMessage();
String msg = "Unexpected exception in methodName()";
   Functions.throwError(msg);
log(msg + ":\n" + e.getClass().getName() );     
}
show(msg + " - see debug log");    
throw e; // Roll back the current transaction
</syntaxhighlight>
</syntaxhighlight>
#:
#:
#: In a catch block inside a method that is called by your code, the rule is to LOG and THROW:
# For a ''top-level'' method that is called by the platform, generate a message, log it, and throw it:
#::<syntaxhighlight lang="java" enclose="div">
#:<syntaxhighlight lang="java" enclose="div">
// LOG and THROW
try {
String msg = "Unexpected exception in methodName()";
  // TOP LEVEL CODE
log(msg + ":\n" + e.getClass().getName() ); //+ "\n" + e.getMessage() );  
}
throw e;
catch (Exception e) {
  String msg = e.getMessage() + "\n yourMethod(): "+e.getClass().getName();
  Logger.info( msg, "YourClass" ); // Additional log statement
  Functions.throwError(msg);
}
</syntaxhighlight>
</syntaxhighlight>
#:
#:
# In the catch block surrounding the call you make to that method, the rule is to SHOW:
# For an ''internal'' method that is called by your code, generate the message and throw it:
#::<syntaxhighlight lang="java" enclose="div">
#:<syntaxhighlight lang="java" enclose="div">
// SHOW
try {
show("Error in getActivities() - see debug log");
  // INTERNAL CODE
return;
}
catch (Exception e) {
  String msg = e.getMessage() + "\n yourMethod(): "+e.getClass().getName();  
  Functions.throwError(msg);
}
</syntaxhighlight>
</syntaxhighlight>
#: (There is no need to log the message here, as it will be logged in the code that catches it.)
#:
#:
# Outside of a catch block, the rule is to THROW using [[Functions.throwError]] to generate an exception that interrupts processing and rolls back the current transaction:
#: That style of error handling generates log entries like these:
#::<syntaxhighlight lang="java" enclose="div">
#:: <tt>someCallingMethod(): Exception</tt>
// THROW
#:: <tt>someCalledMethod(): ArrayIndexOutOfBoundsException</tt>
Functions.throwError("msg");
 
====Tracing Code Execution====
# Calls to [[Functions.showMessage]] are useful in the normal flow of code, but not in a catch-block.<br>(You have to re-throw an exception to be sure it is seen. But when you re-throw it, the message it contains is the only thing the user sees.)
# Use <tt>showMessage()</tt> calls to put breadcrumbs in a block of code. That works as long as no exceptions occur, and the messages are easy to see. But putting the messages in the Debug Log is more reliable. To get the best of both worlds, do both.
# The debug method in the [[Java Class Template]] is designed for tracing code execution. If the code runs properly, you see the trace messages onscreen. If not, they are still in the Debug Log.
#:<syntaxhighlight lang="java" enclose="div">
public void debug(String msg) throws Exception { show(msg); log(msg); }
</syntaxhighlight>
</syntaxhighlight>
{{Tip|<br>Don't indent debug statements you intend to remove. Instead, start them in column 1, where they're easy to see. <br>Only indent debug statements you want to keep around for later use. Comment them out when they've served their purpose.}}

Latest revision as of 20:57, 13 February 2015

The goal of error handling is identify the error that occurred, where it happened, and (ideally) what data was present at the time. The ideas presented in this section can help to achieve those goals.

Error Handling Tools

The Java Class Template embodies the error handling principles explained below. To do so, it uses the following tools:

  • Logger.info - Put a text message into the Debug Log. Add "/n" (newline) to create a line break.
    None of the other tools put an entry into the log. This is the only one that does.
  • Functions.showMessage - Display an HTML message onscreen. Add "<br>" to create a line break.
    Multiple calls to showMessage() are concatenated in the message buffer--but only if no Exceptions occurred.
    The contents of the message buffer are displayed when the code returns to the platform.
  • Functions.throwError - Raise an exception to discontinue processing, display a message, and roll back the current transaction.
    The required message argument (Functions.throwError("some message") is displayed to the user.
    That call is equivalent to throw new Exception("some message").
    Those calls overwrite the message buffer, replacing any previous calls and any stored text from calls to showMessage. so only the last call is seen by the user.
    Whenever such a message is displayed, the Debug Log should contain detailed information on the cause. Follow the steps in the next section to be sure it does.
Learn more:
To test various error conditions and see the results, use this class: ErrorHandlingTest Class.

Thumbsup.gif

Tip:
By all means, take advantage of the Unit Test Framework to identify and fix bugs before your users see them.

Error Handling Principles

  1. Errors are ignored unless you throw them. So:
    a. All calls to platform functions and standard Java functions should be in a try-catch block.
    b. Any code that sees an error (whether inside the try or catch) should call Functions.throwError.
  2. Nothing goes into the log unless you put there. Be sure to capture the information you need for debugging.
  3. A standard Java stack trace is of little value, since it is almost entirely the sequence of platform calls that got to your code. You're more interested in the steps your program followed. To get that information, catch every exception and add the name of the current method to the log, along with the exception's class name:
    a. Call Logger.info. Use the class name as the "category" label (2nd parameter).
    b. Include the method name in the message.
    c. Include the exception's class name, using e.getClass().getName().
    (For a standard Java exception like ArrayIndexOutOfBoundsException, the class name will generally tell you what went wrong.)

Error Handling Snippets

The following code fragments embody those principles:

  1. For normal code in a try-block (for example, when you get back an unexpected value from a call), generate an exception to interrupt processing and roll back the current transaction:
    // Result.getCode() >= 0 on success, -1 on failure
    Result r = somePlatformAPI();
    if (r.getCode() < 0) {
       // THROW THE ERROR
       String msg = "Error <doing something>:\n"+ r.getMessage();
       Functions.throwError(msg);
    }
    
  2. For a top-level method that is called by the platform, generate a message, log it, and throw it:
    try {
       // TOP LEVEL CODE
    }
    catch (Exception e) {
       String msg = e.getMessage() + "\n yourMethod(): "+e.getClass().getName(); 
       Logger.info( msg, "YourClass" );  // Additional log statement
       Functions.throwError(msg);
    }
    
  3. For an internal method that is called by your code, generate the message and throw it:
    try {
       // INTERNAL CODE
    }
    catch (Exception e) {
       String msg = e.getMessage() + "\n yourMethod(): "+e.getClass().getName(); 
       Functions.throwError(msg);
    }
    
    (There is no need to log the message here, as it will be logged in the code that catches it.)
    That style of error handling generates log entries like these:
    someCallingMethod(): Exception
    someCalledMethod(): ArrayIndexOutOfBoundsException

Tracing Code Execution

  1. Calls to Functions.showMessage are useful in the normal flow of code, but not in a catch-block.
    (You have to re-throw an exception to be sure it is seen. But when you re-throw it, the message it contains is the only thing the user sees.)
  2. Use showMessage() calls to put breadcrumbs in a block of code. That works as long as no exceptions occur, and the messages are easy to see. But putting the messages in the Debug Log is more reliable. To get the best of both worlds, do both.
  3. The debug method in the Java Class Template is designed for tracing code execution. If the code runs properly, you see the trace messages onscreen. If not, they are still in the Debug Log.
    public void debug(String msg) throws Exception { show(msg); log(msg); }
    

Thumbsup.gif

Tip:
Don't indent debug statements you intend to remove. Instead, start them in column 1, where they're easy to see.
Only indent debug statements you want to keep around for later use. Comment them out when they've served their purpose.