1 /*
2 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package java.lang;
26
27 import java.util.*;
28
29 /*
30 * Class to track and run user level shutdown hooks registered through
31 * {@link Runtime#addShutdownHook Runtime.addShutdownHook}.
32 *
33 * @see java.lang.Runtime#addShutdownHook
34 * @see java.lang.Runtime#removeShutdownHook
35 */
36
37 class ApplicationShutdownHooks {
38 /* The set of registered hooks */
39 private static IdentityHashMap<Thread, Thread> hooks;
40 static {
41 try {
42 Shutdown.add(1 /* shutdown hook invocation order */,
43 false /* not registered if shutdown in progress */,
44 new Runnable() {
45 public void run() {
46 runHooks();
47 }
48 }
49 );
50 hooks = new IdentityHashMap<>();
51 } catch (IllegalStateException e) {
52 // application shutdown hooks cannot be added if
53 // shutdown is in progress.
54 hooks = null;
55 }
56 }
57
58
59 private ApplicationShutdownHooks() {}
60
61 /* Add a new shutdown hook. Checks the shutdown state and the hook itself,
62 * but does not do any security checks.
63 */
64 static synchronized void add(Thread hook) {
65 if(hooks == null)
66 throw new IllegalStateException("Shutdown in progress");
67
68 if (hook.isAlive())
69 throw new IllegalArgumentException("Hook already running");
70
71 if (hooks.containsKey(hook))
72 throw new IllegalArgumentException("Hook previously registered");
73
74 hooks.put(hook, hook);
75 }
76
77 /* Remove a previously-registered hook. Like the add method, this method
78 * does not do any security checks.
79 */
80 static synchronized boolean remove(Thread hook) {
81 if(hooks == null)
82 throw new IllegalStateException("Shutdown in progress");
83
84 if (hook == null)
85 throw new NullPointerException();
86
87 return hooks.remove(hook) != null;
88 }
89
90 /* Iterates over all application hooks creating a new thread for each
91 * to run in. Hooks are run concurrently and this method waits for
92 * them to finish.
93 */
94 static void runHooks() {
95 Collection<Thread> threads;
96 synchronized(ApplicationShutdownHooks.class) {
97 threads = hooks.keySet();
98 hooks = null;
99 }
100
101 for (Thread hook : threads) {
102 hook.start();
103 }
104 for (Thread hook : threads) {
105 while (true) {
106 try {
107 hook.join();
108 break;
109 } catch (InterruptedException ignored) {
110 }
111 }
112 }
113 }
114 }
115