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