| // |
| // ======================================================================== |
| // Copyright (c) 1995-2017 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 static org.eclipse.jetty.start.UsageException.*; |
| |
| import java.io.BufferedReader; |
| import java.io.BufferedWriter; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.LineNumberReader; |
| import java.io.OutputStream; |
| import java.io.PrintWriter; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.net.ConnectException; |
| import java.net.InetAddress; |
| import java.net.Socket; |
| import java.net.SocketTimeoutException; |
| import java.net.URL; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.StandardOpenOption; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Set; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.jetty.start.config.CommandLineConfigSource; |
| |
| /** |
| * Main start class. |
| * <p> |
| * This class is intended to be the main class listed in the MANIFEST.MF of the start.jar archive. It allows the Jetty Application server to be started with the |
| * command "java -jar start.jar". |
| * <p> |
| * Argument processing steps: |
| * <ol> |
| * <li>Directory Locations: |
| * <ul> |
| * <li>jetty.home=[directory] (the jetty.home location)</li> |
| * <li>jetty.base=[directory] (the jetty.base location)</li> |
| * </ul> |
| * </li> |
| * <li>Start Logging behavior: |
| * <ul> |
| * <li>--debug (debugging enabled)</li> |
| * <li>--start-log-file=logs/start.log (output start logs to logs/start.log location)</li> |
| * </ul> |
| * </li> |
| * <li>Module Resolution</li> |
| * <li>Properties Resolution</li> |
| * <li>Present Optional Informational Options</li> |
| * <li>Normal Startup</li> |
| * </li> |
| * </ol> |
| */ |
| public class Main |
| { |
| private static final String EXITING_LICENSE_NOT_ACKNOWLEDGED = "Exiting: license not acknowledged!"; |
| private static final int EXIT_USAGE = 1; |
| |
| public static String join(Collection<?> objs, String delim) |
| { |
| if (objs==null) |
| { |
| return ""; |
| } |
| StringBuilder str = new StringBuilder(); |
| boolean needDelim = false; |
| for (Object obj : objs) |
| { |
| if (needDelim) |
| { |
| str.append(delim); |
| } |
| str.append(obj); |
| needDelim = true; |
| } |
| return str.toString(); |
| } |
| |
| public static void main(String[] args) |
| { |
| try |
| { |
| Main main = new Main(); |
| StartArgs startArgs = main.processCommandLine(args); |
| main.start(startArgs); |
| } |
| catch (UsageException e) |
| { |
| System.err.println(e.getMessage()); |
| usageExit(e.getCause(),e.getExitCode()); |
| } |
| catch (Throwable e) |
| { |
| usageExit(e,UsageException.ERR_UNKNOWN); |
| } |
| } |
| |
| static void usageExit(int exit) |
| { |
| usageExit(null,exit); |
| } |
| |
| static void usageExit(Throwable t, int exit) |
| { |
| if (t != null) |
| { |
| t.printStackTrace(System.err); |
| } |
| System.err.println(); |
| System.err.println("Usage: java -jar start.jar [options] [properties] [configs]"); |
| System.err.println(" java -jar start.jar --help # for more information"); |
| System.exit(exit); |
| } |
| |
| private BaseHome baseHome; |
| private StartArgs startupArgs; |
| |
| public Main() throws IOException |
| { |
| } |
| |
| private void copyInThread(final InputStream in, final OutputStream out) |
| { |
| new Thread(new Runnable() |
| { |
| @Override |
| public void run() |
| { |
| try |
| { |
| byte[] buf = new byte[1024]; |
| int len = in.read(buf); |
| while (len > 0) |
| { |
| out.write(buf,0,len); |
| len = in.read(buf); |
| } |
| } |
| catch (IOException e) |
| { |
| // e.printStackTrace(); |
| } |
| } |
| |
| }).start(); |
| } |
| |
| private void initFile(StartArgs args, FileArg farg) |
| { |
| try |
| { |
| Path file = baseHome.getBasePath(farg.location); |
| |
| StartLog.debug("[init-file] %s module specified file %s",file.toAbsolutePath(),(FS.exists(file)?"[Exists!]":"")); |
| if (FS.exists(file)) |
| { |
| // file already initialized / downloaded, skip it |
| return; |
| } |
| |
| if (farg.uri!=null) |
| { |
| URL url = new URL(farg.uri); |
| |
| StartLog.log("DOWNLOAD", "%s to %s", url, farg.location); |
| |
| FS.ensureDirectoryExists(file.getParent()); |
| |
| if (args.isTestingModeEnabled()) |
| { |
| StartLog.log("TESTING MODE", "Skipping download of " + url); |
| return; |
| } |
| |
| byte[] buf = new byte[8192]; |
| try (InputStream in = url.openStream(); |
| OutputStream out = Files.newOutputStream(file,StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE)) |
| { |
| while (true) |
| { |
| int len = in.read(buf); |
| |
| if (len > 0) |
| { |
| out.write(buf,0,len); |
| } |
| if (len < 0) |
| { |
| break; |
| } |
| } |
| } |
| } |
| else if (farg.location.endsWith("/")) |
| { |
| StartLog.log("MKDIR",baseHome.toShortForm(file)); |
| FS.ensureDirectoryExists(file); |
| } |
| else |
| { |
| String shortRef = baseHome.toShortForm(file); |
| if (args.isTestingModeEnabled()) |
| { |
| StartLog.log("TESTING MODE","Skipping required file check on: %s",shortRef); |
| return; |
| } |
| StartLog.warn("MISSING: Required file %s",shortRef); |
| } |
| } |
| catch (Exception e) |
| { |
| StartLog.warn("ERROR: processing %s%n%s",farg,e); |
| StartLog.warn(e); |
| usageExit(EXIT_USAGE); |
| } |
| } |
| |
| private void dumpClasspathWithVersions(Classpath classpath) |
| { |
| StartLog.endStartLog(); |
| System.out.println(); |
| System.out.println("Jetty Server Classpath:"); |
| System.out.println("-----------------------"); |
| if (classpath.count() == 0) |
| { |
| System.out.println("No classpath entries and/or version information available show."); |
| return; |
| } |
| |
| System.out.println("Version Information on " + classpath.count() + " entr" + ((classpath.count() > 1)?"ies":"y") + " in the classpath."); |
| System.out.println("Note: order presented here is how they would appear on the classpath."); |
| System.out.println(" changes to the --module=name command line options will be reflected here."); |
| |
| int i = 0; |
| for (File element : classpath.getElements()) |
| { |
| System.out.printf("%2d: %24s | %s\n",i++,getVersion(element),baseHome.toShortForm(element)); |
| } |
| } |
| |
| public BaseHome getBaseHome() |
| { |
| return baseHome; |
| } |
| |
| private String getVersion(File element) |
| { |
| if (element.isDirectory()) |
| { |
| return "(dir)"; |
| } |
| |
| if (element.isFile()) |
| { |
| String name = element.getName().toLowerCase(Locale.ENGLISH); |
| if (name.endsWith(".jar")) |
| { |
| return JarVersion.getVersion(element); |
| } |
| } |
| |
| return ""; |
| } |
| |
| public void invokeMain(ClassLoader classloader, StartArgs args) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException, IOException |
| { |
| Class<?> invoked_class = null; |
| String mainclass = args.getMainClassname(); |
| |
| try |
| { |
| invoked_class = classloader.loadClass(mainclass); |
| } |
| catch (ClassNotFoundException e) |
| { |
| System.out.println("WARNING: Nothing to start, exiting ..."); |
| StartLog.debug(e); |
| usageExit(ERR_INVOKE_MAIN); |
| return; |
| } |
| |
| StartLog.debug("%s - %s",invoked_class,invoked_class.getPackage().getImplementationVersion()); |
| |
| CommandLineBuilder cmd = args.getMainArgs(baseHome,false); |
| String argArray[] = cmd.getArgs().toArray(new String[0]); |
| StartLog.debug("Command Line Args: %s",cmd.toString()); |
| |
| Class<?>[] method_param_types = new Class[] |
| { argArray.getClass() }; |
| |
| Method main = invoked_class.getDeclaredMethod("main",method_param_types); |
| Object[] method_params = new Object[] { argArray }; |
| StartLog.endStartLog(); |
| main.invoke(null,method_params); |
| } |
| |
| public void listConfig(StartArgs args) |
| { |
| StartLog.endStartLog(); |
| |
| // Dump Jetty Home / Base |
| args.dumpEnvironment(baseHome); |
| |
| // Dump JVM Args |
| args.dumpJvmArgs(); |
| |
| // Dump System Properties |
| args.dumpSystemProperties(); |
| |
| // Dump Properties |
| args.dumpProperties(); |
| |
| // Dump Classpath |
| dumpClasspathWithVersions(args.getClasspath()); |
| |
| // Dump Resolved XMLs |
| args.dumpActiveXmls(baseHome); |
| } |
| |
| private void listModules(StartArgs args) |
| { |
| StartLog.endStartLog(); |
| System.out.println(); |
| System.out.println("Jetty All Available Modules:"); |
| System.out.println("----------------------------"); |
| args.getAllModules().dump(); |
| |
| // Dump Enabled Modules |
| System.out.println(); |
| System.out.println("Jetty Active Module Tree:"); |
| System.out.println("-------------------------"); |
| Modules modules = args.getAllModules(); |
| modules.dumpEnabledTree(); |
| } |
| |
| /** |
| * Build out INI file. |
| * <p> |
| * This applies equally for either <code>${jetty.base}/start.ini</code> or |
| * <code>${jetty.base}/start.d/${name}.ini</code> |
| * |
| * @param args the arguments of what modules are enabled |
| * @param name the name of the module to based the build of the ini |
| * @param topLevel |
| * @param appendStartIni true to append to <code>${jetty.base}/start.ini</code>, |
| * false to create a <code>${jetty.base}/start.d/${name}.ini</code> entry instead. |
| * @throws IOException |
| */ |
| private void buildIni(StartArgs args, String name, boolean topLevel, boolean appendStartIni) throws IOException |
| { |
| // Find the start.d relative to the base directory only. |
| Path start_d = baseHome.getBasePath("start.d"); |
| |
| // Is this a module? |
| Modules modules = args.getAllModules(); |
| Module module = modules.get(name); |
| if (module == null) |
| { |
| StartLog.warn("ERROR: No known module for %s",name); |
| return; |
| } |
| |
| boolean transitive = module.isEnabled() && (module.getSources().size() == 0); |
| |
| // Find any named ini file and check it follows the convention |
| Path start_ini = baseHome.getBasePath("start.ini"); |
| String short_start_ini = baseHome.toShortForm(start_ini); |
| Path startd_ini = start_d.resolve(name + ".ini"); |
| String short_startd_ini = baseHome.toShortForm(startd_ini); |
| StartIni module_ini = null; |
| if (FS.exists(startd_ini)) |
| { |
| module_ini = new StartIni(startd_ini); |
| if (module_ini.getLineMatches(Pattern.compile("--module=(.*, *)*" + name)).size() == 0) |
| { |
| StartLog.warn("ERROR: %s is not enabled in %s!",name,short_startd_ini); |
| return; |
| } |
| } |
| |
| if (!args.isApproveAllLicenses()) |
| { |
| if (!module.hasFiles(baseHome) && !module.acknowledgeLicense()) |
| { |
| StartLog.warn(EXITING_LICENSE_NOT_ACKNOWLEDGED); |
| System.exit(1); |
| } |
| } |
| |
| boolean buildIni=false; |
| if (module.isEnabled()) |
| { |
| // is it an explicit request to create an ini file? |
| if (topLevel && !FS.exists(startd_ini) && !appendStartIni) |
| { |
| buildIni=true; |
| } |
| // else is it transitive |
| else if (transitive) |
| { |
| if (module.hasDefaultConfig()) |
| { |
| buildIni = true; |
| StartLog.info("%-15s initialised transitively",name); |
| } |
| } |
| // else must be initialized explicitly |
| else |
| { |
| for (String source : module.getSources()) |
| { |
| StartLog.info("%-15s initialised in %s",name,baseHome.toShortForm(source)); |
| } |
| } |
| } |
| else |
| { |
| buildIni=true; |
| } |
| |
| String source = "<transitive>"; |
| |
| // If we need an ini |
| if (buildIni) |
| { |
| // File BufferedWriter |
| BufferedWriter writer = null; |
| PrintWriter out = null; |
| try |
| { |
| if (appendStartIni) |
| { |
| source = short_start_ini; |
| StartLog.info("%-15s initialised in %s (appended)",name,source); |
| writer = Files.newBufferedWriter(start_ini,StandardCharsets.UTF_8,StandardOpenOption.CREATE,StandardOpenOption.APPEND); |
| out = new PrintWriter(writer); |
| } |
| else |
| { |
| // Create the directory if needed |
| FS.ensureDirectoryExists(start_d); |
| FS.ensureDirectoryWritable(start_d); |
| source = short_startd_ini; |
| StartLog.info("%-15s initialised in %s (created)",name,source); |
| writer = Files.newBufferedWriter(startd_ini,StandardCharsets.UTF_8,StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE); |
| out = new PrintWriter(writer); |
| } |
| |
| if (appendStartIni) |
| { |
| out.println(); |
| } |
| out.println("# --------------------------------------- "); |
| out.println("# Module: " + name); |
| |
| out.println("--module=" + name); |
| |
| args.parse("--module=" + name,source); |
| args.parseModule(module); |
| |
| for (String line : module.getDefaultConfig()) |
| { |
| out.println(line); |
| } |
| } |
| finally |
| { |
| if (out != null) |
| { |
| out.close(); |
| } |
| } |
| } |
| |
| modules.enable(name,Collections.singletonList(source)); |
| |
| // Also list other places this module is enabled |
| for (String src : module.getSources()) |
| { |
| StartLog.debug("also enabled in: %s",src); |
| if (!short_start_ini.equals(src)) |
| { |
| StartLog.info("%-15s enabled in %s",name,baseHome.toShortForm(src)); |
| } |
| } |
| |
| // Do downloads now |
| for (String file : module.getFiles()) |
| { |
| initFile(args, new FileArg(module,file)); |
| } |
| |
| // Process dependencies |
| module.expandProperties(args.getProperties()); |
| modules.registerParentsIfMissing(module); |
| modules.buildGraph(); |
| |
| // process new ini modules |
| if (topLevel) |
| { |
| List<Module> depends = new ArrayList<>(); |
| for (String depend : modules.resolveParentModulesOf(name)) |
| { |
| if (!name.equals(depend)) |
| { |
| Module m = modules.get(depend); |
| m.setEnabled(true); |
| depends.add(m); |
| } |
| } |
| Collections.sort(depends,Collections.reverseOrder(new Module.DepthComparator())); |
| |
| Set<String> done = new HashSet<>(0); |
| while (true) |
| { |
| // initialize known dependencies |
| boolean complete=true; |
| for (Module m : depends) |
| { |
| if (!done.contains(m.getName())) |
| { |
| complete=false; |
| buildIni(args,m.getName(),false,appendStartIni); |
| done.add(m.getName()); |
| } |
| } |
| |
| if (complete) |
| { |
| break; |
| } |
| |
| // look for any new ones resolved via expansion |
| depends.clear(); |
| for (String depend : modules.resolveParentModulesOf(name)) |
| { |
| if (!name.equals(depend)) |
| { |
| Module m = modules.get(depend); |
| m.setEnabled(true); |
| depends.add(m); |
| } |
| } |
| Collections.sort(depends,Collections.reverseOrder(new Module.DepthComparator())); |
| } |
| } |
| } |
| |
| /** |
| * Convenience for <code>processCommandLine(cmdLine.toArray(new String[cmdLine.size()]))</code> |
| */ |
| public StartArgs processCommandLine(List<String> cmdLine) throws Exception |
| { |
| return this.processCommandLine(cmdLine.toArray(new String[cmdLine.size()])); |
| } |
| |
| public StartArgs processCommandLine(String[] cmdLine) throws Exception |
| { |
| // Processing Order is important! |
| // ------------------------------------------------------------ |
| // 1) Configuration Locations |
| CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine); |
| baseHome = new BaseHome(cmdLineSource); |
| |
| StartLog.debug("jetty.home=%s",baseHome.getHome()); |
| StartLog.debug("jetty.base=%s",baseHome.getBase()); |
| |
| // ------------------------------------------------------------ |
| // 2) Parse everything provided. |
| // This would be the directory information + |
| // the various start inis |
| // and then the raw command line arguments |
| StartLog.debug("Parsing collected arguments"); |
| StartArgs args = new StartArgs(); |
| args.parse(baseHome.getConfigSources()); |
| |
| // ------------------------------------------------------------ |
| // 3) Module Registration |
| Modules modules = new Modules(baseHome,args); |
| StartLog.debug("Registering all modules"); |
| modules.registerAll(); |
| |
| // ------------------------------------------------------------ |
| // 4) Active Module Resolution |
| for (String enabledModule : args.getEnabledModules()) |
| { |
| List<String> msources = args.getSources(enabledModule); |
| modules.enable(enabledModule,msources); |
| } |
| |
| StartLog.debug("Building Module Graph"); |
| modules.buildGraph(); |
| |
| args.setAllModules(modules); |
| List<Module> activeModules = modules.resolveEnabled(); |
| |
| // ------------------------------------------------------------ |
| // 5) Lib & XML Expansion / Resolution |
| args.expandLibs(baseHome); |
| args.expandModules(baseHome,activeModules); |
| |
| // ------------------------------------------------------------ |
| // 6) Resolve Extra XMLs |
| args.resolveExtraXmls(baseHome); |
| |
| // ------------------------------------------------------------ |
| // 9) Resolve Property Files |
| args.resolvePropertyFiles(baseHome); |
| |
| return args; |
| } |
| |
| public void start(StartArgs args) throws IOException, InterruptedException |
| { |
| StartLog.debug("StartArgs: %s",args); |
| |
| // Get Desired Classpath based on user provided Active Options. |
| Classpath classpath = args.getClasspath(); |
| |
| System.setProperty("java.class.path",classpath.toString()); |
| |
| // Show the usage information and return |
| if (args.isHelp()) |
| { |
| usage(true); |
| } |
| |
| // Show the version information and return |
| if (args.isListClasspath()) |
| { |
| dumpClasspathWithVersions(classpath); |
| } |
| |
| // Show configuration |
| if (args.isListConfig()) |
| { |
| listConfig(args); |
| } |
| |
| // Show modules |
| if (args.isListModules()) |
| { |
| listModules(args); |
| } |
| |
| // Generate Module Graph File |
| if (args.getModuleGraphFilename() != null) |
| { |
| Path outputFile = baseHome.getBasePath(args.getModuleGraphFilename()); |
| System.out.printf("Generating GraphViz Graph of Jetty Modules at %s%n",baseHome.toShortForm(outputFile)); |
| ModuleGraphWriter writer = new ModuleGraphWriter(); |
| writer.config(args.getProperties()); |
| writer.write(args.getAllModules(),outputFile); |
| } |
| |
| // Show Command Line to execute Jetty |
| if (args.isDryRun()) |
| { |
| CommandLineBuilder cmd = args.getMainArgs(baseHome,true); |
| System.out.println(cmd.toString(File.separatorChar=='/'?" \\\n":" ")); |
| } |
| |
| if (args.isStopCommand()) |
| { |
| doStop(args); |
| } |
| |
| boolean rebuildGraph = false; |
| |
| // Initialize start.ini |
| for (String module : args.getAddToStartIni()) |
| { |
| buildIni(args,module,true,true); |
| rebuildGraph = true; |
| } |
| |
| // Initialize start.d |
| for (String module : args.getAddToStartdIni()) |
| { |
| buildIni(args,module,true,false); |
| rebuildGraph = true; |
| } |
| |
| if (rebuildGraph) |
| { |
| args.getAllModules().clearMissing(); |
| args.getAllModules().buildGraph(); |
| } |
| |
| // If in --create-files, check licenses |
| if(args.isDownload()) |
| { |
| if (!args.isApproveAllLicenses()) |
| { |
| for (Module module : args.getAllModules().resolveEnabled()) |
| { |
| if (!module.hasFiles(baseHome) && !module.acknowledgeLicense()) |
| { |
| StartLog.warn(EXITING_LICENSE_NOT_ACKNOWLEDGED); |
| System.exit(1); |
| } |
| } |
| } |
| } |
| |
| // Check ini files for download possibilities |
| for (FileArg arg : args.getFiles()) |
| { |
| Path file = baseHome.getBasePath(arg.location); |
| if (!FS.exists(file) && args.isDownload()) |
| { |
| initFile(args, arg); |
| } |
| |
| if (!FS.exists(file)) |
| { |
| boolean isDir = arg.location.endsWith("/"); |
| if (isDir) |
| { |
| StartLog.log("MKDIR", baseHome.toShortForm(file)); |
| FS.ensureDirectoryExists(file); |
| /* Startup should not fail to run on missing directories. |
| * See Bug #427204 |
| */ |
| // args.setRun(false); |
| } |
| else |
| { |
| String shortRef = baseHome.toShortForm(file); |
| if (args.isTestingModeEnabled()) |
| { |
| StartLog.log("TESTING MODE","Skipping required file check on: %s",shortRef); |
| return; |
| } |
| |
| StartLog.warn("Missing Required File: %s",baseHome.toShortForm(file)); |
| args.setRun(false); |
| if (arg.uri != null) |
| { |
| StartLog.warn(" Can be downloaded From: %s",arg.uri); |
| StartLog.warn(" Run start.jar --create-files to download"); |
| } |
| } |
| } |
| } |
| |
| // Informational command line, don't run jetty |
| if (!args.isRun()) |
| { |
| return; |
| } |
| |
| // execute Jetty in another JVM |
| if (args.isExec()) |
| { |
| CommandLineBuilder cmd = args.getMainArgs(baseHome,true); |
| cmd.debug(); |
| ProcessBuilder pbuilder = new ProcessBuilder(cmd.getArgs()); |
| StartLog.endStartLog(); |
| final Process process = pbuilder.start(); |
| Runtime.getRuntime().addShutdownHook(new Thread() |
| { |
| @Override |
| public void run() |
| { |
| StartLog.debug("Destroying " + process); |
| process.destroy(); |
| } |
| }); |
| |
| copyInThread(process.getErrorStream(),System.err); |
| copyInThread(process.getInputStream(),System.out); |
| copyInThread(System.in,process.getOutputStream()); |
| process.waitFor(); |
| System.exit(0); // exit JVM when child process ends. |
| return; |
| } |
| |
| if (args.hasJvmArgs() || args.hasSystemProperties()) |
| { |
| System.err.println("WARNING: System properties and/or JVM args set. Consider using --dry-run or --exec"); |
| } |
| |
| ClassLoader cl = classpath.getClassLoader(); |
| Thread.currentThread().setContextClassLoader(cl); |
| |
| // Invoke the Main Class |
| try |
| { |
| invokeMain(cl, args); |
| } |
| catch (Exception e) |
| { |
| usageExit(e,ERR_INVOKE_MAIN); |
| } |
| } |
| |
| private void doStop(StartArgs args) |
| { |
| String stopHost = args.getProperties().getString("STOP.HOST"); |
| int stopPort = Integer.parseInt(args.getProperties().getString("STOP.PORT")); |
| String stopKey = args.getProperties().getString("STOP.KEY"); |
| |
| if (args.getProperties().getString("STOP.WAIT") != null) |
| { |
| int stopWait = Integer.parseInt(args.getProperties().getString("STOP.WAIT")); |
| |
| stop(stopHost,stopPort,stopKey,stopWait); |
| } |
| else |
| { |
| stop(stopHost,stopPort,stopKey); |
| } |
| } |
| |
| /** |
| * Stop a running jetty instance. |
| */ |
| public void stop(String host, int port, String key) |
| { |
| stop(host,port,key,0); |
| } |
| |
| public void stop(String host, int port, String key, int timeout) |
| { |
| if (host==null || host.length()==0) |
| host="127.0.0.1"; |
| |
| try |
| { |
| if (port <= 0) |
| { |
| System.err.println("STOP.PORT system property must be specified"); |
| } |
| if (key == null) |
| { |
| key = ""; |
| System.err.println("STOP.KEY system property must be specified"); |
| System.err.println("Using empty key"); |
| } |
| |
| try (Socket s = new Socket(InetAddress.getByName(host),port)) |
| { |
| if (timeout > 0) |
| { |
| s.setSoTimeout(timeout * 1000); |
| } |
| |
| try (OutputStream out = s.getOutputStream()) |
| { |
| out.write((key + "\r\nstop\r\n").getBytes()); |
| out.flush(); |
| |
| if (timeout > 0) |
| { |
| 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) |
| { |
| StartLog.debug("Received \"%s\"",response); |
| if ("Stopped".equals(response)) |
| { |
| StartLog.warn("Server reports itself as Stopped"); |
| } |
| } |
| } |
| } |
| } |
| } |
| catch (SocketTimeoutException e) |
| { |
| System.err.println("Timed out waiting for stop confirmation"); |
| System.exit(ERR_UNKNOWN); |
| } |
| catch (ConnectException e) |
| { |
| usageExit(e,ERR_NOT_STOPPED); |
| } |
| catch (Exception e) |
| { |
| usageExit(e,ERR_UNKNOWN); |
| } |
| } |
| |
| public void usage(boolean exit) |
| { |
| StartLog.endStartLog(); |
| if(!printTextResource("org/eclipse/jetty/start/usage.txt")) |
| { |
| System.err.println("ERROR: detailed usage resource unavailable"); |
| } |
| if (exit) |
| { |
| System.exit(EXIT_USAGE); |
| } |
| } |
| |
| public static boolean printTextResource(String resourceName) |
| { |
| boolean resourcePrinted = false; |
| try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName)) |
| { |
| if (stream != null) |
| { |
| try (InputStreamReader reader = new InputStreamReader(stream); BufferedReader buf = new BufferedReader(reader)) |
| { |
| resourcePrinted = true; |
| String line; |
| while ((line = buf.readLine()) != null) |
| { |
| System.out.println(line); |
| } |
| } |
| } |
| else |
| { |
| System.out.println("Unable to find resource: " + resourceName); |
| } |
| } |
| catch (IOException e) |
| { |
| StartLog.warn(e); |
| } |
| |
| return resourcePrinted; |
| } |
| |
| // ------------------------------------------------------------ |
| // implement Apache commons daemon (jsvc) lifecycle methods (init, start, stop, destroy) |
| public void init(String[] args) throws Exception |
| { |
| try |
| { |
| startupArgs = processCommandLine(args); |
| } |
| catch (UsageException e) |
| { |
| System.err.println(e.getMessage()); |
| usageExit(e.getCause(),e.getExitCode()); |
| } |
| catch (Throwable e) |
| { |
| usageExit(e,UsageException.ERR_UNKNOWN); |
| } |
| } |
| |
| public void start() throws Exception |
| { |
| start(startupArgs); |
| } |
| |
| public void stop() throws Exception |
| { |
| doStop(startupArgs); |
| } |
| |
| public void destroy() |
| { |
| } |
| } |