Friday, 14th October 2011
Follow WikiJava on twitter now. @Wikijava

Controlling the closure of a program

From WikiJava

Jump to: navigation, search
The author suggests:

buy this book


In this article I'll show you how to make sure you close properly all your files and flush your buffers so that you save your results even in case the user hits on ctrl+c and stops the JVM.

This technique solves your problems when for example your program abruptly exits because of an exception and you want to save the data already calculated, so you can restart your program and just continue, instead of starting again from a scratch.

Contents

The problem

All java programs are run within the Java Virtual Machine. That can be seen as a sort of container for the java programs, the flow is generally that when you run your program in the JVM, you first start the JVM, who then takes care about loading your program running it. In normal cases the JVM waits for the end of your program and then ends itself.

It can happen, though, that the JVM ends when your program does not expect it. For example the user forcibly closed the execution (maybe by hitting on ctrl+c) or an uncaught exception forced your whole process to end.

Such cases are a bit tricky, because if the JVM exits, then your program will just stop being executed, whatever it was doing at the moment it was stopped. In this situation not even the finally blocks will be run. simply your program will stop working, whatever was done was done... the JVM will release all the locks.

The solution

Fortunately we have a way to intercept the closing of the JVM. Using the method:

public void addShutdownHook(Thread thread)

from the java.lang.Runtime class.

We can specify a runnable Thread class that will be run just before the JVM will start closing. By simply writing:

Runtime.getRuntime().addShutdownHook(new OnShuttingDown());

Where OnShuttingDown is a class and can be defined as:

private class OnShuttingDown extends Thread {
	public void run() {
		closeProgram();
	}
}

We can control the closing of our program in any case.

The run method of OnShuttingDown will be called and executed as first thing when the JVM starts to shut down.

It is possible also to remove the shutting down hook by using the method:

public void removeShutdownHook(Thread thread)

Using these two methods you are able to fully control the exiting of the JVM in some parts of your program.

Note, the thread added in the hook will be called in any case when the JVM will stop. Including the normal termination of your program. This means that this technique can substitute any other mechanism to control the closure of the program. in particular, if your program terminates correctly, its full code will be executed, and the ShutdownHook will be run as well after it.

Explanation of the example program

The program below saves the timestamps in a text file. Without using the ShutdownHook, if your user presses ctrl+c, your program will terminate, and the output file would be truncated, with whatever it's written in it at the moment of the termination.

By adding the ShutdownHook we can intercept this event and close properly our file.

In the following example:

  • The shutdownHook is implemented in the OnShuttingDown private inner class.
  • The addShutdownHook(Thread) is called within the init() method
  • the OnShuttingDown class is instanciated with the main object, so when the run method is executed it can call methods of the main class.
  • Once initialized the program will save 5 times the timestamp in a file, if the program is not stopped earlier for some cause.
  • if the user presses Ctrl+c the finally block in the run method is not executed. And so is not the code in the main after the call to the run method.

I recommend you to execute the example below in a command shell and try letting it reach it's natural end or forcing its end by hitting Ctrl+c, to compare the results.

the whole code

package org.wikijava.basic.safeFileWriter;
 
import java.io.BufferedWriter;
 
public class SafeFileWriter {
 
	private BufferedWriter outputStream;
	private OnShuttingDown onShuttingDown;
 
	private class OnShuttingDown extends Thread {
 
		private SafeFileWriter safeFileWriter;
 
		public void run() {
			System.out.println("finish caught. closing");
			this.safeFileWriter.finish();
			System.out.println("closed");
		}
 
		public void setSafeFileWriter(SafeFileWriter safeFileWriter) {
			this.safeFileWriter = safeFileWriter;
		}
	}
 
	public void run() throws InterruptedException {
		try {
			int i = 0;
			while (i++ < 5) {
				try {
					Long data = new Date().getTime();
					this.outputStream.write(data + "\n");
					System.out.println("written: " + data);
					Thread.sleep(1000);
 
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} finally {
			System.out.println("runnning finally");
		}
	}
 
	public static void main(String[] args) throws IOException,
			InterruptedException {
		if (args.length != 1) {
			System.err.println("Usage: \n SafeFileWriter filename");
			return;
		}
 
		SafeFileWriter program = new SafeFileWriter();
		program.init(args);
		program.run();
		System.out.println("exiting main");
	}
 
	public void finish() {
		System.out.print("saving file and releasing resources .... ");
		try {
			this.outputStream.flush();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				this.outputStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		System.out.println("Done");
	}
 
	private void init(String[] args) throws IOException {
 
		this.outputStream = new BufferedWriter(new FileWriter(args[0]));
		this.onShuttingDown = new OnShuttingDown();
		this.onShuttingDown.setSafeFileWriter(this);
		Runtime.getRuntime().addShutdownHook(this.onShuttingDown);
	}
}


Comments from the users

To be notified via mail on the updates of this discussion you can login and click on watch at the top of the page


Comments on wikijava are disabled now, cause excessive spam.