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 7732c85b5..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. @@ -25,6 +25,12 @@ /** * 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 d/day/days, h/hour/hours, m/min/minute/minutes, + * s/sec/second/seconds and ms/millis/msec/milliseconds. */ public enum Environment { // @@ -106,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 diff --git a/common/src/main/java/org/mvndaemon/mvnd/common/TimeUtils.java b/common/src/main/java/org/mvndaemon/mvnd/common/TimeUtils.java index da72775d9..ea07effb7 100644 --- a/common/src/main/java/org/mvndaemon/mvnd/common/TimeUtils.java +++ b/common/src/main/java/org/mvndaemon/mvnd/common/TimeUtils.java @@ -1,10 +1,9 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 + * 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 * @@ -16,36 +15,33 @@ */ package org.mvndaemon.mvnd.common; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.text.NumberFormat; import java.time.Duration; -import java.util.Locale; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Time utils. - * - * Origin file: - * https://github.com/apache/camel/blob/4ea9e6c357371682b855d2d79655b41120331b7a/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java */ public final class TimeUtils { - private static final Pattern NUMBERS_ONLY_STRING_PATTERN = Pattern.compile("^[-]?(\\d)+$", Pattern.CASE_INSENSITIVE); - private static final Pattern WEEK_REGEX_PATTERN = Pattern.compile("((\\d)*(\\d))\\s*w(eek(s)?)?(?=\\b|\\d|$)", - Pattern.CASE_INSENSITIVE); - private static final Pattern DAY_REGEX_PATTERN = Pattern.compile("((\\d)*(\\d))\\s*d(ay(s)?)?(?=\\b|\\d|$)", - Pattern.CASE_INSENSITIVE); - private static final Pattern HOUR_REGEX_PATTERN = Pattern.compile("((\\d)*(\\d))\\s*h(our(s)?)?(?=\\b|\\d|$)", - Pattern.CASE_INSENSITIVE); - private static final Pattern MINUTES_REGEX_PATTERN = Pattern.compile("((\\d)*(\\d))\\s*m(in(ute(s)?)?)?(?=\\b|\\d|$)", + 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( + "(?-?\\d+)" + + "|" + + "(" + + "((?\\d+)\\s*d(ay(s)?)?)?" + "\\s*" + + "((?\\d+)\\s*h(our(s)?)?)?" + "\\s*" + + "((?\\d+)\\s*m(in(ute(s)?)?)?)?" + "\\s*" + + "((?\\d+(\\.\\d+)?)\\s*s(ec(ond(s)?)?)?)?" + "\\s*" + + "((?\\d+(\\.\\d+)?)\\s*m(illi)?s(ec(ond)?(s)?)?)?" + + ")", Pattern.CASE_INSENSITIVE); - private static final Pattern SECONDS_REGEX_PATTERN = Pattern - .compile("((\\d)(\\d)*)(\\.(\\d+))?\\s*s(ec(ond)?(s)?)?(?=\\b|\\d|$)", Pattern.CASE_INSENSITIVE); - private static final Pattern MILLIS_REGEX_PATTERN = Pattern - .compile("((\\d)(\\d)*)(\\.(\\d+))?\\s*m(illi)?s(ec(ond)?(s)?)?(?=\\b|\\d|$)", Pattern.CASE_INSENSITIVE); private TimeUtils() { } @@ -59,46 +55,41 @@ public static String printDuration(Duration uptime) { } /** - * Prints the duration in a human readable format as X days Y hours Z minutes etc. + * This will print time in human readable format from milliseconds. + * Examples: + * 500 -> 500ms + * 1300 -> 1s300ms + * 310300 -> 5m10s300ms + * 6600000 -> 1h50m * - * @param uptime the uptime in millis - * @return the time used for displaying on screen or in logs + * @param millis time in milliseconds + * @return time in string */ - public static String printDuration(double uptime) { - // Code taken from Karaf - // https://svn.apache.org/repos/asf/karaf/trunk/shell/commands/src/main/java/org/apache/karaf/shell/commands/impl/InfoAction.java - - NumberFormat fmtI = new DecimalFormat("###,###", new DecimalFormatSymbols(Locale.ENGLISH)); - NumberFormat fmtD = new DecimalFormat("###,##0.000", new DecimalFormatSymbols(Locale.ENGLISH)); - - uptime /= 1000; - if (uptime < 60) { - return fmtD.format(uptime) + " seconds"; + 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; } - uptime /= 60; - if (uptime < 60) { - long minutes = (long) uptime; - String s = fmtI.format(minutes) + (minutes > 1 ? " minutes" : " minute"); - return s; + if (millis >= HOURS_MILLIS) { + sb.append(millis / HOURS_MILLIS).append("h"); + millis %= HOURS_MILLIS; } - uptime /= 60; - if (uptime < 24) { - long hours = (long) uptime; - long minutes = (long) ((uptime - hours) * 60); - String s = fmtI.format(hours) + (hours > 1 ? " hours" : " hour"); - if (minutes != 0) { - s += " " + fmtI.format(minutes) + (minutes > 1 ? " minutes" : " minute"); - } - return s; + if (millis >= MINUTES_MILLIS) { + sb.append(millis / MINUTES_MILLIS).append("m"); + millis %= MINUTES_MILLIS; } - uptime /= 24; - long days = (long) uptime; - long hours = (long) ((uptime - days) * 24); - String s = fmtI.format(days) + (days > 1 ? " days" : " day"); - if (hours != 0) { - s += " " + fmtI.format(hours) + (hours > 1 ? " hours" : " hour"); + if (millis >= SECONDS_MILLIS) { + sb.append(millis / SECONDS_MILLIS).append("s"); + millis %= SECONDS_MILLIS; } - return s; + 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 { @@ -106,139 +97,25 @@ public static Duration toDuration(String source) throws IllegalArgumentException } public static long toMilliSeconds(String source) throws IllegalArgumentException { - // quick conversion if its only digits - boolean digit = true; - for (int i = 0; i < source.length(); i++) { - char ch = source.charAt(i); - // special for first as it can be negative number - if (i == 0 && ch == '-') { - continue; - } - // quick check if its 0..9 - if (ch < '0' || ch > '9') { - digit = false; - break; - } - } - if (digit) { - return Long.parseLong(source); + Matcher matcher = DURATION_PATTERN.matcher(source); + if (!matcher.matches()) { + throw new IllegalArgumentException("Unable to parse duration: '" + source + "'"); } - - long milliseconds = 0; - boolean foundFlag = false; - - checkCorrectnessOfPattern(source); - Matcher matcher; - - matcher = createMatcher(NUMBERS_ONLY_STRING_PATTERN, source); - if (matcher.find()) { - // Note: This will also be used for regular numeric strings. - // This String -> long converter will be used for all strings. - milliseconds = Long.parseLong(source); + String n = matcher.group("n"); + if (n != null) { + return Long.parseLong(n); } else { - matcher = createMatcher(WEEK_REGEX_PATTERN, source); - if (matcher.find()) { - milliseconds += 7 * TimeUnit.DAYS.toMillis(Long.parseLong(matcher.group(1))); - foundFlag = true; - } - - matcher = createMatcher(DAY_REGEX_PATTERN, source); - if (matcher.find()) { - milliseconds += TimeUnit.DAYS.toMillis(Long.parseLong(matcher.group(1))); - foundFlag = true; - } - - matcher = createMatcher(HOUR_REGEX_PATTERN, source); - if (matcher.find()) { - milliseconds += TimeUnit.HOURS.toMillis(Long.parseLong(matcher.group(1))); - foundFlag = true; - } - - matcher = createMatcher(MINUTES_REGEX_PATTERN, source); - if (matcher.find()) { - milliseconds += TimeUnit.MINUTES.toMillis(Long.parseLong(matcher.group(1))); - foundFlag = true; - } - - matcher = createMatcher(SECONDS_REGEX_PATTERN, source); - if (matcher.find()) { - milliseconds += TimeUnit.SECONDS.toMillis(Long.parseLong(matcher.group(1))); - if (matcher.group(5) != null && !matcher.group(5).isEmpty()) { - milliseconds += TimeUnit.MILLISECONDS.toMillis(Long.parseLong(matcher.group(5))); - } - foundFlag = true; - } - - matcher = createMatcher(MILLIS_REGEX_PATTERN, source); - if (matcher.find()) { - milliseconds += TimeUnit.MILLISECONDS.toMillis(Long.parseLong(matcher.group(1))); - foundFlag = true; - } - - // No pattern matched... initiating fallback check and conversion (if required). - // The source at this point may contain illegal values or special characters - if (!foundFlag) { - milliseconds = Long.parseLong(source); - } + 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); } - - return milliseconds; - } - - private static void checkCorrectnessOfPattern(String source) { - //replace only numbers once - Matcher matcher = createMatcher(NUMBERS_ONLY_STRING_PATTERN, source); - String replaceSource = matcher.replaceFirst(""); - - //replace week string once - matcher = createMatcher(WEEK_REGEX_PATTERN, replaceSource); - if (matcher.find() && matcher.find()) { - throw new IllegalArgumentException("Weeks should not be specified more then once: " + source); - } - replaceSource = matcher.replaceFirst(""); - - //replace day string once - matcher = createMatcher(DAY_REGEX_PATTERN, replaceSource); - if (matcher.find() && matcher.find()) { - throw new IllegalArgumentException("Days should not be specified more then once: " + source); - } - replaceSource = matcher.replaceFirst(""); - - //replace hour string once - matcher = createMatcher(HOUR_REGEX_PATTERN, replaceSource); - if (matcher.find() && matcher.find()) { - throw new IllegalArgumentException("Hours should not be specified more then once: " + source); - } - replaceSource = matcher.replaceFirst(""); - - //replace minutes once - matcher = createMatcher(MINUTES_REGEX_PATTERN, replaceSource); - if (matcher.find() && matcher.find()) { - throw new IllegalArgumentException("Minutes should not be specified more then once: " + source); - } - replaceSource = matcher.replaceFirst(""); - - //replace seconds once - matcher = createMatcher(SECONDS_REGEX_PATTERN, replaceSource); - if (matcher.find() && matcher.find()) { - throw new IllegalArgumentException("Seconds should not be specified more then once: " + source); - } - replaceSource = matcher.replaceFirst(""); - - //replace millis once - matcher = createMatcher(MILLIS_REGEX_PATTERN, replaceSource); - if (matcher.find() && matcher.find()) { - throw new IllegalArgumentException("Milliseconds should not be specified more then once: " + source); - } - replaceSource = matcher.replaceFirst(""); - - if (replaceSource.length() > 0) { - throw new IllegalArgumentException("Illegal characters: " + source); - } - } - - private static Matcher createMatcher(Pattern pattern, String source) { - return pattern.matcher(source); } } 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())); + } + +}