diff --git a/client/src/main/java/org/mvndaemon/mvnd/client/DaemonClientConnection.java b/client/src/main/java/org/mvndaemon/mvnd/client/DaemonClientConnection.java index b3e357dc5..30ceb9609 100644 --- a/client/src/main/java/org/mvndaemon/mvnd/client/DaemonClientConnection.java +++ b/client/src/main/java/org/mvndaemon/mvnd/client/DaemonClientConnection.java @@ -94,7 +94,7 @@ public void dispatch(Message message) throws DaemonException.ConnectException { } public List<Message> receive() throws ConnectException, StaleAddressException { - int maxKeepAliveMs = parameters.keepAliveMs() * parameters.maxLostKeepAlive(); + long maxKeepAliveMs = parameters.keepAlive().toMillis() * parameters.maxLostKeepAlive(); while (true) { try { final Message m = queue.poll(maxKeepAliveMs, TimeUnit.MILLISECONDS); diff --git a/client/src/main/java/org/mvndaemon/mvnd/client/DaemonParameters.java b/client/src/main/java/org/mvndaemon/mvnd/client/DaemonParameters.java index 2dae6196f..e4a363911 100644 --- a/client/src/main/java/org/mvndaemon/mvnd/client/DaemonParameters.java +++ b/client/src/main/java/org/mvndaemon/mvnd/client/DaemonParameters.java @@ -21,6 +21,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -41,6 +42,7 @@ import org.mvndaemon.mvnd.common.BuildProperties; import org.mvndaemon.mvnd.common.Environment; import org.mvndaemon.mvnd.common.Os; +import org.mvndaemon.mvnd.common.TimeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,6 +51,8 @@ */ public class DaemonParameters { + public static final String LOG_EXTENSION = ".log"; + 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"; @@ -182,11 +186,11 @@ public Path registry() { } public Path daemonLog(String daemon) { - return daemonStorage().resolve("daemon-" + daemon + ".log"); + return daemonStorage().resolve("daemon-" + daemon + LOG_EXTENSION); } public Path daemonOutLog(String daemon) { - return daemonStorage().resolve("daemon-" + daemon + ".out.log"); + return daemonStorage().resolve("daemon-" + daemon + ".out" + LOG_EXTENSION); } public Path multiModuleProjectDirectory() { @@ -268,8 +272,8 @@ public DaemonParameters cd(Path newUserDir) { .put(Environment.USER_DIR, newUserDir)); } - public int keepAliveMs() { - return property(Environment.DAEMON_KEEP_ALIVE_MS).orFail().asInt(); + public Duration keepAlive() { + return property(Environment.DAEMON_KEEP_ALIVE).orFail().asDuration(); } public int maxLostKeepAlive() { @@ -284,6 +288,10 @@ public int rollingWindowSize() { return property(Environment.MVND_ROLLING_WINDOW_SIZE).orFail().asInt(); } + public Duration purgeLogPeriod() { + return property(Environment.MVND_LOG_PURGE_PERIOD).orFail().asDuration(); + } + public static String findDefaultMultimoduleProjectDirectory(Path pwd) { Path dir = pwd; do { @@ -579,5 +587,9 @@ public int asInt(IntUnaryOperator function) { return function.applyAsInt(asInt()); } + public Duration asDuration() { + return TimeUtils.toDuration(get()); + } + } } diff --git a/client/src/main/java/org/mvndaemon/mvnd/client/DefaultClient.java b/client/src/main/java/org/mvndaemon/mvnd/client/DefaultClient.java index 05f0b3738..7981f16ff 100644 --- a/client/src/main/java/org/mvndaemon/mvnd/client/DefaultClient.java +++ b/client/src/main/java/org/mvndaemon/mvnd/client/DefaultClient.java @@ -15,13 +15,22 @@ */ package org.mvndaemon.mvnd.client; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.FileTime; +import java.time.Duration; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import org.fusesource.jansi.Ansi; import org.jline.utils.AttributedString; import org.jline.utils.AttributedStyle; @@ -33,11 +42,14 @@ import org.mvndaemon.mvnd.common.Message; import org.mvndaemon.mvnd.common.Message.BuildException; import org.mvndaemon.mvnd.common.OsUtils; +import org.mvndaemon.mvnd.common.TimeUtils; import org.mvndaemon.mvnd.common.logging.ClientOutput; import org.mvndaemon.mvnd.common.logging.TerminalOutput; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.mvndaemon.mvnd.client.DaemonParameters.LOG_EXTENSION; + public class DefaultClient implements Client { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultClient.class); @@ -183,6 +195,12 @@ public ExecutionResult execute(ClientOutput output, List<String> argv) { } return new DefaultResult(argv, null); } + boolean purge = args.remove("--purge"); + if (purge) { + String result = purgeLogs(); + output.accept(Message.display(result != null ? result : "Nothing to purge")); + return new DefaultResult(argv, null); + } if (args.stream().noneMatch(arg -> arg.startsWith("-T") || arg.equals("--threads"))) { args.add("--threads"); @@ -216,27 +234,112 @@ public ExecutionResult execute(ClientOutput output, List<String> argv) { output.accept(Message.buildStatus("Build request sent")); - while (true) { - final List<Message> messages = daemon.receive(); - output.accept(messages); - for (Message m : messages) { - switch (m.getType()) { - case Message.CANCEL_BUILD: - return new DefaultResult(argv, - new InterruptedException("The build was canceled")); - case Message.BUILD_EXCEPTION: - final BuildException e = (BuildException) m; - return new DefaultResult(argv, - new Exception(e.getClassName() + ": " + e.getMessage() + "\n" + e.getStackTrace())); - case Message.BUILD_STOPPED: - return new DefaultResult(argv, null); + // We've sent the request, so it gives us a bit of time to purge the logs + AtomicReference<String> purgeMessage = new AtomicReference<>(); + Thread purgeLog = new Thread(() -> { + purgeMessage.set(purgeLogs()); + }, "Log purge"); + purgeLog.setDaemon(true); + purgeLog.start(); + + try { + while (true) { + final List<Message> messages = daemon.receive(); + output.accept(messages); + for (Message m : messages) { + switch (m.getType()) { + case Message.CANCEL_BUILD: + return new DefaultResult(argv, + new InterruptedException("The build was canceled")); + case Message.BUILD_EXCEPTION: + final BuildException e = (BuildException) m; + return new DefaultResult(argv, + new Exception(e.getClassName() + ": " + e.getMessage() + "\n" + e.getStackTrace())); + case Message.BUILD_STOPPED: + return new DefaultResult(argv, null); + } } } + } finally { + String msg = purgeMessage.get(); + if (msg != null) { + output.accept(Message.display(msg)); + } } } } } + private String purgeLogs() { + Path storage = parameters.daemonStorage(); + Duration purgeLogPeriod = parameters.purgeLogPeriod(); + if (!Files.isDirectory(storage) || !TimeUtils.isPositive(purgeLogPeriod)) { + return null; + } + String date = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.systemDefault()).format(Instant.now()); + Path log = storage.resolve("purge-" + date + ".log"); + List<Path> deleted = new ArrayList<>(); + List<Throwable> exceptions = new ArrayList<>(); + FileTime limit = FileTime.from(Instant.now().minus(purgeLogPeriod)); + try { + Files.list(storage) + .filter(p -> p.getFileName().toString().endsWith(LOG_EXTENSION)) + .filter(p -> !log.equals(p)) + .filter(p -> { + try { + FileTime lmt = Files.getLastModifiedTime(p); + return lmt.compareTo(limit) < 0; + } catch (IOException e) { + exceptions.add(e); + return false; + } + }) + .forEach(p -> { + try { + Files.delete(p); + deleted.add(p); + } catch (IOException e) { + exceptions.add(e); + } + }); + } catch (Exception e) { + exceptions.add(e); + } + if (exceptions.isEmpty() && deleted.isEmpty()) { + return null; + } + String logMessage; + try (PrintWriter w = new PrintWriter(Files.newBufferedWriter(log, + StandardOpenOption.WRITE, StandardOpenOption.APPEND, StandardOpenOption.CREATE))) { + w.printf("Purge executed at %s%n", Instant.now().toString()); + if (deleted.isEmpty()) { + w.printf("No files deleted.%n"); + } else { + w.printf("Deleted files:%n"); + for (Path p : deleted) { + w.printf(" %s%n", p.toString()); + } + } + if (!exceptions.isEmpty()) { + w.printf("%d exception(s) occurred during the purge", exceptions.size()); + for (Throwable t : exceptions) { + t.printStackTrace(w); + } + } + char[] buf = new char[80]; + Arrays.fill(buf, '='); + w.printf("%s%n", new String(buf)); + logMessage = "log available in " + log.toString(); + } catch (IOException e) { + logMessage = "an exception occurred when writing log to " + log.toString() + ": " + e.toString(); + } + if (exceptions.isEmpty()) { + return String.format("Purged %d log files (%s)", deleted.size(), logMessage); + } else { + return String.format("Purged %d log files with %d exceptions (%s)", deleted.size(), exceptions.size(), logMessage); + } + } + private static class DefaultResult implements ExecutionResult { private final Exception exception; diff --git a/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java b/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java index c23d8efe0..e6eb6e3ff 100644 --- a/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java +++ b/common/src/main/java/org/mvndaemon/mvnd/common/Environment.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 the original author or authors. + * Copyright 2020 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. @@ -17,14 +17,20 @@ import java.nio.file.Path; import java.nio.file.Paths; +import java.time.Duration; import java.util.Collection; import java.util.Locale; import java.util.Objects; import java.util.Properties; -import java.util.concurrent.TimeUnit; /** * Collects system properties and environment variables used by mvnd client or server. + * + * Duration properties such as {@link #DAEMON_IDLE_TIMEOUT}, {@link #DAEMON_KEEP_ALIVE}, + * {@link #DAEMON_EXPIRATION_CHECK_DELAY} or {@link #MVND_LOG_PURGE_PERIOD} are expressed + * in a human readable format such as {@code 2h30m}, {@code 600ms} or {@code 10 seconds}. + * The available units are <i>d/day/days</i>, <i>h/hour/hours</i>, <i>m/min/minute/minutes</i>, + * <i>s/sec/second/seconds</i> and <i>ms/millis/msec/milliseconds</i>. */ public enum Environment { // @@ -68,14 +74,18 @@ public String asCommandLineProperty(String value) { * The number of log lines to display for each Maven module that is built in parallel. */ MVND_ROLLING_WINDOW_SIZE("mvnd.rollingWindowSize", null, "0", false), + /** + * The automatic log purge period + */ + MVND_LOG_PURGE_PERIOD("mvnd.logPurgePeriod", null, "1w", false), /** * The path to the daemon registry */ DAEMON_REGISTRY("daemon.registry", null, null, false), MVND_NO_DAEMON("mvnd.noDaemon", "MVND_NO_DAEMON", "false", true), 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_IDLE_TIMEOUT("daemon.idleTimeout", null, "3 hours", true), + DAEMON_KEEP_ALIVE("daemon.keepAlive", null, "1 sec", 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. @@ -102,7 +112,7 @@ public String asCommandLineProperty(String value) { /** * The maven builder name to use. Ignored if the user passes * - * @{@code -b} or @{@code --builder} on the command line + * {@code -b} or {@code --builder} on the command line */ MVND_BUILDER("mvnd.builder", null, "smart", false) { @Override @@ -146,17 +156,13 @@ public String asCommandLineProperty(String value) { /** * Interval to check if the daemon should expire */ - DAEMON_EXPIRATION_CHECK_DELAY_MS("daemon.expirationCheckDelayMs", null, TimeUnit.SECONDS.toMillis(10), true), + DAEMON_EXPIRATION_CHECK_DELAY("daemon.expirationCheckDelay", null, "10 seconds", true), /** * Period after which idle daemons will shut down */ - DAEMON_DUPLICATE_DAEMON_GRACE_PERIOD_MS("daemon.duplicateDaemonGracePeriodMs", null, TimeUnit.SECONDS.toMillis(10), true), + DAEMON_DUPLICATE_DAEMON_GRACE_PERIOD("daemon.duplicateDaemonGracePeriod", null, "10 seconds", 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); - static Properties properties = System.getProperties(); public static void setProperties(Properties properties) { @@ -219,6 +225,10 @@ public Path asPath() { return Paths.get(result); } + public Duration asDuration() { + return TimeUtils.toDuration(asString()); + } + public String asCommandLineProperty(String value) { return "-D" + property + "=" + value; } diff --git a/common/src/main/java/org/mvndaemon/mvnd/common/TimeUtils.java b/common/src/main/java/org/mvndaemon/mvnd/common/TimeUtils.java new file mode 100644 index 000000000..ea07effb7 --- /dev/null +++ b/common/src/main/java/org/mvndaemon/mvnd/common/TimeUtils.java @@ -0,0 +1,121 @@ +/* + * Copyright 2020 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.mvndaemon.mvnd.common; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Time utils. + */ +public final class TimeUtils { + + private static final int ONE_UNIT = 1; + public static final long DAYS_MILLIS = TimeUnit.DAYS.toMillis(ONE_UNIT); + public static final long HOURS_MILLIS = TimeUnit.HOURS.toMillis(ONE_UNIT); + public static final long MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(ONE_UNIT); + public static final long SECONDS_MILLIS = TimeUnit.SECONDS.toMillis(ONE_UNIT); + + private static final Pattern DURATION_PATTERN = Pattern.compile( + "(?<n>-?\\d+)" + + "|" + + "(" + + "((?<d>\\d+)\\s*d(ay(s)?)?)?" + "\\s*" + + "((?<h>\\d+)\\s*h(our(s)?)?)?" + "\\s*" + + "((?<m>\\d+)\\s*m(in(ute(s)?)?)?)?" + "\\s*" + + "((?<s>\\d+(\\.\\d+)?)\\s*s(ec(ond(s)?)?)?)?" + "\\s*" + + "((?<l>\\d+(\\.\\d+)?)\\s*m(illi)?s(ec(ond)?(s)?)?)?" + + ")", + Pattern.CASE_INSENSITIVE); + + private TimeUtils() { + } + + public static boolean isPositive(Duration dur) { + return dur.getSeconds() > 0 || dur.getNano() != 0; + } + + public static String printDuration(Duration uptime) { + return printDuration(uptime.toMillis()); + } + + /** + * This will print time in human readable format from milliseconds. + * Examples: + * 500 -> 500ms + * 1300 -> 1s300ms + * 310300 -> 5m10s300ms + * 6600000 -> 1h50m + * + * @param millis time in milliseconds + * @return time in string + */ + public static String printDuration(long millis) { + if (millis < 0) { + return Long.toString(millis); + } + final StringBuilder sb = new StringBuilder(); + if (millis >= DAYS_MILLIS) { + sb.append(millis / DAYS_MILLIS).append("d"); + millis %= DAYS_MILLIS; + } + if (millis >= HOURS_MILLIS) { + sb.append(millis / HOURS_MILLIS).append("h"); + millis %= HOURS_MILLIS; + } + if (millis >= MINUTES_MILLIS) { + sb.append(millis / MINUTES_MILLIS).append("m"); + millis %= MINUTES_MILLIS; + } + if (millis >= SECONDS_MILLIS) { + sb.append(millis / SECONDS_MILLIS).append("s"); + millis %= SECONDS_MILLIS; + } + if (millis >= ONE_UNIT || sb.length() == 0) { + sb.append(millis / ONE_UNIT).append("ms"); + } + return sb.toString(); + } + + public static Duration toDuration(String source) throws IllegalArgumentException { + return Duration.ofMillis(toMilliSeconds(source)); + } + + public static long toMilliSeconds(String source) throws IllegalArgumentException { + Matcher matcher = DURATION_PATTERN.matcher(source); + if (!matcher.matches()) { + throw new IllegalArgumentException("Unable to parse duration: '" + source + "'"); + } + String n = matcher.group("n"); + if (n != null) { + return Long.parseLong(n); + } else { + String d = matcher.group("d"); + String h = matcher.group("h"); + String m = matcher.group("m"); + String s = matcher.group("s"); + String l = matcher.group("l"); + return (d != null ? TimeUnit.DAYS.toMillis(Long.parseLong(d)) : 0) + + (h != null ? TimeUnit.HOURS.toMillis(Long.parseLong(h)) : 0) + + (m != null ? TimeUnit.MINUTES.toMillis(Long.parseLong(m)) : 0) + + (s != null ? TimeUnit.SECONDS.toMillis(Long.parseLong(s)) : 0) + + (l != null ? TimeUnit.MILLISECONDS.toMillis(Long.parseLong(l)) : 0); + } + } + +} diff --git a/common/src/test/java/org/mvndaemon/mvnd/common/TimeUtilsTest.java b/common/src/test/java/org/mvndaemon/mvnd/common/TimeUtilsTest.java new file mode 100644 index 000000000..12bfcc25a --- /dev/null +++ b/common/src/test/java/org/mvndaemon/mvnd/common/TimeUtilsTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2020 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.mvndaemon.mvnd.common; + +import java.time.Duration; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TimeUtilsTest { + + @Test + public void testToTimeAsString() { + assertEquals("600ms", TimeUtils.printDuration(TimeUtils.toDuration("600ms"))); + assertEquals("-1", TimeUtils.printDuration(TimeUtils.toDuration("-1"))); + assertEquals("0ms", TimeUtils.printDuration(TimeUtils.toDuration("0ms"))); + assertEquals("1s", TimeUtils.printDuration(TimeUtils.toDuration("1000ms"))); + assertEquals("1m600ms", TimeUtils.printDuration(TimeUtils.toDuration("1minute 600ms"))); + assertEquals("1m1s100ms", TimeUtils.printDuration(TimeUtils.toDuration("1m1100ms"))); + assertEquals("5m10s300ms", TimeUtils.printDuration(310300)); + assertEquals("5s500ms", TimeUtils.printDuration(5500)); + assertEquals("1h50m", TimeUtils.printDuration(6600000)); + assertEquals("2d3h4m", TimeUtils.printDuration(Duration.parse("P2DT3H4M").toMillis())); + assertEquals("2d4m", TimeUtils.printDuration(Duration.parse("P2DT4M").toMillis())); + } + +} diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/daemon/DaemonExpiration.java b/daemon/src/main/java/org/mvndaemon/mvnd/daemon/DaemonExpiration.java index bbbda8a2b..d91d59a05 100644 --- a/daemon/src/main/java/org/mvndaemon/mvnd/daemon/DaemonExpiration.java +++ b/daemon/src/main/java/org/mvndaemon/mvnd/daemon/DaemonExpiration.java @@ -17,6 +17,8 @@ import java.nio.file.Files; import java.nio.file.Paths; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -28,6 +30,7 @@ import org.mvndaemon.mvnd.common.DaemonInfo; import org.mvndaemon.mvnd.common.DaemonState; import org.mvndaemon.mvnd.common.Environment; +import org.mvndaemon.mvnd.common.TimeUtils; import static org.mvndaemon.mvnd.common.DaemonExpirationStatus.DO_NOT_EXPIRE; import static org.mvndaemon.mvnd.common.DaemonExpirationStatus.GRACEFUL_EXPIRE; @@ -41,8 +44,6 @@ */ public class DaemonExpiration { - public static final int DUPLICATE_DAEMON_GRACE_PERIOD_MS = 10000; - public interface DaemonExpirationStrategy { DaemonExpirationResult checkExpiration(Server daemon); @@ -53,7 +54,7 @@ public static DaemonExpirationStrategy master() { return any( any(gcTrashing(), lowHeapSpace(), lowNonHeap()), all(compatible(), duplicateGracePeriod(), notMostRecentlyUsed()), - idleTimeout(Environment.DAEMON_IDLE_TIMEOUT_MS.asInt()), + idleTimeout(Environment.DAEMON_IDLE_TIMEOUT.asDuration()), all(duplicateGracePeriod(), notMostRecentlyUsed(), lowMemory(0.05)), registryUnavailable()); } @@ -82,26 +83,14 @@ static DaemonExpirationStrategy lowMemory(double minFreeMemoryPercentage) { } static DaemonExpirationStrategy duplicateGracePeriod() { - return idleTimeout(Environment.DAEMON_DUPLICATE_DAEMON_GRACE_PERIOD_MS.asInt()); + return idleTimeout(Environment.DAEMON_DUPLICATE_DAEMON_GRACE_PERIOD.asDuration()); } - private static final long HOUR = 60 * 60 * 1000; - private static final long MINUTE = 60 * 1000; - private static final long SECOND = 1000; - - static DaemonExpirationStrategy idleTimeout(long timeout) { + static DaemonExpirationStrategy idleTimeout(Duration timeout) { return daemon -> { - long idl = System.currentTimeMillis() - daemon.getLastIdle(); - if (daemon.getState() == DaemonState.Idle && idl > timeout) { - String str; - if (idl > HOUR) { - str = (idl / HOUR) + " hours"; - } else if (idl > MINUTE) { - str = (idl / MINUTE) + " minutes"; - } else { - str = (idl / SECOND) + " seconds"; - } - return new DaemonExpirationResult(QUIET_EXPIRE, "after being idle for " + str); + Duration idl = Duration.between(Instant.ofEpochMilli(daemon.getLastIdle()), Instant.now()); + if (daemon.getState() == DaemonState.Idle && idl.compareTo(timeout) > 0) { + return new DaemonExpirationResult(QUIET_EXPIRE, "after being idle for " + TimeUtils.printDuration(idl)); } else { return NOT_TRIGGERED; } diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java b/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java index c3b1d506d..1703e19e0 100644 --- a/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java +++ b/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java @@ -21,6 +21,7 @@ import java.net.InetSocketAddress; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -165,9 +166,9 @@ public void clearCache(String clazzName, String fieldName) { public void run() { try { - int expirationCheckDelayMs = Environment.DAEMON_EXPIRATION_CHECK_DELAY_MS.asInt(); + Duration expirationCheckDelay = Environment.DAEMON_EXPIRATION_CHECK_DELAY.asDuration(); executor.scheduleAtFixedRate(this::expirationCheck, - expirationCheckDelayMs, expirationCheckDelayMs, TimeUnit.MILLISECONDS); + expirationCheckDelay.toMillis(), expirationCheckDelay.toMillis(), TimeUnit.MILLISECONDS); LOGGER.info("Daemon started"); if (noDaemon) { try (SocketChannel socket = this.socket.accept()) { @@ -405,7 +406,7 @@ private void cancelNow() { private void handle(DaemonConnection connection, BuildRequest buildRequest) { updateState(Busy); try { - int keepAlive = Environment.DAEMON_KEEP_ALIVE_MS.asInt(); + Duration keepAlive = Environment.DAEMON_KEEP_ALIVE.asDuration(); LOGGER.info("Executing request"); @@ -421,7 +422,7 @@ private void handle(DaemonConnection connection, BuildRequest buildRequest) { while (true) { Message m; if (flushed) { - m = sendQueue.poll(keepAlive, TimeUnit.MILLISECONDS); + m = sendQueue.poll(keepAlive.toMillis(), TimeUnit.MILLISECONDS); if (m == null) { m = Message.KEEP_ALIVE_SINGLETON; } diff --git a/integration-tests/src/test/java/org/mvndaemon/mvnd/junit/MvndTestExtension.java b/integration-tests/src/test/java/org/mvndaemon/mvnd/junit/MvndTestExtension.java index 7d44db20d..ed9134864 100644 --- a/integration-tests/src/test/java/org/mvndaemon/mvnd/junit/MvndTestExtension.java +++ b/integration-tests/src/test/java/org/mvndaemon/mvnd/junit/MvndTestExtension.java @@ -35,6 +35,7 @@ import org.mvndaemon.mvnd.client.DefaultClient; import org.mvndaemon.mvnd.common.DaemonRegistry; import org.mvndaemon.mvnd.common.Environment; +import org.mvndaemon.mvnd.common.TimeUtils; import static org.mvndaemon.mvnd.junit.TestUtils.deleteDir; @@ -205,8 +206,8 @@ public static MvndResource create(String className, String rawProjectDir, boolea Paths.get(System.getProperty("java.home")).toAbsolutePath().normalize(), localMavenRepository, settingsPath, logback, - Environment.DEFAULT_IDLE_TIMEOUT, - Environment.DEFAULT_KEEP_ALIVE, + TimeUtils.toDuration(Environment.DAEMON_IDLE_TIMEOUT.getDef()), + TimeUtils.toDuration(Environment.DAEMON_KEEP_ALIVE.getDef()), Integer.parseInt(Environment.DAEMON_MAX_LOST_KEEP_ALIVE.getDef())); final TestRegistry registry = new TestRegistry(parameters.registry()); diff --git a/integration-tests/src/test/java/org/mvndaemon/mvnd/junit/TestParameters.java b/integration-tests/src/test/java/org/mvndaemon/mvnd/junit/TestParameters.java index a42247b5b..0f48c5357 100644 --- a/integration-tests/src/test/java/org/mvndaemon/mvnd/junit/TestParameters.java +++ b/integration-tests/src/test/java/org/mvndaemon/mvnd/junit/TestParameters.java @@ -16,8 +16,10 @@ package org.mvndaemon.mvnd.junit; import java.nio.file.Path; +import java.time.Duration; import org.mvndaemon.mvnd.client.DaemonParameters; import org.mvndaemon.mvnd.common.Environment; +import org.mvndaemon.mvnd.common.TimeUtils; public class TestParameters extends DaemonParameters { static final int TEST_MIN_THREADS = 2; @@ -26,7 +28,7 @@ public class TestParameters extends DaemonParameters { 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) { + Duration idleTimeout, Duration keepAlive, int maxLostKeepAlive) { super(new PropertiesBuilder().put(Environment.MVND_PROPERTIES_PATH, mvndPropertiesPath) .put(Environment.MVND_HOME, mavenHome) .put(Environment.USER_HOME, userHome) @@ -36,8 +38,8 @@ public TestParameters(Path testDir, Path mvndPropertiesPath, Path mavenHome, Pat .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_IDLE_TIMEOUT, TimeUtils.printDuration(idleTimeout)) + .put(Environment.DAEMON_KEEP_ALIVE, TimeUtils.printDuration(keepAlive)) .put(Environment.DAEMON_MAX_LOST_KEEP_ALIVE, maxLostKeepAlive) .put(Environment.MVND_MIN_THREADS, TEST_MIN_THREADS)); this.testDir = testDir;