1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.exec;
19
20 import java.util.Vector;
21 import java.util.concurrent.atomic.AtomicBoolean;
22
23
24
25
26 public class ShutdownHookProcessDestroyer implements ProcessDestroyer, Runnable {
27
28 private final class ProcessDestroyerThread extends Thread {
29
30 private AtomicBoolean shouldDestroy = new AtomicBoolean(true);
31
32 public ProcessDestroyerThread() {
33 super("ProcessDestroyer Shutdown Hook");
34 }
35
36 @Override
37 public void run() {
38 if (shouldDestroy.get()) {
39 ShutdownHookProcessDestroyer.this.run();
40 }
41 }
42
43 public void setShouldDestroy(final boolean shouldDestroy) {
44 this.shouldDestroy.compareAndSet(!shouldDestroy, shouldDestroy);
45 }
46 }
47
48
49 private final Vector<Process> processes = new Vector<>();
50
51
52 private ProcessDestroyerThread destroyProcessThread;
53
54
55 private AtomicBoolean added = new AtomicBoolean();
56
57
58
59
60 private AtomicBoolean running = new AtomicBoolean();
61
62
63
64
65
66
67
68 public ShutdownHookProcessDestroyer() {
69 }
70
71
72
73
74
75
76
77 @Override
78 public boolean add(final Process process) {
79 synchronized (processes) {
80
81 if (processes.isEmpty()) {
82 addShutdownHook();
83 }
84 processes.addElement(process);
85 return processes.contains(process);
86 }
87 }
88
89
90
91
92 private void addShutdownHook() {
93 if (!running.get()) {
94 destroyProcessThread = new ProcessDestroyerThread();
95 Runtime.getRuntime().addShutdownHook(destroyProcessThread);
96 added.compareAndSet(false, true);
97 }
98 }
99
100
101
102
103
104
105 public boolean isAddedAsShutdownHook() {
106 return added.get();
107 }
108
109
110
111
112
113
114
115 public boolean isEmpty() {
116 return size() == 0;
117 }
118
119
120
121
122
123
124
125 @Override
126 public boolean remove(final Process process) {
127 synchronized (processes) {
128 final boolean processRemoved = processes.removeElement(process);
129 if (processRemoved && processes.isEmpty()) {
130 removeShutdownHook();
131 }
132 return processRemoved;
133 }
134 }
135
136
137
138
139 private void removeShutdownHook() {
140 if (added.get() && !running.get()) {
141 final boolean removed = Runtime.getRuntime().removeShutdownHook(destroyProcessThread);
142 if (!removed) {
143 System.err.println("Could not remove shutdown hook");
144 }
145
146
147 destroyProcessThread.setShouldDestroy(false);
148 destroyProcessThread.start();
149
150 try {
151 destroyProcessThread.join(20000);
152 } catch (final InterruptedException ignore) {
153
154
155 }
156 destroyProcessThread = null;
157 added.compareAndSet(true, false);
158 }
159 }
160
161
162
163
164 @Override
165 public void run() {
166 synchronized (processes) {
167 running.compareAndSet(false, true);
168 processes.forEach(process -> {
169 try {
170 process.destroy();
171 } catch (final Throwable t) {
172 System.err.println("Unable to terminate process during process shutdown");
173 }
174 });
175 }
176 }
177
178
179
180
181
182
183 @Override
184 public int size() {
185 return processes.size();
186 }
187 }