From cb215241dd15b3b8456998f1de1124efa7abaff5 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 5 Nov 2020 15:02:24 +0100 Subject: [PATCH] Refactor usage of properties in the client / daemon, fixes #188 --- .../jboss/fuse/mvnd/client/ClientLayout.java | 196 ------- .../mvnd/client/DaemonClientConnection.java | 10 +- .../fuse/mvnd/client/DaemonConnector.java | 73 +-- .../fuse/mvnd/client}/DaemonDiagnostics.java | 14 +- .../fuse/mvnd/client/DaemonParameters.java | 522 ++++++++++++++++++ .../jboss/fuse/mvnd/client/DefaultClient.java | 117 +--- .../fuse/mvnd/client}/EnvironmentTest.java | 31 +- .../jboss/fuse/mvnd/common/DaemonInfo.java | 23 +- .../fuse/mvnd/common/DaemonRegistry.java | 6 +- .../jboss/fuse/mvnd/common/Environment.java | 423 +++++--------- .../org/jboss/fuse/mvnd/common/Layout.java | 120 ---- .../jboss/fuse/mvnd/common/MavenDaemon.java | 5 +- .../fuse/mvnd/common/DaemonRegistryTest.java | 2 +- .../org/apache/maven/cli/DaemonMavenCli.java | 4 +- .../fuse/mvnd/builder/ReactorBuildStats.java | 6 +- .../fuse/mvnd/daemon/DaemonExpiration.java | 11 +- .../org/jboss/fuse/mvnd/daemon/Server.java | 52 +- .../main/distro/mvn/conf/logging/logback.xml | 2 +- .../jboss/fuse/mvnd/it/DaemonCrashTest.java | 8 +- .../fuse/mvnd/it/ExtensionsNativeIT.java | 10 +- .../jboss/fuse/mvnd/it/InvokerNativeIT.java | 8 +- .../fuse/mvnd/it/ModuleAndPluginNativeIT.java | 12 +- .../jboss/fuse/mvnd/it/MultiModuleTest.java | 10 +- .../fuse/mvnd/it/SingleModuleNativeIT.java | 10 +- .../fuse/mvnd/it/UpgradesInBomNativeIT.java | 10 +- .../jboss/fuse/mvnd/it/VersionNativeIT.java | 6 +- .../jboss/fuse/mvnd/junit/ClientFactory.java | 4 +- .../fuse/mvnd/junit/MvndTestExtension.java | 39 +- .../fuse/mvnd/junit/NativeTestClient.java | 33 +- .../org/jboss/fuse/mvnd/junit/TestLayout.java | 38 -- .../jboss/fuse/mvnd/junit/TestParameters.java | 58 ++ 31 files changed, 927 insertions(+), 936 deletions(-) delete mode 100644 client/src/main/java/org/jboss/fuse/mvnd/client/ClientLayout.java rename {common/src/main/java/org/jboss/fuse/mvnd/common => client/src/main/java/org/jboss/fuse/mvnd/client}/DaemonDiagnostics.java (89%) create mode 100644 client/src/main/java/org/jboss/fuse/mvnd/client/DaemonParameters.java rename {common/src/test/java/org/jboss/fuse/mvnd/common => client/src/test/java/org/jboss/fuse/mvnd/client}/EnvironmentTest.java (77%) delete mode 100644 common/src/main/java/org/jboss/fuse/mvnd/common/Layout.java delete mode 100644 integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/TestLayout.java create mode 100644 integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/TestParameters.java diff --git a/client/src/main/java/org/jboss/fuse/mvnd/client/ClientLayout.java b/client/src/main/java/org/jboss/fuse/mvnd/client/ClientLayout.java deleted file mode 100644 index 004409b68..000000000 --- a/client/src/main/java/org/jboss/fuse/mvnd/client/ClientLayout.java +++ /dev/null @@ -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 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 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() + "]"; - } - -} diff --git a/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonClientConnection.java b/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonClientConnection.java index 79919fff9..ceb4d0d49 100644 --- a/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonClientConnection.java +++ b/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonClientConnection.java @@ -25,12 +25,10 @@ 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 = 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 DaemonClientConnection(DaemonConnection connection, DaemonInfo daemon, 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 Message receive() throws ConnectException, StaleAddressException { + "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); diff --git a/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonConnector.java b/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonConnector.java index 465d619c4..2e5ed12ce 100644 --- a/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonConnector.java +++ b/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonConnector.java @@ -34,7 +34,6 @@ 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 DaemonClientConnection maybeConnect(DaemonInfo daemon) { 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> idleBusy = registry.getAll().stream() .collect(Collectors.groupingBy(di -> di.getState() == DaemonState.Idle)); @@ -107,7 +106,7 @@ public DaemonClientConnection connect(DaemonCompatibilitySpec constraint, Client // 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 idleDaemons, Collection busyDaemons) { @@ -221,9 +220,9 @@ private DaemonClientConnection findConnection(List compatibleDaemons 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 DaemonClientConnection startDaemon(DaemonCompatibilitySpec constraint) { 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 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 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 @@ private DaemonClientConnection connectToDaemonWithId(String daemon, boolean newD 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 @@ private DaemonClientConnection connectToDaemon(DaemonInfo daemon, 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; diff --git a/common/src/main/java/org/jboss/fuse/mvnd/common/DaemonDiagnostics.java b/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonDiagnostics.java similarity index 89% rename from common/src/main/java/org/jboss/fuse/mvnd/common/DaemonDiagnostics.java rename to client/src/main/java/org/jboss/fuse/mvnd/client/DaemonDiagnostics.java index 58fda80be..041e176cb 100644 --- a/common/src/main/java/org/jboss/fuse/mvnd/common/DaemonDiagnostics.java +++ b/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonDiagnostics.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jboss.fuse.mvnd.common; +package org.jboss.fuse.mvnd.client; import java.io.BufferedReader; import java.io.IOException; @@ -35,26 +35,26 @@ public class DaemonDiagnostics { private final static int TAIL_SIZE = 20; private final String uid; - private final Layout layout; + private final DaemonParameters parameters; - public DaemonDiagnostics(String uid, Layout layout) { + public DaemonDiagnostics(String uid, DaemonParameters parameters) { this.uid = uid; - this.layout = layout; + this.parameters = parameters; } @Override public String toString() { return "{" + "uid=" + uid - + ", layout=" + layout + + ", parameters=" + parameters + '}'; } public String describe() { StringBuilder sb = new StringBuilder(); sb.append("Daemon uid: ").append(uid).append("\n"); - tail(sb, "log file", layout.daemonLog(uid)); - tail(sb, "output", layout.daemonOutLog(uid)); + tail(sb, "log file", parameters.daemonLog(uid)); + tail(sb, "output", parameters.daemonOutLog(uid)); return sb.toString(); } diff --git a/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonParameters.java b/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonParameters.java new file mode 100644 index 000000000..812b62824 --- /dev/null +++ b/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonParameters.java @@ -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 mvndProperties = new ConcurrentHashMap<>(); + protected final Function provider = path -> mvndProperties.computeIfAbsent(path, + p -> loadProperties(path)); + protected final Properties properties; + + public DaemonParameters(Properties properties) { + this.properties = properties; + } + + public List getDaemonOpts() { + return Arrays.stream(Environment.values()) + .filter(Environment::isDiscriminating) + .map(v -> v.asDaemonOpt(property(v).orFail().asString())) + .collect(Collectors.toList()); + } + + public List 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 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 cp = parseExtClasspath(userHome()); + return String.join(",", cp); + } else if (env == Environment.DAEMON_CORE_EXTENSIONS) { + try { + List 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 parseExtClasspath(Path userDir) { + String extClassPath = System.getProperty(EXT_CLASS_PATH); + List 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 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 descriptionFunction; + final Supplier valueSupplier; + + public ValueSource(Function descriptionFunction, Supplier 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 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 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 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 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 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()); + } + + } +} diff --git a/client/src/main/java/org/jboss/fuse/mvnd/client/DefaultClient.java b/client/src/main/java/org/jboss/fuse/mvnd/client/DefaultClient.java index def000eaa..bcc4b5810 100644 --- a/client/src/main/java/org/jboss/fuse/mvnd/client/DefaultClient.java +++ b/client/src/main/java/org/jboss/fuse/mvnd/client/DefaultClient.java @@ -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 @@ 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 lazyLayout; - private final BuildProperties buildProperties; + private final Supplier lazyParameters; public static void main(String[] argv) throws Exception { final List args = new ArrayList<>(argv.length); @@ -83,13 +69,12 @@ public static void main(String[] argv) throws Exception { } 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 layout, BuildProperties buildProperties) { - this.lazyLayout = layout; - this.buildProperties = buildProperties; + public DefaultClient(Supplier lazyParameters) { + this.lazyParameters = lazyParameters; } @Override @@ -135,6 +120,7 @@ public ExecutionResult execute(ClientOutput output, List argv) { // 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 ExecutionResult execute(ClientOutput output, List argv) { */ } - 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 ExecutionResult execute(ClientOutput output, List argv) { 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 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 ExecutionResult execute(ClientOutput output, List argv) { } } - private List getDaemonOpts(ClientLayout layout) { - List options = new ArrayList<>(); - // Classpath - List jars = parseExtClasspath(layout); - if (!jars.isEmpty()) { - options.add(Environment.DAEMON_EXT_CLASSPATH.asCommandLineProperty( - jars.stream().map(Path::toString).collect(Collectors.joining(",")))); - } - // Extensions - try { - List 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 parseExtClasspath(ClientLayout layout) { - String extClassPath = System.getProperty(EXT_CLASS_PATH); - List 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 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 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; diff --git a/common/src/test/java/org/jboss/fuse/mvnd/common/EnvironmentTest.java b/client/src/test/java/org/jboss/fuse/mvnd/client/EnvironmentTest.java similarity index 77% rename from common/src/test/java/org/jboss/fuse/mvnd/common/EnvironmentTest.java rename to client/src/test/java/org/jboss/fuse/mvnd/client/EnvironmentTest.java index beee9cd5d..3750aecf8 100644 --- a/common/src/test/java/org/jboss/fuse/mvnd/common/EnvironmentTest.java +++ b/client/src/test/java/org/jboss/fuse/mvnd/client/EnvironmentTest.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jboss.fuse.mvnd.common; +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; @@ -28,7 +29,7 @@ public class EnvironmentTest { void prop() { try (EnvironmentResource env = new EnvironmentResource()) { env.props("mvnd.home", "/maven/home/prop"); - Assertions.assertEquals("/maven/home/prop", Environment.MVND_HOME.systemProperty().asString()); + Assertions.assertEquals("/maven/home/prop", DaemonParameters.systemProperty(Environment.MVND_HOME).asString()); } } @@ -36,7 +37,7 @@ void prop() { void env() { try (EnvironmentResource env = new EnvironmentResource()) { env.env("MVND_HOME", "/maven/home/env"); - Assertions.assertEquals("/maven/home/env", Environment.MVND_HOME.environmentVariable().asString()); + Assertions.assertEquals("/maven/home/env", DaemonParameters.environmentVariable(Environment.MVND_HOME).asString()); } } @@ -46,10 +47,10 @@ void localProps() { final Properties localProps = new Properties(); localProps.put("mvnd.home", "/maven/home/local"); Assertions.assertEquals(Paths.get("/maven/home/local"), - Environment.MVND_HOME - .environmentVariable() + DaemonParameters + .environmentVariable(Environment.MVND_HOME) .orSystemProperty() - .orLocalProperty(() -> localProps, Paths.get("/local/properties")) + .orLocalProperty(path -> localProps, Paths.get("/local/properties")) .orFail() .asPath()); } @@ -61,8 +62,8 @@ void envBeforeProp() { env.props("mvnd.home", "/maven/home/prop"); env.env("MVND_HOME", "/maven/home/env"); Assertions.assertEquals("/maven/home/env", - Environment.MVND_HOME - .environmentVariable() + DaemonParameters + .environmentVariable(Environment.MVND_HOME) .orSystemProperty() .asString()); } @@ -73,8 +74,8 @@ void fail() { try (EnvironmentResource env = new EnvironmentResource()) { try { Assertions.assertEquals("/maven/home/env", - Environment.MVND_HOME - .environmentVariable() + DaemonParameters + .environmentVariable(Environment.MVND_HOME) .orSystemProperty() .orFail() .asString()); @@ -89,7 +90,7 @@ void fail() { @Test void cygwin() { - Assertions.assertEquals("C:\\jdk-11.0.2\\", Environment.EnvValue.cygpath("/cygdrive/c/jdk-11.0.2/")); + Assertions.assertEquals("C:\\jdk-11.0.2\\", Environment.cygpath("/cygdrive/c/jdk-11.0.2/")); } static class EnvironmentResource implements AutoCloseable { @@ -98,8 +99,8 @@ static class EnvironmentResource implements AutoCloseable { private final Map env = new HashMap<>(); public EnvironmentResource() { - Environment.env = env; - Environment.properties = props; + DaemonParameters.EnvValue.env = env; + Environment.setProperties(props); } public void props(String... props) { @@ -118,8 +119,8 @@ public void env(String... env) { @Override public void close() { - Environment.env = System.getenv(); - Environment.properties = System.getProperties(); + DaemonParameters.EnvValue.env = System.getenv(); + Environment.setProperties(System.getProperties()); } } diff --git a/common/src/main/java/org/jboss/fuse/mvnd/common/DaemonInfo.java b/common/src/main/java/org/jboss/fuse/mvnd/common/DaemonInfo.java index 97c9ba0bd..4f5e7f7cf 100644 --- a/common/src/main/java/org/jboss/fuse/mvnd/common/DaemonInfo.java +++ b/common/src/main/java/org/jboss/fuse/mvnd/common/DaemonInfo.java @@ -28,10 +28,9 @@ public class DaemonInfo { private final String uid; private final String javaHome; - private final String mavenHome; + private final String mvndHome; private final int pid; private final int address; - private final int idleTimeout; private final String locale; private final List options; private final DaemonState state; @@ -39,15 +38,14 @@ public class DaemonInfo { private final long lastBusy; public DaemonInfo(String uid, String javaHome, String mavenHome, - int pid, int address, int idleTimeout, + int pid, int address, String locale, List options, DaemonState state, long lastIdle, long lastBusy) { this.uid = uid; this.javaHome = javaHome; - this.mavenHome = mavenHome; + this.mvndHome = mavenHome; this.pid = pid; this.address = address; - this.idleTimeout = idleTimeout; this.locale = locale; this.options = options; this.state = state; @@ -63,8 +61,8 @@ public String getJavaHome() { return javaHome; } - public String getMavenHome() { - return mavenHome; + public String getMvndHome() { + return mvndHome; } public int getPid() { @@ -75,10 +73,6 @@ public int getAddress() { return address; } - public int getIdleTimeout() { - return idleTimeout; - } - public String getLocale() { return locale; } @@ -111,8 +105,8 @@ public DaemonInfo withState(DaemonState state) { li = lastIdle; lb = lastBusy; } - return new DaemonInfo(uid, javaHome, mavenHome, pid, address, - idleTimeout, locale, options, state, li, lb); + return new DaemonInfo(uid, javaHome, mvndHome, pid, address, + locale, options, state, li, lb); } @Override @@ -125,10 +119,9 @@ public String toString() { public StringBuilder appendNonKeyFields(StringBuilder sb) { return sb.append("javaHome=").append(javaHome) .append(", options=").append(options) - .append(", mavenHome=").append(mavenHome) + .append(", mavenHome=").append(mvndHome) .append(", pid=").append(pid) .append(", address=").append(address) - .append(", idleTimeout=").append(idleTimeout) .append(", locale=").append(locale) .append(", state=").append(state) .append(", lastIdle=").append(lastIdle) diff --git a/common/src/main/java/org/jboss/fuse/mvnd/common/DaemonRegistry.java b/common/src/main/java/org/jboss/fuse/mvnd/common/DaemonRegistry.java index b70ebf50f..54f4fba78 100644 --- a/common/src/main/java/org/jboss/fuse/mvnd/common/DaemonRegistry.java +++ b/common/src/main/java/org/jboss/fuse/mvnd/common/DaemonRegistry.java @@ -242,7 +242,6 @@ private void doUpdate(Runnable updater) { String mavenHome = readString(); int pid = buffer.getInt(); int address = buffer.getInt(); - int idle = buffer.getInt(); String locale = readString(); List opts = new ArrayList<>(); int nbOpts = buffer.getInt(); @@ -252,7 +251,7 @@ private void doUpdate(Runnable updater) { DaemonState state = DaemonState.values()[buffer.get()]; long lastIdle = buffer.getLong(); long lastBusy = buffer.getLong(); - DaemonInfo di = new DaemonInfo(uid, javaHome, mavenHome, pid, address, idle, locale, opts, state, + DaemonInfo di = new DaemonInfo(uid, javaHome, mavenHome, pid, address, locale, opts, state, lastIdle, lastBusy); infosMap.putIfAbsent(di.getUid(), di); } @@ -276,10 +275,9 @@ private void doUpdate(Runnable updater) { for (DaemonInfo di : infosMap.values()) { writeString(di.getUid()); writeString(di.getJavaHome()); - writeString(di.getMavenHome()); + writeString(di.getMvndHome()); buffer.putInt(di.getPid()); buffer.putInt(di.getAddress()); - buffer.putInt(di.getIdleTimeout()); writeString(di.getLocale()); buffer.putInt(di.getOptions().size()); for (String opt : di.getOptions()) { diff --git a/common/src/main/java/org/jboss/fuse/mvnd/common/Environment.java b/common/src/main/java/org/jboss/fuse/mvnd/common/Environment.java index f1a096c87..6d7ef4053 100644 --- a/common/src/main/java/org/jboss/fuse/mvnd/common/Environment.java +++ b/common/src/main/java/org/jboss/fuse/mvnd/common/Environment.java @@ -15,367 +15,208 @@ */ package org.jboss.fuse.mvnd.common; -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.Locale; -import java.util.Map; -import java.util.Optional; +import java.util.Objects; import java.util.Properties; import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * 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"), - MVND_HOME("mvnd.home", "MVND_HOME"), - MAVEN_REPO_LOCAL("maven.repo.local", null), - MAVEN_MULTIMODULE_PROJECT_DIRECTORY("maven.multiModuleProjectDirectory", null), - MVND_PROPERTIES_PATH("mvnd.properties.path", "MVND_PROPERTIES_PATH"), - DAEMON_DEBUG("daemon.debug", null), - DAEMON_IDLE_TIMEOUT_MS("daemon.idleTimeoutMs", null), - DAEMON_KEEP_ALIVE_MS("daemon.keepAliveMs", null), - DAEMON_MAX_LOST_KEEP_ALIVE("daemon.maxLostKeepAlive", null), + LOGBACK_CONFIGURATION_FILE("logback.configurationFile", null, null, false), + JAVA_HOME("java.home", "JAVA_HOME", null, false), + MVND_HOME("mvnd.home", "MVND_HOME", null, false), + USER_HOME("user.home", null, null, false), + USER_DIR("user.dir", null, null, false), + MAVEN_REPO_LOCAL("maven.repo.local", null, null, false), + MAVEN_SETTINGS("maven.settings", null, null, false) { + @Override + public boolean hasCommandLineProperty(Collection args) { + return args.stream().anyMatch(arg -> arg.startsWith("-s") || arg.startsWith("--settings")); + } + + @Override + public String asCommandLineProperty(String value) { + return "--settings=" + value; + } + }, + MAVEN_MULTIMODULE_PROJECT_DIRECTORY("maven.multiModuleProjectDirectory", null, null, false), + MVND_PROPERTIES_PATH("mvnd.properties.path", "MVND_PROPERTIES_PATH", null, false), + MVND_DAEMON_STORAGE("mvnd.daemon.storage", null, null, false), + /** + * The path to the daemon registry + */ + DAEMON_REGISTRY("daemon.registry", null, null, false), + DAEMON_DEBUG("daemon.debug", null, false, true), + DAEMON_IDLE_TIMEOUT_MS("daemon.idleTimeoutMs", null, TimeUnit.HOURS.toMillis(3), true), + DAEMON_KEEP_ALIVE_MS("daemon.keepAliveMs", null, TimeUnit.SECONDS.toMillis(1), true), + DAEMON_MAX_LOST_KEEP_ALIVE("daemon.maxLostKeepAlive", null, 3, false), /** * The minimum number of threads to use when constructing the default {@code -T} parameter for the daemon. - * This value is ignored if the user passes @{@code-T}, @{@code --threads} or {@code -Dmvnd.threads} on the command + * This value is ignored if the user passes @{@code -T}, @{@code --threads} or {@code -Dmvnd.threads} on the command * line or if he sets {@code mvnd.threads} in {@code ~/.m2/mvnd.properties}. */ - MVND_MIN_THREADS("mvnd.minThreads", null), + MVND_MIN_THREADS("mvnd.minThreads", null, 1, false), /** * The number of threads to pass to the daemon; same syntax as Maven's {@code -T}/{@code --threads} option. Ignored - * if the user passes @{@code-T}, @{@code --threads} or {@code -Dmvnd.threads} on the command + * if the user passes @{@code -T}, @{@code --threads} or {@code -Dmvnd.threads} on the command * line. */ - MVND_THREADS("mvnd.threads", null), - DAEMON_UID("daemon.uid", null), + MVND_THREADS("mvnd.threads", null, null, false) { + @Override + public boolean hasCommandLineProperty(Collection args) { + return args.stream().anyMatch(arg -> arg.startsWith("-T") || arg.startsWith("--threads")); + } + + @Override + public String asCommandLineProperty(String value) { + return "--threads=" + value; + } + }, + /** + * The maven builder name to use. Ignored if the user passes + * + * @{@code -b} or @{@code --builder} on the command line + */ + MVND_BUILDER("mvnd.builder", null, "smart", false) { + @Override + public boolean hasCommandLineProperty(Collection args) { + return args.stream().anyMatch(arg -> arg.startsWith("-b") || arg.startsWith("--builder")); + } + + @Override + public String asCommandLineProperty(String value) { + return "--builder=" + value; + } + }, + /** + * Internal system property set by the client when starting the daemon to identify its id + */ + DAEMON_UID("daemon.uid", null, null, false), /** * Internal option to specify the maven extension classpath */ - DAEMON_EXT_CLASSPATH("daemon.ext.classpath", null), + DAEMON_EXT_CLASSPATH("daemon.ext.classpath", null, null, true), /** * Internal option to specify the list of maven extension to register */ - DAEMON_CORE_EXTENSIONS("daemon.core.extensions", null), + DAEMON_CORE_EXTENSIONS("daemon.core.extensions", null, null, true), + /** + * JVM options for the daemon + */ + DAEMON_MIN_HEAP_SIZE("daemon.minHeapSize", null, "128M", true), + /** + * JVM options for the daemon + */ + DAEMON_MAX_HEAP_SIZE("daemon.maxHeapSize", null, "2G", true), + /** + * JVM options for the daemon + */ + DAEMON_ENABLE_ASSERTIONS("daemon.enableAssertions", null, false, true), /** * Interval to check if the daemon should expire */ - EXPIRATION_CHECK_DELAY_MS("daemon.expirationCheckDelayMs", null), + DAEMON_EXPIRATION_CHECK_DELAY_MS("daemon.expirationCheckDelayMs", null, TimeUnit.SECONDS.toMillis(10), true), + /** + * Period after which idle daemons will shut down + */ + DAEMON_DUPLICATE_DAEMON_GRACE_PERIOD_MS("daemon.duplicateDaemonGracePeriodMs", null, TimeUnit.SECONDS.toMillis(10), true), ; public static final int DEFAULT_IDLE_TIMEOUT = (int) TimeUnit.HOURS.toMillis(3); public static final int DEFAULT_KEEP_ALIVE = (int) TimeUnit.SECONDS.toMillis(1); - public static final int DEFAULT_EXPIRATION_CHECK_DELAY = (int) TimeUnit.SECONDS.toMillis(10); - - public static final int DEFAULT_MAX_LOST_KEEP_ALIVE = 3; - - public static final int DEFAULT_MIN_THREADS = 1; - - private static final Consumer LOG; - private static final boolean DEBUG_ENABLED; - public static final String DEBUG_ENVIRONMENT_PROP = "mvnd.environment.debug"; + static Properties properties = System.getProperties(); - static { - Consumer log = null; - boolean debugEnabled = false; - try { - Logger logger = LoggerFactory.getLogger(Environment.class); - log = logger::debug; - debugEnabled = logger.isDebugEnabled(); - } catch (java.lang.NoClassDefFoundError e) { - if (e.getMessage().contains("org/slf4j/LoggerFactory")) { - /* This is when we are in the daemon's boot class path where slf4j is not available */ - if (Boolean.getBoolean(DEBUG_ENVIRONMENT_PROP)) { - log = s -> System.out.println("mvnd.environment: " + s); - debugEnabled = true; - } - } else { - throw e; - } - } - LOG = log != null ? log : s -> { - }; - DEBUG_ENABLED = debugEnabled; + public static void setProperties(Properties properties) { + Environment.properties = properties; } - static Properties properties = System.getProperties(); - static Map env = System.getenv(); + public static String getProperty(String property) { + return properties.getProperty(property); + } private final String property; private final String environmentVariable; + private final String def; + private final boolean discriminating; - Environment(String property, String environmentVariable) { - this.property = property; + Environment(String property, String environmentVariable, Object def, boolean discriminating) { + this.property = Objects.requireNonNull(property); this.environmentVariable = environmentVariable; + this.def = def != null ? def.toString() : null; + this.discriminating = discriminating; } - public Environment.EnvValue systemProperty() { - return new EnvValue(this, systemPropertySource()); + public String getProperty() { + return property; } - public Environment.EnvValue commandLineProperty(Supplier commandLineProperties) { - return new EnvValue(this, new ValueSource( - description -> description.append("command line property ").append(property), - () -> commandLineProperties.get().getProperty(property))); + public String getEnvironmentVariable() { + return environmentVariable; } - public Environment.EnvValue environmentVariable() { - return new EnvValue(this, environmentVariableSource()); + public String getDef() { + return def; } - public EnvValue fromValueSource(ValueSource valueSource) { - return new EnvValue(this, valueSource); + public boolean isDiscriminating() { + return discriminating; } - public String asCommandLineProperty(String value) { - return "-D" + property + "=" + value; - } - - public boolean hasCommandLineProperty(Collection args) { - final String prefix = "-D" + property + "="; - return args.stream().anyMatch(s -> s.startsWith(prefix)); - } - - public static Path findJavaHome(Supplier 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 String asString() { + String val = getProperty(property); + if (val == null) { + throw new IllegalStateException("The system property " + property + " is missing"); } + return val; } - public static Path findMvndPropertiesPath() { - return MVND_PROPERTIES_PATH - .environmentVariable() - .orSystemProperty() - .orDefault(() -> Paths.get(System.getProperty("user.home"), ".m2", "mvnd.properties").toString()) - .asPath() - .toAbsolutePath().normalize(); + public int asInt() { + return Integer.parseInt(asString()); } - public static EnvValue findBasicMavenHome() { - return MVND_HOME - .environmentVariable() - .orSystemProperty(); + public boolean asBoolean() { + return Boolean.parseBoolean(asString()); } - public static Path findMultiModuleProjectDirectory(Path pwd) { - return MAVEN_MULTIMODULE_PROJECT_DIRECTORY - .systemProperty() - .orDefault(() -> findDefaultMultimoduleProjectDirectory(pwd)) - .asPath() - .toAbsolutePath().normalize(); - } - - 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 static Path findLogbackConfigurationPath(Supplier mvndProperties, Path mvndPropertiesPath, - Path mvndHome) { - return LOGBACK_CONFIGURATION_FILE - .systemProperty() - .orLocalProperty(mvndProperties, mvndPropertiesPath) - .orDefault(() -> mvndHome.resolve("mvn/conf/logging/logback.xml").toString()) - .orFail() - .asPath(); + public Path asPath() { + String result = asString(); + if (Os.current().isCygwin()) { + result = cygpath(result); + } + return Paths.get(result); } - public static boolean isNative() { - return "executable".equals(System.getProperty("org.graalvm.nativeimage.kind")); + public String asCommandLineProperty(String value) { + return "-D" + property + "=" + value; } - 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)); + public String asDaemonOpt(String value) { + return property + "=" + value; } - 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)); + public boolean hasCommandLineProperty(Collection args) { + final String prefix = "-D" + getProperty() + "="; + return args.stream().anyMatch(s -> s.startsWith(prefix)); } - /** - * A source of an environment value with a description capability. - */ - public static class ValueSource { - final Function descriptionFunction; - final Supplier valueSupplier; - - public ValueSource(Function descriptionFunction, Supplier valueSupplier) { - this.descriptionFunction = descriptionFunction; - this.valueSupplier = valueSupplier; - } - - /** Mostly for debugging */ - @Override - public String toString() { - return descriptionFunction.apply(new StringBuilder()).toString(); + public static String cygpath(String result) { + String path = result.replace('/', '\\'); + if (path.matches("\\\\cygdrive\\\\[a-z]\\\\.*")) { + String s = path.substring("\\cygdrive\\".length()); + result = s.substring(0, 1).toUpperCase(Locale.ENGLISH) + ":" + s.substring(1); } - + return result; } - /** - * 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 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 EnvValue or(ValueSource source) { - return new EnvValue(this, envKey, source); - } - - public Environment.EnvValue orDefault(Supplier 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 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 && DEBUG_ENABLED) { - StringBuilder sb = new StringBuilder("Loaded environment value for key [") - .append(envKey.name()) - .append("] from "); - valueSource.descriptionFunction.apply(sb); - sb.append(": [") - .append(result) - .append(']'); - LOG.accept(sb.toString()); - } - return result; - } - - public String asString() { - return get(); - } - - public Optional asOptional() { - return Optional.ofNullable(get()); - } - - public Path asPath() { - String result = get(); - if (result != null && Os.current().isCygwin()) { - result = cygpath(result); - } - return result == null ? null : Paths.get(result); - } - - static String cygpath(String result) { - String path = result.replace('/', '\\'); - if (path.matches("\\\\cygdrive\\\\[a-z]\\\\.*")) { - String s = path.substring("\\cygdrive\\".length()); - result = s.substring(0, 1).toUpperCase(Locale.ENGLISH) + ":" + s.substring(1); - } - return result; - } - - public boolean asBoolean() { - return Boolean.parseBoolean(get()); - } - - public int asInt() { - return Integer.parseInt(get()); - } - + public static boolean isNative() { + return "executable".equals(System.getProperty("org.graalvm.nativeimage.kind")); } } diff --git a/common/src/main/java/org/jboss/fuse/mvnd/common/Layout.java b/common/src/main/java/org/jboss/fuse/mvnd/common/Layout.java deleted file mode 100644 index c6f218d3d..000000000 --- a/common/src/main/java/org/jboss/fuse/mvnd/common/Layout.java +++ /dev/null @@ -1,120 +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.common; - -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.Properties; -import java.util.function.Supplier; - -public class Layout { - - private static Layout ENV_INSTANCE; - - protected final Path mavenHome; - protected final Path userDir; - protected final Path multiModuleProjectDirectory; - protected final Path mvndPropertiesPath; - - public Layout(Path mvndPropertiesPath, Path mavenHome, Path userDir, Path multiModuleProjectDirectory) { - super(); - this.mvndPropertiesPath = mvndPropertiesPath; - this.mavenHome = mavenHome; - this.userDir = userDir; - this.multiModuleProjectDirectory = multiModuleProjectDirectory; - } - - public Path mavenHome() { - return mavenHome; - } - - public Path userDir() { - return userDir; - } - - public Path registry() { - return mavenHome.resolve("daemon/registry.bin"); - } - - public Path daemonLog(String daemon) { - return mavenHome.resolve("daemon/daemon-" + daemon + ".log"); - } - - public Path daemonOutLog(String daemon) { - return mavenHome.resolve("daemon/daemon-" + daemon + ".out.log"); - } - - public Path multiModuleProjectDirectory() { - return multiModuleProjectDirectory; - } - - public Path getMvndPropertiesPath() { - return mvndPropertiesPath; - } - - public static Layout getEnvInstance() { - if (ENV_INSTANCE == null) { - final Path mvndPropertiesPath = Environment.findMvndPropertiesPath(); - final Supplier mvndProperties = lazyMvndProperties(mvndPropertiesPath); - final Path pwd = Paths.get(".").toAbsolutePath().normalize(); - - ENV_INSTANCE = new Layout( - mvndPropertiesPath, - Environment.MVND_HOME - .systemProperty() - .orFail() - .asPath() - .toAbsolutePath().normalize(), - pwd, - Environment.findMultiModuleProjectDirectory(pwd)); - } - return ENV_INSTANCE; - } - - public static Supplier lazyMvndProperties(Path mvndPropertiesPath) { - return new Supplier() { - - 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 " + mvndPropertiesPath); - } - } - this.properties = result; - } - return result; - } - }; - } - - @Override - public String toString() { - return "Layout [mavenHome=" + mavenHome + ", userDir=" + userDir + ", multiModuleProjectDirectory=" - + multiModuleProjectDirectory + "]"; - } - -} diff --git a/common/src/main/java/org/jboss/fuse/mvnd/common/MavenDaemon.java b/common/src/main/java/org/jboss/fuse/mvnd/common/MavenDaemon.java index 970215314..b8ddfdeb4 100644 --- a/common/src/main/java/org/jboss/fuse/mvnd/common/MavenDaemon.java +++ b/common/src/main/java/org/jboss/fuse/mvnd/common/MavenDaemon.java @@ -30,8 +30,7 @@ public static void main(String[] args) throws Exception { // loaded from jars that are built by a previous run new File("txt").toURI().toURL().openConnection().setDefaultUseCaches(false); - final String uidStr = Environment.DAEMON_UID.systemProperty().orFail().asString(); - final Path mvndHome = Environment.MVND_HOME.systemProperty().orFail().asPath(); + final Path mvndHome = Environment.MVND_HOME.asPath(); URL[] classpath = Stream.concat( /* jars */ Stream.of("mvn/lib/ext", "mvn/lib", "mvn/boot") @@ -73,7 +72,7 @@ protected Class findClass(String name) throws ClassNotFoundException { }; Thread.currentThread().setContextClassLoader(loader); Class clazz = loader.loadClass("org.jboss.fuse.mvnd.daemon.Server"); - try (AutoCloseable server = (AutoCloseable) clazz.getConstructor(String.class).newInstance(uidStr)) { + try (AutoCloseable server = (AutoCloseable) clazz.getConstructor().newInstance()) { ((Runnable) server).run(); } } diff --git a/common/src/test/java/org/jboss/fuse/mvnd/common/DaemonRegistryTest.java b/common/src/test/java/org/jboss/fuse/mvnd/common/DaemonRegistryTest.java index f8f81210f..a85023911 100644 --- a/common/src/test/java/org/jboss/fuse/mvnd/common/DaemonRegistryTest.java +++ b/common/src/test/java/org/jboss/fuse/mvnd/common/DaemonRegistryTest.java @@ -42,7 +42,7 @@ public void testReadWrite() throws IOException { byte[] token = new byte[16]; new Random().nextBytes(token); reg1.store(new DaemonInfo("the-uid", "/java/home/", - "/data/reg/", 0x12345678, 7502, 65536, + "/data/reg/", 0x12345678, 7502, Locale.getDefault().toLanguageTag(), Arrays.asList("-Xmx"), DaemonState.Idle, System.currentTimeMillis(), System.currentTimeMillis())); diff --git a/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java b/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java index bf8802065..64ace0c15 100644 --- a/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java +++ b/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java @@ -417,14 +417,14 @@ void container() } List extClassPath = Stream - .of(Environment.DAEMON_EXT_CLASSPATH.systemProperty().orDefault(() -> "").asString().split(",")) + .of(Environment.DAEMON_EXT_CLASSPATH.asString().split(",")) .map(File::new) .collect(Collectors.toList()); CoreExtensionEntry coreEntry = CoreExtensionEntry.discoverFrom(coreRealm); List extensions = Stream - .of(Environment.DAEMON_CORE_EXTENSIONS.systemProperty().orDefault(() -> "").asString().split(",")) + .of(Environment.DAEMON_CORE_EXTENSIONS.asString().split(",")) .filter(s -> s != null && !s.isEmpty()) .map(s -> { String[] parts = s.split(":"); diff --git a/daemon/src/main/java/org/jboss/fuse/mvnd/builder/ReactorBuildStats.java b/daemon/src/main/java/org/jboss/fuse/mvnd/builder/ReactorBuildStats.java index 635c95043..d151273dd 100644 --- a/daemon/src/main/java/org/jboss/fuse/mvnd/builder/ReactorBuildStats.java +++ b/daemon/src/main/java/org/jboss/fuse/mvnd/builder/ReactorBuildStats.java @@ -79,7 +79,11 @@ public void recordStop() { } public void recordServiceTime(MavenProject project, long durationNanos) { - serviceTimes.get(projectGA(project)).addAndGet(durationNanos); + AtomicLong serviceTime = serviceTimes.get(projectGA(project)); + if (serviceTime == null) { + throw new IllegalStateException("Unknown project " + projectGA(project) + ", found " + serviceTimes.keySet()); + } + serviceTime.addAndGet(durationNanos); } public void recordBottlenecks(Set projects, int degreeOfConcurrency, diff --git a/daemon/src/main/java/org/jboss/fuse/mvnd/daemon/DaemonExpiration.java b/daemon/src/main/java/org/jboss/fuse/mvnd/daemon/DaemonExpiration.java index fc998500b..758c965f9 100644 --- a/daemon/src/main/java/org/jboss/fuse/mvnd/daemon/DaemonExpiration.java +++ b/daemon/src/main/java/org/jboss/fuse/mvnd/daemon/DaemonExpiration.java @@ -21,13 +21,13 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; -import java.util.function.ToLongFunction; import java.util.stream.Collectors; import org.jboss.fuse.mvnd.common.DaemonCompatibilitySpec; import org.jboss.fuse.mvnd.common.DaemonCompatibilitySpec.Result; import org.jboss.fuse.mvnd.common.DaemonExpirationStatus; import org.jboss.fuse.mvnd.common.DaemonInfo; import org.jboss.fuse.mvnd.common.DaemonState; +import org.jboss.fuse.mvnd.common.Environment; import static org.jboss.fuse.mvnd.common.DaemonExpirationStatus.DO_NOT_EXPIRE; import static org.jboss.fuse.mvnd.common.DaemonExpirationStatus.GRACEFUL_EXPIRE; @@ -53,7 +53,7 @@ public static DaemonExpirationStrategy master() { return any( any(gcTrashing(), lowHeapSpace(), lowNonHeap()), all(compatible(), duplicateGracePeriod(), notMostRecentlyUsed()), - idleTimeout(Server::getIdleTimeout), + idleTimeout(Environment.DAEMON_IDLE_TIMEOUT_MS.asInt()), all(duplicateGracePeriod(), notMostRecentlyUsed(), lowMemory(0.05)), registryUnavailable()); } @@ -82,18 +82,17 @@ static DaemonExpirationStrategy lowMemory(double minFreeMemoryPercentage) { } static DaemonExpirationStrategy duplicateGracePeriod() { - return idleTimeout(daemon -> DUPLICATE_DAEMON_GRACE_PERIOD_MS); + return idleTimeout(Environment.DAEMON_DUPLICATE_DAEMON_GRACE_PERIOD_MS.asInt()); } private static final long HOUR = 60 * 60 * 1000; private static final long MINUTE = 60 * 1000; private static final long SECOND = 1000; - static DaemonExpirationStrategy idleTimeout(ToLongFunction timeout) { + static DaemonExpirationStrategy idleTimeout(long timeout) { return daemon -> { - long len = timeout.applyAsLong(daemon); long idl = System.currentTimeMillis() - daemon.getLastIdle(); - if (daemon.getState() == DaemonState.Idle && idl > len) { + if (daemon.getState() == DaemonState.Idle && idl > timeout) { String str; if (idl > HOUR) { str = (idl / HOUR) + " hours"; diff --git a/daemon/src/main/java/org/jboss/fuse/mvnd/daemon/Server.java b/daemon/src/main/java/org/jboss/fuse/mvnd/daemon/Server.java index 1b0a2abe1..518dfa596 100644 --- a/daemon/src/main/java/org/jboss/fuse/mvnd/daemon/Server.java +++ b/daemon/src/main/java/org/jboss/fuse/mvnd/daemon/Server.java @@ -21,9 +21,9 @@ import java.net.InetSocketAddress; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Locale; @@ -36,6 +36,7 @@ import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; import org.apache.maven.cli.CliRequest; import org.apache.maven.cli.CliRequestBuilder; import org.apache.maven.cli.DaemonMavenCli; @@ -48,7 +49,6 @@ import org.jboss.fuse.mvnd.common.DaemonState; import org.jboss.fuse.mvnd.common.DaemonStopEvent; import org.jboss.fuse.mvnd.common.Environment; -import org.jboss.fuse.mvnd.common.Layout; import org.jboss.fuse.mvnd.common.Message; import org.jboss.fuse.mvnd.common.Message.BuildEvent; import org.jboss.fuse.mvnd.common.Message.BuildEvent.Type; @@ -76,7 +76,6 @@ public class Server implements AutoCloseable, Runnable { private final DaemonMavenCli cli; private volatile DaemonInfo info; private final DaemonRegistry registry; - private final Layout layout; private final ScheduledExecutorService executor; private final DaemonExpirationStrategy strategy; @@ -85,33 +84,33 @@ public class Server implements AutoCloseable, Runnable { private final Condition condition = stateLock.newCondition(); private final DaemonMemoryStatus memoryStatus; - public Server(String uid) throws IOException { - this.uid = uid; - this.layout = Layout.getEnvInstance(); + public Server() throws IOException { + this.uid = Environment.DAEMON_UID.asString(); try { cli = new DaemonMavenCli(); - - registry = new DaemonRegistry(layout.registry()); + registry = new DaemonRegistry(Environment.DAEMON_REGISTRY.asPath()); socket = ServerSocketChannel.open().bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); - - final int idleTimeout = Environment.DAEMON_IDLE_TIMEOUT_MS - .systemProperty() - .orDefault(() -> String.valueOf(Environment.DEFAULT_IDLE_TIMEOUT)) - .asInt(); executor = Executors.newScheduledThreadPool(1); strategy = DaemonExpiration.master(); memoryStatus = new DaemonMemoryStatus(executor); List opts = new ArrayList<>(); - Environment.DAEMON_EXT_CLASSPATH.systemProperty().asOptional() - .ifPresent(s -> opts.add(Environment.DAEMON_EXT_CLASSPATH.asCommandLineProperty(s))); - Environment.DAEMON_CORE_EXTENSIONS.systemProperty().asOptional() - .ifPresent(s -> opts.add(Environment.DAEMON_CORE_EXTENSIONS.asCommandLineProperty(s))); + Arrays.stream(Environment.values()) + .filter(Environment::isDiscriminating) + .map(v -> v.getProperty() + "=" + v.asString()) + .forEach(opts::add); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(opts.stream().collect(Collectors.joining( + "\n ", "Initializing daemon with properties:\n ", "\n"))); + } long cur = System.currentTimeMillis(); - final Path javaHome = Paths.get(System.getProperty("mvnd.java.home")); - info = new DaemonInfo(uid, javaHome.toString(), layout.mavenHome().toString(), - DaemonRegistry.getProcessId(), socket.socket().getLocalPort(), - idleTimeout, Locale.getDefault().toLanguageTag(), opts, + info = new DaemonInfo(uid, + Environment.JAVA_HOME.asString(), + Environment.MVND_HOME.asString(), + DaemonRegistry.getProcessId(), + socket.socket().getLocalPort(), + Locale.getDefault().toLanguageTag(), + opts, Busy, cur, cur); registry.store(info); } catch (Exception e) { @@ -162,10 +161,7 @@ public void clearCache(String clazzName, String fieldName) { public void run() { try { - int expirationCheckDelayMs = Environment.EXPIRATION_CHECK_DELAY_MS - .systemProperty() - .orDefault(() -> String.valueOf(Environment.DEFAULT_EXPIRATION_CHECK_DELAY)) - .asInt(); + int expirationCheckDelayMs = Environment.DAEMON_EXPIRATION_CHECK_DELAY_MS.asInt(); executor.scheduleAtFixedRate(this::expirationCheck, expirationCheckDelayMs, expirationCheckDelayMs, TimeUnit.MILLISECONDS); LOGGER.info("Daemon started"); @@ -397,7 +393,7 @@ private void cancelNow() { private void handle(DaemonConnection connection, BuildRequest buildRequest) { updateState(Busy); try { - int keepAlive = Environment.DAEMON_KEEP_ALIVE_MS.systemProperty().asInt(); + int keepAlive = Environment.DAEMON_KEEP_ALIVE_MS.asInt(); LOGGER.info("Executing request"); CliRequest req = new CliRequestBuilder() @@ -509,10 +505,6 @@ public DaemonInfo getInfo() { return info; } - public int getIdleTimeout() { - return info.getIdleTimeout(); - } - public String getUid() { return info.getUid(); } diff --git a/dist/src/main/distro/mvn/conf/logging/logback.xml b/dist/src/main/distro/mvn/conf/logging/logback.xml index dbb573eb8..59c7445ad 100644 --- a/dist/src/main/distro/mvn/conf/logging/logback.xml +++ b/dist/src/main/distro/mvn/conf/logging/logback.xml @@ -33,7 +33,7 @@ - ${mvnd.home}/daemon/daemon-${daemon.uid}.log + ${mvnd.daemon.storage}/daemon-${daemon.uid}.log %d{HH:mm:ss.SSS} %.-1level %msg%n diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/DaemonCrashTest.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/DaemonCrashTest.java index 29785d03c..01960669f 100644 --- a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/DaemonCrashTest.java +++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/DaemonCrashTest.java @@ -22,7 +22,7 @@ import javax.inject.Inject; import org.assertj.core.api.Assertions; import org.jboss.fuse.mvnd.client.Client; -import org.jboss.fuse.mvnd.client.ClientLayout; +import org.jboss.fuse.mvnd.client.DaemonParameters; import org.jboss.fuse.mvnd.common.DaemonException; import org.jboss.fuse.mvnd.common.logging.ClientOutput; import org.jboss.fuse.mvnd.junit.MvndTest; @@ -39,18 +39,18 @@ public class DaemonCrashTest { Client client; @Inject - ClientLayout layout; + DaemonParameters parameters; @Test void cleanInstall() throws IOException, InterruptedException { - final Path helloPath = layout.multiModuleProjectDirectory().resolve("hello/target/hello.txt"); + final Path helloPath = parameters.multiModuleProjectDirectory().resolve("hello/target/hello.txt"); try { Files.deleteIfExists(helloPath); } catch (IOException e) { throw new RuntimeException("Could not delete " + helloPath); } - final Path localMavenRepo = layout.getLocalMavenRepository(); + final Path localMavenRepo = parameters.mavenRepoLocal(); TestUtils.deleteDir(localMavenRepo); final Path[] installedJars = { localMavenRepo.resolve( diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/ExtensionsNativeIT.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/ExtensionsNativeIT.java index 3c95ea847..fccde2701 100644 --- a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/ExtensionsNativeIT.java +++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/ExtensionsNativeIT.java @@ -16,11 +16,10 @@ package org.jboss.fuse.mvnd.it; import java.io.IOException; -import java.util.Collections; import javax.inject.Inject; import org.assertj.core.api.Assertions; import org.jboss.fuse.mvnd.client.Client; -import org.jboss.fuse.mvnd.client.ClientLayout; +import org.jboss.fuse.mvnd.client.DaemonParameters; import org.jboss.fuse.mvnd.common.DaemonInfo; import org.jboss.fuse.mvnd.common.logging.ClientOutput; import org.jboss.fuse.mvnd.junit.MvndNativeTest; @@ -28,7 +27,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; @MvndNativeTest(projectDir = "src/test/projects/extensions") public class ExtensionsNativeIT { @@ -37,7 +36,7 @@ public class ExtensionsNativeIT { Client client; @Inject - ClientLayout layout; + DaemonParameters parameters; @Inject TestRegistry registry; @@ -51,8 +50,7 @@ void version() throws IOException, InterruptedException { client.execute(o, "-v").assertSuccess(); Assertions.assertThat(registry.getAll().size()).isEqualTo(1); DaemonInfo daemon = registry.getAll().iterator().next(); - assertEquals(Collections.singletonList("-Ddaemon.core.extensions=io.takari.aether:takari-local-repository:0.11.3"), - daemon.getOptions()); + assertTrue(daemon.getOptions().contains("daemon.core.extensions=io.takari.aether:takari-local-repository:0.11.3")); registry.awaitIdle(daemon.getUid()); diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/InvokerNativeIT.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/InvokerNativeIT.java index 6fc0a00e8..1214bbf15 100644 --- a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/InvokerNativeIT.java +++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/InvokerNativeIT.java @@ -24,7 +24,7 @@ import javax.inject.Inject; import org.assertj.core.api.Assertions; import org.jboss.fuse.mvnd.client.Client; -import org.jboss.fuse.mvnd.client.ClientLayout; +import org.jboss.fuse.mvnd.client.DaemonParameters; import org.jboss.fuse.mvnd.common.logging.ClientOutput; import org.jboss.fuse.mvnd.junit.MvndNativeTest; import org.junit.jupiter.api.Test; @@ -37,18 +37,18 @@ public class InvokerNativeIT { Client client; @Inject - ClientLayout layout; + DaemonParameters parameters; @Test void cleanInstall() throws IOException, InterruptedException { - final Path helloPath = layout.multiModuleProjectDirectory().resolve("target/it/invoke-hello/target/hello.txt"); + final Path helloPath = parameters.multiModuleProjectDirectory().resolve("target/it/invoke-hello/target/hello.txt"); try { Files.deleteIfExists(helloPath); } catch (IOException e) { throw new RuntimeException("Could not delete " + helloPath); } - final Path logPath = layout.multiModuleProjectDirectory().resolve("target/it/invoke-hello/build.log"); + final Path logPath = parameters.multiModuleProjectDirectory().resolve("target/it/invoke-hello/build.log"); try { Files.deleteIfExists(logPath); } catch (IOException e) { diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/ModuleAndPluginNativeIT.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/ModuleAndPluginNativeIT.java index a6abda460..0d5c4e42a 100644 --- a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/ModuleAndPluginNativeIT.java +++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/ModuleAndPluginNativeIT.java @@ -23,7 +23,7 @@ import javax.inject.Inject; import org.assertj.core.api.Assertions; import org.jboss.fuse.mvnd.client.Client; -import org.jboss.fuse.mvnd.client.ClientLayout; +import org.jboss.fuse.mvnd.client.DaemonParameters; import org.jboss.fuse.mvnd.common.logging.ClientOutput; import org.jboss.fuse.mvnd.junit.MvndNativeTest; import org.jboss.fuse.mvnd.junit.TestUtils; @@ -37,12 +37,12 @@ public class ModuleAndPluginNativeIT { Client client; @Inject - ClientLayout layout; + DaemonParameters parameters; @Test void cleanInstall() throws IOException, InterruptedException { - final Path helloPath = layout.multiModuleProjectDirectory().resolve("hello/target/hello.txt"); - final Path helloPropertyPath = layout.multiModuleProjectDirectory().resolve("hello/target/hello.property.txt"); + final Path helloPath = parameters.multiModuleProjectDirectory().resolve("hello/target/hello.txt"); + final Path helloPropertyPath = parameters.multiModuleProjectDirectory().resolve("hello/target/hello.property.txt"); Stream.of(helloPath, helloPropertyPath).forEach(p -> { try { Files.deleteIfExists(p); @@ -51,7 +51,7 @@ void cleanInstall() throws IOException, InterruptedException { } }); - final Path localMavenRepo = layout.getLocalMavenRepository(); + final Path localMavenRepo = parameters.mavenRepoLocal(); TestUtils.deleteDir(localMavenRepo); final Path[] installedJars = { localMavenRepo.resolve( @@ -74,7 +74,7 @@ void cleanInstall() throws IOException, InterruptedException { /* Build #2: with the mojo source changed to output "Hi" to target/hello.txt */ { - final Path mojoPath = layout.multiModuleProjectDirectory() + final Path mojoPath = parameters.multiModuleProjectDirectory() .resolve("plugin/src/main/java/org/jboss/fuse/mvnd/test/module/plugin/mojo/HelloMojo.java"); TestUtils.replace(mojoPath, "\"Hello\".getBytes", "\"Hi\".getBytes"); diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/MultiModuleTest.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/MultiModuleTest.java index 6a78eec96..f3a4376e0 100644 --- a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/MultiModuleTest.java +++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/MultiModuleTest.java @@ -24,7 +24,7 @@ import org.jboss.fuse.mvnd.assertj.EqualsInOrderAmongOthers; import org.jboss.fuse.mvnd.assertj.MatchInOrderAmongOthers; import org.jboss.fuse.mvnd.client.Client; -import org.jboss.fuse.mvnd.client.ClientLayout; +import org.jboss.fuse.mvnd.client.DaemonParameters; import org.jboss.fuse.mvnd.common.logging.ClientOutput; import org.jboss.fuse.mvnd.junit.MvndTest; import org.jboss.fuse.mvnd.junit.TestUtils; @@ -41,13 +41,13 @@ public class MultiModuleTest { Client client; @Inject - ClientLayout layout; + DaemonParameters parameters; @Test void cleanInstall() throws IOException, InterruptedException { final Path[] helloFilePaths = { - layout.multiModuleProjectDirectory().resolve("hello/target/hello.txt"), - layout.multiModuleProjectDirectory().resolve("hi/target/hi.txt") + parameters.multiModuleProjectDirectory().resolve("hello/target/hello.txt"), + parameters.multiModuleProjectDirectory().resolve("hi/target/hi.txt") }; Stream.of(helloFilePaths).forEach(path -> { try { @@ -57,7 +57,7 @@ void cleanInstall() throws IOException, InterruptedException { } }); - final Path localMavenRepo = layout.getLocalMavenRepository(); + final Path localMavenRepo = parameters.mavenRepoLocal(); TestUtils.deleteDir(localMavenRepo); final Path[] installedJars = { localMavenRepo.resolve( diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/SingleModuleNativeIT.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/SingleModuleNativeIT.java index 14892d181..98c4252a8 100644 --- a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/SingleModuleNativeIT.java +++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/SingleModuleNativeIT.java @@ -22,7 +22,7 @@ import javax.inject.Inject; import org.assertj.core.api.Assertions; import org.jboss.fuse.mvnd.client.Client; -import org.jboss.fuse.mvnd.client.ClientLayout; +import org.jboss.fuse.mvnd.client.DaemonParameters; import org.jboss.fuse.mvnd.common.logging.ClientOutput; import org.jboss.fuse.mvnd.junit.MvndNativeTest; import org.jboss.fuse.mvnd.junit.TestUtils; @@ -39,16 +39,16 @@ public class SingleModuleNativeIT { Client client; @Inject - ClientLayout layout; + DaemonParameters parameters; @Test void cleanInstall() throws IOException, InterruptedException { - final Path helloFilePath = layout.multiModuleProjectDirectory().resolve("target/hello.txt"); + final Path helloFilePath = parameters.multiModuleProjectDirectory().resolve("target/hello.txt"); if (Files.exists(helloFilePath)) { Files.delete(helloFilePath); } - final Path localMavenRepo = layout.getLocalMavenRepository(); + final Path localMavenRepo = parameters.mavenRepoLocal(); TestUtils.deleteDir(localMavenRepo); final Path installedJar = localMavenRepo.resolve( "org/jboss/fuse/mvnd/test/single-module/single-module/0.0.1-SNAPSHOT/single-module-0.0.1-SNAPSHOT.jar"); @@ -56,7 +56,7 @@ void cleanInstall() throws IOException, InterruptedException { final ClientOutput o = Mockito.mock(ClientOutput.class); client.execute(o, "clean", "install", "-e").assertSuccess(); - final Properties props = MvndTestUtil.properties(layout.multiModuleProjectDirectory().resolve("pom.xml")); + final Properties props = MvndTestUtil.properties(parameters.multiModuleProjectDirectory().resolve("pom.xml")); final InOrder inOrder = Mockito.inOrder(o); inOrder.verify(o).accept(any(), Mockito.contains("Building single-module")); diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/UpgradesInBomNativeIT.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/UpgradesInBomNativeIT.java index 85bd51c78..39a9d21b9 100644 --- a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/UpgradesInBomNativeIT.java +++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/UpgradesInBomNativeIT.java @@ -25,7 +25,7 @@ import org.jboss.fuse.mvnd.common.logging.ClientOutput; import org.jboss.fuse.mvnd.junit.ClientFactory; import org.jboss.fuse.mvnd.junit.MvndNativeTest; -import org.jboss.fuse.mvnd.junit.TestLayout; +import org.jboss.fuse.mvnd.junit.TestParameters; import org.jboss.fuse.mvnd.junit.TestRegistry; import org.jboss.fuse.mvnd.junit.TestUtils; import org.junit.jupiter.api.Test; @@ -35,7 +35,7 @@ public class UpgradesInBomNativeIT { @Inject - TestLayout layout; + TestParameters parameters; @Inject TestRegistry registry; @@ -47,7 +47,7 @@ public class UpgradesInBomNativeIT { void upgrade() throws IOException, InterruptedException { /* Install the dependencies */ for (String artifactDir : Arrays.asList("project/hello-0.0.1", "project/hello-0.0.2-SNAPSHOT")) { - final Client cl = clientFactory.newClient(layout.cd(layout.getTestDir().resolve(artifactDir))); + final Client cl = clientFactory.newClient(parameters.cd(parameters.getTestDir().resolve(artifactDir))); final ClientOutput output = Mockito.mock(ClientOutput.class); cl.execute(output, "clean", "install", "-e").assertSuccess(); registry.killAll(); @@ -55,8 +55,8 @@ void upgrade() throws IOException, InterruptedException { Assertions.assertThat(registry.getAll().size()).isEqualTo(0); /* Build the initial state of the test project */ - final Path parentDir = layout.getTestDir().resolve("project/parent"); - final Client cl = clientFactory.newClient(layout.cd(parentDir)); + final Path parentDir = parameters.getTestDir().resolve("project/parent"); + final Client cl = clientFactory.newClient(parameters.cd(parentDir)); { final ClientOutput output = Mockito.mock(ClientOutput.class); cl.execute(output, "clean", "install", "-e").assertSuccess(); diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/VersionNativeIT.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/VersionNativeIT.java index 120d5d66b..b187f9dbd 100644 --- a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/VersionNativeIT.java +++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/VersionNativeIT.java @@ -20,7 +20,7 @@ import org.assertj.core.api.Assertions; import org.jboss.fuse.mvnd.assertj.MatchInOrderAmongOthers; import org.jboss.fuse.mvnd.client.Client; -import org.jboss.fuse.mvnd.client.ClientLayout; +import org.jboss.fuse.mvnd.client.DaemonParameters; import org.jboss.fuse.mvnd.common.logging.ClientOutput; import org.jboss.fuse.mvnd.junit.MvndNativeTest; import org.jboss.fuse.mvnd.junit.MvndTestExtension; @@ -37,7 +37,7 @@ public class VersionNativeIT { Client client; @Inject - ClientLayout layout; + DaemonParameters parameters; @Test void version() throws IOException, InterruptedException { @@ -55,6 +55,6 @@ void version() throws IOException, InterruptedException { + "-" + System.getProperty("os.detected.name") + "-" + System.getProperty("os.detected.arch") + "\\E", - "\\QMaven home: " + layout.mavenHome() + "\\E")); + "\\QMaven home: " + parameters.mvndHome() + "\\E")); } } diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/ClientFactory.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/ClientFactory.java index 0fe9f70c4..1825ac204 100644 --- a/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/ClientFactory.java +++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/ClientFactory.java @@ -16,8 +16,8 @@ package org.jboss.fuse.mvnd.junit; import org.jboss.fuse.mvnd.client.Client; -import org.jboss.fuse.mvnd.client.ClientLayout; +import org.jboss.fuse.mvnd.client.DaemonParameters; public interface ClientFactory { - Client newClient(ClientLayout layout); + Client newClient(DaemonParameters parameters); } diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/MvndTestExtension.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/MvndTestExtension.java index 8b30209d0..c9592e5ce 100644 --- a/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/MvndTestExtension.java +++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/MvndTestExtension.java @@ -26,12 +26,10 @@ import java.util.stream.Stream; import org.apache.log4j.Logger; import org.jboss.fuse.mvnd.client.Client; -import org.jboss.fuse.mvnd.client.ClientLayout; +import org.jboss.fuse.mvnd.client.DaemonParameters; import org.jboss.fuse.mvnd.client.DefaultClient; -import org.jboss.fuse.mvnd.common.BuildProperties; import org.jboss.fuse.mvnd.common.DaemonRegistry; import org.jboss.fuse.mvnd.common.Environment; -import org.jboss.fuse.mvnd.common.Layout; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; @@ -96,12 +94,13 @@ public void beforeEach(ExtensionContext context) throws Exception { f.setAccessible(true); if (DaemonRegistry.class.isAssignableFrom(f.getType())) { f.set(testInstance, resource.registry); - } else if (Layout.class.isAssignableFrom(f.getType())) { - f.set(testInstance, resource.layout); + } else if (DaemonParameters.class.isAssignableFrom(f.getType())) { + f.set(testInstance, resource.parameters); } else if (f.getType() == Client.class) { - f.set(testInstance, newClient(resource.isNative, resource.layout, resource.timeoutMs)); + f.set(testInstance, newClient(resource.isNative, resource.parameters, resource.timeoutMs)); } else if (f.getType() == ClientFactory.class) { - final ClientFactory cf = customLayout -> newClient(resource.isNative, customLayout, resource.timeoutMs); + final ClientFactory cf = customParameters -> newClient(resource.isNative, customParameters, + resource.timeoutMs); f.set(testInstance, cf); } } @@ -110,9 +109,9 @@ public void beforeEach(ExtensionContext context) throws Exception { } } - Client newClient(boolean isNative, ClientLayout layout, long timeoutMs) { + Client newClient(boolean isNative, DaemonParameters parameters, long timeoutMs) { if (isNative) { - final Path mvndNativeExecutablePath = layout.mavenHome().resolve( + final Path mvndNativeExecutablePath = parameters.mvndHome().resolve( System.getProperty("os.name").toLowerCase(Locale.ROOT).startsWith("windows") ? "bin/mvnd.exe" : "bin/mvnd") @@ -120,9 +119,9 @@ Client newClient(boolean isNative, ClientLayout layout, long timeoutMs) { if (!Files.isRegularFile(mvndNativeExecutablePath)) { throw new IllegalStateException("mvnd executable does not exist: " + mvndNativeExecutablePath); } - return new NativeTestClient(layout, mvndNativeExecutablePath, timeoutMs); + return new NativeTestClient(parameters, mvndNativeExecutablePath, timeoutMs); } else { - return new DefaultClient(() -> layout, BuildProperties.getInstance()); + return new DefaultClient(() -> parameters); } } @@ -137,7 +136,7 @@ public void afterAll(ExtensionContext context) throws Exception { static class MvndResource implements ExtensionContext.Store.CloseableResource { - private final TestLayout layout; + private final TestParameters parameters; private final TestRegistry registry; private final boolean isNative; private final long timeoutMs; @@ -181,7 +180,7 @@ public static MvndResource create(String className, String rawProjectDir, boolea } } final Path multiModuleProjectDirectory = Paths - .get(Environment.findDefaultMultimoduleProjectDirectory(testExecutionDir)); + .get(DaemonParameters.findDefaultMultimoduleProjectDirectory(testExecutionDir)); final Path mvndHome = Paths .get(Objects.requireNonNull(System.getProperty("mvnd.home"), "System property mvnd.home must be set")) @@ -195,10 +194,12 @@ public static MvndResource create(String className, String rawProjectDir, boolea final Path localMavenRepository = deleteDir(testDir.resolve("local-maven-repo")); final Path settingsPath = createSettings(testDir.resolve("settings.xml")); final Path logback = Paths.get("src/test/resources/logback.xml").toAbsolutePath(); - final TestLayout layout = new TestLayout( + final Path home = deleteDir(testDir.resolve("home")); + final TestParameters parameters = new TestParameters( testDir, mvndPropertiesPath, mvndHome, + home, testExecutionDir, multiModuleProjectDirectory, Paths.get(System.getProperty("java.home")).toAbsolutePath().normalize(), @@ -206,10 +207,10 @@ public static MvndResource create(String className, String rawProjectDir, boolea logback, Environment.DEFAULT_IDLE_TIMEOUT, Environment.DEFAULT_KEEP_ALIVE, - Environment.DEFAULT_MAX_LOST_KEEP_ALIVE); - final TestRegistry registry = new TestRegistry(layout.registry()); + Integer.parseInt(Environment.DAEMON_MAX_LOST_KEEP_ALIVE.getDef())); + final TestRegistry registry = new TestRegistry(parameters.registry()); - return new MvndResource(layout, registry, isNative, timeoutMs); + return new MvndResource(parameters, registry, isNative, timeoutMs); } static Path createSettings(Path settingsPath) { @@ -234,9 +235,9 @@ static Path createSettings(Path settingsPath) { return settingsPath; } - public MvndResource(TestLayout layout, TestRegistry registry, boolean isNative, long timeoutMs) { + public MvndResource(TestParameters parameters, TestRegistry registry, boolean isNative, long timeoutMs) { super(); - this.layout = layout; + this.parameters = parameters; this.registry = registry; this.isNative = isNative; this.timeoutMs = timeoutMs; diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/NativeTestClient.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/NativeTestClient.java index bc3709bc8..b3127524a 100644 --- a/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/NativeTestClient.java +++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/NativeTestClient.java @@ -22,7 +22,7 @@ import java.util.Map; import java.util.function.Consumer; import org.jboss.fuse.mvnd.client.Client; -import org.jboss.fuse.mvnd.client.ClientLayout; +import org.jboss.fuse.mvnd.client.DaemonParameters; import org.jboss.fuse.mvnd.client.ExecutionResult; import org.jboss.fuse.mvnd.common.Environment; import org.jboss.fuse.mvnd.common.OsUtils.CommandProcess; @@ -33,15 +33,15 @@ */ public class NativeTestClient implements Client { - private final ClientLayout layout; + private final DaemonParameters parameters; private final Path mvndNativeExecutablePath; private final long timeoutMs; - public NativeTestClient(ClientLayout layout, Path mvndNativeExecutablePath, long timeoutMs) { + public NativeTestClient(DaemonParameters parameters, Path mvndNativeExecutablePath, long timeoutMs) { super(); - this.layout = layout; + this.parameters = parameters; this.mvndNativeExecutablePath = mvndNativeExecutablePath; this.timeoutMs = timeoutMs; } @@ -51,24 +51,27 @@ public ExecutionResult execute(ClientOutput output, List args) throws In final List cmd = new ArrayList(args.size() + 1); cmd.add(mvndNativeExecutablePath.toString()); cmd.addAll(args); - if (!Environment.MVND_PROPERTIES_PATH.hasCommandLineProperty(args)) { - cmd.add(Environment.MVND_PROPERTIES_PATH.asCommandLineProperty(layout.getMvndPropertiesPath().toString())); + if (!Environment.MVND_DAEMON_STORAGE.hasCommandLineProperty(args)) { + Path daemonStorage = parameters.daemonStorage(); + cmd.add(Environment.MVND_DAEMON_STORAGE.asCommandLineProperty(daemonStorage.toString())); } if (!Environment.MAVEN_REPO_LOCAL.hasCommandLineProperty(args)) { - cmd.add(Environment.MAVEN_REPO_LOCAL.asCommandLineProperty(layout.getLocalMavenRepository().toString())); + Path mavenRepoLocal = parameters.mavenRepoLocal(); + cmd.add(Environment.MAVEN_REPO_LOCAL.asCommandLineProperty(mavenRepoLocal.toString())); } - final Path settings = layout.getSettings(); - if (settings != null && args.stream().noneMatch(arg -> arg.equals("-s") || arg.equals("--settings"))) { - cmd.add("-s"); - cmd.add(settings.toString()); + if (!Environment.MAVEN_SETTINGS.hasCommandLineProperty(args)) { + final Path settings = parameters.settings(); + if (settings != null) { + cmd.add(Environment.MAVEN_SETTINGS.asCommandLineProperty(settings.toString())); + } } - if (args.stream().noneMatch(arg -> arg.startsWith("-T") || arg.equals("--threads"))) { - final int threads = Math.max(Runtime.getRuntime().availableProcessors() - 1, TestLayout.TEST_MIN_THREADS); - cmd.add("-T" + threads); + if (!Environment.MVND_THREADS.hasCommandLineProperty(args)) { + final String threads = parameters.threads(); + cmd.add(Environment.MVND_THREADS.asCommandLineProperty(threads)); } final ProcessBuilder builder = new ProcessBuilder(cmd.toArray(new String[0])) - .directory(layout.userDir().toFile()) // + .directory(parameters.userDir().toFile()) // .redirectErrorStream(true); final Map env = builder.environment(); diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/TestLayout.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/TestLayout.java deleted file mode 100644 index 01f7656de..000000000 --- a/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/TestLayout.java +++ /dev/null @@ -1,38 +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.junit; - -import java.nio.file.Path; -import org.jboss.fuse.mvnd.client.ClientLayout; - -public class TestLayout extends ClientLayout { - static final int TEST_MIN_THREADS = 2; - private final Path testDir; - - public TestLayout(Path testDir, Path mvndPropertiesPath, Path mavenHome, Path userDir, Path multiModuleProjectDirectory, - Path javaHome, Path localMavenRepository, Path settings, Path logbackConfigurationPath, - int idleTimeout, int keepAlive, int maxLostKeepAlive) { - super(mvndPropertiesPath, mavenHome, userDir, multiModuleProjectDirectory, javaHome, localMavenRepository, - settings, logbackConfigurationPath, idleTimeout, keepAlive, maxLostKeepAlive, - String.valueOf(Math.max(Runtime.getRuntime().availableProcessors() - 1, TEST_MIN_THREADS))); - this.testDir = testDir; - } - - public Path getTestDir() { - return testDir; - } - -} diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/TestParameters.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/TestParameters.java new file mode 100644 index 000000000..0ce27c461 --- /dev/null +++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/junit/TestParameters.java @@ -0,0 +1,58 @@ +/* + * 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.junit; + +import java.nio.file.Path; +import java.util.Properties; +import org.jboss.fuse.mvnd.client.DaemonParameters; +import org.jboss.fuse.mvnd.common.Environment; + +public class TestParameters extends DaemonParameters { + static final int TEST_MIN_THREADS = 2; + private final Path testDir; + + public TestParameters(Path testDir, Path mvndPropertiesPath, Path mavenHome, Path userHome, Path userDir, + Path multiModuleProjectDirectory, + Path javaHome, Path localMavenRepository, Path settings, Path logbackConfigurationPath, + int idleTimeout, int keepAlive, int maxLostKeepAlive) { + super(new Properties()); + this.testDir = testDir; + put(Environment.MVND_PROPERTIES_PATH, mvndPropertiesPath); + put(Environment.MVND_HOME, mavenHome); + put(Environment.USER_HOME, userHome); + put(Environment.USER_DIR, userDir); + put(Environment.MAVEN_MULTIMODULE_PROJECT_DIRECTORY, multiModuleProjectDirectory); + put(Environment.JAVA_HOME, javaHome); + put(Environment.MAVEN_REPO_LOCAL, localMavenRepository); + put(Environment.MAVEN_SETTINGS, settings); + put(Environment.LOGBACK_CONFIGURATION_FILE, logbackConfigurationPath); + put(Environment.DAEMON_IDLE_TIMEOUT_MS, idleTimeout); + put(Environment.DAEMON_KEEP_ALIVE_MS, keepAlive); + put(Environment.DAEMON_MAX_LOST_KEEP_ALIVE, maxLostKeepAlive); + put(Environment.MVND_MIN_THREADS, TEST_MIN_THREADS); + } + + private void put(Environment env, Object value) { + if (value != null) { + this.properties.put(env.getProperty(), value.toString()); + } + } + + public Path getTestDir() { + return testDir; + } + +}