diff --git a/domain/src/main/java/org/fao/geonet/domain/ISODate.java b/domain/src/main/java/org/fao/geonet/domain/ISODate.java index c2166fd2540..7aada4b4863 100644 --- a/domain/src/main/java/org/fao/geonet/domain/ISODate.java +++ b/domain/src/main/java/org/fao/geonet/domain/ISODate.java @@ -209,6 +209,7 @@ public String getDateAndTime() { } } + public void setDateAndTime(String isoDate) { String timeAndDate = isoDate; @@ -370,58 +371,82 @@ public boolean equals(Object obj) { private void parseDate(@Nonnull String isoDate) { try { - String[] parts = isoDate.split("[-/]"); - if ((parts.length == 0) || (parts.length > 3)) { - throw new IllegalArgumentException("Invalid ISO date: " + isoDate); - } + int startPos = 0; + int partNumber = 0; + int year = -1; + int month = -1; + int day = -1; - _shortDate = (parts.length == 3); - _shortDateYearMonth = (parts.length == 2); - _shortDateYear = (parts.length == 1); - - int year; - if (parts[0].length() < 4) { - int shortYear = Integer.parseInt(parts[0]); - String thisYear = String.valueOf(ZonedDateTime.now(ZoneOffset.UTC).getYear()); - int century = Integer.parseInt(thisYear.substring(0, 2)) * 100; - int yearInCentury = Integer.parseInt(thisYear.substring(2)); - - if (shortYear <= yearInCentury) { - year = century + shortYear; - } else { - year = century - 100 + shortYear; + ZoneId offset = ZoneId.systemDefault(); + // canonicalize the string + isoDate = isoDate.replace('/', '-'); + // until we've processed the whole string + while (startPos < isoDate.length()) { + if (partNumber >= 3) { + break; } - } else { - year = Integer.parseInt(parts[0]); + // try to find the next chunk + int nextPos = isoDate.indexOf('-', startPos); + if (nextPos == -1) { + // no next chunk to be found? This means this is the last chunk, process it accordingly + nextPos = isoDate.length(); + } + String subString = isoDate.substring(startPos, nextPos); + switch (partNumber) { + case 0: + // First part: year + int parsedInt = Integer.parseInt(subString); + if ((nextPos - startPos) < 4) { + int thisYear = ZonedDateTime.now(ZoneOffset.UTC).getYear(); + int century = thisYear / 100; + int yearInCentury = thisYear % 100; + year = (century * 100) + parsedInt; + if (parsedInt > yearInCentury) { + // If year is 2024, turn 32-05-05 into 1932, not 2032 + year -= 100; + } + } else { + year = parsedInt; + } + break; + case 1: + // Second part: month + month = Integer.parseInt(subString); + break; + case 2: + // Third part: day + if (subString.toLowerCase().endsWith("z")) { + offset = ZoneOffset.UTC; + day = Integer.parseInt(subString.substring(0, subString.length() - 1)); + } else { + day = Integer.parseInt(subString); + } + break; + default: + throw new IllegalStateException("Should not reach partNumber " + partNumber); + } + partNumber++; + startPos = nextPos + 1; } + if (partNumber == 0 || partNumber > 3) { + throw new IllegalArgumentException("Invalid ISO date: " + isoDate); + } + + _shortDate = (partNumber == 3); + _shortDateYearMonth = (partNumber == 2); + _shortDateYear = (partNumber == 1); - int month; - if (_shortDate || _shortDateYearMonth) { - month = Integer.parseInt(parts[1]); - } else { + if (!_shortDate && !_shortDateYearMonth) { month = 12; } - int day; - ZoneId offset = ZoneId.systemDefault(); - if (_shortDate) { - - if (parts[2].toLowerCase().endsWith("z")) { - offset = ZoneOffset.UTC; - day = Integer.parseInt(parts[2].substring(0, parts[2].length() - 1)); - } else { - day = Integer.parseInt(parts[2]); - } - } else { + if (!_shortDate) { // Calculate the last day for the year/month day = YearMonth.of(year, month).atEndOfMonth().getDayOfMonth(); } _shortDate = true; - internalDateTime = ZonedDateTime.now(offset).withYear(year).withMonth(month).withDayOfMonth(day).withHour(0).withMinute(0) - .withSecond(0).withNano(0); - //..ZonedDateTime.of(year, month, day, hour, minute, second, 0, offset); - + internalDateTime = ZonedDateTime.of(year, month, day, 0, 0, 0, 0, offset); } catch (Exception e) { throw new IllegalArgumentException("Invalid ISO date: " + isoDate, e); } diff --git a/jmh/.gitignore b/jmh/.gitignore new file mode 100644 index 00000000000..916e17c097a --- /dev/null +++ b/jmh/.gitignore @@ -0,0 +1 @@ +dependency-reduced-pom.xml diff --git a/jmh/README.md b/jmh/README.md new file mode 100644 index 00000000000..c70b67f807a --- /dev/null +++ b/jmh/README.md @@ -0,0 +1,22 @@ +# Geonetwork JMH Benchmark Suite + +This module contains micro benchmarks for some GN functions. +It is used to validate the performance of individual functions and snippets of code. +For performance tests at a larger scale, consider using another tool like jmeter + +To get started using JMH, see the [JMH docs](https://github.com/openjdk/jmh) + +## Adding new benchmarks + +New benchmark can be added by simply +1. Adding the module of the class to test to the gn-jmh module +2. Writing a benchmark in this module + +## Running the benchmarks + +1. Make sure the `jmh` profile is enabled (or enable it using `-Pjmh` when running maven) +2. Run `mvn verify` to build the benchmarks +3. Run the benchmark using `java -jar jmh/target/benchmarks.jar` in this module + +If you want to get additional inside, you can append `--prof stack`, which outputs text-base stack sampling, or `--prof jfr` to get java flight recorder profile that can be read by applications like VisualVM. +Make sure to only use additional profilers when analysing the data, not when actually recording numbers. diff --git a/jmh/pom.xml b/jmh/pom.xml new file mode 100644 index 00000000000..d8b2c53a62c --- /dev/null +++ b/jmh/pom.xml @@ -0,0 +1,93 @@ + + + 4.0.0 + + geonetwork + org.geonetwork-opensource + 4.4.6-SNAPSHOT + + + gn-jmh + jar + JMH benchmarks for Geonetwork + + + + ${project.groupId} + gn-domain + ${project.version} + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + provided + + + + + UTF-8 + + 1.37 + + benchmarks + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + + -processor + org.openjdk.jmh.generators.BenchmarkProcessor + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.0 + + + package + + shade + + + ${uberjar.name} + + + org.openjdk.jmh.Main + + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + + diff --git a/jmh/src/main/java/org/fao/geonet/domain/ISODateBenchmark.java b/jmh/src/main/java/org/fao/geonet/domain/ISODateBenchmark.java new file mode 100644 index 00000000000..8d156fd1c48 --- /dev/null +++ b/jmh/src/main/java/org/fao/geonet/domain/ISODateBenchmark.java @@ -0,0 +1,23 @@ +package org.fao.geonet.domain; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.concurrent.TimeUnit; + +@State(Scope.Benchmark) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS) +@Fork(1) +public class ISODateBenchmark { + @Param({"1976-06-03", "1976/06/03", "24-06-06"}) + public String arg; + + @Benchmark + public void measureIsoSimple(Blackhole bh) { + ISODate isoDate = new ISODate(arg); + bh.consume(isoDate); + } +} diff --git a/pom.xml b/pom.xml index 4c491177d3f..8d45551fd21 100644 --- a/pom.xml +++ b/pom.xml @@ -1437,6 +1437,12 @@ jmeter + + jmh + + jmh + + macOS