Lecture 3

Exceptions, Singleton, Bridge & Logging

Exceptions

One of the important aspects of writing an application is error handling. What should a program do if an error occurs? This question is quite broad, the program should react differently to different kinds of errors.

Suppose that after a method was invoked on an object, the object is left in an inconsistent state. For example function open on the file class was invoked with a bad file path. What should the program do?

Should the program terminate altogether, or maybe report an error and offer a user another try?

In many procedural languages functions would look like this:

int foo() {
	if ( !doSomething1() )
		return -1;
	...
	if ( !doSomething2() )
		 return -2;
	...
}

If some error happens we return from a function with an error code. The code above is oversimplified. In reality we would have to remember to free the allocated resources before every return. The code was quite elaborate, hard to follow. Some programmers resolved this by having less error checking in the code altogether. Some actually used labels to jump to cleanup and then return.

Java offers different means of error handling, called Exceptions. Exceptions are better than conventional error handling means because they provide more explicit, compact, robust and direct way to signal errors. In Java Exceptions are part of method declaration:

public interface IAlgorithm {
	public void run() throws ExecutionException, IllegalDataException;
}

This sort of declaration makes exceptions part of the interface. The creator of the Algorithm interface is explicitly warning the implementer that in case of failure run method will throw ExecutionException or IllegalDataException.

Not only that, but now compiler will force the caller to deal with the exception.

int[] iArray = randomSortedIntArray();
IAlgorithm alg = new BinarySearch( iArray, 29 );
try {
	alg.run();
} catch ( IllegalDataException e1 ) {
	System.err.println( "Illegal Data Exception while running Binary Search: " + e.getMessage() );
	e.printStackTrace();
} catch ( ExecutionException e ) {
	System.err.println( "Execution Exception while running Binary Search " + e.getMessage() );
	e.printStackTrace();
}

The above illustrates how we may go about catching the ExecutionException. Notice the syntax of try/catch construct. The exceptions are handled in order of the declaration in the catch block. It is essentially translated into:

if ( thrown exception instance of IllegalDataException  ) {
	...
} else if ( thrown exception  instanceof ExecutionException ) {
	...
}

This is why it is important to declare the catch blocks in the right order.

e.printStackTrace is a useful method (defined in the Exception base class), which prints the execution stack.

java.io.FileNotFoundException: fred.txt 
        at java.io.FileInputStream.(FileInputStream.java) 
        at java.io.FileInputStream.(FileInputStream.java) 
        at ExTest.readMyFile(ExTest.java:19) 
        at ExTest.main(ExTest.java:7)

The stack trace is the sequence of methods on the stack of the currently running Thread. Just as a reminder, lets recall the basic way that stack and heap are organized:

So an exception is thrown when an unexpected error condition occurs. The exception is caught by an encompassing clause further up the stack. If the exception is not caught (can be the case with runtime exceptions), the default exception handler catches it and prints an error message.

Where should we generally catch Exception? The rule of thumb is simple. If you can handle it in the menaingful way - catch it. Whatever you do, please never do this:

try {
	anObject.method( arg1 );
} catch ( Exception e ) {}
// Swallowing of exceptions is a major NO-NO!

The way that nested try catch block look on the stack is this:

The inner-most block gets a first shot at catching exception if its catch clauses match. If not, the stack is unwined further until the next try/catch block.

As you saw, the Exceptions in Java are objects. The advantage is that we can convey more information and do better error handling. Consider the following example:

public class ParserException extends Exception {
	public ParserException( File f, int iLine ) {
		this.f = f;
		this.iLine = iLine;
	}
	public final File getFile() { return f; }
	public final int  getLine() { return iLine; }
	
	private File f;
	private int iLine;
}

So we can create our own exception classes by extending the core ones. The base class of all exceptions is Throwable; it contains a string which can be used to describe an exception. Java offers two types of exceptions: checked and unchecked. The checked exceptions are those that compiler can test for during the compilation. The unchecked ones are those thrown by Java runtime environment. The example would be OutOfMemoryException, which occurs when the virtual machine memory is exhausted.

All of the exception classes that we create are checked exceptions. They are all derived from Exception class. Since Exceptions are classes and since we can create our own we may add methods and fields to them, and make them carry more information.

Is this useful? As with everything we need to search for the right balance to maintain sanity. Applications should not be centered around error handling. Maximum error handling should be placed in the code which is interacting with the end user.

Errors of type

Runtime error #3256 at 0x34AD546

are not terribly useful for users of graphical interfaces. Frankly, Bill, we are all sick of them.

When should you create your own exception classes? You should do it when it will allow you to discriminate between the different sorts of errors and do better recovery For example, if you are developing application framework, you might want to create base class called ApplicationException. This will be basic exception thrown in the application API. Then you might add more specific exceptions like ApplicationOpenFailedException, ApplicationCloseFailedException which will extend the ApplicationException. For example, if the application reads/saves state, then you will have the following as a part of application API:

public interface IApplication {
	public void open() throws ApplicationOpenFailedException, IOException;
	public void close() throws ApplicationCloseFailedException, IOException;
	...
}

What about situations when you want a certain piece of code to be executed regardless of whether error has or has not occurred?

Very convenient way of doing this is provided in Java. Every try clause can be followed by several catch clauses plus finally clause. finally always gets called in case if error occurs and if it does not. This is very useful when dealing with streams.

