org.eclipse.jetty.start.StartArgs Maven / Gradle / Ivy
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.start;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.eclipse.jetty.start.Props.Prop;
import org.eclipse.jetty.start.config.ConfigSource;
import org.eclipse.jetty.start.config.ConfigSources;
import org.eclipse.jetty.start.config.DirConfigSource;
import org.eclipse.jetty.util.JavaVersion;
import org.eclipse.jetty.util.ManifestUtils;
/**
* The Arguments required to start Jetty.
*/
public class StartArgs
{
public static final String VERSION;
public static final Set ALL_PARTS = Set.of("java", "opts", "path", "main", "args", "envs");
public static final Set ARG_PARTS = Set.of("args", "envs");
public static final String ARG_ALLOW_INSECURE_HTTP_DOWNLOADS = "--allow-insecure-http-downloads";
private static final String JETTY_VERSION_KEY = "jetty.version";
private static final String JETTY_TAG_NAME_KEY = "jetty.tag.version";
private static final String JETTY_BUILDNUM_KEY = "jetty.build";
static
{
// Use command line versions
String ver = System.getProperty(JETTY_VERSION_KEY);
String tag = System.getProperty(JETTY_TAG_NAME_KEY);
// Use META-INF/MANIFEST.MF versions
if (Utils.isBlank(ver))
{
ver = ManifestUtils.getManifest(StartArgs.class)
.map(Manifest::getMainAttributes)
.filter(attributes -> "Eclipse Jetty Project".equals(attributes.getValue(Attributes.Name.IMPLEMENTATION_VENDOR)))
.map(attributes -> attributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION))
.orElse(null);
}
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// use old jetty-version.properties (as seen within various linux distro repackaging of Jetty)
Props jettyVerProps = Props.load(classLoader, "jetty-version.properties");
// use build-time properties (included in start.jar) to pull version and buildNumber
Props buildProps = Props.load(classLoader, "org/eclipse/jetty/start/build.properties");
String sha = buildProps.getString("buildNumber", System.getProperty(JETTY_BUILDNUM_KEY));
if (Utils.isNotBlank(sha))
{
System.setProperty(JETTY_BUILDNUM_KEY, sha);
}
if (Utils.isBlank(ver))
{
ver = jettyVerProps.getString("version", buildProps.getString("version", "0.0"));
}
if (Utils.isBlank(tag))
{
tag = jettyVerProps.getString("tag", buildProps.getString("tag", "jetty-" + ver));
}
VERSION = ver;
System.setProperty(JETTY_VERSION_KEY, VERSION);
System.setProperty(JETTY_TAG_NAME_KEY, tag);
}
private static final String MAIN_CLASS = "org.eclipse.jetty.xml.XmlConfiguration";
private static final String MODULE_MAIN_CLASS = "org.eclipse.jetty.xml/org.eclipse.jetty.xml.XmlConfiguration";
private final BaseHome baseHome;
/**
* Set of enabled modules
*/
private final Set modules = new HashSet<>();
/**
* List of modules to skip [files] section validation
*/
private final Set skipFileValidationModules = new HashSet<>();
/**
* Map of enabled modules to the source of where that activation occurred
*/
Map> sources = new HashMap<>();
/**
* List of all active [files] sections from enabled modules
*/
private final List files = new ArrayList<>();
/**
* JVM arguments, found via command line and in all active [exec] sections from enabled modules
*/
private final Map jvmArgSources = new LinkedHashMap<>();
private final Map systemPropertySource = new HashMap<>();
private static final Map environments = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
// jetty.base - build out commands
/**
* --add-modules=[module,[module]]
*/
private final List startModules = new ArrayList<>();
/**
* List of all active [jpms] sections for enabled modules
*/
private final Set _jmodAdds = new LinkedHashSet<>();
private final Map> _jmodPatch = new LinkedHashMap<>();
private final Map> _jmodOpens = new LinkedHashMap<>();
private final Map> _jmodExports = new LinkedHashMap<>();
private final Map> _jmodReads = new LinkedHashMap<>();
// module inspection commands
/**
* --write-module-graph=[filename]
*/
private String moduleGraphFilename;
/**
* Collection of all modules
*/
private Modules allModules;
/**
* Should the server be run?
*/
private boolean run = true;
/**
* Files related args
*/
private boolean createFiles = false;
private boolean licenseCheckRequired = false;
private boolean testingMode = false;
private boolean help = false;
private boolean stopCommand = false;
private List listModules = null;
private List showModules = null;
private boolean listClasspath = false;
private boolean listConfig = false;
private boolean version = false;
private boolean dryRun = false;
private boolean multiLine = false;
private final Set dryRunParts = new HashSet<>();
private boolean jpms = false;
private boolean createStartD = false;
private boolean createStartIni = false;
private boolean updateIni = false;
private String mavenBaseUri;
private boolean exec = false;
private String execProperties;
private boolean allowInsecureHttpDownloads = false;
private boolean approveAllLicenses = false;
/**
* The jetty environment holds the main configuration used from the primary classloader for Jetty.
* It is never created as a real environment within the server.
* */
private final StartEnvironment jettyEnvironment;
public StartArgs(BaseHome baseHome)
{
this.baseHome = baseHome;
jettyEnvironment = new StartEnvironment("Jetty", baseHome);
}
public void expandEnvironments(List activeModules) throws IOException
{
// 5) Lib & XML Expansion / Resolution
expandSystemProperties();
jettyEnvironment.resolveLibs();
expandModules(activeModules);
// 6) Resolve Extra XMLs
// 7) JPMS Expansion
// 8) Resolve Property Files
jettyEnvironment.resolve();
// 7) JPMS Expansion
resolveJPMS(activeModules); // TODO we need layers
for (StartEnvironment environment : environments.values())
{
environment.resolveLibs();
environment.resolve();
// JPMS???
}
}
public StartEnvironment getJettyEnvironment()
{
return jettyEnvironment;
}
public Collection getEnvironments()
{
return environments.values();
}
public StartEnvironment getEnvironment(String envName)
{
return environments.computeIfAbsent(envName, k -> new StartEnvironment(k, baseHome));
}
private void addFile(Module module, String uriLocation)
{
if (module != null && module.isSkipFilesValidation())
{
StartLog.debug("Not validating module %s [files] for %s", module, uriLocation);
return;
}
StartEnvironment environment = getEnvironment(module);
FileArg arg = new FileArg(module, environment.getProperties().expand(uriLocation));
if (!files.contains(arg))
files.add(arg);
}
private StartEnvironment getEnvironment(Module module)
{
String envName = module == null ? null : module.getEnvironment();
StartEnvironment environment = envName == null ? getJettyEnvironment() : getEnvironment(envName);
return environment;
}
public void dumpJavaEnvironment(PrintStream out)
{
// Java Details
out.println();
out.println("JVM Version & Properties:");
out.println("-------------------------");
dumpSystemProperty(out, "java.home");
dumpSystemProperty(out, "java.vm.vendor");
dumpSystemProperty(out, "java.vm.version");
dumpSystemProperty(out, "java.vm.name");
dumpSystemProperty(out, "java.vm.info");
dumpSystemProperty(out, "java.runtime.name");
dumpSystemProperty(out, "java.runtime.version");
dumpSystemProperty(out, "java.io.tmpdir");
dumpSystemProperty(out, "user.dir");
dumpSystemProperty(out, "user.language");
dumpSystemProperty(out, "user.country");
// Jetty Server Environment
out.println();
out.println("Jetty Version & Properties:");
out.println("---------------------------");
StartEnvironment jettyEnvironment = getJettyEnvironment();
jettyEnvironment.dumpProperty(out, JETTY_VERSION_KEY);
jettyEnvironment.dumpProperty(out, JETTY_TAG_NAME_KEY);
jettyEnvironment.dumpProperty(out, JETTY_BUILDNUM_KEY);
jettyEnvironment.dumpProperty(out, "jetty.home");
jettyEnvironment.dumpProperty(out, "jetty.base");
// Jetty Configuration Environment
out.println();
out.println("Config Search Order:");
out.println("--------------------");
for (ConfigSource config : baseHome.getConfigSources())
{
out.printf(" %s", config.getId());
if (config instanceof DirConfigSource)
{
DirConfigSource dirsource = (DirConfigSource)config;
if (dirsource.isPropertyBased())
{
out.printf(" -> %s", dirsource.getDir());
}
}
out.println();
}
}
public void dumpJvmArgs(PrintStream out)
{
if (jvmArgSources.isEmpty())
return;
out.println();
out.println("Forked JVM Arguments:");
out.println("---------------------");
jvmArgSources.forEach((key, sourceRef) ->
{
String value = System.getProperty(key);
String source = StartLog.isDebugEnabled() ? '(' + sourceRef + ')' : "";
if (value != null)
out.printf(" %s = %s %s%n", key, value, source);
else
out.printf(" %s %s%n", key, source);
});
}
public void dumpSystemProperties(PrintStream out)
{
out.println();
out.println("System Properties:");
out.println("------------------");
if (systemPropertySource.keySet().isEmpty())
{
out.println(" (no system properties specified)");
return;
}
List sortedKeys = new ArrayList<>(systemPropertySource.keySet());
Collections.sort(sortedKeys);
for (String key : sortedKeys)
{
dumpSystemProperty(out, key);
}
}
private void dumpSystemProperty(PrintStream out, String key)
{
String value = System.getProperty(key);
// "source" is where this property came from (jvm, command line, configuration file, etc)
String source = "";
if (systemPropertySource.get(key) != null)
source = String.format(" (%s)", systemPropertySource.get(key));
out.printf(" %s = %s%s%n", key, value, source);
}
/**
* 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 (systemPropertySource.containsKey(key))
{
return; // done
}
StartEnvironment jettyEnvironment = getJettyEnvironment();
if (jettyEnvironment.getProperties().containsKey(key))
{
Prop prop = jettyEnvironment.getProperties().getProp(key);
if (prop == null)
return; // no value set;
String val = jettyEnvironment.getProperties().expand(prop.value);
// setup system property
systemPropertySource.put(key, "property:" + prop.source);
System.setProperty(key, val);
}
}
/**
* Expand any command line added {@code --libs} lib references.
*/
public void expandSystemProperties()
{
StartLog.debug("Expanding System Properties");
for (String key : systemPropertySource.keySet())
{
String value = getJettyEnvironment().getProperties().getString(key);
if (value != null)
{
String expanded = getJettyEnvironment().getProperties().expand(value);
if (!value.equals(expanded))
System.setProperty(key, expanded);
}
}
}
/**
* Build up the Classpath and XML file references based on enabled Module list.
*
* @param activeModules the active (selected) modules
* @throws IOException if unable to expand the modules
*/
public void expandModules(List activeModules) throws IOException
{
StartLog.debug("Expanding Modules");
for (Module module : activeModules)
{
StartEnvironment environment = getEnvironment(module);
// Find and Expand Libraries
for (String rawlibref : module.getLibs())
{
StartLog.debug("rawlibref = %s", rawlibref);
String libref = environment.getProperties().expand(rawlibref);
StartLog.debug("expanded = %s", libref);
for (Path libpath : baseHome.getPaths(libref))
{
environment.getClasspath().addComponent(libpath);
}
}
for (String jvmArg : module.getJvmArgs())
{
exec = true;
jvmArgSources.put(jvmArg, String.format("module[%s|jvm]", module.getName()));
}
// Find and Expand XML files
for (String xmlRef : module.getXmls())
{
// Straight Reference
xmlRef = environment.getProperties().expand(xmlRef);
Path xmlfile = baseHome.getPath(xmlRef);
environment.addUniqueXmlFile(xmlRef, xmlfile);
}
// Register Download operations
for (String file : module.getFiles())
{
StartLog.debug("Adding module specified file: %s", file);
addFile(module, file);
}
}
}
public List getStartModules()
{
return startModules;
}
public Modules getAllModules()
{
return allModules;
}
/**
*
* The list of selected Modules to enable based on configuration
* obtained from {@code start.d/*.ini}, {@code start.ini}, and command line.
*
*
*
* For full list of enabled modules, use {@link Modules#getEnabled()}
*
*
* @return the set of selected modules (by name) that the configuration has.
* @see Modules#getEnabled()
*/
public Set getSelectedModules()
{
return this.modules;
}
public List getFiles()
{
return files;
}
/**
* Return ordered Map of JVM arguments to Source (locations)
*
* @return the ordered map of JVM Argument to Source (locations)
*/
public Map getJvmArgSources()
{
return jvmArgSources;
}
public CommandLineBuilder getMainArgs(Set parts) throws IOException
{
if (parts.isEmpty())
parts = ALL_PARTS;
CommandLineBuilder cmd = new CommandLineBuilder(multiLine);
// Special Stop/Shutdown properties
ensureSystemPropertySet("STOP.PORT");
ensureSystemPropertySet("STOP.KEY");
ensureSystemPropertySet("STOP.WAIT");
if (parts.contains("java"))
cmd.addArg(CommandLineBuilder.findJavaBin());
if (parts.contains("opts"))
{
cmd.addOption("-D", "java.io.tmpdir", System.getProperty("java.io.tmpdir"));
cmd.addOption("-D", "jetty.home", baseHome.getHome());
cmd.addOption("-D", "jetty.base", baseHome.getBase());
Props properties = jettyEnvironment.getProperties();
for (String x : getJvmArgSources().keySet())
{
if (x.startsWith("-D"))
{
String[] assign = x.substring(2).split("=", 2);
String key = assign[0];
String value = assign.length == 1 ? "" : assign[1];
Prop p = processSystemProperty(key, value, null);
cmd.addOption("-D", p.key, properties.expand(p.value));
}
else
{
cmd.addArg(properties.expand(x));
}
}
// System Properties
for (String propKey : systemPropertySource.keySet())
{
String value = System.getProperty(propKey);
cmd.addOption("-D", propKey, value);
}
}
if (parts.contains("path"))
{
Classpath classpath = jettyEnvironment.getClasspath();
StartLog.debug("classpath=%s - isJPMS=%b", classpath, isJPMS());
if (isJPMS())
{
Map> dirsAndFiles = StreamSupport.stream(classpath.spliterator(), false)
.collect(Collectors.groupingBy(Files::isDirectory));
List paths = dirsAndFiles.get(false);
Set files = new HashSet<>((paths == null) ? Collections.emptyList() : paths);
// FIXME I'm not sure it's a good idea especially with multiple environment..
// ee9 may use jakarta.annotation 2.0.0
// but ee10 use jakarta.annotation 2.1.0
// and both having different module-info.
getEnvironments().stream().filter(environment -> !environment.getName().equals(jettyEnvironment.getName()))
.forEach(environment ->
{
Map> dirsAndFilesModules = StreamSupport.stream(environment.getClasspath().spliterator(), false)
.collect(Collectors.groupingBy(Files::isDirectory));
dirsAndFiles.putAll(dirsAndFilesModules);
if (dirsAndFilesModules.containsKey(false))
{
files.addAll(dirsAndFilesModules.get(false));
}
else
{
System.out.println("null dirsAndFilesModules");
}
});
if (!files.isEmpty())
{
cmd.addOption("--module-path");
String modules = files.stream()
.map(Path::toAbsolutePath)
.map(Path::toString)
.collect(Collectors.joining(FS.pathSeparator()));
cmd.addArg(modules);
}
List dirs = dirsAndFiles.get(true);
if (dirs != null && !dirs.isEmpty())
{
cmd.addOption("--class-path");
String directories = dirs.stream()
.map(Path::toAbsolutePath)
.map(Path::toString)
.collect(Collectors.joining(FS.pathSeparator()));
cmd.addArg(directories);
}
generateJpmsArgs(cmd);
StartLog.debug("JPMS resulting cmd=%s", cmd.toCommandLine());
}
else if (!classpath.isEmpty())
{
cmd.addOption("--class-path");
cmd.addArg(classpath.toString());
}
}
if (parts.contains("main"))
{
if (isJPMS())
cmd.addOption("--module");
cmd.addArg(getMainClassname());
}
// do properties and xmls
if (parts.contains("args"))
{
Props properties = jettyEnvironment.getProperties();
if (dryRun && execProperties == null)
{
// pass properties as args
for (Prop p : properties)
{
if (!p.key.startsWith("java.version."))
cmd.addArg(p.key, properties.expand(p.value));
}
}
else if (properties.size() > 0)
{
// pass properties as a temp property file
Path propPath;
if (execProperties == null)
{
propPath = Files.createTempFile("start_", ".properties");
propPath.toFile().deleteOnExit();
}
else
{
propPath = Paths.get(execProperties);
}
try (OutputStream out = Files.newOutputStream(propPath))
{
properties.store(out, "start.jar properties");
}
cmd.addArg(propPath.toAbsolutePath().toString());
}
for (Path propertyFile : jettyEnvironment.getPropertyFiles())
{
cmd.addArg(propertyFile.toAbsolutePath().toString());
}
for (Path xml : jettyEnvironment.getXmlFiles())
{
cmd.addArg(xml.toAbsolutePath().toString());
}
}
if (parts.contains("envs"))
{
for (StartEnvironment environment : getEnvironments())
{
if (environment == jettyEnvironment)
continue;
cmd.addArg("--env");
cmd.addArg(environment.getName());
environment.getClasspath().getElements().stream()
.map(Path::toAbsolutePath)
.map(Path::toString)
.forEach(s ->
{
cmd.addArg("-cp");
cmd.addArg(s);
});
// TODO module path
Props props = environment.getProperties();
for (Prop property : props)
cmd.addArg(property.key, props.expand(property.value));
for (Path xmlFile : environment.getXmlFiles())
cmd.addArg(xmlFile.toAbsolutePath().toString());
}
}
return cmd;
}
private void resolveJPMS(List activeModules) throws IOException
{
// TODO does this need to do layer stuff for Environments?
for (Module module : activeModules)
{
for (String line : module.getJPMS())
{
line = getJettyEnvironment().getProperties().expand(line);
String directive;
if (line.startsWith(directive = "add-modules:"))
{
String[] names = line.substring(directive.length()).split(",");
Arrays.stream(names).map(String::trim).collect(Collectors.toCollection(() -> _jmodAdds));
}
else if (line.startsWith(directive = "patch-module:"))
{
parseJPMSKeyValue(module, line, directive, true, _jmodPatch);
}
else if (line.startsWith(directive = "add-opens:"))
{
parseJPMSKeyValue(module, line, directive, false, _jmodOpens);
}
else if (line.startsWith(directive = "add-exports:"))
{
parseJPMSKeyValue(module, line, directive, false, _jmodExports);
}
else if (line.startsWith(directive = "add-reads:"))
{
parseJPMSKeyValue(module, line, directive, false, _jmodReads);
}
else
{
throw new IllegalArgumentException("Invalid [jpms] directive " + directive + " in module " + module.getName() + ": " + line);
}
}
}
_jmodAdds.add("ALL-MODULE-PATH");
StartLog.debug("Expanded JPMS directives:%n add-modules: %s%n patch-modules: %s%n add-opens: %s%n add-exports: %s%n add-reads: %s",
_jmodAdds, _jmodPatch, _jmodOpens, _jmodExports, _jmodReads);
}
private void parseJPMSKeyValue(Module module, String line, String directive, boolean valueIsFile, Map> output) throws IOException
{
String valueString = line.substring(directive.length());
int equals = valueString.indexOf('=');
if (equals <= 0)
throw new IllegalArgumentException("Invalid [jpms] directive " + directive + " in module " + module.getName() + ": " + line);
String delimiter = valueIsFile ? FS.pathSeparator() : ",";
String key = valueString.substring(0, equals).trim();
String[] values = valueString.substring(equals + 1).split(delimiter);
Set result = output.computeIfAbsent(key, k -> new LinkedHashSet<>());
for (String value : values)
{
value = value.trim();
if (valueIsFile)
{
List paths = baseHome.getPaths(value);
paths.stream().map(Path::toAbsolutePath).map(Path::toString).collect(Collectors.toCollection(() -> result));
}
else
{
result.add(value);
}
}
}
private void generateJpmsArgs(CommandLineBuilder cmd)
{
if (!_jmodAdds.isEmpty())
{
cmd.addOption("--add-modules");
cmd.addArg(String.join(",", _jmodAdds));
}
for (Map.Entry> entry : _jmodPatch.entrySet())
{
cmd.addOption("--patch-module");
cmd.addArg(entry.getKey(), String.join(File.pathSeparator, entry.getValue()));
}
for (Map.Entry> entry : _jmodOpens.entrySet())
{
cmd.addOption("--add-opens");
cmd.addArg(entry.getKey(), String.join(",", entry.getValue()));
}
for (Map.Entry> entry : _jmodExports.entrySet())
{
cmd.addOption("--add-exports");
cmd.addArg(entry.getKey(), String.join(",", entry.getValue()));
}
for (Map.Entry> entry : _jmodReads.entrySet())
{
cmd.addOption("--add-reads");
cmd.addArg(entry.getKey(), String.join(",", entry.getValue()));
}
}
public String getMainClassname()
{
String mainClass = System.getProperty("jetty.server", isJPMS() ? MODULE_MAIN_CLASS : MAIN_CLASS);
Prop mainClassProp = getJettyEnvironment().getProperties().getProp("main.class", true);
if (mainClassProp != null)
return mainClassProp.value;
return mainClass;
}
public String getMavenLocalRepoDir()
{
String localRepo = getJettyEnvironment().getProperties().getString("maven.local.repo");
if (Utils.isBlank(localRepo))
localRepo = System.getenv("JETTY_MAVEN_LOCAL_REPO");
if (Utils.isBlank(localRepo))
localRepo = System.getenv("MAVEN_LOCAL_REPO");
return localRepo;
}
public Path findMavenLocalRepoDir()
{
// Try property first
String localRepo = getMavenLocalRepoDir();
if (Utils.isBlank(localRepo))
{
// Try generic env variable
Path home = Paths.get(System.getProperty("user.home"));
Path localMavenRepository = home.resolve(".m2/repository");
if (Files.exists(localMavenRepository))
localRepo = localMavenRepository.toString();
}
// TODO: possibly use Eclipse Aether to manage it ?
// TODO: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=449511
// Still blank? then its not specified
if (Utils.isBlank(localRepo))
{
return null;
}
Path localRepoDir = Paths.get(localRepo);
localRepoDir = localRepoDir.normalize().toAbsolutePath();
if (Files.exists(localRepoDir) && Files.isDirectory(localRepoDir))
{
return localRepoDir;
}
StartLog.warn("Not a valid maven local repository directory: %s", localRepoDir);
// Not a valid repository directory, skip it
return null;
}
public String getModuleGraphFilename()
{
return moduleGraphFilename;
}
public Set getSkipFileValidationModules()
{
return skipFileValidationModules;
}
public Set getSources(String module)
{
return sources.get(module);
}
public boolean hasJvmArgs()
{
return !jvmArgSources.isEmpty();
}
public boolean hasSystemProperties()
{
for (String key : systemPropertySource.keySet())
{
// ignored keys
if ("jetty.home".equals(key) || "jetty.base".equals(key) || "main.class".equals(key))
{
// skip
continue;
}
return true;
}
return false;
}
public Map getSystemProperties()
{
return systemPropertySource;
}
public boolean isAllowInsecureHttpDownloads()
{
return allowInsecureHttpDownloads;
}
public boolean isApproveAllLicenses()
{
return approveAllLicenses;
}
public boolean isCreateFiles()
{
return createFiles;
}
public boolean isJPMS()
{
return jpms;
}
public boolean isDryRun()
{
return dryRun;
}
public Set getDryRunParts()
{
return dryRunParts;
}
public boolean isExec()
{
return exec;
}
public boolean isLicenseCheckRequired()
{
return licenseCheckRequired;
}
public boolean isNormalMainClass()
{
return MAIN_CLASS.equals(getMainClassname());
}
public boolean isHelp()
{
return help;
}
public boolean isListClasspath()
{
return listClasspath;
}
public boolean isListConfig()
{
return listConfig;
}
public List getListModules()
{
return listModules;
}
public List getShowModules()
{
return showModules;
}
public boolean isRun()
{
return run;
}
public boolean isStopCommand()
{
return stopCommand;
}
public boolean isTestingModeEnabled()
{
return testingMode;
}
public boolean isVersion()
{
return version;
}
public boolean isCreateStartD()
{
return createStartD;
}
public boolean isCreateStartIni()
{
return createStartIni;
}
public boolean isUpdateIni()
{
return updateIni;
}
public String getMavenBaseUri()
{
return mavenBaseUri;
}
public void parse(ConfigSources sources)
{
ListIterator iter = sources.reverseListIterator();
while (iter.hasPrevious())
{
// Start with the Jetty environment.
StartEnvironment environment = getJettyEnvironment();
ConfigSource source = iter.previous();
for (RawArgs.Entry arg : source.getArgs())
environment = parse(environment, arg.getLine(), arg.getOrigin());
}
}
/**
* Parse a single line of argument.
*
* @param arg the raw argument to parse
* @param source the origin of this line of argument
*/
public StartEnvironment parse(StartEnvironment environment, String arg, String source)
{
StartLog.debug("parse(\"%s\", \"%s\")", arg, source);
if (arg == null)
return environment;
arg = arg.trim();
if (arg.length() == 0)
return environment;
if (arg.startsWith("#"))
return environment;
if ("--help".equals(arg) || "-?".equals(arg))
{
help = true;
run = false;
return environment;
}
if ("--debug".equals(arg) || arg.startsWith("--start-log-file"))
{
// valid, but handled in StartLog instead
return environment;
}
if ("--testing-mode".equals(arg))
{
System.setProperty("org.eclipse.jetty.start.testing", "true");
testingMode = true;
return environment;
}
if (arg.startsWith("--commands="))
{
Path commands = baseHome.getPath(Props.getValue(arg));
if (!Files.exists(commands) || !Files.isReadable(commands))
throw new UsageException(UsageException.ERR_BAD_ARG, "--commands file must be readable: %s", commands);
try
{
TextFile file = new TextFile(commands);
StartLog.info("reading commands from %s", baseHome.toShortForm(commands));
String s = source + "|" + baseHome.toShortForm(commands);
StartEnvironment originalEnvironment = environment;
for (String line : file)
environment = parse(environment, line, s);
environment = originalEnvironment; // environment doesn't propagate beyond command file.
}
catch (IOException e)
{
throw new RuntimeException(e);
}
return environment;
}
if (arg.startsWith("--include-jetty-dir=") || arg.startsWith("--add-config-dir="))
{
// valid, but handled in ConfigSources instead
return environment;
}
if ("--stop".equals(arg))
{
stopCommand = true;
run = false;
return environment;
}
if (arg.startsWith("--download=") || arg.startsWith("--files="))
{
addFile(null, Props.getValue(arg));
run = false;
createFiles = true;
return environment;
}
if (arg.equals("--create-files"))
{
run = false;
createFiles = true;
licenseCheckRequired = true;
return environment;
}
if (arg.equals("--update-ini") || arg.equals("--update-inis"))
{
run = false;
updateIni = true;
return environment;
}
if ("--list-classpath".equals(arg) || "--version".equals(arg) || "-v".equals(arg) || "--info".equals(arg))
{
listClasspath = true;
run = false;
return environment;
}
if ("--list-config".equals(arg))
{
listConfig = true;
run = false;
return environment;
}
if ("--jpms".equals(arg))
{
jpms = true;
// Forking is simpler; otherwise we need to add the
// JPMS directives such as "--add-modules" via API.
exec = true;
return environment;
}
if ("--dry-run".equals(arg) || "--exec-print".equals(arg))
{
dryRun = true;
run = false;
return environment;
}
if (arg.startsWith("--dry-run="))
{
int colon = arg.indexOf('=');
for (String part : arg.substring(colon + 1).split(","))
{
if ("multiline".equalsIgnoreCase(part))
{
multiLine = true;
continue;
}
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 environment;
}
// Enable forked execution of Jetty server
if ("--exec".equals(arg))
{
exec = true;
return environment;
}
// Assign a fixed name to the property file for exec
if (arg.startsWith("--exec-properties="))
{
execProperties = Props.getValue(arg);
if (!execProperties.endsWith(".properties"))
throw new UsageException(UsageException.ERR_BAD_ARG, "--exec-properties filename must have .properties suffix: %s", execProperties);
return environment;
}
// Allow insecure-http downloads
if (ARG_ALLOW_INSECURE_HTTP_DOWNLOADS.equals(arg))
{
allowInsecureHttpDownloads = true;
return environment;
}
// Enable forked execution of Jetty server
if ("--approve-all-licenses".equals(arg))
{
approveAllLicenses = true;
return environment;
}
// Module Management
if ("--list-all-modules".equals(arg))
{
listModules = Collections.singletonList("*");
run = false;
return environment;
}
// Module Management
if ("--list-module".equals(arg) || "--list-modules".equals(arg))
{
listModules = Collections.singletonList("-internal");
run = false;
return environment;
}
if (arg.startsWith("--list-module=") || arg.startsWith("--list-modules="))
{
listModules = Props.getValues(arg);
run = false;
return environment;
}
// Module Management
if ("--show-module".equals(arg) || "--show-modules".equals(arg))
{
showModules = Collections.emptyList();
run = false;
return environment;
}
if (arg.startsWith("--show-module=") || arg.startsWith("--show-modules="))
{
showModules = Props.getValues(arg);
run = false;
return environment;
}
// jetty.base build-out : add to ${jetty.base}/start.ini
if ("--create-start-ini".equals(arg))
{
createStartIni = true;
run = false;
createFiles = true;
licenseCheckRequired = true;
return environment;
}
if ("--create-startd".equals(arg) || "--create-start-d".equals(arg))
{
createStartD = true;
run = false;
createFiles = true;
licenseCheckRequired = true;
return environment;
}
if (arg.startsWith("--add-module=") || arg.startsWith("--add-modules=") || arg.startsWith("--add-to-start=") || arg.startsWith("--add-to-startd="))
{
if (arg.startsWith("--add-to-start=") || arg.startsWith("--add-to-startd="))
{
String value = Props.getValue(arg);
StartLog.warn("Option %s is deprecated! Instead use: --add-modules=%s", arg.split("=")[0], value);
}
startModules.addAll(Props.getValues(arg));
run = false;
createFiles = true;
licenseCheckRequired = true;
return environment;
}
// Select a module to eventually be enabled
if (arg.startsWith("--module=") || arg.startsWith("--modules="))
{
List moduleNames = Props.getValues(arg);
selectModules(source, moduleNames);
Module module = getAllModules().get(moduleNames.get(moduleNames.size() - 1));
String envName = module == null ? null : module.getEnvironment();
return envName == null ? jettyEnvironment : getEnvironment(envName);
}
// Skip [files] validation on a module
if (arg.startsWith("--skip-file-validation=") || arg.startsWith("--skip-create-files="))
{
List moduleNames = Props.getValues(arg);
skipFileValidationModules.addAll(moduleNames);
return environment;
}
// Create graphviz output of module graph
if (arg.startsWith("--write-module-graph="))
{
this.moduleGraphFilename = Props.getValue(arg);
run = false;
return environment;
}
if (environment == null)
environment = getJettyEnvironment();
// Arbitrary Libraries
if (arg.startsWith("--lib=") || arg.startsWith("--libs="))
{
String cp = Props.getValue(arg);
StringTokenizer t = new StringTokenizer(cp, FS.pathSeparator());
while (t.hasMoreTokens())
environment.addLibRef(t.nextToken());
return environment;
}
// Start property (syntax similar to System property)
if (arg.startsWith("-D"))
{
String[] assign = arg.substring(2).split("=", 2);
String key = assign[0];
String value = assign.length == 1 ? "" : assign[1];
Prop p = processSystemProperty(key, value, source);
systemPropertySource.put(p.key, p.source);
setProperty(environment, p.key, p.value, p.source);
System.setProperty(p.key, p.value);
return environment;
}
// Anything else with a "-" is considered a JVM argument
if (arg.startsWith("-"))
{
StartLog.debug("Unrecognized Arg (possible JVM Arg): %s (from %s)", arg, source);
// always use the latest source (overriding any past tracked source)
jvmArgSources.put(arg, source);
return environment;
}
// Is this a raw property declaration?
int equals = arg.indexOf('=');
if (equals >= 0)
{
String key = arg.substring(0, equals);
String value = arg.substring(equals + 1);
processAndSetProperty(environment, key, value, source);
return environment;
}
// Is this an xml file?
if (FS.isXml(arg))
{
environment.addXmlRef(arg);
return environment;
}
if (FS.isPropertyFile(arg))
{
environment.addPropertyFileRef(arg);
return environment;
}
// Anything else is unrecognized
throw new UsageException(UsageException.ERR_BAD_ARG, "Unrecognized argument: \"%s\" in %s", arg, source);
}
protected Prop processSystemProperty(String key, String value, String source)
{
if (key.endsWith("+"))
{
key = key.substring(0, key.length() - 1);
String orig = System.getProperty(key);
if (orig == null || orig.isEmpty())
{
if (value.startsWith(","))
value = value.substring(1);
}
else
{
value = orig + value;
if (source != null && systemPropertySource.containsKey(key))
source = systemPropertySource.get(key) + "," + source;
}
}
else if (key.endsWith("?"))
{
key = key.substring(0, key.length() - 1);
String preset = System.getProperty(key);
if (preset != null)
{
value = preset;
source = systemPropertySource.get(key);
}
else if (source != null)
source = source + "?=";
}
return new Prop(key, value, source);
}
private void processAndSetProperty(StartEnvironment environment, String key, String value, String source)
{
if (key.endsWith("+"))
{
key = key.substring(0, key.length() - 1);
Prop orig = environment.getProperties().getProp(key);
if (orig == null)
{
if (value.startsWith(","))
value = value.substring(1);
}
else
{
value = orig.value + value;
source = orig.source + "," + source;
}
}
else if (key.endsWith("?"))
{
key = key.substring(0, key.length() - 1);
Prop preset = environment.getProperties().getProp(key);
if (preset != null)
return;
if (source != null)
source = source + "?=";
}
setProperty(environment, key, value, source);
}
private void selectModules(String source, List moduleNames)
{
for (String moduleName : moduleNames)
{
if (modules.add(moduleName))
{
Set set = sources.computeIfAbsent(moduleName, k -> new HashSet<>());
set.add(source);
}
}
}
public void setAllModules(Modules allModules)
{
this.allModules = allModules;
}
public void setProperty(StartEnvironment environment, String key, String value, String source)
{
if (environment == null)
environment = getJettyEnvironment();
Props properties = environment.getProperties();
// Special / Prevent override from start.ini's
if (key.equals("jetty.home"))
{
properties.setProperty("jetty.home", System.getProperty("jetty.home"), source);
return;
}
// Special / Prevent override from start.ini's
if (key.equals("jetty.base"))
{
properties.setProperty("jetty.base", System.getProperty("jetty.base"), source);
return;
}
properties.setProperty(key, value, source);
if (key.equals("java.version"))
{
try
{
JavaVersion ver = JavaVersion.parse(value);
properties.setProperty("java.version.platform", Integer.toString(ver.getPlatform()), source);
// @deprecated - below will be removed in Jetty 10.x
properties.setProperty("java.version.major", Integer.toString(ver.getMajor()), "Deprecated");
properties.setProperty("java.version.minor", Integer.toString(ver.getMinor()), "Deprecated");
properties.setProperty("java.version.micro", Integer.toString(ver.getMicro()), "Deprecated");
// ALPN feature exists
properties.setProperty("runtime.feature.alpn", Boolean.toString(isMethodAvailable(javax.net.ssl.SSLParameters.class, "getApplicationProtocols", null)), source);
}
catch (Throwable x)
{
UsageException ue = new UsageException(UsageException.ERR_BAD_ARG, x.getMessage() == null ? x.toString() : x.getMessage());
ue.initCause(x);
throw ue;
}
}
// to override default https://repo1.maven.org/maven2/
if (key.equals("maven.repo.uri"))
{
this.mavenBaseUri = value;
}
}
private boolean isMethodAvailable(Class> clazz, String methodName, Class>[] params)
{
try
{
clazz.getMethod(methodName, params);
return true;
}
catch (NoSuchMethodException e)
{
return false;
}
}
public void setRun(boolean run)
{
this.run = run;
}
@Override
public String toString()
{
return String.format("%s[enabledModules=%s, xml=%s, properties=%s, jvmArgs=%s]",
getClass().getSimpleName(), modules, getJettyEnvironment().getXmlFiles(), getJettyEnvironment().getProperties(), jvmArgSources.keySet());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy