From 820f2b9195b187eb1d651a154a2835a1cbac39d7 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 2 Sep 2020 23:40:05 +0200 Subject: [PATCH] Support Apache Commons Daemon methods in XmlConfiguration (#5199) * Support Apache Commons Daemon methods in XmlConfiguration so it can substitute for start.Main after a --dry-run * + added --dry-run=parts to printout partial dry run + added --no-exec Signed-off-by: Greg Wilkins * + updated for review feedback Signed-off-by: Greg Wilkins * Feedback from review: + removed features other than --dry-run parts + added documentation --- .../administration/startup/start-jar.adoc | 57 +++++++++- .../java/org/eclipse/jetty/start/Main.java | 6 +- .../org/eclipse/jetty/start/StartArgs.java | 107 ++++++++++++------ .../org/eclipse/jetty/start/usage.txt | 26 ++++- .../org/eclipse/jetty/start/MainTest.java | 2 +- 5 files changed, 156 insertions(+), 42 deletions(-) diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/start-jar.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/start-jar.adoc index ba4d96a34d3..12b2651eeab 100644 --- a/jetty-documentation/src/main/asciidoc/administration/startup/start-jar.adoc +++ b/jetty-documentation/src/main/asciidoc/administration/startup/start-jar.adoc @@ -65,7 +65,7 @@ When executed `start.jar` performs the following actions: 3. Uses default behavior of `java.io.File` (Relative to `System.getProperty` ("user.dir") and then as absolute file system path). * Loads any dependent modules (merges XXNK, library, and properties results with active command line). * Builds out server classpath. -* Determines run mode: +* Determines run mode as one of: ** Shows informational command line options and exit. ** Executes Jetty normally, waits for Jetty to stop. ** Executes a forked JVM to run Jetty in, waits for forked JVM to exit. @@ -89,9 +89,36 @@ Lists the resolved configuration that will start Jetty. * Server classpath * Server XML configuration files --dry-run:: -Prints the resolved command line that `start.jar` should use to start a forked instance of Jetty. +Print the command line that the start.jar generates, then exit. This may be used to generate command lines when the start.ini includes -X or -D arguments: +.... +$ java -jar start.jar --dry-run > jetty.sh +$ . jetty.sh +.... +--dry-run=:: +Print specific parts of the command line. The parts are a comma separated list of: + + * "java" - the JVM to run + * "opts" - the JVM options (eg -D and -X flags) + * "path" - the JVM class path or JPMS modules options + * "main" - the main class to run + * "args" - the arguments passed to the main class + +It is possible to decompose the start command: +.... +$ OPTS=$(java -jar start.jar --dry-run=opts,path) +$ MAIN=$(java -jar start.jar --dry-run=main) +$ ARGS=$(java -jar start.jar --dry-run=args) +$ java $OPTS -Dextra=opt $MAIN $ARGS extra=arg +.... +Alternatively to create an args file for java: +.... +$ java -jar start.jar --dry-run=opts,path,main,args > /tmp/args +$ java @/tmp/args +.... --exec:: -Starts a forked instance of Jetty. +Forces the start to use a forked instance of java to run Jetty. +Some modules include `--exec` in order to set java command line options. +Some start options, such as `--jpms` also imply `--exec` --exec-properties=:: Assign a fixed name to the file used to transfer properties to the sub process. This allows the generated properties file to be saved and reused. @@ -99,7 +126,7 @@ Without this option, a temporary file is used. --commands=:: Instructs `start.jar` to use each line of the specified file as arguments on the command line. -===== Debugg and Start Logging +===== Debug and Start Logging --debug:: Enables debugging output of the startup procedure. @@ -275,3 +302,25 @@ If you have a need for a shaded version of `start.jar` (such as for Gradle), you shaded .... + +==== Start.jar without exec or forking. + +Some Jetty modules include the `--exec` option so that java command line options can be set. +Also some `start.jar` options (eg. `--jpms`) include an implicit `--exec`. +To start jetty without forking a new JVM instance from the start JVM, the `--dry-run` option can be used to generate a command line: +.... +$ CMD=$(java -jar start.jar --dry-run) +$ $CMD +.... +It is possible to decompose the start command so that it can be modified: +.... +$ OPTS=$(java -jar start.jar --dry-run=opts,path) +$ MAIN=$(java -jar start.jar --dry-run=main) +$ ARGS=$(java -jar start.jar --dry-run=args) +$ java $OPTS -Dextra=opt $MAIN $ARGS extra=arg +.... +Alternatively to create an args file for java: +.... +$ java -jar start.jar --dry-run=opts,path,main,args > /tmp/args +$ java @/tmp/args +.... \ No newline at end of file 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 c3c944afcb8..a061bc74f96 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 @@ -206,7 +206,7 @@ public class Main StartLog.debug("%s - %s", invokedClass, invokedClass.getPackage().getImplementationVersion()); - CommandLineBuilder cmd = args.getMainArgs(false); + CommandLineBuilder cmd = args.getMainArgs(StartArgs.ARG_PARTS); String[] argArray = cmd.getArgs().toArray(new String[0]); StartLog.debug("Command Line Args: %s", cmd.toString()); @@ -415,7 +415,7 @@ public class Main // Show Command Line to execute Jetty if (args.isDryRun()) { - CommandLineBuilder cmd = args.getMainArgs(true); + CommandLineBuilder cmd = args.getMainArgs(args.getDryRunParts()); System.out.println(cmd.toString(StartLog.isDebugEnabled() ? " \\\n" : " ")); } @@ -454,7 +454,7 @@ public class Main // execute Jetty in another JVM if (args.isExec()) { - CommandLineBuilder cmd = args.getMainArgs(true); + CommandLineBuilder cmd = args.getMainArgs(StartArgs.ALL_PARTS); cmd.debug(); ProcessBuilder pbuilder = new ProcessBuilder(cmd.getArgs()); StartLog.endStartLog(); diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java index c59eed581c2..7161387a288 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java @@ -57,6 +57,14 @@ import org.eclipse.jetty.util.ManifestUtils; public class StartArgs { public static final String VERSION; + public static final Set ALL_PARTS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( + "java", + "opts", + "path", + "main", + "args"))); + public static final Set ARG_PARTS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( + "args"))); static { @@ -219,6 +227,7 @@ public class StartArgs private boolean listConfig = false; private boolean version = false; private boolean dryRun = false; + private final Set dryRunParts = new HashSet<>(); private boolean jpms = false; private boolean createStartd = false; private boolean updateIni = false; @@ -675,8 +684,11 @@ public class StartArgs return jvmArgs; } - public CommandLineBuilder getMainArgs(boolean addJavaInit) throws IOException + public CommandLineBuilder getMainArgs(Set parts) throws IOException { + if (parts.isEmpty()) + parts = ALL_PARTS; + CommandLineBuilder cmd = new CommandLineBuilder(); // Special Stop/Shutdown properties @@ -684,10 +696,11 @@ public class StartArgs ensureSystemPropertySet("STOP.KEY"); ensureSystemPropertySet("STOP.WAIT"); - if (addJavaInit) - { + if (parts.contains("java")) cmd.addRawArg(CommandLineBuilder.findJavaBin()); + if (parts.contains("opts")) + { cmd.addRawArg("-Djava.io.tmpdir=" + System.getProperty("java.io.tmpdir")); cmd.addRawArg("-Djetty.home=" + baseHome.getHome()); cmd.addRawArg("-Djetty.base=" + baseHome.getBase()); @@ -702,6 +715,7 @@ public class StartArgs Prop p = processSystemProperty(key, value, null); cmd.addRawArg("-D" + p.key + "=" + getProperties().expand(p.value)); + } else { @@ -715,7 +729,10 @@ public class StartArgs String value = System.getProperty(propKey); cmd.addEqualsArg("-D" + propKey, value); } + } + if (parts.contains("path")) + { if (isJPMS()) { Map> dirsAndFiles = StreamSupport.stream(classpath.spliterator(), false) @@ -764,52 +781,58 @@ public class StartArgs cmd.addRawArg("--add-reads"); cmd.addRawArg(entry.getKey() + "=" + String.join(",", entry.getValue())); } - - cmd.addRawArg("--module"); - cmd.addRawArg(getMainClassname()); } else { cmd.addRawArg("-cp"); cmd.addRawArg(classpath.toString()); - cmd.addRawArg(getMainClassname()); } } + if (parts.contains("main")) + { + if (isJPMS()) + cmd.addRawArg("--module"); + cmd.addRawArg(getMainClassname()); + } + // pass properties as args or as a file - if (dryRun && execProperties == null) + if (parts.contains("args")) { - for (Prop p : properties) + if (dryRun && execProperties == null) { - cmd.addRawArg(CommandLineBuilder.quote(p.key) + "=" + CommandLineBuilder.quote(p.value)); + for (Prop p : properties) + { + cmd.addRawArg(CommandLineBuilder.quote(p.key) + "=" + CommandLineBuilder.quote(p.value)); + } } - } - else if (properties.size() > 0) - { - Path propPath; - if (execProperties == null) + else if (properties.size() > 0) { - propPath = Files.createTempFile("start_", ".properties"); - propPath.toFile().deleteOnExit(); - } - else - propPath = new File(execProperties).toPath(); + Path propPath; + if (execProperties == null) + { + propPath = Files.createTempFile("start_", ".properties"); + propPath.toFile().deleteOnExit(); + } + else + propPath = new File(execProperties).toPath(); - try (OutputStream out = Files.newOutputStream(propPath)) + try (OutputStream out = Files.newOutputStream(propPath)) + { + properties.store(out, "start.jar properties"); + } + cmd.addRawArg(propPath.toAbsolutePath().toString()); + } + + for (Path xml : xmls) { - properties.store(out, "start.jar properties"); + cmd.addRawArg(xml.toAbsolutePath().toString()); } - cmd.addRawArg(propPath.toAbsolutePath().toString()); - } - for (Path xml : xmls) - { - cmd.addRawArg(xml.toAbsolutePath().toString()); - } - - for (Path propertyFile : propertyFiles) - { - cmd.addRawArg(propertyFile.toAbsolutePath().toString()); + for (Path propertyFile : propertyFiles) + { + cmd.addRawArg(propertyFile.toAbsolutePath().toString()); + } } return cmd; @@ -935,6 +958,11 @@ public class StartArgs return dryRun; } + public Set getDryRunParts() + { + return dryRunParts; + } + public boolean isExec() { return exec; @@ -1152,6 +1180,21 @@ public class StartArgs return; } + if (arg.startsWith("--dry-run=")) + { + int colon = arg.indexOf('='); + for (String part : arg.substring(colon + 1).split(",")) + { + if (!ALL_PARTS.contains(part)) + throw new UsageException(UsageException.ERR_BAD_ARG, "Unrecognized --dry-run=\"%s\" in %s", part, source); + + dryRunParts.add(part); + } + dryRun = true; + run = false; + return; + } + // Enable forked execution of Jetty server if ("--exec".equals(arg)) { diff --git a/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt b/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt index f0ec8ce742b..4bd9fa87be5 100644 --- a/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt +++ b/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt @@ -28,7 +28,30 @@ Command Line Options: --dry-run Print the command line that the start.jar generates, then exit. This may be used to generate command lines - when the start.ini includes -X or -D arguments. + when the start.ini includes -X or -D arguments: + + java -jar start.jar --dry-run > jetty.sh + . jetty.sh + + --dry-run=parts Print specific parts of the command line. The parts + are a comma separated list of + o "java" - the JVM to run + o "opts" - the JVM options (eg -D and -X flags) + o "path" - the JVM class path or JPMS modules options + o "main" - the main class to run + o "args" - the arguments passed to the main class + + It is possible to decompose the start command: + + OPTS=$(java -jar start.jar --dry-run=opts,path) + MAIN=$(java -jar start.jar --dry-run=main) + ARGS=$(java -jar start.jar --dry-run=args) + java $OPTS -Dextra=opt $MAIN $ARGS extra=arg + + Alternatively to create an args file for java: + + java -jar start.jar --dry-run=opts,path,main,args > /tmp/args + java @/tmp/args --exec Run the generated command line (see --dry-run) in a sub process. This can be used when start.ini @@ -59,7 +82,6 @@ Debug and Start Logging: issues where the jetty specific logger has not yet kicked in due to startup configuration errors. - Module Management: ------------------ diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java index 1eda5681817..3c58faabcb0 100644 --- a/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java @@ -193,7 +193,7 @@ public class MainTest assertThat("jetty.home", baseHome.getHome(), is(homePath.toString())); assertThat("jetty.base", baseHome.getBase(), is(homePath.toString())); - CommandLineBuilder commandLineBuilder = args.getMainArgs(true); + CommandLineBuilder commandLineBuilder = args.getMainArgs(StartArgs.ALL_PARTS); String commandLine = commandLineBuilder.toString("\n"); String expectedExpansion = String.format("-Xloggc:%s/logs/gc-%s.log", baseHome.getBase(), System.getProperty("java.version")