Shutdown hooks are a very important feature of the JVM. They provide the capability to do resource cleanup or store application state when the JVM shuts down.



The JVM provides the capability to add Shutdown hooks at Runtime. This shutdown hooks can be used to perform any resource cleanup or save state as required just before the JVM shuts down either normally or abruptly.

There can be more than one shutdown hooks that can be registered any time when the JVM is running via Runtime API. This hook is implemented as a Java Thread. The thread has the run() method which will get executed when the hook is executed by JVM at shutdown.


The below shows an example of a typical shutdown hook. Depending on your application the resources and its cleanup could be different. Here we simulate that data written to a file and not flushed as well as the file handle is not closed before the JVM shuts down. So when the JVM is about to shut down as the program execution ends, it runs the thread associated as the Shutdown Hook. This thread flushes the file and closes the file handle cleanly before shutdown.


/**
 * This snippet shows how to writer and register the shutdown hook with the VM.
 * 
 * @author java4learners
 * 
 */
public class ShutdownHookExample {

	/**
	 * Main method
	 * 
	 * @param args
	 * @throws Exception
	 */
	public static final void main(String args[]) throws Exception {

		File file = new File("test.dat");
		// open the file writer
		final FileWriter fileWriter = new FileWriter(file);

		// Add the shutdown hook as new java thread to the runtime.
		// can be added as inner class or a separate class that implements
		// Runnable or extends Thread
		Runtime.getRuntime().addShutdownHook(new Thread() {
		public void run() {
			System.out.println("in : run () : shutdownHook");

			// save state, resource clean,up etc.
			if (fileWriter != null) {
				try {
					// try to close the open file
					fileWriter.flush();
					fileWriter.close();
					System.out.println("File closed successfully");
				} catch (IOException e) {
					System.out.println("Failed to flush/close the file :" + e.getMessage());
					e.printStackTrace();
				}
			}
			System.out.println("Shutdown hook completed...");
		}
		});

		// main sample code, open file and write.
		// once the JVM terminates, the shutdown hook, would close the file.

		for (int i = 0; i < 10; i++) {
			fileWriter.write("Line : " + i);
		}
		
	}
}


In a typical J2EE application there are lot of different type of resources like Database Connection Pool, Thread Pools, Job Schedulers etc. that are instantiated and need to be cleaned up on shutdown. Using the concept of shutdown hook this can be achieved very easily.


import java.util.Iterator;
import java.util.List;
import org.quartz.JobExecutionContext;
import org.quartz.Scheduler;
import com.livrona.server.config.AppConfig;
import com.livrona.comono.logger.Logger;
import com.livrona.comono.logger.LoggerFactory;

public class ServerShutdownHook extends Thread {
	private static final Logger log = LoggerFactory.createLogger(ServerShutdownHook.class);

	/**
	 * Start the Shutdown Hook
	 */
	public void run() {
		log.info("method : run()");
		shutdownPinger();
		shutDownThreadPool();
		shutDownDatabasePool();
		shutdownScheduler();
	}

	private void shutDownDatabasePool() {
		log.info("in : shutDownDatabasePool");
		try {
			DataSourceManager.getInstance().shutdown();
		} catch (Exception e) {
			log.error("Failed to shutdown the Database Pool : " + e.getMessage(), e);
		}
	}

	private void shutDownThreadPool() {
		log.info("Shutdown Thread Pool");
		try {
			ThreadPoolManager.getInstance().shutdown();
		} catch (Exception e) {
			log.error("Failed to shutdown the Thread Pool : " + e.getMessage(), e);
		}
	}

	/**
	 * Shutdown Pinger Service
	 * 
	 */
	private void shutdownPinger() {
		log.info("Shutdown Pinger Service");
		try {
			PingerManager pinger = PingerManager.getInstance();
			pinger.shutdown();
		} catch (Exception e) {
			log.error("Failed to shutdown the Pinger : " + e.getMessage(), e);
		}
	}

	/**
	 * Shutdown the scheduler
	 */
	private void shutdownScheduler() {
		try {

		log.info("Performing Job Resources CleanUp");
		Scheduler scheduler = SchedulerManager.getInstance().getScheduler();
		if (scheduler != null) {
			List jobExeContextList = scheduler.getCurrentlyExecutingJobs();
			log.info("There are " + jobExeContextList.size() + " running jobs at shutdown.");
			Iterator it = jobExeContextList.iterator();
			while (it.hasNext()) {
				JobExecutionContext jobExeContext = (JobExecutionContext) it.next();
				BasicJob job = (BasicJob) jobExeContext.getJobInstance();
				job.cleanup();
			}
			log.info("Shutting down Quartz Scheduler…");
			scheduler.shutdown(true);
		} else {
			log.info("Quartz Scheduler is not running");
		}
		} catch (Exception e) {
			log.info("Error shutting down Quartz: " + e.getMessage(), e);
		}
	}
}