mirror of
https://github.com/apache/maven-mvnd.git
synced 2025-09-27 16:08:21 +00:00
Properly test the --install feature
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
package org.jboss.fuse.mvnd.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Local paths relevant for the {@link DefaultClient}.
|
||||
@@ -19,31 +19,32 @@ public class ClientLayout extends Layout {
|
||||
|
||||
public static ClientLayout getEnvInstance() {
|
||||
if (ENV_INSTANCE == null) {
|
||||
final Properties mvndProperties = loadMvndProperties();
|
||||
final Path mvndPropertiesPath = Environment.findMvndPropertiesPath();
|
||||
final Supplier<Properties> mvndProperties = lazyMvndProperties(mvndPropertiesPath);
|
||||
final Path pwd = Paths.get(".").toAbsolutePath().normalize();
|
||||
|
||||
final Path mvndHome = findMavenHome(mvndProperties);
|
||||
final Path mvndHome = Environment.findMavenHome(mvndProperties, mvndPropertiesPath);
|
||||
ENV_INSTANCE = new ClientLayout(
|
||||
mvndPropertiesPath,
|
||||
mvndHome,
|
||||
pwd,
|
||||
findMultiModuleProjectDirectory(pwd),
|
||||
findJavaHome(mvndProperties),
|
||||
Environment.findMultiModuleProjectDirectory(pwd),
|
||||
Environment.findJavaHome(mvndProperties, mvndPropertiesPath),
|
||||
findLocalRepo(),
|
||||
null);
|
||||
}
|
||||
return ENV_INSTANCE;
|
||||
}
|
||||
|
||||
public ClientLayout(Path mavenHome, Path userDir, Path multiModuleProjectDirectory, Path javaHome,
|
||||
public ClientLayout(Path mvndPropertiesPath, Path mavenHome, Path userDir, Path multiModuleProjectDirectory, Path javaHome,
|
||||
Path localMavenRepository, Path settings) {
|
||||
super(mavenHome, userDir, multiModuleProjectDirectory);
|
||||
super(mvndPropertiesPath, mavenHome, userDir, multiModuleProjectDirectory);
|
||||
this.localMavenRepository = localMavenRepository;
|
||||
this.settings = settings;
|
||||
this.javaHome = Objects.requireNonNull(javaHome, "javaHome");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return absolute normalized path to local Maven repository or {@code null}
|
||||
* @return absolute normalized path to local Maven repository or {@code null} if the server is supposed to use the default
|
||||
*/
|
||||
public Path getLocalMavenRepository() {
|
||||
return localMavenRepository;
|
||||
@@ -61,38 +62,14 @@ public class ClientLayout extends Layout {
|
||||
}
|
||||
|
||||
static Path findLocalRepo() {
|
||||
final String rawValue = System.getProperty("maven.repo.local");
|
||||
return rawValue != null ? Paths.get(rawValue) : null;
|
||||
return Environment.MAVEN_REPO_LOCAL.systemProperty().asPath();
|
||||
}
|
||||
|
||||
static Path findJavaHome(Properties mvndProperties) {
|
||||
String rawValue = System.getenv("JAVA_HOME");
|
||||
if (rawValue == null) {
|
||||
rawValue = mvndProperties.getProperty("java.home");
|
||||
}
|
||||
if (rawValue == null) {
|
||||
rawValue = System.getProperty("java.home");
|
||||
}
|
||||
if (rawValue == null) {
|
||||
throw new IllegalStateException(
|
||||
"Either environment variable JAVA_HOME or java.home property in ~/.m2/mvnd.properties or system property java.home must be set");
|
||||
}
|
||||
final Path path = Paths.get(rawValue);
|
||||
try {
|
||||
return path.toRealPath();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not get a real path from path " + path);
|
||||
}
|
||||
}
|
||||
|
||||
static Path findLogbackConfigurationFile(Properties mvndProperties, Path mvndHome) {
|
||||
String rawValue = mvndProperties.getProperty("logback.configurationFile");
|
||||
if (rawValue == null) {
|
||||
rawValue = System.getProperty("logback.configurationFile");
|
||||
}
|
||||
if (rawValue == null) {
|
||||
rawValue = System.getProperty("logback.configurationFile");
|
||||
}
|
||||
static Path findLogbackConfigurationFile(Supplier<Properties> mvndProperties, Path mvndPropertiesPath, Path mvndHome) {
|
||||
final String rawValue = Environment.LOGBACK_CONFIGURATION_FILE
|
||||
.systemProperty()
|
||||
.orLocalProperty(mvndProperties, mvndPropertiesPath)
|
||||
.asString();
|
||||
if (rawValue != null) {
|
||||
return Paths.get(rawValue).toAbsolutePath().normalize();
|
||||
}
|
||||
|
@@ -46,8 +46,6 @@ import org.slf4j.LoggerFactory;
|
||||
public class DefaultClient implements Client {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultClient.class);
|
||||
public static final String DAEMON_DEBUG = "daemon.debug";
|
||||
public static final String DAEMON_IDLE_TIMEOUT = "daemon.idleTimeout";
|
||||
public static final int DEFAULT_IDLE_TIMEOUT = (int) TimeUnit.HOURS.toMillis(3);
|
||||
public static final int DEFAULT_PERIODIC_CHECK_INTERVAL_MILLIS = 10 * 1000;
|
||||
public static final int CANCEL_TIMEOUT = 10 * 1000;
|
||||
@@ -67,10 +65,6 @@ public class DefaultClient implements Client {
|
||||
} else {
|
||||
throw new IllegalArgumentException("-l and --log-file need to befollowed by a path");
|
||||
}
|
||||
} else if ("--install".equals(arg)) {
|
||||
install(false);
|
||||
} else if ("--update".equals(arg)) {
|
||||
install(true);
|
||||
} else {
|
||||
args.add(arg);
|
||||
}
|
||||
@@ -81,21 +75,35 @@ public class DefaultClient implements Client {
|
||||
}
|
||||
}
|
||||
|
||||
private static void install(boolean overwrite) {
|
||||
private static void install(boolean overwrite, final Properties commandLineProperties) {
|
||||
final Properties buildProps = loadBuildProperties();
|
||||
final String version = buildProps.getProperty("version");
|
||||
final String rawZipUri = System.getProperty("mvnd.zip.uri", "https://github.com/ppalaga/mvnd/releases/download/"+ version +"/mvnd-" + version + ".zip");
|
||||
final String rawZipUri = Environment.MVND_DIST_URI
|
||||
.commandLineProperty(() -> commandLineProperties)
|
||||
.orEnvironmentVariable()
|
||||
.orSystemProperty()
|
||||
.orDefault(() -> "https://github.com/mvndaemon/mvnd/releases/download/" + version + "/mvnd-dist.zip")
|
||||
.asString();
|
||||
final URI zipUri = URI.create(rawZipUri);
|
||||
final Path mvndHome;
|
||||
final String rawMvndHome = Layout.findEnvMavenHome();
|
||||
if (rawMvndHome == null) {
|
||||
mvndHome = Paths.get(System.getProperty("user.home")).resolve(".m2/mvnd/" + version);
|
||||
} else {
|
||||
mvndHome = Paths.get(rawMvndHome);
|
||||
}
|
||||
final String rawJavaHome = System.getProperty("java.home");
|
||||
final Path javaHome = rawJavaHome != null ? Paths.get(rawJavaHome) : null;
|
||||
Installer.installServer(zipUri, Layout.MVND_PROPS_PATH, mvndHome, javaHome, overwrite);
|
||||
final Path mvndHome = Environment.MAVEN_HOME
|
||||
.commandLineProperty(() -> commandLineProperties)
|
||||
.orEnvironmentVariable()
|
||||
.orSystemProperty()
|
||||
.orDefault(() -> Paths.get(System.getProperty("user.home")).resolve(".m2/mvnd/" + version).toString())
|
||||
.asPath()
|
||||
.toAbsolutePath().normalize();
|
||||
final Path javaHome = Environment.JAVA_HOME
|
||||
.systemProperty() // only write java.home to mvnd.properties if it was explicitly set on command line
|
||||
// via -Djava.home=...
|
||||
.asPath();
|
||||
final Path mvndPropertiesPath = Environment.MVND_PROPERTIES_PATH
|
||||
.commandLineProperty(() -> commandLineProperties)
|
||||
.orEnvironmentVariable()
|
||||
.orSystemProperty()
|
||||
.orDefault(() -> Paths.get(System.getProperty("user.home")).resolve(".m2/mvnd.properties").toString())
|
||||
.asPath()
|
||||
.toAbsolutePath().normalize();
|
||||
Installer.installServer(zipUri, mvndPropertiesPath, mvndHome, javaHome, overwrite);
|
||||
}
|
||||
|
||||
public DefaultClient(ClientLayout layout) {
|
||||
@@ -103,7 +111,7 @@ public class DefaultClient implements Client {
|
||||
this.buildProperties = loadBuildProperties();
|
||||
}
|
||||
|
||||
static Properties loadBuildProperties() {
|
||||
public static Properties loadBuildProperties() {
|
||||
final Properties result = new Properties();
|
||||
try (InputStream is = DefaultClient.class.getResourceAsStream("build.properties")) {
|
||||
result.load(is);
|
||||
@@ -117,14 +125,56 @@ public class DefaultClient implements Client {
|
||||
public ExecutionResult execute(ClientOutput output, List<String> argv) {
|
||||
LOGGER.debug("Starting client");
|
||||
|
||||
final List<String> args = new ArrayList<>(argv);
|
||||
final List<String> args = new ArrayList<>(argv.size());
|
||||
boolean version = false;
|
||||
boolean showVersion = false;
|
||||
boolean debug = false;
|
||||
boolean install = false;
|
||||
final Properties commandLineProperties = new Properties();
|
||||
for (String arg : argv) {
|
||||
switch (arg) {
|
||||
case "-v":
|
||||
case "-version":
|
||||
case "--version":
|
||||
version = true;
|
||||
args.add(arg);
|
||||
break;
|
||||
case "-V":
|
||||
case "--show-version":
|
||||
showVersion = true;
|
||||
args.add(arg);
|
||||
break;
|
||||
case "-X":
|
||||
case "--debug":
|
||||
debug = true;
|
||||
args.add(arg);
|
||||
break;
|
||||
case "--install":
|
||||
install = true;
|
||||
break;
|
||||
default:
|
||||
if (arg.startsWith("-D")) {
|
||||
final int eqPos = arg.indexOf('=');
|
||||
if (eqPos >= 0) {
|
||||
commandLineProperties.setProperty(arg.substring(2, eqPos), arg.substring(eqPos+1));
|
||||
} else {
|
||||
commandLineProperties.setProperty(arg.substring(2), null);
|
||||
}
|
||||
}
|
||||
args.add(arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (install) {
|
||||
install(false, commandLineProperties);
|
||||
return new DefaultResult(argv, null);
|
||||
}
|
||||
|
||||
|
||||
// Print version if needed
|
||||
boolean version = args.contains("-v") || args.contains("-version") || args.contains("--version");
|
||||
boolean showVersion = args.contains("-V") || args.contains("--show-version");
|
||||
boolean debug = args.contains("-X") || args.contains("--debug");
|
||||
if (version || showVersion || debug) {
|
||||
final String nativeSuffix = Layout.isNative() ? " (native)" : "";
|
||||
final String nativeSuffix = Environment.isNative() ? " (native)" : "";
|
||||
final String v = Ansi.ansi().bold().a("Maven Daemon " + buildProperties.getProperty("version") + nativeSuffix)
|
||||
.reset().toString();
|
||||
output.accept(v);
|
||||
@@ -145,7 +195,7 @@ public class DefaultClient implements Client {
|
||||
LocalDateTime.ofInstant(
|
||||
Instant.ofEpochMilli(Math.max(d.getLastIdle(), d.getLastBusy())),
|
||||
ZoneId.systemDefault()))));
|
||||
return new DefaultResult(argv, true);
|
||||
return new DefaultResult(argv, null);
|
||||
}
|
||||
boolean stop = args.remove("--stop");
|
||||
if (stop) {
|
||||
@@ -164,7 +214,7 @@ public class DefaultClient implements Client {
|
||||
}
|
||||
}
|
||||
}
|
||||
return new DefaultResult(argv, true);
|
||||
return new DefaultResult(argv, null);
|
||||
}
|
||||
|
||||
setDefaultArgs(args);
|
||||
@@ -191,15 +241,16 @@ public class DefaultClient implements Client {
|
||||
while (true) {
|
||||
Message m = daemon.receive();
|
||||
if (m instanceof BuildException) {
|
||||
output.error((BuildException) m);
|
||||
return new DefaultResult(argv, false);
|
||||
final BuildException e = (BuildException) m;
|
||||
output.error(e);
|
||||
return new DefaultResult(argv, new Exception(e.getClassName() + ": "+ e.getMessage() + "\n" + e.getStackTrace()));
|
||||
} else if (m instanceof BuildEvent) {
|
||||
BuildEvent be = (BuildEvent) m;
|
||||
switch (be.getType()) {
|
||||
case BuildStarted:
|
||||
break;
|
||||
case BuildStopped:
|
||||
return new DefaultResult(argv, true);
|
||||
return new DefaultResult(argv, null);
|
||||
case ProjectStarted:
|
||||
case MojoStarted:
|
||||
case MojoStopped:
|
||||
@@ -248,16 +299,16 @@ public class DefaultClient implements Client {
|
||||
args.add("\"" + layout.javaHome().resolve(java) + "\"");
|
||||
args.add("-classpath");
|
||||
args.add("\"" + classpath + "\"");
|
||||
if (Boolean.getBoolean(DAEMON_DEBUG)) {
|
||||
if (Environment.DAEMON_DEBUG.systemProperty().orDefault(() -> "false").asBoolean()) {
|
||||
args.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8000");
|
||||
}
|
||||
args.add("-Dmaven.home=\"" + mavenHome + "\"");
|
||||
args.add("-Dlogback.configurationFile=logback.xml");
|
||||
args.add("-Ddaemon.uid=" + uid);
|
||||
args.add("-Xmx4g");
|
||||
final String timeout = System.getProperty(DAEMON_IDLE_TIMEOUT);
|
||||
final String timeout = Environment.DAEMON_IDLE_TIMEOUT.systemProperty().asString();
|
||||
if (timeout != null) {
|
||||
args.add("-D" + DAEMON_IDLE_TIMEOUT + "=" + timeout);
|
||||
args.add(Environment.DAEMON_IDLE_TIMEOUT.asCommandLineProperty(timeout));
|
||||
}
|
||||
args.add("\"-Dmaven.multiModuleProjectDirectory=" + layout.multiModuleProjectDirectory().toString() + "\"");
|
||||
|
||||
@@ -291,26 +342,26 @@ public class DefaultClient implements Client {
|
||||
|
||||
private class DefaultResult implements ExecutionResult {
|
||||
|
||||
private final boolean success;
|
||||
private final Exception exception;
|
||||
private final List<String> args;
|
||||
|
||||
private DefaultResult(List<String> args, boolean success) {
|
||||
private DefaultResult(List<String> args, Exception exception) {
|
||||
super();
|
||||
this.args = args;
|
||||
this.success = success;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutionResult assertSuccess() {
|
||||
if (!this.success) {
|
||||
throw new AssertionError(appendCommand(new StringBuilder("Build failed: ")));
|
||||
if (exception != null) {
|
||||
throw new AssertionError(appendCommand(new StringBuilder("Build failed: ")).toString(), exception);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutionResult assertFailure() {
|
||||
if (this.success) {
|
||||
if (exception == null) {
|
||||
throw new AssertionError(appendCommand(new StringBuilder("Build did not fail: ")));
|
||||
}
|
||||
return this;
|
||||
@@ -318,7 +369,7 @@ public class DefaultClient implements Client {
|
||||
|
||||
@Override
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
return exception == null;
|
||||
}
|
||||
|
||||
StringBuilder appendCommand(StringBuilder sb) {
|
||||
|
256
client/src/main/java/org/jboss/fuse/mvnd/client/Environment.java
Normal file
256
client/src/main/java/org/jboss/fuse/mvnd/client/Environment.java
Normal file
@@ -0,0 +1,256 @@
|
||||
package org.jboss.fuse.mvnd.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Collects system properties and environment variables used by mvnd client or server.
|
||||
*/
|
||||
public enum Environment {
|
||||
LOGBACK_CONFIGURATION_FILE("logback.configurationFile", null),
|
||||
JAVA_HOME("java.home", "JAVA_HOME"),
|
||||
MAVEN_HOME("maven.home", "MAVEN_HOME"),
|
||||
MAVEN_REPO_LOCAL("maven.repo.local", null),
|
||||
MAVEN_MULTIMODULE_PROJECT_DIRECTORY("maven.multiModuleProjectDirectory", null),
|
||||
MVND_PROPERTIES_PATH("mvnd.properties.path", "MVND_PROPERTIES_PATH"),
|
||||
MVND_DIST_URI("mvnd.dist.uri", "MVND_DIST_URI"),
|
||||
DAEMON_DEBUG("daemon.debug", null),
|
||||
DAEMON_IDLE_TIMEOUT("daemon.idleTimeout", null),
|
||||
DAEMON_UID("daemon.uid", null);
|
||||
|
||||
static Properties properties = System.getProperties();
|
||||
static Map<String, String> env = System.getenv();
|
||||
|
||||
private final String property;
|
||||
private final String environmentVariable;
|
||||
|
||||
Environment(String property, String environmentVariable) {
|
||||
this.property = property;
|
||||
this.environmentVariable = environmentVariable;
|
||||
}
|
||||
|
||||
public Environment.EnvValue systemProperty() {
|
||||
return new EnvValue(this, systemPropertySource());
|
||||
}
|
||||
|
||||
public Environment.EnvValue commandLineProperty(Supplier<Properties> commandLineProperties) {
|
||||
return new EnvValue(this, new ValueSource(
|
||||
description -> description.append("command line property ").append(property),
|
||||
() -> commandLineProperties.get().getProperty(property)));
|
||||
}
|
||||
|
||||
public Environment.EnvValue environmentVariable() {
|
||||
return new EnvValue(this, environmentVariableSource());
|
||||
}
|
||||
|
||||
public String asCommandLineProperty(String value) {
|
||||
return "-D" + property + "=" + value;
|
||||
}
|
||||
|
||||
public boolean hasCommandLineProperty(Collection<String> args) {
|
||||
final String prefix = "-D" + property + "=";
|
||||
return args.stream().anyMatch(s -> s.startsWith(prefix));
|
||||
}
|
||||
|
||||
public static Path findJavaHome(Supplier<Properties> mvndProperties, Path mvndPropertiesPath) {
|
||||
final Path result = JAVA_HOME
|
||||
.environmentVariable()
|
||||
.orLocalProperty(mvndProperties, mvndPropertiesPath)
|
||||
.orSystemProperty()
|
||||
.orFail()
|
||||
.asPath();
|
||||
try {
|
||||
return result
|
||||
.toRealPath();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not get a real path from path " + result);
|
||||
}
|
||||
}
|
||||
|
||||
public static Path findMvndPropertiesPath() {
|
||||
return MVND_PROPERTIES_PATH
|
||||
.environmentVariable()
|
||||
.orSystemProperty()
|
||||
.orDefault(() -> Paths.get(System.getProperty("user.home")).resolve(".m2/mvnd.properties").toString())
|
||||
.asPath()
|
||||
.toAbsolutePath().normalize();
|
||||
}
|
||||
|
||||
public static Path findMavenHome(Supplier<Properties> mvndProperties, Path mvndPropertiesPath) {
|
||||
return findBasicMavenHome()
|
||||
.orLocalProperty(mvndProperties, mvndPropertiesPath)
|
||||
.orFail()
|
||||
.asPath()
|
||||
.toAbsolutePath().normalize();
|
||||
}
|
||||
|
||||
public static EnvValue findBasicMavenHome() {
|
||||
return MAVEN_HOME
|
||||
.environmentVariable()
|
||||
.orSystemProperty();
|
||||
}
|
||||
|
||||
public static Path findMultiModuleProjectDirectory(Path pwd) {
|
||||
return MAVEN_MULTIMODULE_PROJECT_DIRECTORY
|
||||
.systemProperty()
|
||||
.orDefault(() -> {
|
||||
Path dir = pwd;
|
||||
do {
|
||||
if (Files.isDirectory(dir.resolve(".mvn"))) {
|
||||
return dir.toString();
|
||||
}
|
||||
dir = dir.getParent();
|
||||
} while (dir != null);
|
||||
throw new IllegalStateException("Could not detect maven.multiModuleProjectDirectory by climbing up from ["
|
||||
+ pwd
|
||||
+ "] seeking a .mvn directory. You may want to create a .mvn directory in the root directory of your source tree.");
|
||||
})
|
||||
.asPath()
|
||||
.toAbsolutePath().normalize();
|
||||
}
|
||||
|
||||
public static boolean isNative() {
|
||||
return "executable".equals(System.getProperty("org.graalvm.nativeimage.kind"));
|
||||
}
|
||||
|
||||
private Environment.ValueSource systemPropertySource() {
|
||||
if (property == null) {
|
||||
throw new IllegalStateException("Cannot use " + Environment.class.getName() + " for getting a system property");
|
||||
}
|
||||
return new ValueSource(
|
||||
description -> description.append("system property ").append(property),
|
||||
() -> properties.getProperty(property));
|
||||
}
|
||||
|
||||
private Environment.ValueSource environmentVariableSource() {
|
||||
if (environmentVariable == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot use " + Environment.class.getName() + "."+ name() +" for getting an environment variable");
|
||||
}
|
||||
return new ValueSource(
|
||||
description -> description.append("environment variable ").append(environmentVariable),
|
||||
() -> env.get(environmentVariable));
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of an environment value with a description capability.
|
||||
*/
|
||||
public static class ValueSource {
|
||||
final Function<StringBuilder, StringBuilder> descriptionFunction;
|
||||
final Supplier<String> valueSupplier;
|
||||
|
||||
public ValueSource(Function<StringBuilder, StringBuilder> descriptionFunction, Supplier<String> valueSupplier) {
|
||||
this.descriptionFunction = descriptionFunction;
|
||||
this.valueSupplier = valueSupplier;
|
||||
}
|
||||
|
||||
/** Mostly for debugging */
|
||||
@Override
|
||||
public String toString() {
|
||||
return descriptionFunction.apply(new StringBuilder()).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A chained lazy environment value.
|
||||
*/
|
||||
public static class EnvValue {
|
||||
private final Environment envKey;
|
||||
private final Environment.ValueSource valueSource;
|
||||
protected Environment.EnvValue previous;
|
||||
|
||||
public EnvValue(Environment envKey, Environment.ValueSource valueSource) {
|
||||
this.previous = null;
|
||||
this.envKey = envKey;
|
||||
this.valueSource = valueSource;
|
||||
}
|
||||
|
||||
public EnvValue(Environment.EnvValue previous, Environment envKey, Environment.ValueSource valueSource) {
|
||||
this.previous = previous;
|
||||
this.envKey = envKey;
|
||||
this.valueSource = valueSource;
|
||||
}
|
||||
|
||||
public Environment.EnvValue orSystemProperty() {
|
||||
return new EnvValue(this, envKey, envKey.systemPropertySource());
|
||||
}
|
||||
|
||||
public Environment.EnvValue orLocalProperty(Supplier<Properties> localProperties, Path localPropertiesPath) {
|
||||
return new EnvValue(this, envKey, new ValueSource(
|
||||
description -> description.append("property ").append(envKey.property).append(" in ")
|
||||
.append(localPropertiesPath),
|
||||
() -> localProperties.get().getProperty(envKey.property)));
|
||||
}
|
||||
|
||||
public Environment.EnvValue orEnvironmentVariable() {
|
||||
return new EnvValue(this, envKey, envKey.environmentVariableSource());
|
||||
}
|
||||
|
||||
public Environment.EnvValue orDefault(Supplier<String> defaultSupplier) {
|
||||
return new EnvValue(this, envKey,
|
||||
new ValueSource(sb -> sb.append("default").append(defaultSupplier.get()), defaultSupplier));
|
||||
}
|
||||
|
||||
public Environment.EnvValue orFail() {
|
||||
return new EnvValue(this, envKey, new ValueSource(sb -> sb, () -> {
|
||||
final StringBuilder sb = new StringBuilder("Could not get value for ")
|
||||
.append(Environment.class.getSimpleName())
|
||||
.append(".").append(envKey.name()).append(" from any of the following sources: ");
|
||||
|
||||
/* Compose the description functions to invert the order thus getting the resolution order in the message */
|
||||
Function<StringBuilder, StringBuilder> description = (s -> s);
|
||||
EnvValue val = this;
|
||||
while (val != null) {
|
||||
description = description.compose(val.valueSource.descriptionFunction);
|
||||
val = val.previous;
|
||||
if (val != null) {
|
||||
description = description.compose(s -> s.append(", "));
|
||||
}
|
||||
}
|
||||
description.apply(sb);
|
||||
throw new IllegalStateException(sb.toString());
|
||||
}));
|
||||
}
|
||||
|
||||
String get() {
|
||||
if (previous != null) {
|
||||
final String result = previous.get();
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return valueSource.valueSupplier.get();
|
||||
}
|
||||
|
||||
public String asString() {
|
||||
return get();
|
||||
}
|
||||
|
||||
public Optional<String> asOptional() {
|
||||
return Optional.ofNullable(get());
|
||||
}
|
||||
|
||||
public Path asPath() {
|
||||
final String result = get();
|
||||
return result == null ? null : Paths.get(result);
|
||||
}
|
||||
|
||||
public boolean asBoolean() {
|
||||
return Boolean.parseBoolean(get());
|
||||
}
|
||||
|
||||
public int asInt() {
|
||||
return Integer.parseInt(get());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -25,10 +25,14 @@ import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Installer {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Installer.class);
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
private static final int MAX_PERMISSIONS = 0777;
|
||||
|
||||
public static void installServer(URI zipUri, Path mvndPropsPath, Path mvndHome, Path javaHome, boolean overwrite) {
|
||||
final boolean mvndHomeExists = Files.exists(mvndHome);
|
||||
if (!overwrite && mvndHomeExists) {
|
||||
@@ -47,7 +51,7 @@ public class Installer {
|
||||
writeMvndProperties(mvndPropsPath, mvndHome, javaHome);
|
||||
}
|
||||
|
||||
private static void deleteIfExists(Path path) {
|
||||
static void deleteIfExists(Path path) {
|
||||
if (Files.isRegularFile(path)) {
|
||||
try {
|
||||
Files.delete(path);
|
||||
@@ -71,6 +75,7 @@ public class Installer {
|
||||
}
|
||||
|
||||
static void writeMvndProperties(Path mvndPropsPath, Path mvndHome, Path javaHome) {
|
||||
LOG.debug("Writing {}", mvndPropsPath);
|
||||
final String template = readTemplate();
|
||||
final String javaHomeLine = javaHome == null ? "" : "java.home = " + javaHome.toString();
|
||||
final String content = String.format(template, mvndHome.toString(), javaHomeLine);
|
||||
@@ -91,21 +96,22 @@ public class Installer {
|
||||
}
|
||||
}
|
||||
|
||||
static void unzip(Path localZip, Path mvndHome) {
|
||||
static void unzip(Path localZip, Path destinationDir) {
|
||||
LOG.debug("Unzipping {} to {}", localZip, destinationDir);
|
||||
try {
|
||||
Files.createDirectories(mvndHome);
|
||||
Files.createDirectories(destinationDir);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not create directories " + mvndHome, e);
|
||||
throw new RuntimeException("Could not create directories " + destinationDir, e);
|
||||
}
|
||||
try (ZipFile zip = new ZipFile(Files.newByteChannel(localZip))) {
|
||||
final Map<Integer, Set<PosixFilePermission>> permissionCache = new HashMap<>();
|
||||
final Enumeration<ZipArchiveEntry> entries = zip.getEntries();
|
||||
while (entries.hasMoreElements()) {
|
||||
final ZipArchiveEntry entry = entries.nextElement();
|
||||
final Path dest = mvndHome.resolve(entry.getName()).normalize();
|
||||
if (!dest.startsWith(mvndHome)) {
|
||||
final Path dest = destinationDir.resolve(entry.getName()).normalize();
|
||||
if (!dest.startsWith(destinationDir)) {
|
||||
/* Avoid writing to paths outside of mvndHome */
|
||||
throw new IllegalStateException("Tainted ZIP entry name " + entry.getName());
|
||||
throw new IllegalStateException("Possibly tainted ZIP entry name " + entry.getName() + " would have to be unpacked outside of " + destinationDir);
|
||||
}
|
||||
if (entry.isDirectory()) {
|
||||
Files.createDirectories(dest);
|
||||
@@ -120,21 +126,24 @@ public class Installer {
|
||||
"Could not unzip entry " + entry.getName() + " from " + localZip + " to " + dest);
|
||||
}
|
||||
}
|
||||
final int mode = (int) (entry.getUnixMode() & MAX_PERMISSIONS);
|
||||
if (mode != 0) {
|
||||
final PosixFileAttributeView attributes = Files.getFileAttributeView(dest, PosixFileAttributeView.class);
|
||||
if (attributes != null) {
|
||||
int mode = (int) (entry.getUnixMode() & MAX_PERMISSIONS);
|
||||
Files.setPosixFilePermissions(dest, permissionCache.computeIfAbsent(mode, Installer::toPermissionSet));
|
||||
}
|
||||
}
|
||||
Files.setLastModifiedTime(dest, FileTime.from(entry.getTime(), TimeUnit.MILLISECONDS));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not unzip " + localZip, e);
|
||||
throw new RuntimeException("Could not unzip " + localZip + " to " + destinationDir, e);
|
||||
}
|
||||
}
|
||||
|
||||
static Path download(URI zipUri) {
|
||||
try {
|
||||
final Path localZip = Files.createTempFile("", "-mvnd-dist.zip");
|
||||
LOG.debug("Downloading {} to {}", zipUri, localZip);
|
||||
try (
|
||||
InputStream in = new BufferedInputStream(zipUri.toURL().openStream(), BUFFER_SIZE);
|
||||
OutputStream out = new BufferedOutputStream(Files.newOutputStream(localZip), BUFFER_SIZE)) {
|
||||
|
@@ -17,25 +17,24 @@ package org.jboss.fuse.mvnd.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Layout {
|
||||
|
||||
public static final Path MVND_PROPS_PATH = Paths.get(System.getProperty("user.home")).resolve(".m2/mvnd.properties");
|
||||
|
||||
private static Layout ENV_INSTANCE;
|
||||
|
||||
private final Path mavenHome;
|
||||
private final Path userDir;
|
||||
private final Path multiModuleProjectDirectory;
|
||||
private final Path mvndPropertiesPath;
|
||||
|
||||
public Layout(Path mavenHome, Path userDir, Path multiModuleProjectDirectory) {
|
||||
public Layout(Path mvndPropertiesPath, Path mavenHome, Path userDir, Path multiModuleProjectDirectory) {
|
||||
super();
|
||||
this.mvndPropertiesPath = mvndPropertiesPath;
|
||||
this.mavenHome = mavenHome;
|
||||
this.userDir = userDir;
|
||||
this.multiModuleProjectDirectory = multiModuleProjectDirectory;
|
||||
@@ -61,90 +60,47 @@ public class Layout {
|
||||
return multiModuleProjectDirectory;
|
||||
}
|
||||
|
||||
public static boolean isNative() {
|
||||
return "executable".equals(System.getProperty("org.graalvm.nativeimage.kind"));
|
||||
public Path getMvndPropertiesPath() {
|
||||
return mvndPropertiesPath;
|
||||
}
|
||||
|
||||
public static Layout getEnvInstance() {
|
||||
if (ENV_INSTANCE == null) {
|
||||
final Properties mvndProperties = loadMvndProperties();
|
||||
final Path mvndPropertiesPath = Environment.findMvndPropertiesPath();
|
||||
final Supplier<Properties> mvndProperties = lazyMvndProperties(mvndPropertiesPath);
|
||||
final Path pwd = Paths.get(".").toAbsolutePath().normalize();
|
||||
|
||||
ENV_INSTANCE = new Layout(
|
||||
findMavenHome(mvndProperties),
|
||||
mvndPropertiesPath,
|
||||
Environment.findMavenHome(mvndProperties, mvndPropertiesPath),
|
||||
pwd,
|
||||
findMultiModuleProjectDirectory(pwd));
|
||||
Environment.findMultiModuleProjectDirectory(pwd));
|
||||
}
|
||||
return ENV_INSTANCE;
|
||||
}
|
||||
|
||||
static Supplier<Properties> lazyMvndProperties(Path mvndPropertiesPath) {
|
||||
return new Supplier<Properties>() {
|
||||
|
||||
static Properties loadMvndProperties() {
|
||||
final Properties result = new Properties();
|
||||
if (Files.exists(MVND_PROPS_PATH)) {
|
||||
try (InputStream in = Files.newInputStream(MVND_PROPS_PATH)) {
|
||||
private volatile Properties properties;
|
||||
|
||||
@Override
|
||||
public Properties get() {
|
||||
Properties result = this.properties;
|
||||
if (result == null) {
|
||||
result = new Properties();
|
||||
if (Files.exists(mvndPropertiesPath)) {
|
||||
try (InputStream in = Files.newInputStream(mvndPropertiesPath)) {
|
||||
result.load(in);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not read " + MVND_PROPS_PATH);
|
||||
throw new RuntimeException("Could not read " + mvndPropertiesPath);
|
||||
}
|
||||
}
|
||||
this.properties = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static Path findMavenHome(Properties mvndProperties) {
|
||||
String rawValue = findEnvMavenHome();
|
||||
if (isNative()) {
|
||||
try {
|
||||
final Path nativeExecutablePath = Paths.get(Class.forName("org.graalvm.nativeimage.ProcessProperties").getMethod("getExecutableName").invoke(null).toString()).toAbsolutePath().normalize();
|
||||
final Path bin = nativeExecutablePath.getParent();
|
||||
if (bin.getFileName().toString().equals("bin")) {
|
||||
final Path candidateMvnHome = bin.getParent();
|
||||
final Path libExt = candidateMvnHome.resolve("lib/ext");
|
||||
if (Files.isDirectory(libExt)) {
|
||||
try (Stream<Path> files = Files.list(libExt)) {
|
||||
if (files.filter(path -> path.getFileName().toString().startsWith("mvnd-")).findFirst().isPresent()) {
|
||||
rawValue = candidateMvnHome.toString();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not list " + libExt);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException
|
||||
| SecurityException | ClassNotFoundException e) {
|
||||
throw new RuntimeException("Could not invoke org.graalvm.nativeimage.ProcessProperties.getExecutableName() via reflection");
|
||||
}
|
||||
}
|
||||
if (rawValue == null) {
|
||||
rawValue = mvndProperties.getProperty("maven.home");
|
||||
}
|
||||
if (rawValue == null) {
|
||||
throw new IllegalStateException("Either environment variable MAVEN_HOME or maven.home property in ~/.m2/mvnd.properties or system property maven.home must be set");
|
||||
}
|
||||
return Paths.get(rawValue).toAbsolutePath().normalize();
|
||||
}
|
||||
|
||||
public static String findEnvMavenHome() {
|
||||
String rawValue = System.getenv("MAVEN_HOME");
|
||||
if (rawValue == null) {
|
||||
rawValue = System.getProperty("maven.home");
|
||||
}
|
||||
return rawValue;
|
||||
}
|
||||
|
||||
static Path findMultiModuleProjectDirectory(Path pwd) {
|
||||
final String multiModuleProjectDirectory = System.getProperty("maven.multiModuleProjectDirectory");
|
||||
if (multiModuleProjectDirectory != null) {
|
||||
return Paths.get(multiModuleProjectDirectory).toAbsolutePath().normalize();
|
||||
}
|
||||
Path dir = pwd;
|
||||
do {
|
||||
if (Files.isDirectory(dir.resolve(".mvn"))) {
|
||||
return dir.toAbsolutePath().normalize();
|
||||
}
|
||||
dir = dir.getParent();
|
||||
} while (dir != null);
|
||||
throw new IllegalStateException("Could not detect maven.multiModuleProjectDirectory by climbing up from ["+ pwd +"] seeking a .mvn directory. You may want to create a .mvn directory in the root directory of your source tree.");
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -20,19 +20,13 @@ import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ServerMain {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String uidStr = System.getProperty("daemon.uid");
|
||||
String mavenHomeStr = System.getProperty("maven.home");
|
||||
if (uidStr == null || mavenHomeStr == null) {
|
||||
throw new IllegalStateException("The system properties 'daemon.uid' and 'maven.home' must be valid");
|
||||
}
|
||||
|
||||
Path mavenHome = Paths.get(mavenHomeStr);
|
||||
final String uidStr = Environment.DAEMON_UID.systemProperty().orFail().asString();
|
||||
final Path mavenHome = Environment.MAVEN_HOME.systemProperty().orFail().asPath();
|
||||
URL[] classpath =
|
||||
Stream.concat(
|
||||
Stream.concat(Files.list(mavenHome.resolve("lib/ext")),
|
||||
|
@@ -0,0 +1,92 @@
|
||||
package org.jboss.fuse.mvnd.client.svm;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
import org.apache.commons.compress.archivers.zip.AsiExtraField;
|
||||
import org.apache.commons.compress.archivers.zip.ExtraFieldParsingBehavior;
|
||||
import org.apache.commons.compress.archivers.zip.ExtraFieldUtils;
|
||||
import org.apache.commons.compress.archivers.zip.ExtraFieldUtils.UnparseableExtraField;
|
||||
import org.apache.commons.compress.archivers.zip.ZipExtraField;
|
||||
import org.apache.commons.compress.archivers.zip.ZipShort;
|
||||
|
||||
import com.oracle.svm.core.annotate.Alias;
|
||||
import com.oracle.svm.core.annotate.KeepOriginal;
|
||||
import com.oracle.svm.core.annotate.RecomputeFieldValue;
|
||||
import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind;
|
||||
import com.oracle.svm.core.annotate.Substitute;
|
||||
import com.oracle.svm.core.annotate.TargetClass;
|
||||
|
||||
@TargetClass(ExtraFieldUtils.class)
|
||||
@Substitute
|
||||
public final class ExtraFieldUtilsSubstitution {
|
||||
|
||||
@Alias
|
||||
@RecomputeFieldValue(kind = Kind.None)
|
||||
private static final Map<ZipShort, Class<?>> implementations;
|
||||
|
||||
static {
|
||||
implementations = new ConcurrentHashMap<>();
|
||||
registerInst(new AsiExtraField());
|
||||
}
|
||||
|
||||
|
||||
public static void registerInst(ZipExtraField ze) {
|
||||
implementations.put(ze.getHeaderId(), ze.getClass());
|
||||
}
|
||||
|
||||
@KeepOriginal
|
||||
public static ZipExtraField createExtraField(final ZipShort headerId)
|
||||
throws InstantiationException, IllegalAccessException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@KeepOriginal
|
||||
public static ZipExtraField createExtraFieldNoDefault(final ZipShort headerId)
|
||||
throws InstantiationException, IllegalAccessException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@KeepOriginal
|
||||
public static ZipExtraField[] parse(final byte[] data) throws ZipException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@KeepOriginal
|
||||
public static ZipExtraField[] parse(final byte[] data, final boolean local)
|
||||
throws ZipException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@KeepOriginal
|
||||
public static ZipExtraField[] parse(final byte[] data, final boolean local,
|
||||
final UnparseableExtraField onUnparseableData)
|
||||
throws ZipException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@KeepOriginal
|
||||
public static ZipExtraField[] parse(final byte[] data, final boolean local,
|
||||
final ExtraFieldParsingBehavior parsingBehavior)
|
||||
throws ZipException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@KeepOriginal
|
||||
public static byte[] mergeLocalFileDataData(final ZipExtraField[] data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@KeepOriginal
|
||||
public static byte[] mergeCentralDirectoryData(final ZipExtraField[] data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@KeepOriginal
|
||||
public static ZipExtraField fillExtraField(final ZipExtraField ze, final byte[] data, final int off,
|
||||
final int len, final boolean local) throws ZipException {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@@ -8,7 +8,8 @@ import com.oracle.svm.core.annotate.AutomaticFeature;
|
||||
public class ReflectionRegistration implements Feature {
|
||||
public void beforeAnalysis(BeforeAnalysisAccess access) {
|
||||
// try {
|
||||
// } catch (NoSuchMethodException | SecurityException e) {
|
||||
// RuntimeReflection.register(AsiExtraField.class.getConstructors());
|
||||
// } catch (SecurityException e) {
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jboss.fuse.mvnd.daemon;
|
||||
package org.jboss.fuse.mvnd.client;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
@@ -0,0 +1,108 @@
|
||||
package org.jboss.fuse.mvnd.client;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class EnvironmentTest {
|
||||
|
||||
@Test
|
||||
void prop() {
|
||||
try (EnvironmentResource env = new EnvironmentResource()) {
|
||||
env.props("maven.home", "/maven/home/prop");
|
||||
Assertions.assertEquals("/maven/home/prop", Environment.MAVEN_HOME.systemProperty().asString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void env() {
|
||||
try (EnvironmentResource env = new EnvironmentResource()) {
|
||||
env.env("MAVEN_HOME", "/maven/home/env");
|
||||
Assertions.assertEquals("/maven/home/env", Environment.MAVEN_HOME.environmentVariable().asString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void localProps() {
|
||||
try (EnvironmentResource env = new EnvironmentResource()) {
|
||||
final Properties localProps = new Properties();
|
||||
localProps.put("maven.home", "/maven/home/local");
|
||||
Assertions.assertEquals(Paths.get("/maven/home/local"),
|
||||
Environment.MAVEN_HOME
|
||||
.environmentVariable()
|
||||
.orSystemProperty()
|
||||
.orLocalProperty(() -> localProps, Paths.get("/local/properties"))
|
||||
.orFail()
|
||||
.asPath());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void envBeforeProp() {
|
||||
try (EnvironmentResource env = new EnvironmentResource()) {
|
||||
env.props("maven.home", "/maven/home/prop");
|
||||
env.env("MAVEN_HOME", "/maven/home/env");
|
||||
Assertions.assertEquals("/maven/home/env",
|
||||
Environment.MAVEN_HOME
|
||||
.environmentVariable()
|
||||
.orSystemProperty()
|
||||
.asString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void fail() {
|
||||
try (EnvironmentResource env = new EnvironmentResource()) {
|
||||
try {
|
||||
Assertions.assertEquals("/maven/home/env",
|
||||
Environment.MAVEN_HOME
|
||||
.environmentVariable()
|
||||
.orSystemProperty()
|
||||
.orFail()
|
||||
.asString());
|
||||
Assertions.fail("IllegalStateException expected");
|
||||
} catch (IllegalStateException e) {
|
||||
Assertions.assertEquals(
|
||||
"Could not get value for Environment.MAVEN_HOME from any of the following sources: environment variable MAVEN_HOME, system property maven.home",
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class EnvironmentResource implements AutoCloseable {
|
||||
|
||||
private final Properties props = new Properties();
|
||||
private final Map<String, String> env = new HashMap<>();
|
||||
|
||||
public EnvironmentResource() {
|
||||
Environment.env = env;
|
||||
Environment.properties = props;
|
||||
}
|
||||
|
||||
public void props(String... props) {
|
||||
int i = 0;
|
||||
while (i < props.length) {
|
||||
this.props.setProperty(props[i++], props[i++]);
|
||||
}
|
||||
}
|
||||
|
||||
public void env(String... env) {
|
||||
int i = 0;
|
||||
while (i < env.length) {
|
||||
this.env.put(env[i++], env[i++]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Environment.env = System.getenv();
|
||||
Environment.properties = System.getProperties();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -43,6 +43,7 @@ import org.apache.maven.cli.CliRequest;
|
||||
import org.apache.maven.cli.CliRequestBuilder;
|
||||
import org.apache.maven.cli.DaemonMavenCli;
|
||||
import org.jboss.fuse.mvnd.client.DefaultClient;
|
||||
import org.jboss.fuse.mvnd.client.Environment;
|
||||
import org.jboss.fuse.mvnd.client.DaemonConnection;
|
||||
import org.jboss.fuse.mvnd.client.DaemonException;
|
||||
import org.jboss.fuse.mvnd.client.DaemonExpirationStatus;
|
||||
@@ -91,12 +92,10 @@ public class Server implements AutoCloseable, Runnable {
|
||||
registry = new DaemonRegistry(layout.registry());
|
||||
socket = ServerSocketChannel.open().bind(new InetSocketAddress(0));
|
||||
|
||||
int idleTimeout;
|
||||
if (System.getProperty(DefaultClient.DAEMON_IDLE_TIMEOUT) != null) {
|
||||
idleTimeout = Integer.parseInt(System.getProperty(DefaultClient.DAEMON_IDLE_TIMEOUT));
|
||||
} else {
|
||||
idleTimeout = DefaultClient.DEFAULT_IDLE_TIMEOUT;
|
||||
}
|
||||
final int idleTimeout = Environment.DAEMON_IDLE_TIMEOUT
|
||||
.systemProperty()
|
||||
.orDefault(() -> String.valueOf(DefaultClient.DEFAULT_IDLE_TIMEOUT))
|
||||
.asInt();
|
||||
executor = Executors.newScheduledThreadPool(1);
|
||||
strategy = DaemonExpiration.master();
|
||||
|
||||
|
@@ -58,6 +58,7 @@
|
||||
<systemPropertyVariables>
|
||||
<project.version>${project.version}</project.version>
|
||||
<mvnd.home>${project.basedir}/../daemon/target/maven-distro</mvnd.home>
|
||||
<mvnd.dist.path>${project.basedir}/../daemon/target/mvnd-dist-${project.version}.zip</mvnd.dist.path>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
@@ -94,6 +95,7 @@
|
||||
<project.version>${project.version}</project.version>
|
||||
<mvnd.home>${project.basedir}/../daemon/target/maven-distro</mvnd.home>
|
||||
<mvnd.native.executable>${mvnd.native.executable}</mvnd.native.executable>
|
||||
<mvnd.dist.path>${project.basedir}/../daemon/target/mvnd-dist-${project.version}.zip</mvnd.dist.path>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
@@ -0,0 +1,76 @@
|
||||
package org.jboss.fuse.mvnd.it;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.PosixFileAttributeView;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.jboss.fuse.mvnd.client.Client;
|
||||
import org.jboss.fuse.mvnd.client.ClientOutput;
|
||||
import org.jboss.fuse.mvnd.client.DefaultClient;
|
||||
import org.jboss.fuse.mvnd.client.Environment;
|
||||
import org.jboss.fuse.mvnd.junit.MvndNativeTest;
|
||||
import org.jboss.fuse.mvnd.junit.TestLayout;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
@MvndNativeTest(projectDir = "src/test/projects/single-module")
|
||||
public class InstallDaemonNativeIT {
|
||||
|
||||
@Inject
|
||||
Client client;
|
||||
|
||||
@Inject
|
||||
TestLayout layout;
|
||||
|
||||
@Test
|
||||
void installDaemon() throws IOException, InterruptedException {
|
||||
|
||||
final Path testDir = layout.getTestDir();
|
||||
final Path mvndPropertiesPath = testDir.resolve("installation-mvnd.properties");
|
||||
final Path mavenHome = testDir.resolve("installation-maven-home");
|
||||
|
||||
Assertions.assertThat(mvndPropertiesPath).doesNotExist();
|
||||
Assertions.assertThat(mavenHome).doesNotExist();
|
||||
|
||||
final ClientOutput o = Mockito.mock(ClientOutput.class);
|
||||
final Path mvndDistPath = Paths.get(Objects.requireNonNull(System.getProperty("mvnd.dist.path")))
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
Assertions.assertThat(mvndDistPath).exists();
|
||||
|
||||
final String mvndDistUri = mvndDistPath.toUri().toString();
|
||||
|
||||
client.execute(o,
|
||||
"--install",
|
||||
Environment.MVND_DIST_URI.asCommandLineProperty(mvndDistUri),
|
||||
Environment.MVND_PROPERTIES_PATH.asCommandLineProperty(mvndPropertiesPath.toString()),
|
||||
Environment.JAVA_HOME.asCommandLineProperty(layout.javaHome().toString()),
|
||||
Environment.MAVEN_HOME.asCommandLineProperty(mavenHome.toString()))
|
||||
.assertSuccess();
|
||||
|
||||
Assertions.assertThat(mvndPropertiesPath).exists();
|
||||
Assertions.assertThat(mavenHome).exists();
|
||||
final Path mvndShPath = mavenHome.resolve("bin/mvnd");
|
||||
Assertions.assertThat(mvndShPath).exists();
|
||||
if (!System.getProperty("os.name").toLowerCase().contains("windows")) {
|
||||
final PosixFileAttributeView attributes = Files.getFileAttributeView(mvndShPath, PosixFileAttributeView.class);
|
||||
Assertions.assertThat(attributes).isNotNull();
|
||||
final Set<PosixFilePermission> permissions = attributes.readAttributes().permissions();
|
||||
Assertions.assertThat(permissions).contains(PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OWNER_EXECUTE,
|
||||
PosixFilePermission.OTHERS_EXECUTE);
|
||||
}
|
||||
|
||||
final String version = DefaultClient.loadBuildProperties().getProperty("version");
|
||||
Assertions.assertThat(mavenHome.resolve("lib/ext/mvnd-client-" + version + ".jar")).exists();
|
||||
Assertions.assertThat(mavenHome.resolve("lib/ext/mvnd-daemon-" + version + ".jar")).exists();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package org.jboss.fuse.mvnd.it;
|
||||
|
||||
import org.jboss.fuse.mvnd.junit.MvndTest;
|
||||
|
||||
@MvndTest(projectDir = "src/test/projects/single-module")
|
||||
public class InstallDaemonTest extends InstallDaemonNativeIT {
|
||||
|
||||
}
|
@@ -13,7 +13,6 @@ import org.jboss.fuse.mvnd.client.ClientLayout;
|
||||
import org.jboss.fuse.mvnd.client.ClientOutput;
|
||||
import org.jboss.fuse.mvnd.junit.MvndNativeTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
@@ -27,7 +26,7 @@ public class SingleModuleNativeIT {
|
||||
ClientLayout layout;
|
||||
|
||||
@Test
|
||||
void cleanInstall(TestInfo testInfo) throws IOException, InterruptedException {
|
||||
void cleanInstall() throws IOException, InterruptedException {
|
||||
final Path helloFilePath = layout.multiModuleProjectDirectory().resolve("target/hello.txt");
|
||||
if (Files.exists(helloFilePath)) {
|
||||
Files.delete(helloFilePath);
|
||||
|
@@ -68,13 +68,11 @@ public class MvndTestExtension implements BeforeAllCallback, BeforeEachCallback,
|
||||
f.setAccessible(true);
|
||||
if (f.getType() == DaemonRegistry.class) {
|
||||
f.set(testInstance, resource.registry);
|
||||
} else if (f.getType() == ClientLayout.class) {
|
||||
f.set(testInstance, resource.layout);
|
||||
} else if (f.getType() == Layout.class) {
|
||||
} else if (Layout.class.isAssignableFrom(f.getType())) {
|
||||
f.set(testInstance, resource.layout);
|
||||
} else if (f.getType() == Client.class) {
|
||||
if (resource.isNative) {
|
||||
final Path mvndNativeExecutablePath = Paths.get(System.getProperty("mvnd.native.executable"));
|
||||
final Path mvndNativeExecutablePath = Paths.get(System.getProperty("mvnd.native.executable")).toAbsolutePath().normalize();
|
||||
if (!Files.isRegularFile(mvndNativeExecutablePath)) {
|
||||
throw new IllegalStateException("mvnd executable does not exist: " + mvndNativeExecutablePath);
|
||||
}
|
||||
@@ -101,7 +99,7 @@ public class MvndTestExtension implements BeforeAllCallback, BeforeEachCallback,
|
||||
|
||||
static class MvndResource implements ExtensionContext.Store.CloseableResource {
|
||||
|
||||
private final ClientLayout layout;
|
||||
private final TestLayout layout;
|
||||
private final DaemonRegistry registry;
|
||||
private final boolean isNative;
|
||||
private final long timeoutMs;
|
||||
@@ -141,9 +139,12 @@ public class MvndTestExtension implements BeforeAllCallback, BeforeEachCallback,
|
||||
throw new IllegalStateException(
|
||||
"The value of mvnd.home system property points at a path that does not exist or is not a directory");
|
||||
}
|
||||
final Path mvndPropertiesPath = testDir.resolve("mvnd.properties");
|
||||
final Path localMavenRepository = deleteDir(testDir.resolve("local-maven-repo"));
|
||||
final Path settingsPath = createSettings(testDir.resolve("settings.xml"));
|
||||
final ClientLayout layout = new ClientLayout(
|
||||
final TestLayout layout = new TestLayout(
|
||||
testDir,
|
||||
mvndPropertiesPath,
|
||||
mvndHome,
|
||||
testExecutionDir,
|
||||
testExecutionDir,
|
||||
@@ -188,7 +189,7 @@ public class MvndTestExtension implements BeforeAllCallback, BeforeEachCallback,
|
||||
return settingsPath;
|
||||
}
|
||||
|
||||
public MvndResource(ClientLayout layout, DaemonRegistry registry, boolean isNative, long timeoutMs) {
|
||||
public MvndResource(TestLayout layout, DaemonRegistry registry, boolean isNative, long timeoutMs) {
|
||||
super();
|
||||
this.layout = layout;
|
||||
this.registry = registry;
|
||||
|
@@ -16,6 +16,7 @@ import java.util.stream.Collectors;
|
||||
import org.jboss.fuse.mvnd.client.Client;
|
||||
import org.jboss.fuse.mvnd.client.ClientLayout;
|
||||
import org.jboss.fuse.mvnd.client.ClientOutput;
|
||||
import org.jboss.fuse.mvnd.client.Environment;
|
||||
import org.jboss.fuse.mvnd.client.ExecutionResult;
|
||||
|
||||
/**
|
||||
@@ -43,14 +44,23 @@ public class NativeTestClient implements Client {
|
||||
final List<String> cmd = new ArrayList<String>(args.size() + 1);
|
||||
cmd.add(mvndNativeExecutablePath.toString());
|
||||
args.stream().forEach(cmd::add);
|
||||
cmd.add("-Dmaven.repo.local=" + layout.getLocalMavenRepository().toString());
|
||||
ProcessBuilder builder = new ProcessBuilder(cmd.toArray(new String[0]))
|
||||
if (!Environment.MVND_PROPERTIES_PATH.hasCommandLineProperty(args)) {
|
||||
cmd.add(Environment.MVND_PROPERTIES_PATH.asCommandLineProperty(layout.getMvndPropertiesPath().toString()));
|
||||
}
|
||||
if (!Environment.MAVEN_REPO_LOCAL.hasCommandLineProperty(args)) {
|
||||
cmd.add(Environment.MAVEN_REPO_LOCAL.asCommandLineProperty(layout.getLocalMavenRepository().toString()));
|
||||
}
|
||||
final ProcessBuilder builder = new ProcessBuilder(cmd.toArray(new String[0]))
|
||||
.directory(layout.userDir().toFile()) //
|
||||
.redirectErrorStream(false);
|
||||
.redirectErrorStream(true);
|
||||
|
||||
Map<String, String> env = builder.environment();
|
||||
final Map<String, String> env = builder.environment();
|
||||
if (!Environment.MAVEN_HOME.hasCommandLineProperty(args)) {
|
||||
env.put("MAVEN_HOME", System.getProperty("mvnd.home"));
|
||||
}
|
||||
if (!Environment.JAVA_HOME.hasCommandLineProperty(args)) {
|
||||
env.put("JAVA_HOME", System.getProperty("java.home"));
|
||||
}
|
||||
final String cmdString = cmd.stream().collect(Collectors.joining(" "));
|
||||
output.accept("Executing " + cmdString);
|
||||
try (CommandProcess process = new CommandProcess(builder.start(), cmd, output)) {
|
||||
@@ -64,15 +74,16 @@ public class NativeTestClient implements Client {
|
||||
|
||||
private final int exitCode;
|
||||
private final List<String> args;
|
||||
private final List<String> log;
|
||||
|
||||
public Result(List<String> args, int exitCode) {
|
||||
public Result(List<String> args, int exitCode, List<String> log) {
|
||||
super();
|
||||
this.args = new ArrayList<>(args);
|
||||
this.exitCode = exitCode;
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
StringBuilder appendCommand(StringBuilder sb) {
|
||||
sb.append("mvnd");
|
||||
for (String arg : args) {
|
||||
sb.append(" \"").append(arg).append('"');
|
||||
}
|
||||
@@ -90,7 +101,16 @@ public class NativeTestClient implements Client {
|
||||
|
||||
public Result assertSuccess() {
|
||||
if (exitCode != 0) {
|
||||
throw new AssertionError(appendCommand(new StringBuilder("mvnd returned ").append(exitCode).append(": ")));
|
||||
final StringBuilder sb = appendCommand(new StringBuilder("mvnd returned ").append(exitCode));
|
||||
if (exitCode == TIMEOUT_EXIT_CODE) {
|
||||
sb.append(" (timeout)");
|
||||
}
|
||||
sb.append("--- stderr+stdout start ---");
|
||||
synchronized (log) {
|
||||
log.stream().forEach(s -> sb.append('\n').append(s));
|
||||
}
|
||||
sb.append("--- stderr+stdout end ---");
|
||||
throw new AssertionError(sb);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -114,13 +134,19 @@ public class NativeTestClient implements Client {
|
||||
private final Process process;
|
||||
private final Thread shutDownHook;
|
||||
private final StreamGobbler stdOut;
|
||||
private List<String> args;
|
||||
private final List<String> args;
|
||||
private final List<String> log = new ArrayList<>();
|
||||
|
||||
public CommandProcess(Process process, List<String> args, Consumer<String> outputConsumer) {
|
||||
super();
|
||||
this.process = process;
|
||||
this.args = args;
|
||||
this.stdOut = new StreamGobbler(process.getInputStream(), outputConsumer);
|
||||
final Consumer<String> loggingConsumer = s -> {
|
||||
synchronized (log) {
|
||||
log.add(s);
|
||||
}
|
||||
};
|
||||
this.stdOut = new StreamGobbler(process.getInputStream(), loggingConsumer.andThen(outputConsumer));
|
||||
stdOut.start();
|
||||
|
||||
this.shutDownHook = new Thread(new Runnable() {
|
||||
@@ -148,7 +174,8 @@ public class NativeTestClient implements Client {
|
||||
Runtime.getRuntime().removeShutdownHook(shutDownHook);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return new Result(args, timeouted ? TIMEOUT_EXIT_CODE : process.exitValue());
|
||||
final int exitCode = timeouted ? TIMEOUT_EXIT_CODE : process.exitValue();
|
||||
return new Result(args, exitCode, log);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,21 @@
|
||||
package org.jboss.fuse.mvnd.junit;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.jboss.fuse.mvnd.client.ClientLayout;
|
||||
|
||||
public class TestLayout extends ClientLayout {
|
||||
private final Path testDir;
|
||||
|
||||
public TestLayout(Path testDir, Path mvndPropertiesPath, Path mavenHome, Path userDir, Path multiModuleProjectDirectory,
|
||||
Path javaHome,
|
||||
Path localMavenRepository, Path settings) {
|
||||
super(mvndPropertiesPath, mavenHome, userDir, multiModuleProjectDirectory, javaHome, localMavenRepository, settings);
|
||||
this.testDir = testDir;
|
||||
}
|
||||
|
||||
public Path getTestDir() {
|
||||
return testDir;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user