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 }