| /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under |
| * the terms of the Common Public License v1.0 which accompanies this distribution, |
| * and is available at http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * $Id: ExitHookManager.java,v 1.1.1.1 2004/05/09 16:57:58 vlad_r Exp $ |
| */ |
| package com.vladium.util.exit; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import sun.misc.Signal; |
| import sun.misc.SignalHandler; |
| |
| import com.vladium.util.IJREVersion; |
| import com.vladium.util.Property; |
| import com.vladium.emma.IAppConstants; |
| |
| // ---------------------------------------------------------------------------- |
| /** |
| * @author Vlad Roubtsov, (C) 2003 |
| */ |
| public |
| abstract class ExitHookManager implements IJREVersion |
| { |
| // public: ................................................................ |
| |
| // TOTO: handle thread groups as well? |
| |
| public abstract boolean addExitHook (Runnable runnable); |
| public abstract boolean removeExitHook (Runnable runnable); |
| |
| public static synchronized ExitHookManager getSingleton () |
| { |
| if (s_singleton == null) |
| { |
| if (JRE_1_3_PLUS) |
| { |
| s_singleton = new JRE13ExitHookManager (); |
| } |
| else if (JRE_SUN_SIGNAL_COMPATIBLE) |
| { |
| s_singleton = new SunJREExitHookManager (); |
| } |
| else |
| { |
| throw new UnsupportedOperationException ("no shutdown hook manager available [JVM: " + Property.getSystemFingerprint () + "]"); |
| } |
| } |
| |
| return s_singleton; |
| } |
| |
| // protected: ............................................................. |
| |
| |
| protected ExitHookManager () {} |
| |
| // package: ............................................................... |
| |
| // private: ............................................................... |
| |
| |
| private static final class JRE13ExitHookManager extends ExitHookManager |
| { |
| public synchronized boolean addExitHook (final Runnable runnable) |
| { |
| if ((runnable != null) && ! m_exitThreadMap.containsKey (runnable)) |
| { |
| final Thread exitThread = new Thread (runnable, IAppConstants.APP_NAME + " shutdown handler thread"); |
| |
| try |
| { |
| Runtime.getRuntime ().addShutdownHook (exitThread); |
| m_exitThreadMap.put (runnable, exitThread); // TODO: use identity here |
| |
| return true; |
| } |
| catch (Exception e) |
| { |
| System.out.println ("exception caught while adding a shutdown hook:"); |
| e.printStackTrace (System.out); |
| } |
| } |
| |
| return false; |
| } |
| |
| public synchronized boolean removeExitHook (final Runnable runnable) |
| { |
| if (runnable != null) |
| { |
| final Thread exitThread = (Thread) m_exitThreadMap.get (runnable); // TODO: use identity here |
| |
| if (exitThread != null) |
| { |
| try |
| { |
| Runtime.getRuntime ().removeShutdownHook (exitThread); |
| m_exitThreadMap.remove (runnable); |
| |
| return true; |
| } |
| catch (Exception e) |
| { |
| System.out.println ("exception caught while removing a shutdown hook:"); |
| e.printStackTrace (System.out); |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| JRE13ExitHookManager () |
| { |
| m_exitThreadMap = new HashMap (); |
| } |
| |
| |
| private final Map /* Runnable->Thread */ m_exitThreadMap; |
| |
| } // end of nested class |
| |
| |
| private static final class SunJREExitHookManager extends ExitHookManager |
| { |
| public synchronized boolean addExitHook (final Runnable runnable) |
| { |
| if ((runnable != null) && ! m_signalHandlerMap.containsKey (runnable)) |
| { |
| final INTSignalHandler handler = new INTSignalHandler (runnable); |
| |
| try |
| { |
| handler.register (); |
| m_signalHandlerMap.put (runnable, handler); // TODO: use identity here |
| |
| return true; |
| } |
| catch (Throwable t) |
| { |
| System.out.println ("exception caught while adding a shutdown hook:"); |
| t.printStackTrace (System.out); |
| } |
| } |
| |
| return false; |
| } |
| |
| public synchronized boolean removeExitHook (final Runnable runnable) |
| { |
| if (runnable != null) |
| { |
| final INTSignalHandler handler = (INTSignalHandler) m_signalHandlerMap.get (runnable); // TODO: use identity here |
| if (handler != null) |
| { |
| try |
| { |
| handler.unregister (); |
| m_signalHandlerMap.remove (runnable); |
| |
| return true; |
| } |
| catch (Exception e) |
| { |
| System.out.println ("exception caught while removing a shutdown hook:"); |
| e.printStackTrace (System.out); |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| SunJREExitHookManager () |
| { |
| m_signalHandlerMap = new HashMap (); |
| } |
| |
| |
| private final Map /* Runnable->INTSignalHandler */ m_signalHandlerMap; |
| |
| } // end of nested class |
| |
| |
| private static final class INTSignalHandler implements SignalHandler |
| { |
| public synchronized void handle (final Signal signal) |
| { |
| if (m_runnable != null) |
| { |
| try |
| { |
| m_runnable.run (); |
| } |
| catch (Throwable ignore) {} |
| } |
| m_runnable = null; |
| |
| if ((m_previous != null) && (m_previous != SIG_DFL) && (m_previous != SIG_IGN)) |
| { |
| try |
| { |
| // this does not work: |
| //Signal.handle (signal, m_previous); |
| //Signal.raise (signal); |
| |
| m_previous.handle (signal); |
| } |
| catch (Throwable ignore) {} |
| } |
| else |
| { |
| System.exit (0); |
| } |
| } |
| |
| INTSignalHandler (final Runnable runnable) |
| { |
| m_runnable = runnable; |
| } |
| |
| synchronized void register () |
| { |
| m_previous = Signal.handle (new Signal ("INT"), this); |
| } |
| |
| synchronized void unregister () |
| { |
| // if (m_previous != null) |
| // { |
| // Signal.handle (new Signal ("INT"), m_previous); |
| // m_previous = null; |
| // } |
| |
| m_runnable = null; |
| } |
| |
| |
| private Runnable m_runnable; |
| private SignalHandler m_previous; |
| |
| } // end of nested class |
| |
| |
| private static ExitHookManager s_singleton; |
| |
| } // end of class |
| // ---------------------------------------------------------------------------- |