]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/server/ShutdownMonitor.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / server / ShutdownMonitor.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
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  * Shutdown/Stop Monitor thread.
35  * <p>
36  * This thread listens on the port specified by the STOP.PORT system parameter (defaults to -1 for not listening) for request authenticated with the key given
37  * by the STOP.KEY system parameter (defaults to "eclipse") for admin requests.
38  * <p>
39  * If the stop port is set to zero, then a random port is assigned and the port number is printed to stdout.
40  * <p>
41  * Commands "stop" and "status" are currently supported.
42  */
43 public class ShutdownMonitor 
44 {
45     // Implementation of safe lazy init, using Initialization on Demand Holder technique.
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      * ShutdownMonitorThread
58      *
59      * Thread for listening to STOP.PORT for command to stop Jetty.
60      * If ShowndownMonitor.exitVm is true, then Sytem.exit will also be
61      * called after the stop.
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                         // Graceful Shutdown
103                         debug("Issuing graceful shutdown..");
104                         ShutdownThread.getInstance().run();
105                         
106                         //Stop accepting any more
107                         close(serverSocket);
108                         serverSocket = null;
109                         
110                         //Shutdown input from client
111                         shutdownInput(socket);
112
113                         // Reply to client
114                         debug("Informing client that we are stopped.");
115                         out.write("Stopped\r\n".getBytes(StandardCharsets.UTF_8));
116                         out.flush();
117
118                         // Shutdown Monitor
119                         socket.shutdownOutput();
120                         close(socket);
121                         socket = null;                        
122                         debug("Shutting down monitor");
123
124                         if (exitVm)
125                         {
126                             // Kill JVM
127                             debug("Killing JVM");
128                             System.exit(0);
129                         }
130                     }
131                     else if ("status".equals(cmd))
132                     {
133                         // Reply to client
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                 // TODO why are we reentrant here?
156                 if (DEBUG)
157                     System.err.printf("ShutdownMonitorThread already started");
158                 return; // cannot start it again
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                     // server assigned port in use
187                     port = serverSocket.getLocalPort();
188                     System.out.printf("STOP.PORT=%d%n",port);
189                 }
190
191                 if (key == null)
192                 {
193                     // create random key
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                 // establish the port and key that are in use
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      * Create a ShutdownMonitor using configuration from the System properties.
226      * <p>
227      * <code>STOP.PORT</code> = the port to listen on (empty, null, or values less than 0 disable the stop ability)<br>
228      * <code>STOP.KEY</code> = the magic key/passphrase to allow the stop (defaults to "eclipse")<br>
229      * <p>
230      * Note: server socket will only listen on localhost, and a successful stop will issue a System.exit() call.
231      */
232     private ShutdownMonitor()
233     {
234         Properties props = System.getProperties();
235
236         this.DEBUG = props.containsKey("DEBUG");
237
238         // Use values passed thru via /jetty-start/
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                 // TODO why are we reentrant here?
381                 if (DEBUG)
382                     System.err.printf("ShutdownMonitorThread already started");
383                 return; // cannot start it again
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 }