1 /*
   2  * Copyright (c) 2002-2012, the original author or authors.
   3  *
   4  * This software is distributable under the BSD license. See the terms of the
   5  * BSD license in the documentation provided with this software.
   6  *
   7  * http://www.opensource.org/licenses/bsd-license.php
   8  */
   9 package jdk.internal.jline.internal;
  10 
  11 import java.util.ArrayList;
  12 import java.util.List;
  13 
  14 import static jdk.internal.jline.internal.Preconditions.checkNotNull;
  15 
  16 /**
  17  * Manages the JLine shutdown-hook thread and tasks to execute on shutdown.
  18  *
  19  * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
  20  * @since 2.7
  21  */
  22 public class ShutdownHooks
  23 {
  24     public static final String JLINE_SHUTDOWNHOOK = "jline.shutdownhook";
  25 
  26     private static final boolean enabled = Configuration.getBoolean(JLINE_SHUTDOWNHOOK, true);
  27 
  28     private static final List<Task> tasks = new ArrayList<Task>();
  29 
  30     private static Thread hook;
  31 
  32     public static synchronized <T extends Task> T add(final T task) {
  33         checkNotNull(task);
  34 
  35         // If not enabled ignore
  36         if (!enabled) {
  37             Log.debug("Shutdown-hook is disabled; not installing: ", task);
  38             return task;
  39         }
  40 
  41         // Install the hook thread if needed
  42         if (hook == null) {
  43             hook = addHook(new Thread("JLine Shutdown Hook")
  44             {
  45                 @Override
  46                 public void run() {
  47                     runTasks();
  48                 }
  49             });
  50         }
  51 
  52         // Track the task
  53         Log.debug("Adding shutdown-hook task: ", task);
  54         tasks.add(task);
  55 
  56         return task;
  57     }
  58 
  59     private static synchronized void runTasks() {
  60         Log.debug("Running all shutdown-hook tasks");
  61 
  62         // Iterate through copy of tasks list
  63         for (Task task : tasks.toArray(new Task[tasks.size()])) {
  64             Log.debug("Running task: ", task);
  65             try {
  66                 task.run();
  67             }
  68             catch (Throwable e) {
  69                 Log.warn("Task failed", e);
  70             }
  71         }
  72 
  73         tasks.clear();
  74     }
  75 
  76     private static Thread addHook(final Thread thread) {
  77         Log.debug("Registering shutdown-hook: ", thread);
  78         try {
  79             Runtime.getRuntime().addShutdownHook(thread);
  80         }
  81         catch (AbstractMethodError e) {
  82             // JDK 1.3+ only method. Bummer.
  83             Log.debug("Failed to register shutdown-hook", e);
  84         }
  85         return thread;
  86     }
  87 
  88     public static synchronized void remove(final Task task) {
  89         checkNotNull(task);
  90 
  91         // ignore if not enabled or hook never installed
  92         if (!enabled || hook == null) {
  93             return;
  94         }
  95 
  96         // Drop the task
  97         tasks.remove(task);
  98 
  99         // If there are no more tasks, then remove the hook thread
 100         if (tasks.isEmpty()) {
 101             removeHook(hook);
 102             hook = null;
 103         }
 104     }
 105 
 106     private static void removeHook(final Thread thread) {
 107         Log.debug("Removing shutdown-hook: ", thread);
 108 
 109         try {
 110             Runtime.getRuntime().removeShutdownHook(thread);
 111         }
 112         catch (AbstractMethodError e) {
 113             // JDK 1.3+ only method. Bummer.
 114             Log.debug("Failed to remove shutdown-hook", e);
 115         }
 116         catch (IllegalStateException e) {
 117             // The VM is shutting down, not a big deal; ignore
 118         }
 119     }
 120 
 121     /**
 122      * Essentially a {@link Runnable} which allows running to throw an exception.
 123      */
 124     public static interface Task
 125     {
 126         void run() throws Exception;
 127     }
 128 }