Properly test the --install feature

This commit is contained in:
Peter Palaga
2020-06-17 12:37:22 +02:00
parent 3c23c154d7
commit aeb13b7fcf
18 changed files with 774 additions and 197 deletions

View File

@@ -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();
}

View File

@@ -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) {

View 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());
}
}
}

View File

@@ -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)) {

View File

@@ -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

View File

@@ -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")),

View File

@@ -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;
}
}

View File

@@ -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);
// }
}

View File

@@ -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;

View File

@@ -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();
}
}
}

View File

@@ -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();

View File

@@ -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>

View File

@@ -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();
}
}

View File

@@ -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 {
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;
}
}