All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.server.ShutdownMonitor Maven / Gradle / Ivy

There is a newer version: 12.0.13
Show newest version
//
//  ========================================================================
//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.server;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

import org.eclipse.jetty.util.component.Destroyable;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.thread.ShutdownThread;

/**
 * Shutdown/Stop Monitor thread.
 * 

* 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 * by the STOP.KEY system parameter (defaults to "eclipse") for admin requests. *

* If the stop port is set to zero, then a random port is assigned and the port number is printed to stdout. *

* Commands "stop" and "status" are currently supported. */ public class ShutdownMonitor { private final Set _lifeCycles = new CopyOnWriteArraySet(); // Implementation of safe lazy init, using Initialization on Demand Holder technique. static class Holder { static ShutdownMonitor instance = new ShutdownMonitor(); } public static ShutdownMonitor getInstance() { return Holder.instance; } /* ------------------------------------------------------------ */ public static synchronized void register(LifeCycle... lifeCycles) { getInstance()._lifeCycles.addAll(Arrays.asList(lifeCycles)); } /* ------------------------------------------------------------ */ public static synchronized void deregister(LifeCycle lifeCycle) { getInstance()._lifeCycles.remove(lifeCycle); } /* ------------------------------------------------------------ */ public static synchronized boolean isRegistered(LifeCycle lifeCycle) { return getInstance()._lifeCycles.contains(lifeCycle); } /** * ShutdownMonitorRunnable * * Thread for listening to STOP.PORT for command to stop Jetty. * If ShowndownMonitor.exitVm is true, then Sytem.exit will also be * called after the stop. * */ private class ShutdownMonitorRunnable implements Runnable { public ShutdownMonitorRunnable() { startListenSocket(); } @Override public void run() { if (serverSocket == null) { return; } while (serverSocket != null) { Socket socket = null; try { socket = serverSocket.accept(); LineNumberReader lin = new LineNumberReader(new InputStreamReader(socket.getInputStream())); String receivedKey = lin.readLine(); if (!key.equals(receivedKey)) { System.err.println("Ignoring command with incorrect key"); continue; } OutputStream out = socket.getOutputStream(); String cmd = lin.readLine(); debug("command=%s",cmd); if ("stop".equalsIgnoreCase(cmd)) //historic, for backward compatibility { //Stop the lifecycles, only if they are registered with the ShutdownThread, only destroying if vm is exiting debug("Issuing stop..."); for (LifeCycle l:_lifeCycles) { try { if (l.isStarted() && ShutdownThread.isRegistered(l)) { l.stop(); } if ((l instanceof Destroyable) && exitVm) ((Destroyable)l).destroy(); } catch (Exception e) { debug(e); } } //Stop accepting any more commands stopInput(socket); // Reply to client debug("Informing client that we are stopped."); informClient(out, "Stopped\r\n"); //Stop the output and close the monitor socket stopOutput(socket); if (exitVm) { // Kill JVM debug("Killing JVM"); System.exit(0); } } else if ("forcestop".equalsIgnoreCase(cmd)) { debug("Issuing force stop..."); //Ensure that objects are stopped, destroyed only if vm is forcibly exiting stopLifeCycles(exitVm); //Stop accepting any more commands stopInput(socket); // Reply to client debug("Informing client that we are stopped."); informClient(out, "Stopped\r\n"); //Stop the output and close the monitor socket stopOutput(socket); //Honour any pre-setup config to stop the jvm when this command is given if (exitVm) { // Kill JVM debug("Killing JVM"); System.exit(0); } } else if ("stopexit".equalsIgnoreCase(cmd)) { debug("Issuing stop and exit..."); //Make sure that objects registered with the shutdown thread will be stopped stopLifeCycles(true); //Stop accepting any more input stopInput(socket); // Reply to client debug("Informing client that we are stopped."); informClient(out, "Stopped\r\n"); //Stop the output and close the monitor socket stopOutput(socket); debug("Killing JVM"); System.exit(0); } else if ("exit".equalsIgnoreCase(cmd)) { debug("Killing JVM"); System.exit(0); } else if ("status".equalsIgnoreCase(cmd)) { // Reply to client informClient(out, "OK\r\n"); } } catch (Exception e) { debug(e); System.err.println(e.toString()); } finally { close(socket); socket = null; } } } public void stopInput (Socket socket) { //Stop accepting any more input close(serverSocket); serverSocket = null; //Shutdown input from client shutdownInput(socket); } public void stopOutput (Socket socket) throws IOException { socket.shutdownOutput(); close(socket); socket = null; debug("Shutting down monitor"); serverSocket = null; } public void informClient (OutputStream out, String message) throws IOException { out.write(message.getBytes(StandardCharsets.UTF_8)); out.flush(); } /** * Stop the registered lifecycles, optionally * calling destroy on them. * * @param destroy */ public void stopLifeCycles (boolean destroy) { for (LifeCycle l:_lifeCycles) { try { if (l.isStarted()) { l.stop(); } if ((l instanceof Destroyable) && destroy) ((Destroyable)l).destroy(); } catch (Exception e) { debug(e); } } } public void startListenSocket() { if (port < 0) { if (DEBUG) System.err.println("ShutdownMonitor not in use (port < 0): " + port); return; } try { serverSocket = new ServerSocket(port,1,InetAddress.getByName("127.0.0.1")); if (port == 0) { // server assigned port in use port = serverSocket.getLocalPort(); System.out.printf("STOP.PORT=%d%n",port); } if (key == null) { // create random key key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()),36); System.out.printf("STOP.KEY=%s%n",key); } } catch (Exception e) { debug(e); System.err.println("Error binding monitor port " + port + ": " + e.toString()); serverSocket = null; } finally { // establish the port and key that are in use debug("STOP.PORT=%d",port); debug("STOP.KEY=%s",key); debug("%s",serverSocket); } } } private boolean DEBUG; private int port; private String key; private boolean exitVm; private ServerSocket serverSocket; private Thread thread; /** * Create a ShutdownMonitor using configuration from the System properties. *

* STOP.PORT = the port to listen on (empty, null, or values less than 0 disable the stop ability)
* STOP.KEY = the magic key/passphrase to allow the stop (defaults to "eclipse")
*

* Note: server socket will only listen on localhost, and a successful stop will issue a System.exit() call. */ private ShutdownMonitor() { Properties props = System.getProperties(); this.DEBUG = props.containsKey("DEBUG"); // Use values passed thru via /jetty-start/ this.port = Integer.parseInt(props.getProperty("STOP.PORT","-1")); this.key = props.getProperty("STOP.KEY",null); this.exitVm = true; } private void close(ServerSocket server) { if (server == null) { return; } try { server.close(); } catch (IOException ignore) { debug(ignore); } } private void close(Socket socket) { if (socket == null) { return; } try { socket.close(); } catch (IOException ignore) { debug(ignore); } } private void shutdownInput(Socket socket) { if (socket == null) return; try { socket.shutdownInput(); } catch (IOException ignore) { debug(ignore); } } private void debug(String format, Object... args) { if (DEBUG) { System.err.printf("[ShutdownMonitor] " + format + "%n",args); } } private void debug(Throwable t) { if (DEBUG) { t.printStackTrace(System.err); } } public String getKey() { return key; } public int getPort() { return port; } public ServerSocket getServerSocket() { return serverSocket; } public boolean isExitVm() { return exitVm; } public void setDebug(boolean flag) { this.DEBUG = flag; } /** * @param exitVm */ public void setExitVm(boolean exitVm) { synchronized (this) { if (thread != null && thread.isAlive()) { throw new IllegalStateException("ShutdownMonitorThread already started"); } this.exitVm = exitVm; } } public void setKey(String key) { synchronized (this) { if (thread != null && thread.isAlive()) { throw new IllegalStateException("ShutdownMonitorThread already started"); } this.key = key; } } public void setPort(int port) { synchronized (this) { if (thread != null && thread.isAlive()) { throw new IllegalStateException("ShutdownMonitorThread already started"); } this.port = port; } } protected void start() throws Exception { Thread t = null; synchronized (this) { if (thread != null && thread.isAlive()) { if (DEBUG) System.err.printf("ShutdownMonitorThread already started"); return; // cannot start it again } thread = new Thread(new ShutdownMonitorRunnable()); thread.setDaemon(true); thread.setName("ShutdownMonitor"); t = thread; } if (t != null) t.start(); } protected boolean isAlive () { boolean result = false; synchronized (this) { result = (thread != null && thread.isAlive()); } return result; } @Override public String toString() { return String.format("%s[port=%d]",this.getClass().getName(),port); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy