View Javadoc

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 jline.internal;
10  
11  import java.util.ArrayList;
12  import java.util.List;
13  
14  import static 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 }