public class AlwaysDoThatWithStreams {
	public static void main( String[] args ) {
		FileWriter fw = null;
		try {
			fw = new FileWriter( "args.txt" );
			for ( int i = 0; i < args.length; i++ ) {
				os.println( "ARG[" + i + "] = [" + args[ i ] + "]" );
			}
		 } catch ( IOException e ) {
		 	System.out.println( "Error while writing args: " + e.getMessage() );
		 } finally {
		 	if ( fw != null ) fw.close();
		}
	}
}

Don’t go crazy with exceptions. Exceptions are meant to be used in case of unexpected errors. For example, consider parsing an integer from a string.

public static int parseInt(String s, int radix)
	throws NumberFormatException

This is a good example of correct usage of exceptions. When bad string is supplied there is no integer value that can be returned. The only choice in this case is to throw an exception. Don’t be afraid to throw an exception from a constructor or an init method. Unlike in older versions of C++, this is not a problem in Java.

Logging

Most applications that are released these days have application logs. The log is simply a file to which application prints some (preferably) useful message about the path of execution and errors, which are occurring. It would be quite useful to design a few logging classes to use in our project. Here is an extract from a hypothetical log file

01/01/2002 9:45:03 [EntityReader] ERROR: class StrangeEntity not found
01/01/2002 9:45:05 [EntityReader] DEBUG: created instance of BasicEntity

Code to do that may look like this:

Log.error( this, "class StrangeEntity not found" );
Log.debug( this, "created instance of BasicEntity" );

It makes sense to have one global Log instance in every application, just like it makes sense to have only one instance of WindowManager or FileSystem.

A class which allows only one instance of itself is called Singleton and is well known creational Design Pattern.

How do we implement Singleton in Java?

First of all we need to make sure that user can only create one instance of this class. We start by making constructor private and introducing static method called getInstance like this

public class Singleton {
	public static Singleton getInstance() {
		return instance;
 	}	
	
	private Singleton() {}
	
	// create instance 
	private static Singleton instance = new Singleton();
	
	// Test
	public static void main( String[] args ) {
		Singleton handle1 = Singleton.getInstance();
		Singleton handle2 = Singleton.getInstance();

		System.out.println( "Handle1 [" + handle1 + "]" );
		System.out.println( "Handle2 [" + handle2 + "]" );
 }
}

Produces something like this:

Handle1 [Singleton@1c9f7e]
Handle2 [Singleton@1c9f7e]

As you can see it is pretty easy to implement Singleton in Java.

Now, the Log should be a singleton, there should be only one instance of the log object in your application. One thing that we note is that we don’t want to be saying

Produces something like this:
Log.getInstance().error( "Something is wrong" );

We want to say instead:

Produces something like this:
Log.error( "Something is wrong" );

How do we do this?

Well, our Log will not be exactly a singleton - it will not allow any instances of itself at all. All of its methods will be static and it will not have getInstance method. This type of class is called a utility class.

public final class Log {
	public static void error( String msg );
	public static void error( Object o, String msg );
	public static void debug( String msg );
	public static void debug( Object o, String msg );
}

There are two error methods: one, which takes just an error message, and another, which takes an object as well. The second one is there so that we can print the name of the class, which is logging the message:

public static void error( Object o, String msg ) {
	Log( "ERROR: " + o.getClass().getName() + ":" + msg );
}

What kinds of levels for logging would you want? Probably, it is sufficient to have debug, warning and error.

One more important thing that we need to address is that sometimes we want the output of the log to go to a file, sometimes to the standard output and sometimes to the Text Area in the GUI. It would be nice to have this functionality within our Log.

A simple way to do this is to create an interface ILogImpl, and make it pluggable into Log.

public class ILogImpl {
	public void print( String sMessage );
}	

Our Log will delegate the actual printing to the ILogImpl, so we will have:

public class Log {
	...
	public static void setLogImpl( ILogImpl lm ) {
		logImpl = lm;
	}
	...
	private void print( String s ) {
		logImpl.print( s );
	}
	private static ILogImpl logImpl;
}

First, this technique of delegating the implementation of the actual operation (or creating an interface for the implementation) is essentially another Design Pattern called Bridge. Second, by outsourcing the printing we now can plug-in different ILogImpl implementations.

For example to print to standard output we would do this:

public final class LogImplStdout implements ILogImpl {
	public void print( String sMessage ) {
		System.out.println( sMessage );
	}
}

The complete Log looks like this now:

		
public class Log {
   /**
     * @param Object o    - the object, which signals the error
     * @param String sMsg - the error message
     * @param Throwable   - the exception which occurred
   */
    public static void error( Object o, String sMsg, Throwable e );
    public static void error( String sMsg, Throwable e );
    public static void error( Object o, String sMsg );
    public static void error( String sMsg );
	
    public static void debug( Object o, String sMsg );
    public static void debug( String sMsg );
	
    public static void warning( Object o, String sMsg );
    public static void warning( String sMsg );
	
 /**
   * @param Boolean bDebug - indicates if the debugging messages
   * should be printed or not
  */
    public static void setDebug( boolean bDebug );
    public static boolean getDebug();

 /**
   * @param ILogImpl logImpl - the settable delegate which 
   * actually does the printing of the messages.
   * It is specific to the medium where the messages go.
  */
   public static void setLogImpl( ILogImpl logImpl );
	
   private Log();
}

Please read

Arnold & Gosling Java Programming language

Chapters on Exceptions and Garbage Collection

Chapters 16 and 25 from Agile Software Development Book

Assignment 1 Define & implement Log, ILogImpl, StdOutLogImpl, FileLogImpl and MultiLogImpl