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