diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index c891d63066d..9fc62e9f57d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.server; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Enumeration; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -260,11 +261,14 @@ public class Server extends HandlerWrapper implements Attributes @Override protected void doStart() throws Exception { - if (getStopAtShutdown()) + if (getStopAtShutdown()) { ShutdownThread.register(this); + ShutdownMonitor.getInstance(); // initialize + } LOG.info("jetty-"+__version); HttpGenerator.setServerVersion(__version); + MultiException mex=new MultiException(); if (_threadPool==null) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java new file mode 100644 index 00000000000..25335475957 --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java @@ -0,0 +1,235 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 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.util.Properties; + +import org.eclipse.jetty.util.StringUtil; +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 extends Thread +{ + private static final ShutdownMonitor INSTANCE = new ShutdownMonitor(); + + public static ShutdownMonitor getInstance() + { + return INSTANCE; + } + + private final boolean DEBUG; + private final int port; + private final String key; + private final ServerSocket serverSocket; + + private ShutdownMonitor() + { + Properties props = System.getProperties(); + + // Use the same debug option as /jetty-start/ + this.DEBUG = props.containsKey("DEBUG"); + + // Use values passed thru /jetty-start/ + int stopPort = Integer.parseInt(props.getProperty("STOP.PORT","-1")); + String stopKey = props.getProperty("STOP.KEY",null); + + ServerSocket sock = null; + + try + { + if (stopPort < 0) + { + System.out.println("ShutdownMonitor not in use"); + sock = null; + return; + } + + setDaemon(true); + setName("ShutdownMonitor"); + + sock = new ServerSocket(stopPort,1,InetAddress.getByName("127.0.0.1")); + if (stopPort == 0) + { + // server assigned port in use + stopPort = sock.getLocalPort(); + System.out.printf("STOP.PORT=%d%n",stopPort); + } + + if (stopKey == null) + { + // create random key + stopKey = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()),36); + System.out.printf("STOP.KEY=%s%n",stopKey); + } + + } + catch (Exception e) + { + debug(e); + System.err.println("Error binding monitor port " + stopPort + ": " + e.toString()); + } + finally + { + // establish the port and key that are in use + this.port = stopPort; + this.key = stopKey; + + this.serverSocket = sock; + debug("STOP.PORT=%d", port); + debug("STOP.KEY=%s", key); + debug("%s", serverSocket); + } + + this.start(); + } + + @Override + public String toString() + { + return String.format("%s[port=%d]",this.getClass().getName(),port); + } + + private void debug(Throwable t) + { + if (DEBUG) + { + t.printStackTrace(System.err); + } + } + + private void debug(String format, Object... args) + { + if (DEBUG) + { + System.err.printf("[ShutdownMonitor] " + format + "%n",args); + } + } + + @Override + public void run() + { + while (true) + { + Socket socket = null; + try + { + socket = serverSocket.accept(); + + LineNumberReader lin = new LineNumberReader(new InputStreamReader(socket.getInputStream())); + String key = lin.readLine(); + if (!this.key.equals(key)) + { + System.err.println("Ignoring command with incorrect key"); + continue; + } + + OutputStream out = socket.getOutputStream(); + + String cmd = lin.readLine(); + debug("command=%s",cmd); + if ("stop".equals(cmd)) + { + // Graceful Shutdown + debug("Issuing graceful shutdown.."); + ShutdownThread.getInstance().run(); + + // Reply to client + debug("Informing client that we are stopped."); + out.write("Stopped\r\n".getBytes(StringUtil.__UTF8)); + out.flush(); + + // Shutdown Monitor + debug("Shutting down monitor"); + close(socket); + close(serverSocket); + + // Kill JVM + debug("Killing JVM"); + System.exit(0); + } + else if ("status".equals(cmd)) + { + // Reply to client + out.write("OK\r\n".getBytes(StringUtil.__UTF8)); + out.flush(); + } + } + catch (Exception e) + { + debug(e); + System.err.println(e.toString()); + } + finally + { + close(socket); + socket = null; + } + } + } + + private void close(Socket socket) + { + if (socket == null) + { + return; + } + + try + { + socket.close(); + } + catch (IOException ignore) + { + /* ignore */ + } + } + + private void close(ServerSocket server) + { + if (server == null) + { + return; + } + + try + { + server.close(); + } + catch (IOException ignore) + { + /* ignore */ + } + } +} diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java index b066a569145..4ab1764fb36 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java @@ -53,12 +53,13 @@ import java.util.Set; /*-------------------------------------------*/ /** *

- * Main start class. This class is intended to be the main class listed in the MANIFEST.MF of the start.jar archive. It allows an application to be started with - * the command "java -jar start.jar". + * Main start class. This class is intended to be the main class listed in the MANIFEST.MF of the start.jar archive. It + * allows an application to be started with the command "java -jar start.jar". *

- * + * *

- * The behaviour of Main is controlled by the parsing of the {@link Config} "org/eclipse/start/start.config" file obtained as a resource or file. + * The behaviour of Main is controlled by the parsing of the {@link Config} "org/eclipse/start/start.config" file + * obtained as a resource or file. *

*/ public class Main @@ -91,7 +92,7 @@ public class Main Main main = new Main(); List arguments = main.expandCommandLine(args); List xmls = main.processCommandLine(arguments); - if (xmls!=null) + if (xmls != null) main.start(xmls); } catch (Throwable e) @@ -143,7 +144,7 @@ public class Main List parseStartIniFiles() { - List ini_args=new ArrayList(); + List ini_args = new ArrayList(); File start_ini = new File(_jettyHome,"start.ini"); if (start_ini.exists()) ini_args.addAll(loadStartIni(start_ini)); @@ -179,14 +180,14 @@ public class Main _showUsage = true; continue; } - + if ("--stop".equals(arg)) { int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1")); String key = Config.getProperty("STOP.KEY",null); - int timeout = Integer.parseInt(Config.getProperty("STOP.WAIT", "0")); + int timeout = Integer.parseInt(Config.getProperty("STOP.WAIT","0")); stop(port,key,timeout); - return null; + return null; } if ("--version".equals(arg) || "-v".equals(arg) || "--info".equals(arg)) @@ -226,7 +227,7 @@ public class Main if (!startDir.exists() || !startDir.canWrite()) startDir = new File("."); - File startLog = new File(startDir, START_LOG_ROLLOVER_DATEFORMAT.format(new Date())); + File startLog = new File(startDir,START_LOG_ROLLOVER_DATEFORMAT.format(new Date())); if (!startLog.exists() && !startLog.createNewFile()) { @@ -246,7 +247,7 @@ public class Main PrintStream logger = new PrintStream(new FileOutputStream(startLog,false)); System.setOut(logger); System.setErr(logger); - System.out.println("Establishing "+ START_LOG_FILENAME + " on " + new Date()); + System.out.println("Establishing " + START_LOG_FILENAME + " on " + new Date()); continue; } @@ -492,11 +493,6 @@ public class Main /* ------------------------------------------------------------ */ public void start(List xmls) throws IOException, InterruptedException { - // Setup Start / Stop Monitoring - int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1")); - String key = Config.getProperty("STOP.KEY",null); - Monitor monitor = new Monitor(port,key); - // Load potential Config (start.config) List configuredXmls = loadConfig(xmls); @@ -581,9 +577,8 @@ public class Main copyInThread(process.getErrorStream(),System.err); copyInThread(process.getInputStream(),System.out); copyInThread(System.in,process.getOutputStream()); - monitor.setProcess(process); process.waitFor(); - + System.exit(0); // exit JVM when child process ends. return; } @@ -688,11 +683,18 @@ public class Main cmd.addArg(x); } cmd.addRawArg("-Djetty.home=" + _jettyHome); + + // Special Stop/Shutdown properties + ensureSystemPropertySet("STOP.PORT"); + ensureSystemPropertySet("STOP.KEY"); + + // System Properties for (String p : _sysProps) { String v = System.getProperty(p); cmd.addEqualsArg("-D" + p,v); } + cmd.addArg("-cp"); cmd.addRawArg(classpath.toString()); cmd.addRawArg(_config.getMainClassname()); @@ -715,6 +717,34 @@ public class Main return cmd; } + /** + * Ensure that the System Properties are set (if defined as a System property, or start.config property, or + * start.ini property) + * + * @param key + * the key to be sure of + */ + private void ensureSystemPropertySet(String key) + { + if (_sysProps.contains(key)) + { + return; // done + } + + Properties props = Config.getProperties(); + if (props.containsKey(key)) + { + String val = props.getProperty(key,null); + if (val == null) + { + return; // no value to set + } + // setup system property + _sysProps.add(key); + System.setProperty(key,val); + } + } + private String findJavaBin() { File javaHome = new File(System.getProperty("java.home")); @@ -927,10 +957,10 @@ public class Main /** * Load Configuration. - * - * No specific configuration is real until a {@link Config#getCombinedClasspath(java.util.Collection)} is used to execute the {@link Class} specified by - * {@link Config#getMainClassname()} is executed. - * + * + * No specific configuration is real until a {@link Config#getCombinedClasspath(java.util.Collection)} is used to + * execute the {@link Class} specified by {@link Config#getMainClassname()} is executed. + * * @param xmls * the command line specified xml configuration options. * @return the list of xml configurations arriving via command line and start.config choices. @@ -1007,11 +1037,10 @@ public class Main */ public void stop(int port, String key) { - stop (port,key, 0); + stop(port,key,0); } - - public void stop (int port, String key, int timeout) + public void stop(int port, String key, int timeout) { int _port = port; String _key = key; @@ -1031,7 +1060,7 @@ public class Main Socket s = new Socket(InetAddress.getByName("127.0.0.1"),_port); if (timeout > 0) - s.setSoTimeout(timeout*1000); + s.setSoTimeout(timeout * 1000); try { OutputStream out = s.getOutputStream(); @@ -1040,7 +1069,7 @@ public class Main if (timeout > 0) { - System.err.printf("Waiting %,d seconds for jetty to stop%n", timeout); + System.err.printf("Waiting %,d seconds for jetty to stop%n",timeout); LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream())); String response; while ((response = lin.readLine()) != null) diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Monitor.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Monitor.java deleted file mode 100644 index 1f5667b09af..00000000000 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/Monitor.java +++ /dev/null @@ -1,157 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 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.start; -import java.io.InputStreamReader; -import java.io.LineNumberReader; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; - -/*-------------------------------------------*/ -/** 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 Monitor extends Thread -{ - private Process _process; - private final int _port; - private final String _key; - - ServerSocket _socket; - - public Monitor(int port,String key) - { - try - { - if(port<0) - return; - setDaemon(true); - setName("StopMonitor"); - _socket=new ServerSocket(port,1,InetAddress.getByName("127.0.0.1")); - if (port==0) - { - port=_socket.getLocalPort(); - System.out.println(port); - } - - if (key==null) - { - key=Long.toString((long)(Long.MAX_VALUE*Math.random()+this.hashCode()+System.currentTimeMillis()),36); - System.out.println("STOP.KEY="+key); - } - } - catch(Exception e) - { - Config.debug(e); - System.err.println("Error binding monitor port "+port+": "+e.toString()); - } - finally - { - _port=port; - _key=key; - } - - if (_socket!=null) - this.start(); - else - System.err.println("WARN: Not listening on monitor port: "+_port); - } - - public Process getProcess() - { - return _process; - } - - public void setProcess(Process process) - { - _process = process; - } - - @Override - public void run() - { - while (true) - { - Socket socket=null; - try{ - socket=_socket.accept(); - - LineNumberReader lin= - new LineNumberReader(new InputStreamReader(socket.getInputStream())); - String key=lin.readLine(); - if (!_key.equals(key)) - { - System.err.println("Ignoring command with incorrect key"); - continue; - } - - String cmd=lin.readLine(); - Config.debug("command=" + cmd); - if ("stop".equals(cmd)) - { - Config.debug("Child Process: " + _process); - if (_process!=null) - { - //if we have a child process, wait for it to finish before we stop - try - { - _process.destroy(); - _process.waitFor(); - } - catch (InterruptedException e) - { - System.err.println("Interrupted waiting for child to terminate"); - } - } - socket.getOutputStream().write("Stopped\r\n".getBytes()); - try {socket.close();}catch(Exception e){e.printStackTrace();} - try {_socket.close();}catch(Exception e){e.printStackTrace();} - System.exit(0); - } - else if ("status".equals(cmd)) - { - socket.getOutputStream().write("OK\r\n".getBytes()); - socket.getOutputStream().flush(); - } - } - catch(Exception e) - { - Config.debug(e); - System.err.println(e.toString()); - } - finally - { - if (socket!=null) - { - try{socket.close();}catch(Exception e){} - } - socket=null; - } - } - } - -}