mirror of
https://github.com/apache/maven-mvnd.git
synced 2025-09-28 08:47:29 +00:00
Refactor usage of properties in the client / daemon, fixes #188
This commit is contained in:

committed by
Peter Palaga

parent
759ca73c2d
commit
4030f8b5a4
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jboss.fuse.mvnd.client;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Supplier;
|
||||
import org.jboss.fuse.mvnd.common.BuildProperties;
|
||||
import org.jboss.fuse.mvnd.common.Environment;
|
||||
import org.jboss.fuse.mvnd.common.Environment.ValueSource;
|
||||
import org.jboss.fuse.mvnd.common.Layout;
|
||||
|
||||
/**
|
||||
* Local paths relevant for the {@link DefaultClient}.
|
||||
*/
|
||||
public class ClientLayout extends Layout {
|
||||
|
||||
private static ClientLayout ENV_INSTANCE;
|
||||
|
||||
private final Path localMavenRepository;
|
||||
private final Path settings;
|
||||
private final Path javaHome;
|
||||
private final Path logbackConfigurationPath;
|
||||
private final int idleTimeoutMs;
|
||||
private final int keepAliveMs;
|
||||
private final int maxLostKeepAlive;
|
||||
private final String threads;
|
||||
|
||||
public static ClientLayout getEnvInstance() {
|
||||
if (ENV_INSTANCE == null) {
|
||||
final Path mvndPropertiesPath = Environment.findMvndPropertiesPath();
|
||||
final Supplier<Properties> mvndProperties = lazyMvndProperties(mvndPropertiesPath);
|
||||
final Path pwd = Paths.get(".").toAbsolutePath().normalize();
|
||||
final Path mvndHome = Environment.MVND_HOME
|
||||
.fromValueSource(new ValueSource(
|
||||
description -> description.append("path relative to the mvnd executable"),
|
||||
() -> {
|
||||
Optional<String> cmd = ProcessHandle.current().info().command();
|
||||
if (Environment.isNative() && cmd.isPresent()) {
|
||||
final Path mvndH = Paths.get(cmd.get()).getParent().getParent();
|
||||
if (mvndH != null) {
|
||||
final Path mvndDaemonLib = mvndH
|
||||
.resolve("mvn/lib/ext/mvnd-daemon-" + BuildProperties.getInstance().getVersion()
|
||||
+ ".jar");
|
||||
if (Files.exists(mvndDaemonLib)) {
|
||||
return mvndH.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}))
|
||||
.orSystemProperty()
|
||||
.orEnvironmentVariable()
|
||||
.orLocalProperty(mvndProperties, mvndPropertiesPath)
|
||||
.orFail()
|
||||
.asPath()
|
||||
.toAbsolutePath().normalize();
|
||||
final int idleTimeoutMs = Environment.DAEMON_IDLE_TIMEOUT_MS
|
||||
.systemProperty()
|
||||
.orLocalProperty(mvndProperties, mvndPropertiesPath)
|
||||
.orDefault(() -> Integer.toString(Environment.DEFAULT_IDLE_TIMEOUT))
|
||||
.asInt();
|
||||
final int keepAliveMs = Environment.DAEMON_KEEP_ALIVE_MS
|
||||
.systemProperty()
|
||||
.orLocalProperty(mvndProperties, mvndPropertiesPath)
|
||||
.orDefault(() -> Integer.toString(Environment.DEFAULT_KEEP_ALIVE))
|
||||
.asInt();
|
||||
final int maxLostKeepAlive = Environment.DAEMON_MAX_LOST_KEEP_ALIVE
|
||||
.systemProperty()
|
||||
.orLocalProperty(mvndProperties, mvndPropertiesPath)
|
||||
.orDefault(() -> Integer.toString(Environment.DEFAULT_MAX_LOST_KEEP_ALIVE))
|
||||
.asInt();
|
||||
final String threads = Environment.MVND_THREADS
|
||||
.systemProperty()
|
||||
.orLocalProperty(mvndProperties, mvndPropertiesPath)
|
||||
.orDefault(() -> {
|
||||
final int minThreads = Environment.MVND_MIN_THREADS
|
||||
.systemProperty()
|
||||
.orLocalProperty(mvndProperties, mvndPropertiesPath)
|
||||
.orDefault(() -> Integer.toString(Environment.DEFAULT_MIN_THREADS))
|
||||
.asInt();
|
||||
return String
|
||||
.valueOf(Math.max(Runtime.getRuntime().availableProcessors() - 1, minThreads));
|
||||
})
|
||||
.asString();
|
||||
|
||||
ENV_INSTANCE = new ClientLayout(
|
||||
mvndPropertiesPath,
|
||||
mvndHome,
|
||||
pwd,
|
||||
Environment.findMultiModuleProjectDirectory(pwd),
|
||||
Environment.findJavaHome(mvndProperties, mvndPropertiesPath),
|
||||
findLocalRepo(),
|
||||
null,
|
||||
Environment.findLogbackConfigurationPath(mvndProperties, mvndPropertiesPath, mvndHome),
|
||||
idleTimeoutMs, keepAliveMs, maxLostKeepAlive, threads);
|
||||
}
|
||||
return ENV_INSTANCE;
|
||||
}
|
||||
|
||||
public ClientLayout(Path mvndPropertiesPath, Path mavenHome, Path userDir, Path multiModuleProjectDirectory, Path javaHome,
|
||||
Path localMavenRepository, Path settings, Path logbackConfigurationPath, int idleTimeoutMs, int keepAliveMs,
|
||||
int maxLostKeepAlive, String threads) {
|
||||
super(mvndPropertiesPath, mavenHome, userDir, multiModuleProjectDirectory);
|
||||
this.localMavenRepository = localMavenRepository;
|
||||
this.settings = settings;
|
||||
this.javaHome = Objects.requireNonNull(javaHome, "javaHome");
|
||||
this.logbackConfigurationPath = logbackConfigurationPath;
|
||||
this.idleTimeoutMs = idleTimeoutMs;
|
||||
this.keepAliveMs = keepAliveMs;
|
||||
this.maxLostKeepAlive = maxLostKeepAlive;
|
||||
this.threads = threads;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newUserDir where to change the current directory to
|
||||
* @return a new {@link ClientLayout} with {@code userDir} set to the given {@code newUserDir}
|
||||
*/
|
||||
public ClientLayout cd(Path newUserDir) {
|
||||
return new ClientLayout(mvndPropertiesPath, mavenHome, newUserDir, multiModuleProjectDirectory, javaHome,
|
||||
localMavenRepository, settings, logbackConfigurationPath, idleTimeoutMs, keepAliveMs, maxLostKeepAlive,
|
||||
threads);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return absolute normalized path to {@code settings.xml} or {@code null}
|
||||
*/
|
||||
public Path getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
public Path javaHome() {
|
||||
return javaHome;
|
||||
}
|
||||
|
||||
public Path getLogbackConfigurationPath() {
|
||||
return logbackConfigurationPath;
|
||||
}
|
||||
|
||||
public int getIdleTimeoutMs() {
|
||||
return idleTimeoutMs;
|
||||
}
|
||||
|
||||
public int getKeepAliveMs() {
|
||||
return keepAliveMs;
|
||||
}
|
||||
|
||||
public int getMaxLostKeepAlive() {
|
||||
return maxLostKeepAlive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of threads (same syntax as Maven's {@code -T}/{@code --threads} option) to pass to the daemon
|
||||
* unless the user passes his own `-T` or `--threads`.
|
||||
*/
|
||||
public String getThreads() {
|
||||
return threads;
|
||||
}
|
||||
|
||||
static Path findLocalRepo() {
|
||||
return Environment.MAVEN_REPO_LOCAL.systemProperty().asPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClientLayout [localMavenRepository=" + localMavenRepository + ", settings=" + settings + ", javaHome="
|
||||
+ javaHome + ", mavenHome()=" + mavenHome() + ", userDir()=" + userDir() + ", registry()=" + registry()
|
||||
+ ", multiModuleProjectDirectory()=" + multiModuleProjectDirectory() + "]";
|
||||
}
|
||||
|
||||
}
|
@@ -25,12 +25,10 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import org.jboss.fuse.mvnd.common.DaemonConnection;
|
||||
import org.jboss.fuse.mvnd.common.DaemonDiagnostics;
|
||||
import org.jboss.fuse.mvnd.common.DaemonException;
|
||||
import org.jboss.fuse.mvnd.common.DaemonException.ConnectException;
|
||||
import org.jboss.fuse.mvnd.common.DaemonException.StaleAddressException;
|
||||
import org.jboss.fuse.mvnd.common.DaemonInfo;
|
||||
import org.jboss.fuse.mvnd.common.Layout;
|
||||
import org.jboss.fuse.mvnd.common.Message;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -54,10 +52,10 @@ public class DaemonClientConnection implements Closeable {
|
||||
private final Thread receiver;
|
||||
private final AtomicBoolean running = new AtomicBoolean(true);
|
||||
private final AtomicReference<Exception> exception = new AtomicReference<>();
|
||||
private final Layout layout;
|
||||
private final DaemonParameters parameters;
|
||||
|
||||
public DaemonClientConnection(DaemonConnection connection, DaemonInfo daemon,
|
||||
StaleAddressDetector staleAddressDetector, boolean newDaemon, int maxKeepAliveMs, Layout layout) {
|
||||
StaleAddressDetector staleAddressDetector, boolean newDaemon, int maxKeepAliveMs, DaemonParameters parameters) {
|
||||
this.connection = connection;
|
||||
this.daemon = daemon;
|
||||
this.staleAddressDetector = staleAddressDetector;
|
||||
@@ -65,7 +63,7 @@ public class DaemonClientConnection implements Closeable {
|
||||
this.maxKeepAliveMs = maxKeepAliveMs;
|
||||
this.receiver = new Thread(this::doReceive);
|
||||
this.receiver.start();
|
||||
this.layout = layout;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public DaemonInfo getDaemon() {
|
||||
@@ -105,7 +103,7 @@ public class DaemonClientConnection implements Closeable {
|
||||
+ "ms, daemon may have crashed. You may want to check its status using mvnd --status");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
DaemonDiagnostics diag = new DaemonDiagnostics(daemon.getUid(), layout);
|
||||
DaemonDiagnostics diag = new DaemonDiagnostics(daemon.getUid(), parameters);
|
||||
LOG.debug("Problem receiving message to the daemon. Performing 'on failure' operation...");
|
||||
if (!hasReceived && newDaemon) {
|
||||
throw new ConnectException("Could not receive a message from the daemon.\n" + diag.describe(), e);
|
||||
|
@@ -34,7 +34,6 @@ import org.jboss.fuse.mvnd.common.BuildProperties;
|
||||
import org.jboss.fuse.mvnd.common.DaemonCompatibilitySpec;
|
||||
import org.jboss.fuse.mvnd.common.DaemonCompatibilitySpec.Result;
|
||||
import org.jboss.fuse.mvnd.common.DaemonConnection;
|
||||
import org.jboss.fuse.mvnd.common.DaemonDiagnostics;
|
||||
import org.jboss.fuse.mvnd.common.DaemonException;
|
||||
import org.jboss.fuse.mvnd.common.DaemonInfo;
|
||||
import org.jboss.fuse.mvnd.common.DaemonRegistry;
|
||||
@@ -63,13 +62,11 @@ public class DaemonConnector {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DaemonConnector.class);
|
||||
|
||||
private final DaemonRegistry registry;
|
||||
private final ClientLayout layout;
|
||||
private final BuildProperties buildProperties;
|
||||
private final DaemonParameters parameters;
|
||||
|
||||
public DaemonConnector(ClientLayout layout, DaemonRegistry registry, BuildProperties buildProperties) {
|
||||
this.layout = layout;
|
||||
public DaemonConnector(DaemonParameters parameters, DaemonRegistry registry) {
|
||||
this.parameters = parameters;
|
||||
this.registry = registry;
|
||||
this.buildProperties = buildProperties;
|
||||
}
|
||||
|
||||
public DaemonClientConnection maybeConnect(DaemonCompatibilitySpec constraint) {
|
||||
@@ -85,7 +82,9 @@ public class DaemonConnector {
|
||||
return null;
|
||||
}
|
||||
|
||||
public DaemonClientConnection connect(DaemonCompatibilitySpec constraint, ClientOutput output) {
|
||||
public DaemonClientConnection connect(ClientOutput output) {
|
||||
final DaemonCompatibilitySpec constraint = new DaemonCompatibilitySpec(
|
||||
parameters.javaHome(), parameters.getDaemonOpts());
|
||||
output.buildStatus("Looking up daemon...");
|
||||
Map<Boolean, List<DaemonInfo>> idleBusy = registry.getAll().stream()
|
||||
.collect(Collectors.groupingBy(di -> di.getState() == DaemonState.Idle));
|
||||
@@ -107,7 +106,7 @@ public class DaemonConnector {
|
||||
// No compatible daemons available - start a new daemon
|
||||
String message = handleStopEvents(idleDaemons, busyDaemons);
|
||||
output.buildStatus(message);
|
||||
return startDaemon(constraint);
|
||||
return startDaemon();
|
||||
}
|
||||
|
||||
private String handleStopEvents(Collection<DaemonInfo> idleDaemons, Collection<DaemonInfo> busyDaemons) {
|
||||
@@ -221,9 +220,9 @@ public class DaemonConnector {
|
||||
return null;
|
||||
}
|
||||
|
||||
public DaemonClientConnection startDaemon(DaemonCompatibilitySpec constraint) {
|
||||
public DaemonClientConnection startDaemon() {
|
||||
final String daemon = UUID.randomUUID().toString();
|
||||
final Process process = startDaemon(daemon, constraint.getOptions());
|
||||
final Process process = startDaemon(daemon);
|
||||
LOGGER.debug("Started Maven daemon {}", daemon);
|
||||
long start = System.currentTimeMillis();
|
||||
do {
|
||||
@@ -237,41 +236,49 @@ public class DaemonConnector {
|
||||
throw new DaemonException.InterruptedException(e);
|
||||
}
|
||||
} while (process.isAlive() && System.currentTimeMillis() - start < DEFAULT_CONNECT_TIMEOUT);
|
||||
DaemonDiagnostics diag = new DaemonDiagnostics(daemon, layout);
|
||||
DaemonDiagnostics diag = new DaemonDiagnostics(daemon, parameters);
|
||||
throw new DaemonException.ConnectException("Timeout waiting to connect to the Maven daemon.\n" + diag.describe());
|
||||
}
|
||||
|
||||
private Process startDaemon(String uid, List<String> opts) {
|
||||
final Path mavenHome = layout.mavenHome();
|
||||
final Path workingDir = layout.userDir();
|
||||
private Process startDaemon(String uid) {
|
||||
final Path mvndHome = parameters.mvndHome();
|
||||
final Path workingDir = parameters.userDir();
|
||||
String command = "";
|
||||
try {
|
||||
final String classpath = mavenHome.resolve("mvn/lib/ext/mvnd-common-" + buildProperties.getVersion() + ".jar")
|
||||
.toString();
|
||||
final String java = Os.current().isUnixLike() ? "bin/java" : "bin\\java.exe";
|
||||
List<String> args = new ArrayList<>();
|
||||
args.add(layout.javaHome().resolve(java).toString());
|
||||
// executable
|
||||
final String java = Os.current().isUnixLike() ? "bin/java" : "bin\\java.exe";
|
||||
args.add(parameters.javaHome().resolve(java).toString());
|
||||
// classpath
|
||||
args.add("-classpath");
|
||||
final String mvndCommonPath = "mvn/lib/ext/mvnd-common-" + BuildProperties.getInstance().getVersion() + ".jar";
|
||||
final String classpath = mvndHome.resolve(mvndCommonPath).toString();
|
||||
args.add(classpath);
|
||||
if (Environment.DAEMON_DEBUG.systemProperty().orDefault(() -> "false").asBoolean()) {
|
||||
// debug options
|
||||
if (parameters.property(Environment.DAEMON_DEBUG).asBoolean()) {
|
||||
args.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8000");
|
||||
}
|
||||
args.add("-Dmvnd.home=" + mavenHome);
|
||||
args.add("-Dmvnd.java.home=" + layout.javaHome().toString());
|
||||
args.add("-Dlogback.configurationFile=" + layout.getLogbackConfigurationPath());
|
||||
args.add("-Ddaemon.uid=" + uid);
|
||||
if (Boolean.getBoolean(Environment.DEBUG_ENVIRONMENT_PROP)) {
|
||||
args.add("-D" + Environment.DEBUG_ENVIRONMENT_PROP + "=true");
|
||||
// memory
|
||||
String minHeapSize = parameters.minHeapSize();
|
||||
if (minHeapSize != null) {
|
||||
args.add("-Xms" + minHeapSize);
|
||||
}
|
||||
args.add("-Xmx4g");
|
||||
args.add(Environment.DAEMON_IDLE_TIMEOUT_MS.asCommandLineProperty(Integer.toString(layout.getIdleTimeoutMs())));
|
||||
args.add(Environment.DAEMON_KEEP_ALIVE_MS.asCommandLineProperty(Integer.toString(layout.getKeepAliveMs())));
|
||||
args.addAll(opts);
|
||||
String maxHeapSize = parameters.maxHeapSize();
|
||||
if (maxHeapSize != null) {
|
||||
args.add("-Xmx" + maxHeapSize);
|
||||
}
|
||||
|
||||
args.add(Environment.MVND_HOME.asCommandLineProperty(mvndHome.toString()));
|
||||
args.add(Environment.LOGBACK_CONFIGURATION_FILE
|
||||
.asCommandLineProperty(parameters.logbackConfigurationPath().toString()));
|
||||
args.add(Environment.DAEMON_UID.asCommandLineProperty(uid));
|
||||
args.add(Environment.DAEMON_REGISTRY.asCommandLineProperty(parameters.registry().toString()));
|
||||
args.addAll(parameters.getDaemonCommandLineProperties());
|
||||
args.add(MavenDaemon.class.getName());
|
||||
command = String.join(" ", args);
|
||||
|
||||
LOGGER.debug("Starting daemon process: uid = {}, workingDir = {}, daemonArgs: {}", uid, workingDir, command);
|
||||
ProcessBuilder.Redirect redirect = ProcessBuilder.Redirect.appendTo(layout.daemonOutLog(uid).toFile());
|
||||
ProcessBuilder.Redirect redirect = ProcessBuilder.Redirect.appendTo(parameters.daemonOutLog(uid).toFile());
|
||||
Process process = new ProcessBuilder()
|
||||
.directory(workingDir.toFile())
|
||||
.command(args)
|
||||
@@ -296,7 +303,7 @@ public class DaemonConnector {
|
||||
try {
|
||||
return connectToDaemon(daemonInfo, new CleanupOnStaleAddress(daemonInfo), newDaemon);
|
||||
} catch (DaemonException.ConnectException e) {
|
||||
DaemonDiagnostics diag = new DaemonDiagnostics(daemon, layout);
|
||||
DaemonDiagnostics diag = new DaemonDiagnostics(daemon, parameters);
|
||||
throw new DaemonException.ConnectException("Could not connect to the Maven daemon.\n" + diag.describe(), e);
|
||||
}
|
||||
}
|
||||
@@ -308,9 +315,9 @@ public class DaemonConnector {
|
||||
throws DaemonException.ConnectException {
|
||||
LOGGER.debug("Connecting to Daemon");
|
||||
try {
|
||||
int maxKeepAliveMs = layout.getKeepAliveMs() * layout.getMaxLostKeepAlive();
|
||||
int maxKeepAliveMs = parameters.keepAliveMs() * parameters.maxLostKeepAlive();
|
||||
DaemonConnection connection = connect(daemon.getAddress());
|
||||
return new DaemonClientConnection(connection, daemon, staleAddressDetector, newDaemon, maxKeepAliveMs, layout);
|
||||
return new DaemonClientConnection(connection, daemon, staleAddressDetector, newDaemon, maxKeepAliveMs, parameters);
|
||||
} catch (DaemonException.ConnectException e) {
|
||||
staleAddressDetector.maybeStaleAddress(e);
|
||||
throw e;
|
||||
|
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jboss.fuse.mvnd.client;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collector;
|
||||
|
||||
/**
|
||||
* File origin:
|
||||
* https://github.com/gradle/gradle/blob/v5.6.2/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/diagnostics/DaemonDiagnostics.java
|
||||
*/
|
||||
public class DaemonDiagnostics {
|
||||
|
||||
private final static int TAIL_SIZE = 20;
|
||||
|
||||
private final String uid;
|
||||
private final DaemonParameters parameters;
|
||||
|
||||
public DaemonDiagnostics(String uid, DaemonParameters parameters) {
|
||||
this.uid = uid;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{"
|
||||
+ "uid=" + uid
|
||||
+ ", parameters=" + parameters
|
||||
+ '}';
|
||||
}
|
||||
|
||||
public String describe() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Daemon uid: ").append(uid).append("\n");
|
||||
tail(sb, "log file", parameters.daemonLog(uid));
|
||||
tail(sb, "output", parameters.daemonOutLog(uid));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static void tail(StringBuilder sb, String name, Path log) {
|
||||
try {
|
||||
String tail = tail(log);
|
||||
sb.append(" ").append(name).append(": ").append(log).append("\n");
|
||||
sb.append("----- Last " + TAIL_SIZE + " lines from daemon ").append(name).append(" - ").append(log)
|
||||
.append(" -----\n");
|
||||
sb.append(tail);
|
||||
sb.append("----- End of the daemon ").append(name).append(" -----\n");
|
||||
} catch (NoSuchFileException e) {
|
||||
sb.append(" no ").append(name).append(" at: ").append(log).append("\n");
|
||||
} catch (IOException e) {
|
||||
sb.append(" unable to read from the daemon ").append(name).append(": ").append(log).append(", because of: ")
|
||||
.append(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path to read from tail
|
||||
* @return tail content
|
||||
* @throws IOException when reading failed
|
||||
*/
|
||||
static String tail(Path path) throws IOException {
|
||||
try (BufferedReader r = Files.newBufferedReader(path)) {
|
||||
return String.join("\n", r.lines().collect(lastN(TAIL_SIZE))) + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
static <T> Collector<T, ?, List<T>> lastN(int n) {
|
||||
return Collector.<T, Deque<T>, List<T>> of(ArrayDeque::new, (acc, t) -> {
|
||||
if (acc.size() == n)
|
||||
acc.pollFirst();
|
||||
acc.add(t);
|
||||
}, (acc1, acc2) -> {
|
||||
while (acc2.size() < n && !acc1.isEmpty()) {
|
||||
acc2.addFirst(acc1.pollLast());
|
||||
}
|
||||
return acc2;
|
||||
}, ArrayList::new);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,522 @@
|
||||
/*
|
||||
* Copyright 2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jboss.fuse.mvnd.client;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.maven.cli.internal.extension.model.CoreExtension;
|
||||
import org.apache.maven.cli.internal.extension.model.io.xpp3.CoreExtensionsXpp3Reader;
|
||||
import org.codehaus.plexus.util.StringUtils;
|
||||
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
|
||||
import org.jboss.fuse.mvnd.common.BuildProperties;
|
||||
import org.jboss.fuse.mvnd.common.Environment;
|
||||
import org.jboss.fuse.mvnd.common.Os;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Hold all daemon configuration
|
||||
*/
|
||||
public class DaemonParameters {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DaemonParameters.class);
|
||||
private static final String EXT_CLASS_PATH = "maven.ext.class.path";
|
||||
private static final String EXTENSIONS_FILENAME = ".mvn/extensions.xml";
|
||||
|
||||
protected final Map<Path, Properties> mvndProperties = new ConcurrentHashMap<>();
|
||||
protected final Function<Path, Properties> provider = path -> mvndProperties.computeIfAbsent(path,
|
||||
p -> loadProperties(path));
|
||||
protected final Properties properties;
|
||||
|
||||
public DaemonParameters(Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public List<String> getDaemonOpts() {
|
||||
return Arrays.stream(Environment.values())
|
||||
.filter(Environment::isDiscriminating)
|
||||
.map(v -> v.asDaemonOpt(property(v).orFail().asString()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<String> getDaemonCommandLineProperties() {
|
||||
return Arrays.stream(Environment.values())
|
||||
.filter(Environment::isDiscriminating)
|
||||
.map(v -> v.asCommandLineProperty(property(v).orFail().asString()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Path mvndHome() {
|
||||
return value(Environment.MVND_HOME)
|
||||
.or(new ValueSource(
|
||||
description -> description.append("path relative to the mvnd executable"),
|
||||
this::mvndHomeFromExecutable))
|
||||
.orSystemProperty()
|
||||
.orEnvironmentVariable()
|
||||
.orLocalProperty(provider, suppliedPropertiesPath())
|
||||
.orLocalProperty(provider, localPropertiesPath())
|
||||
.orLocalProperty(provider, userPropertiesPath())
|
||||
.orFail()
|
||||
.asPath()
|
||||
.toAbsolutePath().normalize();
|
||||
}
|
||||
|
||||
private String mvndHomeFromExecutable() {
|
||||
Optional<String> cmd = ProcessHandle.current().info().command();
|
||||
if (Environment.isNative() && cmd.isPresent()) {
|
||||
final Path mvndH = Paths.get(cmd.get()).getParent().getParent();
|
||||
if (mvndH != null) {
|
||||
final Path mvndDaemonLib = mvndH
|
||||
.resolve("mvn/lib/ext/mvnd-daemon-" + BuildProperties.getInstance().getVersion() + ".jar");
|
||||
if (Files.exists(mvndDaemonLib)) {
|
||||
return mvndH.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Path javaHome() {
|
||||
final Path result = value(Environment.JAVA_HOME)
|
||||
.orEnvironmentVariable()
|
||||
.orLocalProperty(provider, suppliedPropertiesPath())
|
||||
.orLocalProperty(provider, localPropertiesPath())
|
||||
.orLocalProperty(provider, userPropertiesPath())
|
||||
.orLocalProperty(provider, globalPropertiesPath())
|
||||
.orSystemProperty()
|
||||
.orFail()
|
||||
.asPath();
|
||||
try {
|
||||
return result.toRealPath();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not get a real path from path " + result);
|
||||
}
|
||||
}
|
||||
|
||||
public Path userDir() {
|
||||
return value(Environment.USER_DIR)
|
||||
.orSystemProperty()
|
||||
.orFail()
|
||||
.asPath();
|
||||
}
|
||||
|
||||
public Path userHome() {
|
||||
return value(Environment.USER_HOME)
|
||||
.orSystemProperty()
|
||||
.orFail()
|
||||
.asPath();
|
||||
}
|
||||
|
||||
public Path suppliedPropertiesPath() {
|
||||
return value(Environment.MVND_PROPERTIES_PATH)
|
||||
.orEnvironmentVariable()
|
||||
.orSystemProperty()
|
||||
.asPath();
|
||||
}
|
||||
|
||||
public Path localPropertiesPath() {
|
||||
return multiModuleProjectDirectory().resolve(".mvn/mvnd.properties");
|
||||
}
|
||||
|
||||
public Path userPropertiesPath() {
|
||||
return userHome().resolve(".m2/mvnd.properties");
|
||||
}
|
||||
|
||||
public Path globalPropertiesPath() {
|
||||
return mvndHome().resolve("conf/mvnd.properties");
|
||||
}
|
||||
|
||||
public Path daemonStorage() {
|
||||
return value(Environment.MVND_DAEMON_STORAGE)
|
||||
.orSystemProperty()
|
||||
.orDefault(() -> userHome().resolve(".mvnd/v" + BuildProperties.getInstance().getVersion()).toString())
|
||||
.asPath();
|
||||
}
|
||||
|
||||
public Path registry() {
|
||||
return daemonStorage().resolve("registry.bin");
|
||||
}
|
||||
|
||||
public Path daemonLog(String daemon) {
|
||||
return daemonStorage().resolve("daemon-" + daemon + ".log");
|
||||
}
|
||||
|
||||
public Path daemonOutLog(String daemon) {
|
||||
return daemonStorage().resolve("daemon-" + daemon + ".out.log");
|
||||
}
|
||||
|
||||
public Path multiModuleProjectDirectory() {
|
||||
return value(Environment.MAVEN_MULTIMODULE_PROJECT_DIRECTORY)
|
||||
.orSystemProperty()
|
||||
.orDefault(() -> findDefaultMultimoduleProjectDirectory(userDir()))
|
||||
.asPath()
|
||||
.toAbsolutePath().normalize();
|
||||
}
|
||||
|
||||
public Path logbackConfigurationPath() {
|
||||
return property(Environment.LOGBACK_CONFIGURATION_FILE)
|
||||
.orDefault(() -> mvndHome().resolve("mvn/conf/logging/logback.xml").toString())
|
||||
.orFail()
|
||||
.asPath();
|
||||
}
|
||||
|
||||
public String minHeapSize() {
|
||||
return property(Environment.DAEMON_MIN_HEAP_SIZE).asString();
|
||||
}
|
||||
|
||||
public String maxHeapSize() {
|
||||
return property(Environment.DAEMON_MAX_HEAP_SIZE).asString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of threads (same syntax as Maven's {@code -T}/{@code --threads} option) to pass to the daemon
|
||||
* unless the user passes his own `-T` or `--threads`.
|
||||
*/
|
||||
public String threads() {
|
||||
return property(Environment.MVND_THREADS)
|
||||
.orDefault(() -> String.valueOf(property(Environment.MVND_MIN_THREADS)
|
||||
.asInt(m -> Math.max(Runtime.getRuntime().availableProcessors() - 1, m))))
|
||||
.orFail()
|
||||
.asString();
|
||||
}
|
||||
|
||||
public String builder() {
|
||||
return property(Environment.MVND_BUILDER).orFail().asString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return absolute normalized path to {@code settings.xml} or {@code null}
|
||||
*/
|
||||
public Path settings() {
|
||||
return property(Environment.MAVEN_SETTINGS).asPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return absolute normalized path to local Maven repository or {@code null} if the server is supposed to use the
|
||||
* default
|
||||
*/
|
||||
public Path mavenRepoLocal() {
|
||||
return property(Environment.MAVEN_REPO_LOCAL).asPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newUserDir where to change the current directory to
|
||||
* @return a new {@link DaemonParameters} with {@code userDir} set to the given {@code newUserDir}
|
||||
*/
|
||||
public DaemonParameters cd(Path newUserDir) {
|
||||
Properties properties = new Properties();
|
||||
properties.putAll(this.properties);
|
||||
properties.put(Environment.USER_DIR.getProperty(), newUserDir.toString());
|
||||
return new DaemonParameters(properties);
|
||||
}
|
||||
|
||||
public int keepAliveMs() {
|
||||
return property(Environment.DAEMON_KEEP_ALIVE_MS).orFail().asInt();
|
||||
}
|
||||
|
||||
public int maxLostKeepAlive() {
|
||||
return property(Environment.DAEMON_MAX_LOST_KEEP_ALIVE).orFail().asInt();
|
||||
}
|
||||
|
||||
public static String findDefaultMultimoduleProjectDirectory(Path pwd) {
|
||||
Path dir = pwd;
|
||||
do {
|
||||
if (Files.isDirectory(dir.resolve(".mvn"))) {
|
||||
return dir.toString();
|
||||
}
|
||||
dir = dir.getParent();
|
||||
} while (dir != null);
|
||||
/*
|
||||
* Return pwd if .mvn directory was not found in the hierarchy.
|
||||
* Maven does the same thing in mvn shell script's find_maven_basedir()
|
||||
* and find_file_argument_basedir() routines
|
||||
*/
|
||||
return pwd.toString();
|
||||
}
|
||||
|
||||
public EnvValue property(Environment env) {
|
||||
return value(env)
|
||||
.orSystemProperty()
|
||||
.orLocalProperty(provider, suppliedPropertiesPath())
|
||||
.orLocalProperty(provider, localPropertiesPath())
|
||||
.orLocalProperty(provider, userPropertiesPath())
|
||||
.orLocalProperty(provider, globalPropertiesPath())
|
||||
.orDefault(() -> defaultValue(env));
|
||||
}
|
||||
|
||||
protected EnvValue value(Environment env) {
|
||||
return new EnvValue(env, new ValueSource(
|
||||
description -> description.append("value: ").append(env.getProperty()),
|
||||
() -> properties.getProperty(env.getProperty())));
|
||||
}
|
||||
|
||||
public static EnvValue systemProperty(Environment env) {
|
||||
return new EnvValue(env, EnvValue.systemPropertySource(env));
|
||||
}
|
||||
|
||||
public static EnvValue environmentVariable(Environment env) {
|
||||
return new EnvValue(env, EnvValue.environmentVariableSource(env));
|
||||
}
|
||||
|
||||
public static EnvValue fromValueSource(Environment env, ValueSource valueSource) {
|
||||
return new EnvValue(env, valueSource);
|
||||
}
|
||||
|
||||
private String defaultValue(Environment env) {
|
||||
if (env == Environment.DAEMON_EXT_CLASSPATH) {
|
||||
List<String> cp = parseExtClasspath(userHome());
|
||||
return String.join(",", cp);
|
||||
} else if (env == Environment.DAEMON_CORE_EXTENSIONS) {
|
||||
try {
|
||||
List<String> extensions = readCoreExtensionsDescriptor(multiModuleProjectDirectory()).stream()
|
||||
.map(e -> e.getGroupId() + ":" + e.getArtifactId() + ":" + e.getVersion())
|
||||
.collect(Collectors.toList());
|
||||
return String.join(",", extensions);
|
||||
} catch (IOException | XmlPullParserException e) {
|
||||
throw new RuntimeException("Unable to parse core extensions", e);
|
||||
}
|
||||
} else {
|
||||
return env.getDef();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> parseExtClasspath(Path userDir) {
|
||||
String extClassPath = System.getProperty(EXT_CLASS_PATH);
|
||||
List<String> jars = new ArrayList<>();
|
||||
if (StringUtils.isNotEmpty(extClassPath)) {
|
||||
for (String jar : StringUtils.split(extClassPath, File.pathSeparator)) {
|
||||
Path path = userDir.resolve(jar).toAbsolutePath();
|
||||
jars.add(path.toString());
|
||||
}
|
||||
}
|
||||
return jars;
|
||||
}
|
||||
|
||||
private static List<CoreExtension> readCoreExtensionsDescriptor(Path multiModuleProjectDirectory)
|
||||
throws IOException, XmlPullParserException {
|
||||
if (multiModuleProjectDirectory == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Path extensionsFile = multiModuleProjectDirectory.resolve(EXTENSIONS_FILENAME);
|
||||
if (!Files.exists(extensionsFile)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
CoreExtensionsXpp3Reader parser = new CoreExtensionsXpp3Reader();
|
||||
try (InputStream is = Files.newInputStream(extensionsFile)) {
|
||||
return parser.read(is).getExtensions();
|
||||
}
|
||||
}
|
||||
|
||||
private static Properties loadProperties(Path path) {
|
||||
Properties result = new Properties();
|
||||
if (Files.exists(path)) {
|
||||
try (InputStream in = Files.newInputStream(path)) {
|
||||
result.load(in);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not read " + path);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
static Map<String, String> env = System.getenv();
|
||||
|
||||
private final Environment envKey;
|
||||
private final ValueSource valueSource;
|
||||
protected EnvValue previous;
|
||||
|
||||
public EnvValue(Environment envKey, ValueSource valueSource) {
|
||||
this.previous = null;
|
||||
this.envKey = envKey;
|
||||
this.valueSource = valueSource;
|
||||
}
|
||||
|
||||
public EnvValue(EnvValue previous, Environment envKey, ValueSource valueSource) {
|
||||
this.previous = previous;
|
||||
this.envKey = envKey;
|
||||
this.valueSource = valueSource;
|
||||
}
|
||||
|
||||
private static ValueSource systemPropertySource(Environment env) {
|
||||
String property = env.getProperty();
|
||||
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),
|
||||
() -> Environment.getProperty(property));
|
||||
}
|
||||
|
||||
private static ValueSource environmentVariableSource(Environment env) {
|
||||
String envVar = env.getEnvironmentVariable();
|
||||
if (envVar == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot use " + Environment.class.getName() + "." + env.name()
|
||||
+ " for getting an environment variable");
|
||||
}
|
||||
return new ValueSource(
|
||||
description -> description.append("environment variable ").append(envVar),
|
||||
() -> EnvValue.env.get(envVar));
|
||||
}
|
||||
|
||||
public EnvValue orSystemProperty() {
|
||||
return new EnvValue(this, envKey, systemPropertySource(envKey));
|
||||
}
|
||||
|
||||
public EnvValue orLocalProperty(Function<Path, Properties> provider, Path localPropertiesPath) {
|
||||
if (localPropertiesPath != null) {
|
||||
return new EnvValue(this, envKey, new ValueSource(
|
||||
description -> description.append("property ").append(envKey.getProperty()).append(" in ")
|
||||
.append(localPropertiesPath),
|
||||
() -> provider.apply(localPropertiesPath).getProperty(envKey.getProperty())));
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public EnvValue orEnvironmentVariable() {
|
||||
return new EnvValue(this, envKey, environmentVariableSource(envKey));
|
||||
}
|
||||
|
||||
public EnvValue or(ValueSource source) {
|
||||
return new EnvValue(this, envKey, source);
|
||||
}
|
||||
|
||||
public EnvValue orDefault() {
|
||||
return orDefault(envKey::getDef);
|
||||
}
|
||||
|
||||
public EnvValue orDefault(Supplier<String> defaultSupplier) {
|
||||
return new EnvValue(this, envKey,
|
||||
new ValueSource(sb -> sb.append("default: ").append(defaultSupplier.get()), defaultSupplier));
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
}
|
||||
final String result = valueSource.valueSupplier.get();
|
||||
if (result != null && LOG.isDebugEnabled()) {
|
||||
StringBuilder sb = new StringBuilder("Loaded environment value for key [")
|
||||
.append(envKey.name())
|
||||
.append("] from ");
|
||||
valueSource.descriptionFunction.apply(sb);
|
||||
sb.append(": [")
|
||||
.append(result)
|
||||
.append(']');
|
||||
LOG.debug(sb.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public String asString() {
|
||||
return get();
|
||||
}
|
||||
|
||||
public Optional<String> asOptional() {
|
||||
return Optional.ofNullable(get());
|
||||
}
|
||||
|
||||
public Path asPath() {
|
||||
String result = get();
|
||||
if (result != null && Os.current().isCygwin()) {
|
||||
result = Environment.cygpath(result);
|
||||
}
|
||||
return result == null ? null : Paths.get(result);
|
||||
}
|
||||
|
||||
public boolean asBoolean() {
|
||||
return Boolean.parseBoolean(get());
|
||||
}
|
||||
|
||||
public int asInt() {
|
||||
return Integer.parseInt(get());
|
||||
}
|
||||
|
||||
public int asInt(IntUnaryOperator function) {
|
||||
return function.applyAsInt(asInt());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -15,27 +15,17 @@
|
||||
*/
|
||||
package org.jboss.fuse.mvnd.client;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.maven.cli.internal.extension.model.CoreExtension;
|
||||
import org.apache.maven.cli.internal.extension.model.io.xpp3.CoreExtensionsXpp3Reader;
|
||||
import org.codehaus.plexus.util.StringUtils;
|
||||
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
|
||||
import org.fusesource.jansi.Ansi;
|
||||
import org.jboss.fuse.mvnd.common.BuildProperties;
|
||||
import org.jboss.fuse.mvnd.common.DaemonCompatibilitySpec;
|
||||
import org.jboss.fuse.mvnd.common.DaemonInfo;
|
||||
import org.jboss.fuse.mvnd.common.DaemonRegistry;
|
||||
import org.jboss.fuse.mvnd.common.Environment;
|
||||
@@ -54,15 +44,11 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DefaultClient implements Client {
|
||||
|
||||
public static final int DEFAULT_PERIODIC_CHECK_INTERVAL_MILLIS = 10 * 1000;
|
||||
public static final int CANCEL_TIMEOUT = 10 * 1000;
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultClient.class);
|
||||
private static final String EXT_CLASS_PATH = "maven.ext.class.path";
|
||||
private static final String EXTENSIONS_FILENAME = ".mvn/extensions.xml";
|
||||
|
||||
private final Supplier<ClientLayout> lazyLayout;
|
||||
private final BuildProperties buildProperties;
|
||||
private final Supplier<DaemonParameters> lazyParameters;
|
||||
|
||||
public static void main(String[] argv) throws Exception {
|
||||
final List<String> args = new ArrayList<>(argv.length);
|
||||
@@ -83,13 +69,12 @@ public class DefaultClient implements Client {
|
||||
}
|
||||
|
||||
try (TerminalOutput output = new TerminalOutput(logFile)) {
|
||||
new DefaultClient(ClientLayout::getEnvInstance, BuildProperties.getInstance()).execute(output, args);
|
||||
new DefaultClient(() -> new DaemonParameters(new Properties())).execute(output, args);
|
||||
}
|
||||
}
|
||||
|
||||
public DefaultClient(Supplier<ClientLayout> layout, BuildProperties buildProperties) {
|
||||
this.lazyLayout = layout;
|
||||
this.buildProperties = buildProperties;
|
||||
public DefaultClient(Supplier<DaemonParameters> lazyParameters) {
|
||||
this.lazyParameters = lazyParameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,6 +120,7 @@ public class DefaultClient implements Client {
|
||||
// Print version if needed
|
||||
if (version || showVersion || debug) {
|
||||
// Print mvnd version
|
||||
BuildProperties buildProperties = BuildProperties.getInstance();
|
||||
final String nativeSuffix = Environment.isNative() ? " (native)" : "";
|
||||
final String v = Ansi.ansi().bold().a(
|
||||
"Maven Daemon "
|
||||
@@ -158,9 +144,8 @@ public class DefaultClient implements Client {
|
||||
*/
|
||||
}
|
||||
|
||||
final ClientLayout layout = lazyLayout.get();
|
||||
final Path javaHome = layout.javaHome();
|
||||
try (DaemonRegistry registry = new DaemonRegistry(layout.registry())) {
|
||||
final DaemonParameters parameters = lazyParameters.get();
|
||||
try (DaemonRegistry registry = new DaemonRegistry(parameters.registry())) {
|
||||
boolean status = args.remove("--status");
|
||||
if (status) {
|
||||
final String template = " %36s %7s %5s %7s %5s %23s %s";
|
||||
@@ -200,27 +185,32 @@ public class DefaultClient implements Client {
|
||||
return new DefaultResult(argv, null);
|
||||
}
|
||||
|
||||
setDefaultArgs(args, layout);
|
||||
final Path settings = layout.getSettings();
|
||||
if (args.stream().noneMatch(arg -> arg.startsWith("-T") || arg.equals("--threads"))) {
|
||||
args.add("--threads");
|
||||
args.add(parameters.threads());
|
||||
}
|
||||
if (args.stream().noneMatch(arg -> arg.startsWith("-b") || arg.equals("--builder"))) {
|
||||
args.add("--builder");
|
||||
args.add(parameters.builder());
|
||||
}
|
||||
final Path settings = parameters.settings();
|
||||
if (settings != null && args.stream().noneMatch(arg -> arg.equals("-s") || arg.equals("--settings"))) {
|
||||
args.add("-s");
|
||||
args.add("--settings");
|
||||
args.add(settings.toString());
|
||||
}
|
||||
|
||||
final Path localMavenRepository = layout.getLocalMavenRepository();
|
||||
if (localMavenRepository != null) {
|
||||
final Path localMavenRepository = parameters.mavenRepoLocal();
|
||||
if (localMavenRepository != null && args.stream().noneMatch(arg -> arg.startsWith("-Dmaven.repo.local="))) {
|
||||
args.add("-Dmaven.repo.local=" + localMavenRepository.toString());
|
||||
}
|
||||
|
||||
List<String> opts = getDaemonOpts(layout);
|
||||
final DaemonConnector connector = new DaemonConnector(layout, registry, buildProperties);
|
||||
try (DaemonClientConnection daemon = connector.connect(new DaemonCompatibilitySpec(javaHome, opts), output)) {
|
||||
final DaemonConnector connector = new DaemonConnector(parameters, registry);
|
||||
try (DaemonClientConnection daemon = connector.connect(output)) {
|
||||
output.buildStatus("Connected to daemon");
|
||||
|
||||
daemon.dispatch(new Message.BuildRequest(
|
||||
args,
|
||||
layout.userDir().toString(),
|
||||
layout.multiModuleProjectDirectory().toString(),
|
||||
parameters.userDir().toString(),
|
||||
parameters.multiModuleProjectDirectory().toString(),
|
||||
System.getenv()));
|
||||
|
||||
output.buildStatus("Build request sent");
|
||||
@@ -259,65 +249,6 @@ public class DefaultClient implements Client {
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getDaemonOpts(ClientLayout layout) {
|
||||
List<String> options = new ArrayList<>();
|
||||
// Classpath
|
||||
List<Path> jars = parseExtClasspath(layout);
|
||||
if (!jars.isEmpty()) {
|
||||
options.add(Environment.DAEMON_EXT_CLASSPATH.asCommandLineProperty(
|
||||
jars.stream().map(Path::toString).collect(Collectors.joining(","))));
|
||||
}
|
||||
// Extensions
|
||||
try {
|
||||
List<CoreExtension> extensions = readCoreExtensionsDescriptor(layout);
|
||||
if (!extensions.isEmpty()) {
|
||||
options.add(Environment.DAEMON_CORE_EXTENSIONS.asCommandLineProperty(
|
||||
extensions.stream().map(e -> e.getGroupId() + ":" + e.getArtifactId() + ":" + e.getVersion())
|
||||
.collect(Collectors.joining(","))));
|
||||
}
|
||||
} catch (IOException | XmlPullParserException e) {
|
||||
throw new RuntimeException("Unable to parse core extensions", e);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
private List<Path> parseExtClasspath(ClientLayout layout) {
|
||||
String extClassPath = System.getProperty(EXT_CLASS_PATH);
|
||||
List<Path> jars = new ArrayList<>();
|
||||
if (StringUtils.isNotEmpty(extClassPath)) {
|
||||
for (String jar : StringUtils.split(extClassPath, File.pathSeparator)) {
|
||||
Path path = layout.userDir().resolve(jar).toAbsolutePath();
|
||||
jars.add(path);
|
||||
}
|
||||
}
|
||||
return jars;
|
||||
}
|
||||
|
||||
private List<CoreExtension> readCoreExtensionsDescriptor(ClientLayout layout)
|
||||
throws IOException, XmlPullParserException {
|
||||
Path multiModuleProjectDirectory = layout.multiModuleProjectDirectory();
|
||||
if (multiModuleProjectDirectory == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Path extensionsFile = multiModuleProjectDirectory.resolve(EXTENSIONS_FILENAME);
|
||||
if (!Files.exists(extensionsFile)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
CoreExtensionsXpp3Reader parser = new CoreExtensionsXpp3Reader();
|
||||
try (InputStream is = Files.newInputStream(extensionsFile)) {
|
||||
return parser.read(is).getExtensions();
|
||||
}
|
||||
}
|
||||
|
||||
static void setDefaultArgs(List<String> args, ClientLayout layout) {
|
||||
if (args.stream().noneMatch(arg -> arg.startsWith("-T") || arg.equals("--threads"))) {
|
||||
args.add("-T" + layout.getThreads());
|
||||
}
|
||||
if (args.stream().noneMatch(arg -> arg.startsWith("-b") || arg.equals("--builder"))) {
|
||||
args.add("-bsmart");
|
||||
}
|
||||
}
|
||||
|
||||
private static class DefaultResult implements ExecutionResult {
|
||||
|
||||
private final Exception exception;
|
||||
|
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
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.jboss.fuse.mvnd.common.Environment;
|
||||
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("mvnd.home", "/maven/home/prop");
|
||||
Assertions.assertEquals("/maven/home/prop", DaemonParameters.systemProperty(Environment.MVND_HOME).asString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void env() {
|
||||
try (EnvironmentResource env = new EnvironmentResource()) {
|
||||
env.env("MVND_HOME", "/maven/home/env");
|
||||
Assertions.assertEquals("/maven/home/env", DaemonParameters.environmentVariable(Environment.MVND_HOME).asString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void localProps() {
|
||||
try (EnvironmentResource env = new EnvironmentResource()) {
|
||||
final Properties localProps = new Properties();
|
||||
localProps.put("mvnd.home", "/maven/home/local");
|
||||
Assertions.assertEquals(Paths.get("/maven/home/local"),
|
||||
DaemonParameters
|
||||
.environmentVariable(Environment.MVND_HOME)
|
||||
.orSystemProperty()
|
||||
.orLocalProperty(path -> localProps, Paths.get("/local/properties"))
|
||||
.orFail()
|
||||
.asPath());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void envBeforeProp() {
|
||||
try (EnvironmentResource env = new EnvironmentResource()) {
|
||||
env.props("mvnd.home", "/maven/home/prop");
|
||||
env.env("MVND_HOME", "/maven/home/env");
|
||||
Assertions.assertEquals("/maven/home/env",
|
||||
DaemonParameters
|
||||
.environmentVariable(Environment.MVND_HOME)
|
||||
.orSystemProperty()
|
||||
.asString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void fail() {
|
||||
try (EnvironmentResource env = new EnvironmentResource()) {
|
||||
try {
|
||||
Assertions.assertEquals("/maven/home/env",
|
||||
DaemonParameters
|
||||
.environmentVariable(Environment.MVND_HOME)
|
||||
.orSystemProperty()
|
||||
.orFail()
|
||||
.asString());
|
||||
Assertions.fail("IllegalStateException expected");
|
||||
} catch (IllegalStateException e) {
|
||||
Assertions.assertEquals(
|
||||
"Could not get value for Environment.MVND_HOME from any of the following sources: environment variable MVND_HOME, system property mvnd.home",
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void cygwin() {
|
||||
Assertions.assertEquals("C:\\jdk-11.0.2\\", Environment.cygpath("/cygdrive/c/jdk-11.0.2/"));
|
||||
}
|
||||
|
||||
static class EnvironmentResource implements AutoCloseable {
|
||||
|
||||
private final Properties props = new Properties();
|
||||
private final Map<String, String> env = new HashMap<>();
|
||||
|
||||
public EnvironmentResource() {
|
||||
DaemonParameters.EnvValue.env = env;
|
||||
Environment.setProperties(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() {
|
||||
DaemonParameters.EnvValue.env = System.getenv();
|
||||
Environment.setProperties(System.getProperties());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user