= Exception Guidelines = == Contingencies and Faults == The following concepts of contingency and fault is derived from [[http://www.oracle.com/technology/pub/articles/dev2arch/2006/11/effective-exceptions.html|Barry Ruzek's Article ''Effective Java Exceptions'']] and it is advised that developers atleast skim it. Grossly oversimplified picture in a nutshell: A ''contingency'' is a control-flow related checked exception. A ''fault'' is an unexpected {{{RuntimeException}}} or {{{Error}}} that does not relate to the workflow of the class/method/application. Faults subclassing {{{Error}}} are generally signs that the entire JVM might be compromised (see also javadoc for the {{{Error}}} class) and should as a rule of thumb not be caught (except possibly by the outermost fault barrier). The following table is copied from Ruzek's article: || '''Condition''' || '''Contingency''' || '''Fault''' || || {{{Is considered to be}}} || A part of the design || A nasty surprise || || {{{Is expected to happen}}} || Regularly but rarely || Never || || {{{Who cares about it}}} || The upstream code that invokes the method || The people who need to fix the problem || || {{{Examples}}} || Alternative return modes || Programming bugs, hardware malfunctions, configuration mistakes, missing files, unavailable servers || || {{{Best Mapping}}} || A checked exception || An unchecked exception|| A ''fault barrier'' is a conceptual layer in the control flow where certain kinds of faults are caught. It is a perfectly acceptable fault barrier to catch general {{{Exception}}}s, used for example to encapsulate unreliable subsystems. It ''must'' be specified in the javadoc of a method that it acts as a fault barrier and what the intended purpose of the fault handling is (including the types of faults it handles). == Best Practices == * A fault is a {{{RuntimeException}}} or in rare cases an {{{Error}}} * It is allowed to wrap a contingency in a fault and send it up stream * A contingency cannot pass a fault barrier although a fault barrier can have contingencies it self. * A fault barrier cannot let a fault pass that is one of the fault types that it is itself designated to handle. * A contingency is specific to one problem or event in the control flow. * Avoid that the same contingency can be thrown by two or more different causes in the same method. If this is the case you should either refactor your method or create different types of exceptions for the other contingencies. * Never have a finally block in a fault barrier - it may throw exceptions that escape the fault barrier ==== Threads ==== * Every thread should have a fault barrier at the top. In practice this means that we put try/catch as the outermost statement in each run method * ''Gotcha'': Beware of implicitly started threads: Webservice-initiated methods, threads started by events like JMS messages or Timer events. * ''Gotcha'': Beware that throwing a nasty fault might only stop the running thread and not the application as a whole '''NOTE:''' To enforce that we minimally log exceptions at the root of a thread we could use [[http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Thread.html#setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)|Thread.setDefaultUncaughtExceptionHandler]]. ==== Finally ==== * Close your resources and files in a {{{finally}}} clause * Never ''ever'' do {{{return}}} statements in a finally clause ==== System.Exit ==== * Only in the {{{main}}} method. Use faults instead, for really nasty faults use {{{Error}}} subclasses * ''Gotcha'': Beware that throwing a nasty fault might only stop the running thread and not the application as a whole == Throwing Exceptions == Generally don't log when throwing exceptions. Instead make sure to put a good error message in the exception. Good error messages include as much context as reasonable. For example {{{ for (Record rec : database) { if (rec.length == 0) { throw new EmptyRecordException ("Record " + rec.getId() + " was empty"); } ... } }}} ==== Rethrowing Exceptions ==== Rethrowing exceptions is fine, as long as the old Exception is wrapped in a new Exception as this preserves the stack trace. Follow the guidelines from the throwing exceptions chapter above. {{{ try { myStream.write(rec.toBytes()); catch (IOException e) { throw new RecordWriterException("Could not store the record '" + rec.getId() + "' to file '" + filename + "'", e); } }}}