Wednesday, 15 February 2012

JAVA EXCEPTIONS for selenium automation learning


JAVA EXCEPTIONS


Contents


Error Handling

Runtime errors can be divided into low-level errors that involve violating constraints, such as:
  • dereference of a null pointer
  • out-of-bounds array access
  • divide by zero
  • attempt to open a non-existent file for reading
  • bad cast (e.g., casting an Object that is actually a Boolean to Integer)
and higher-level, logical errors, such as violations of a function's precondition:
  • call to Stack's "pop" method for an empty stack
  • call to "factorial" function with a negative number
  • call to List's nextElement method when hasMoreElements is false
Logical errors can lead to low-level errors if they are not detected. Often, it is better to detect them (to provide better feedback).
Errors can arise due to:
  • User error (for example, providing a bad file name or a poorly formatted input file). A good program should be written to anticipate these situations, and should deal with them. For example, given a bad file name, an interactive program could print an error message and prompt for a new name.
  • Programmer error (i.e., a buggy program). These errors should be detected as early as possible to provide good feedback. For some programs it may be desirable to do some recovery after detecting this kind of error; for example, writing out current data.
Note that recovery is often not possible at the point of the error (because the error may occur inside some utility function that doesn't know anything about the overall program or what error recovery should involve). Therefore, it is desirable to "pass the error up" to a level that can deal with it.
There are several possible ways to handle errors:
  • Write an error message and quit. This doesn't provide any recovery.
  • Return a special value to indicate that an error occurred. This is the usual approach for C functions (which often return 0 or -1 to signal an error). However:
    • It doesn't work if the function also returns a value on normal completion and all values are possible (i.e., there is no special value that can be used to signal an error).
    • It requires that calling code check for an error. This can reduce the efficiency of the code, and is often omitted by programmers out of laziness or carelessness.
    • It can sometimes make the code more clumsy. For example, if function g might return an error code, one would have to write something like:
                 ret = g(x);
      if (ret == ERROR_CODE) { ... }
      else f(ret);
      instead of just:
          f(g(x));
  • Use a reference parameter or a global variable to hold an error code. This solves the first problem of the previous approach, but not the second or third ones.
  • Use exceptions. This seems to be the method of choice for modern programming languages.

Exceptions

Idea:
  • When an error is detected, an exception is thrown. That is, the code that caused the error stops executing immediately, and control is transferred to the catch clause for that exception of the first enclosing try blockthat has such a clause. The try block might be in the current function (the one that caused the error), or it might be in some function that called the current function (i.e., if the current function is not prepared to handle the exception, it is "passed up" the call chain). If no currently active function is prepared to catch the exception, an error message is printed and the program stops.
Exceptions can be built-in (actually, defined in one of Java's standard libraries) or user-defined. Here are some examples of built-in exceptions with links to their documentation:

How to Catch Exceptions

Catch exceptions using try blocks:
    try {
// statements that might cause exceptions
// possibly including function calls
} catch ( exception-1 id-1 ) {
// statements to handle this exception
} catch ( exception-2 id-2 ) {
// statements to handle this exception
.
.
.
} finally {
// statements to execute every time this try block executes
}
Notes:
  1. Each catch clause specifies the type of one exception, and provides a name for it (similar to the way a function header specifies the type and name of a parameter). Java exceptions are objects, so the statements in a catch clause can refer to the thrown exception object using the specified name.
  2. The finally clause is optional.
  3. In general, there can be one or more catch clauses. If there is a finally clause, there can be zero catch clauses.
Example (a program that tries to open a file named by the first command-line argument for reading)
    public static void main(String[] args)
    {
    InputStream istream;
    File inputFile;

    try {
    inputFile = new File(args[0]);
    istream = new InputStream(inputFile); // may throw FileNotFoundException
    } catch (FileNotFoundException ex) {
    System.out.println("file " + args[0] + " not found");
    }
    }
Notes:
  1. The program really should make sure there is a command-line argument before attempting to use args[0].
  2. Also, it probably makes more sense to put the try block in a loop, so that if the file is not found the user can be asked to enter a new file name, and a new attempt to open the file can be made.
  3. As is, if the user runs the program with a bad file name foo, the message "file foo not found" will be printed, and the program will halt.
  4. If there were no try block and the program were run with a bad file name foo, a more complicated message, something like this:
        java.io.FilenotFoundException: foo
      at java.io.FileInputStream ...
      at ...
      at Test.main ...
    would be printed. (Actually, if there were no try/catch for the FileNotFoundException, the program wouldn't compile because it fails to list that exception as one that might be thrown. We'll come back to that issue later...)

More About the Finally Clause

  • A finally clause is usually included to make sure that some clean-up (e.g., closing opened files) is done.
  • A finally clause always executes when its try block executes (whether or not there is an exception). Furthermore, if the finally clause includes a transfer of control statement (return, break, continue, throw) then that statement overrides any transfer of control initiated in the try or in a catch clause. First, let's assume that the finally clause does not include any transfer of control. Here are the situations that can arise:
    1. No exception occurs during execution of the try, and no transfer of control is executed in the try.
      => The finally clause executes, then the statement following the try block.
    2. No exception occurs during execution of the try, but it does execute a transfer of control.
      => The finally clause executes, then the transfer of control takes place.
    3. An exception does occur during execution of the try, and there is no catch clause for that exception.
      => The finally clause executes, then the uncaught exception is "passed up" to the next enclosing try block, possibly in a calling function.
    4. An exception does occur during execution of the try, and there is a catch clause for that exception. The catch clause does not execute a transfer of control.
      => The catch clause executes, then the finally clause, then the statement following the try block.
    5. An exception does occur during execution of the try, there is a catch clause for that exception, and the catch clause does execute a transfer of control.
      => The catch clause executes, then the finally clause, then the transfer of control takes place.
    If the finally block does include a transfer of control, then that takes precedence over any transfer of control executed in the try or in an executed catch clause. So for all of the cases listed above, the finally clause would execute, then its transfer of control would take place. Here's one example:
          try {
      return 0;
      } finally {
      return 2;
      }
    The result of executing this code is that 2 is returned.Note that this is rather confusing! The moral is that you probably do not want to include transfer-of-control statements in both the try statements and the finally clause, or in both a catch clause and the finally clause.

Checked and Unchecked Exceptions

Every exception is either a checked exception or an unchecked exception. If a method includes code that could cause a checked exception to be thrown, then:
  • the exception must be declared in the method header, using a throws clause, or
  • the code that might cause the exception to be thrown must be inside a try block with a catch clause for that exception.
So in general, you must always include some code that acknowledges the possibility of a checked exception being thrown. If you don't, you will get an error when you try to compile your code.

Exception Hierarchy

                    +--------+
| Object |
+--------+
|
|
+-----------+
| Throwable |
+-----------+
/ \
/ \
+-------+ +-----------+
| Error | | Exception |
+-------+ +-----------+
/ | \ / | \
\________/ \______/ \
+------------------+
unchecked checked | RuntimeException |
+------------------+
/ | | \
\_________________/

unchecked
  • most of the built-in exceptions (e.g., NullPointerException, IndexOutOfBoundsException) are unchecked.
  • IOExceptions (e.g., FileNotFoundException) are checked
  • user-defined exceptions should usually be checked, so they should be subclasses of Exception.

Choices when calling a function that may throw an exception

  1. Catch and handle the exception.
  2. Catch the exception, then re-throw it or throw another exception.
  3. Ignore the exception (let it "pass up" the call chain).
Note that if your code might cause a checked exception to be thrown; i.e.,:
  • your code throws a checked exception, or
  • your code ignores a checked exception that might be thrown by a called function
then your function must include a throws clause listing all such exceptions. For example:
    public static void main(String[] args) throws FileNotFoundException, EOFException
{ // an uncaught FileNotFoundException or EOFException may be thrown here }
Only uncaught checked exceptions need to be listed in a function's throws clause. Unchecked exceptions can be caught in a try block, but if not, they need not be listed in the function's throws clause.

TEST YOURSELF #1
Consider the following program (assume that comments are replaced with actual code that works as specified):
    class TestExceptions {

    static void e() {
    // might cause any of the following unchecked exceptions to be thrown:
    // Ex1, Ex2, Ex3, Ex4
    }

    static void d() {
    try {
    e();
    } catch (Ex1 ex) {
    System.out.println("d caught Ex1");
    }
    }

    static void c() {
    try {
    d();
    } catch (Ex2 ex) {
    System.out.println("c caught Ex2");
    // now cause exception Ex1 to be thrown
    }
    }

    static void b() {
    try {
    c();
    } catch (Ex1 ex) {
    System.out.println("b caught Ex1");
    } catch (Ex3 ex) {
    System.out.println("b caught Ex3");
    }
    }

    static void a() {
    try {
    b();
    } catch (Ex1 ex) {
    System.out.println("a caught Ex1");
    } catch (Ex4 ex) {
    System.out.println("a caught Ex4");
    // now cause exception Ex1 to be thrown
    }
    }

    public static void main(String[] args) {
    a();
    }
    }
Assume that this program is run four times. The first time, function e throws exception Ex1, the second time, it throws exception Ex2, etc. Foe each of the four runs, say what is printed; if an uncaught exception is thrown, say what happens.

How to Define and Throw Exceptions

  • Java exceptions are objects.
  • Define an exception by defining a class, for example:
      public class EmptyStackException extends Exception { }
    Note: New exceptions must be subclasses of Throwable; as discussed above, they are usually subclasses of Exception (so that they are checked). The exceptions you define do not have to be public classes; however, remember that if you do not make them public, then they can only used in the package in which they are defined.
  • Throw an exception using a throw statement:
      public class Stack {
        ...
        public Object Pop() throws EmptyStackException {
          if (Empty()) throw new EmptyStackException();
          ...
        }
      }
    Note:
    • Exceptions are objects, so you cannot simply throw "EmptyStackException" -- you must use "new" to create an exception object.
    • Since the Pop method might throw the (checked) exception EmptyStackException, that must be included in Pop's throws clause.

TEST YOURSELF #2
Question 1: Assume that function f might throw exceptions Ex1, Ex2, or Ex3. Complete function g, outlined below, so that:
  • If the call to f causes Ex1 to be thrown, g will catch that exception and print "Ex1 caught".
  • If the call to f causes Ex2 to be thrown, g will catch that exception, print "Ex2 caught", and then will throw an Ex1 exception.
        static void g() throws ... {
    try {
    f();
    } catch ( ... ) {
    ...
    } ...
    }
Question 2: Consider the following function.
    static void f(int k, int[] A, String S) {
    int j = 1 / k;
    int len = A.length + 1;
    char c;

    try {
    c = S.charAt(0);
    if (k == 10) j = A[3];
    } catch (ArrayIndexOutOfBoundsException ex) {
    System.out.println("array error");
    throw new InternalError();
    } catch (ArithmeticException ex) {
    System.out.println("arithmetic error");
    } catch (NullPointerException ex) {
    System.out.println("null ptr");
    } finally {
    System.out.println("in finally clause");
    }
    System.out.println("after try block");
    }
Part A.
    Assume that variable X is an array of int that has been initialized to be of length 3. For each of the following calls to function f, say what (if anything) is printed by f, and what, if any, uncaught exceptions are thrown by f.A. f(0, X, "hi");
    B. f(10, X, "");
    C. f(10, X, "bye");
    D. f(10, X, null);
Part B.
    Why doesn't f need to have a throws clause that lists the uncaught exceptions that it might throw?

Summary

  • Code that detects errors often does not know how to handle them.
  • Therefore, we need a way to "pass errors up".
  • The best approach is to use exceptions.
  • Java provides both built-in and user-defined exceptions.
  • Exceptions are caught using a try block:
      try {
        // statements (including function calls) that might cause an exception
      } catch ( exception-1 id1 ) {
        // code to handle the first kind of exception
      } catch ( exception-2 id2 ) {
        // code to handle the second kind of exception
      } ...
      } finally {
        // code that will execute whenever this try block does
      }
  • Exceptions are thrown using a throw statement.
  • If an exception is thrown in code that is not inside a try block, or is in a try block with no catch clause for the thrown exception, the exception is "passed up" the call stack.
  • Some exceptions are checked and some are unchecked. If a function might throw one or more checked exceptions, they must be listed in the function's throws clause.
  • Exceptions are objects; they are defined as classes (the class name is the name of the exception) that extend the Exception class.

Solutions to Self-Study Questions

Test Yourself #1

    What is printed for each of the four runs?

    1. d caught Ex1
    2. c caught Ex2
    b caught Ex1
    3. b caught Ex3
    4. a caught Ex4
    execution stops due to uncaught exception Ex1 thrown in main

Test Yourself #2

    Question 1: 

    static void g() throws Ex1, Ex3 {
    try {
    f();
    } catch (Ex1 ex1) {
    System.out.println("Ex1 caught");
    } catch (Ex2 ex2) {
    System.out.println("Ex2 caught");
    throw new Ex1();
    }
    }

    Question 2:
    Part A.

    A. f(0, X, "hi");
    nothing printed
    throws ArithmeticException
    B. f(10, X, "");
    prints "in finally clause"
    throws StringIndexOutOfBoundsException
    C. f(10, X, "bye");
    prints "array error", "in finally clause"
    throws InternalError
    D. f(10, X, null);
    prints "null ptr", "in finally clause", "after try block"


    Part B.

    Function f doesn't need to have a throws clause that lists the
    uncaught exceptions that it might throw because only uncaught CHECKED
    exceptions need to be listed in a function's throws clause. The
    uncaught exceptions that f might throw are all UNCHECKED exceptions.