1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.server;
20
21 import java.io.IOException;
22 import java.io.InputStreamReader;
23 import java.io.LineNumberReader;
24 import java.io.OutputStream;
25 import java.net.InetAddress;
26 import java.net.ServerSocket;
27 import java.net.Socket;
28 import java.nio.charset.StandardCharsets;
29 import java.util.Properties;
30
31 import org.eclipse.jetty.util.thread.ShutdownThread;
32
33
34
35
36
37
38
39
40
41
42
43 public class ShutdownMonitor
44 {
45
46 static class Holder
47 {
48 static ShutdownMonitor instance = new ShutdownMonitor();
49 }
50
51 public static ShutdownMonitor getInstance()
52 {
53 return Holder.instance;
54 }
55
56
57
58
59
60
61
62
63
64 public class ShutdownMonitorThread extends Thread
65 {
66
67 public ShutdownMonitorThread ()
68 {
69 setDaemon(true);
70 setName("ShutdownMonitor");
71 }
72
73 @Override
74 public void run()
75 {
76 if (serverSocket == null)
77 {
78 return;
79 }
80
81 while (serverSocket != null)
82 {
83 Socket socket = null;
84 try
85 {
86 socket = serverSocket.accept();
87
88 LineNumberReader lin = new LineNumberReader(new InputStreamReader(socket.getInputStream()));
89 String receivedKey = lin.readLine();
90 if (!key.equals(receivedKey))
91 {
92 System.err.println("Ignoring command with incorrect key");
93 continue;
94 }
95
96 OutputStream out = socket.getOutputStream();
97
98 String cmd = lin.readLine();
99 debug("command=%s",cmd);
100 if ("stop".equals(cmd))
101 {
102
103 debug("Issuing graceful shutdown..");
104 ShutdownThread.getInstance().run();
105
106
107 close(serverSocket);
108 serverSocket = null;
109
110
111 shutdownInput(socket);
112
113
114 debug("Informing client that we are stopped.");
115 out.write("Stopped\r\n".getBytes(StandardCharsets.UTF_8));
116 out.flush();
117
118
119 socket.shutdownOutput();
120 close(socket);
121 socket = null;
122 debug("Shutting down monitor");
123
124 if (exitVm)
125 {
126
127 debug("Killing JVM");
128 System.exit(0);
129 }
130 }
131 else if ("status".equals(cmd))
132 {
133
134 out.write("OK\r\n".getBytes(StandardCharsets.UTF_8));
135 out.flush();
136 }
137 }
138 catch (Exception e)
139 {
140 debug(e);
141 System.err.println(e.toString());
142 }
143 finally
144 {
145 close(socket);
146 socket = null;
147 }
148 }
149 }
150
151 public void start()
152 {
153 if (isAlive())
154 {
155
156 if (DEBUG)
157 System.err.printf("ShutdownMonitorThread already started");
158 return;
159 }
160
161 startListenSocket();
162
163 if (serverSocket == null)
164 {
165 return;
166 }
167 if (DEBUG)
168 System.err.println("Starting ShutdownMonitorThread");
169 super.start();
170 }
171
172 private void startListenSocket()
173 {
174 if (port < 0)
175 {
176 if (DEBUG)
177 System.err.println("ShutdownMonitor not in use (port < 0): " + port);
178 return;
179 }
180
181 try
182 {
183 serverSocket = new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));
184 if (port == 0)
185 {
186
187 port = serverSocket.getLocalPort();
188 System.out.printf("STOP.PORT=%d%n",port);
189 }
190
191 if (key == null)
192 {
193
194 key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()),36);
195 System.out.printf("STOP.KEY=%s%n",key);
196 }
197 }
198 catch (Exception e)
199 {
200 debug(e);
201 System.err.println("Error binding monitor port " + port + ": " + e.toString());
202 serverSocket = null;
203 }
204 finally
205 {
206
207 debug("STOP.PORT=%d",port);
208 debug("STOP.KEY=%s",key);
209 debug("%s",serverSocket);
210 }
211 }
212
213 }
214
215 private boolean DEBUG;
216 private int port;
217 private String key;
218 private boolean exitVm;
219 private ServerSocket serverSocket;
220 private ShutdownMonitorThread thread;
221
222
223
224
225
226
227
228
229
230
231
232 private ShutdownMonitor()
233 {
234 Properties props = System.getProperties();
235
236 this.DEBUG = props.containsKey("DEBUG");
237
238
239 this.port = Integer.parseInt(props.getProperty("STOP.PORT","-1"));
240 this.key = props.getProperty("STOP.KEY",null);
241 this.exitVm = true;
242 }
243
244 private void close(ServerSocket server)
245 {
246 if (server == null)
247 {
248 return;
249 }
250
251 try
252 {
253 server.close();
254 }
255 catch (IOException ignore)
256 {
257 debug(ignore);
258 }
259 }
260
261 private void close(Socket socket)
262 {
263 if (socket == null)
264 {
265 return;
266 }
267
268 try
269 {
270 socket.close();
271 }
272 catch (IOException ignore)
273 {
274 debug(ignore);
275 }
276 }
277
278
279 private void shutdownInput(Socket socket)
280 {
281 if (socket == null)
282 return;
283
284 try
285 {
286 socket.shutdownInput();
287 }
288 catch (IOException ignore)
289 {
290 debug(ignore);
291 }
292 }
293
294
295 private void debug(String format, Object... args)
296 {
297 if (DEBUG)
298 {
299 System.err.printf("[ShutdownMonitor] " + format + "%n",args);
300 }
301 }
302
303 private void debug(Throwable t)
304 {
305 if (DEBUG)
306 {
307 t.printStackTrace(System.err);
308 }
309 }
310
311 public String getKey()
312 {
313 return key;
314 }
315
316 public int getPort()
317 {
318 return port;
319 }
320
321 public ServerSocket getServerSocket()
322 {
323 return serverSocket;
324 }
325
326 public boolean isExitVm()
327 {
328 return exitVm;
329 }
330
331
332 public void setDebug(boolean flag)
333 {
334 this.DEBUG = flag;
335 }
336
337 public void setExitVm(boolean exitVm)
338 {
339 synchronized (this)
340 {
341 if (thread != null && thread.isAlive())
342 {
343 throw new IllegalStateException("ShutdownMonitorThread already started");
344 }
345 this.exitVm = exitVm;
346 }
347 }
348
349 public void setKey(String key)
350 {
351 synchronized (this)
352 {
353 if (thread != null && thread.isAlive())
354 {
355 throw new IllegalStateException("ShutdownMonitorThread already started");
356 }
357 this.key = key;
358 }
359 }
360
361 public void setPort(int port)
362 {
363 synchronized (this)
364 {
365 if (thread != null && thread.isAlive())
366 {
367 throw new IllegalStateException("ShutdownMonitorThread already started");
368 }
369 this.port = port;
370 }
371 }
372
373 protected void start() throws Exception
374 {
375 ShutdownMonitorThread t = null;
376 synchronized (this)
377 {
378 if (thread != null && thread.isAlive())
379 {
380
381 if (DEBUG)
382 System.err.printf("ShutdownMonitorThread already started");
383 return;
384 }
385
386 thread = new ShutdownMonitorThread();
387 t = thread;
388 }
389
390 if (t != null)
391 t.start();
392 }
393
394
395 protected boolean isAlive ()
396 {
397 boolean result = false;
398 synchronized (this)
399 {
400 result = (thread != null && thread.isAlive());
401 }
402 return result;
403 }
404
405
406 @Override
407 public String toString()
408 {
409 return String.format("%s[port=%d]",this.getClass().getName(),port);
410 }
411 }