/*
 * @(#)ApplicationShutdownHooks.java	1.3 05/12/02
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package java.lang;

import java.util.*;

/*
 * Class to track and run user level shutdown hooks registered through
 * <tt>{@link Runtime#addShutdownHook Runtime.addShutdownHook}</tt>.
 *
 * @see java.lang.Runtime#addShutdownHook
 * @see java.lang.Runtime#removeShutdownHook
 */

class ApplicationShutdownHooks implements Runnable {
    private static ApplicationShutdownHooks instance = null;

    /* The set of registered hooks */
    private static IdentityHashMap<Thread, Thread> hooks = new IdentityHashMap<Thread, Thread>();

    static synchronized ApplicationShutdownHooks hook() {
	if (instance == null)
	    instance = new ApplicationShutdownHooks();

	return instance;
    }

    private void ApplicationShutdownHooks() {}

    /* Add a new shutdown hook.  Checks the shutdown state and the hook itself,
     * but does not do any security checks.
     */
    static synchronized void add(Thread hook) {
	if(hooks == null)
	    throw new IllegalStateException("Shutdown in progress");

	if (hook.isAlive())
	    throw new IllegalArgumentException("Hook already running");

	if (hooks.containsKey(hook))
	    throw new IllegalArgumentException("Hook previously registered");

        hooks.put(hook, hook);
    }

    /* Remove a previously-registered hook.  Like the add method, this method
     * does not do any security checks.
     */
    static synchronized boolean remove(Thread hook) {
	if(hooks == null)
	    throw new IllegalStateException("Shutdown in progress");

	if (hook == null) 
	    throw new NullPointerException();

	return hooks.remove(hook) != null;
    }

    /* Iterates over all application hooks creating a new thread for each
     * to run in. Hooks are run concurrently and this method waits for 
     * them to finish.
     */
    public void run() {
	Collection<Thread> threads;
	synchronized(ApplicationShutdownHooks.class) {
	    threads = hooks.keySet();
	    hooks = null;
	}

	for (Thread hook : threads) {
	    hook.start();
	}
	for (Thread hook : threads) {
	    try {
		hook.join();
	    } catch (InterruptedException x) { }
	}
    }
}

