diff --git a/.config/checkstyle/checkstyle.xml b/.config/checkstyle/checkstyle.xml
index 43b52907..b5a41357 100644
--- a/.config/checkstyle/checkstyle.xml
+++ b/.config/checkstyle/checkstyle.xml
@@ -91,7 +91,7 @@
-
+
@@ -122,9 +122,7 @@
-
-
-
+
diff --git a/.config/pmd/java/ruleset.xml b/.config/pmd/java/ruleset.xml
index 88a7b5ae..c057d1aa 100644
--- a/.config/pmd/java/ruleset.xml
+++ b/.config/pmd/java/ruleset.xml
@@ -2,7 +2,7 @@
+ xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.github.io/ruleset_2_0_0.xsd">
This ruleset checks the code for discouraged programming constructs.
@@ -10,11 +10,14 @@
+
+
+
-
+
@@ -26,6 +29,7 @@
+
@@ -42,6 +46,7 @@
+
@@ -138,12 +143,14 @@
+
+
@@ -151,11 +158,13 @@
+
+
-
+
@@ -182,6 +191,9 @@
+
+
+
@@ -194,4 +206,896 @@
+
+
+
+
+Usually all cases where `StringBuilder` (or the outdated `StringBuffer`) is used are either due to confusing (legacy) logic or in situations where it may be easily replaced by a simpler string concatenation.
+
+Solution:
+* Do not use `StringBuffer` because it's thread-safe and usually this is not needed
+* If `StringBuilder` is only used in a simple method (like `toString`) and is effectively inlined: Use a simpler string concatenation (`"a" + x + "b"`). This will be [optimized by the Java compiler internally](https://docs.oracle.com/javase/specs/jls/se25/html/jls-15.html#jls-15.18.1).
+* In all other cases:
+ * Check what is happening and if it makes ANY sense! If for example a CSV file is built here consider using a proper library instead!
+ * Abstract the Strings into a DTO, join them together using a collection (or `StringJoiner`) or use Java's Streaming API instead
+
+ 3
+
+
+
+
+
+
+
+
+
+
+
+Calling setters of `java.lang.System` usually indicates bad design and likely causes unexpected behavior.
+For example, it may break when multiple Threads are working with the same value.
+It may also overwrite user defined options or properties.
+
+Try to pass the value only to the place where it's really needed and use it there accordingly.
+
+ 3
+
+
+
+
+
+
+
+
+
+
+
+Using a `@PostConstruct` method is usually only done when field injection is used and initialization needs to be performed after that.
+
+It's better to do this directly in the constructor with constructor injection, so that all logic will be encapsulated there.
+This also makes using the bean in environments where JavaEE is not present - for example in tests - a lot easier, as forgetting to call the `@PostConstruct` method is no longer possible.
+
+ 3
+
+
+
+
+
+
+
+
+
+
+
+`@PreDestroy` should be replaced by implementing `AutoCloseable` and overwriting the `close` method instead.
+
+This also makes using the bean in environments where JavaEE is not present - for example in tests - a lot easier, as forgetting to call the `@PreDestroy` method is no much more difficult.
+
+ 3
+
+
+
+
+
+
+
+
+
+
+
+Trying to manually manage threads usually gets quickly out of control and may result in various problems like uncontrollable spawning of threads.
+Threads can also not be cancelled properly.
+
+Use managed Thread services like `ExecutorService` and `CompletableFuture` instead.
+
+ 3
+
+
+
+
+
+
+
+
+
+
+
+ZipEntry name should be sanitized.
+Unsanitized names may contain '..' which can result in path traversal ("ZipSlip").
+
+You can suppress this warning when you properly sanitized the name.
+
+ 4
+
+
+
+
+
+
+
+
+
+
+
+Nearly every known usage of (Java) Object Deserialization has resulted in [a security vulnerability](https://cloud.google.com/blog/topics/threat-intelligence/hunting-deserialization-exploits?hl=en).
+Vulnerabilities are so common that there are [dedicated projects for exploit payload generation](https://github.com/frohoff/ysoserial).
+
+Java Object Serialization may also fail to deserialize properly when the underlying classes are changed.
+This can result in unexpected crashes when outdated data is deserialized.
+
+Use proven data interchange formats like JSON instead.
+
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+Do not use native HTML! Use Vaadin layouts and components to create required structure.
+If you are 100% sure that you escaped the value properly and you have no better options you can suppress this.
+
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+java.text.NumberFormat: DecimalFormat and ChoiceFormat are thread-unsafe.
+
+Solution: Create a new local one when needed in a method.
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+A regular expression is compiled implicitly on every invocation.
+Problem: This can be (CPU) expensive, depending on the length of the regular expression.
+
+Solution: Compile the regex pattern only once and assign it to a private static final Pattern field.
+java.util.Pattern objects are thread-safe, so they can be shared among threads.
+
+ 2
+
+
+
+ 5 and
+(matches(@Image, '[\.\$\|\(\)\[\]\{\}\^\?\*\+\\]+')))
+or
+self::VariableAccess and @Name=ancestor::ClassBody[1]/FieldDeclaration/VariableDeclarator[StringLiteral[string-length(@Image) > 5 and
+(matches(@Image, '[\.\$\|\(\)\[\]\{\}\^\?\*\+\\]+'))] or not(StringLiteral)]/VariableId/@Name]
+]]>
+
+
+
+
+
+
+
+
+
+
+
+The default constructor of ByteArrayOutputStream creates a 32 bytes initial capacity and for StringWriter 16 chars.
+Such a small buffer as capacity usually needs several expensive expansions.
+
+Solution: Explicitly declared the buffer size so that an expansion is not needed in most cases.
+Typically much larger than 32, e.g. 4096.
+
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+The time to find element is O(n); n = the number of enum values.
+This identical processing is executed for every call.
+Considered problematic when `n > 3`.
+
+Solution: Use a static field-to-enum-value Map. Access time is O(1), provided the hashCode is well-defined.
+Implement a fromString method to provide the reverse conversion by using the map.
+
+ 3
+
+
+
+ 3]//MethodDeclaration/Block
+ //MethodCall[pmd-java:matchesSig('java.util.stream.Stream#findFirst()') or pmd-java:matchesSig('java.util.stream.Stream#findAny()')]
+ [//MethodCall[pmd-java:matchesSig('java.util.stream.Stream#of(_)') or pmd-java:matchesSig('java.util.Arrays#stream(_)')]
+ [ArgumentList/MethodCall[pmd-java:matchesSig('_#values()')]]]
+]]>
+
+
+
+
+ fromString(String name) {
+ return Stream.of(values()).filter(v -> v.toString().equals(name)).findAny(); // bad: iterates for every call, O(n) access time
+ }
+}
+
+Usage: `Fruit f = Fruit.fromString("banana");`
+
+// GOOD
+public enum Fruit {
+ APPLE("apple"),
+ ORANGE("orange"),
+ BANANA("banana"),
+ KIWI("kiwi");
+
+ private static final Map nameToValue =
+ Stream.of(values()).collect(toMap(Object::toString, v -> v));
+ private final String name;
+
+ Fruit(String name) { this.name = name; }
+ @Override public String toString() { return name; }
+ public static Optional fromString(String name) {
+ return Optional.ofNullable(nameToValue.get(name)); // good, get from Map, O(1) access time
+ }
+}
+]]>
+
+
+
+
+
+A regular expression is compiled on every invocation.
+Problem: this can be expensive, depending on the length of the regular expression.
+
+Solution: Usually a pattern is a literal, not dynamic and can be compiled only once. Assign it to a private static field.
+java.util.Pattern objects are thread-safe so they can be shared among threads.
+
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Recreating a DateTimeFormatter is relatively expensive.
+
+Solution: Java 8+ java.time.DateTimeFormatter is thread-safe and can be shared among threads.
+Create the formatter from a pattern only once, to initialize a static final field.
+
+ 2
+
+
+
+
+
+
+
+
+
+
+
+Creating a security provider is expensive because of loading of algorithms and other classes.
+Additionally, it uses synchronized which leads to lock contention when used with multiple threads.
+
+Solution: This only needs to happen once in the JVM lifetime, because once loaded the provider is typically available from the Security class.
+Create the security provider only once: Only in case when it's not yet available from the Security class.
+
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Reflection is relatively expensive.
+
+Solution: Avoid reflection. Use the non-reflective, explicit way like generation by IDE.
+
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+java.util.SimpleDateFormat is thread-unsafe.
+The usual solution is to create a new one when needed in a method.
+Creating SimpleDateFormat is relatively expensive.
+
+Solution: Use java.time.DateTimeFormatter. These classes are immutable, thus thread-safe and can be made static.
+
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Blocking calls, for instance remote calls, may exhaust the common pool for some time thereby blocking all other use of the common pool.
+In addition, nested use of the common pool can lead to deadlock. Do not use the common pool for blocking calls.
+The parallelStream() call uses the common pool.
+
+Solution: Use a dedicated thread pool with enough threads to get proper parallelism.
+The number of threads in the common pool is equal to the number of CPUs and meant to utilize all of them.
+It assumes CPU-intensive non-blocking processing of in-memory data.
+
+See also: [_Be Aware of ForkJoinPool#commonPool()_](https://dzone.com/articles/be-aware-of-forkjoinpoolcommonpool)
+
+ 2
+
+
+
+
+
+
+
+
+ list = new ArrayList();
+ final ForkJoinPool myFjPool = new ForkJoinPool(10);
+ final ExecutorService myExePool = Executors.newFixedThreadPool(10);
+
+ void bad1() {
+ list.parallelStream().forEach(elem -> storeDataRemoteCall(elem)); // bad
+ }
+
+ void good1() {
+ CompletableFuture[] futures = list.stream().map(elem -> CompletableFuture.supplyAsync(() -> storeDataRemoteCall(elem), myExePool))
+ .toArray(CompletableFuture[]::new);
+ CompletableFuture.allOf(futures).get(10, TimeUnit.MILLISECONDS));
+ }
+
+ void good2() throws ExecutionException, InterruptedException {
+ myFjPool.submit(() ->
+ list.parallelStream().forEach(elem -> storeDataRemoteCall(elem))
+ ).get();
+ }
+
+ String storeDataRemoteCall(String elem) {
+ // do remote call, blocking. We don't use the returned value.
+ RestTemplate tmpl;
+ return "";
+ }
+}
+]]>
+
+
+
+
+
+CompletableFuture.supplyAsync/runAsync is typically used for remote calls.
+By default it uses the common pool.
+The number of threads in the common pool is equal to the number of CPU's, which is suitable for in-memory processing.
+For I/O, however, this number is typically not suitable because most time is spent waiting for the response and not in CPU.
+The common pool must not be used for blocking calls.
+
+Solution: A separate, properly sized pool of threads (an Executor) should be used for the async calls.
+
+See also: [_Be Aware of ForkJoinPool#commonPool()_](https://dzone.com/articles/be-aware-of-forkjoinpoolcommonpool)
+
+ 2
+
+
+
+
+
+
+
+
+>[] futures = accounts.stream()
+ .map(account -> CompletableFuture.supplyAsync(() -> isAccountBlocked(account))) // bad
+ .toArray(CompletableFuture[]::new);
+ }
+
+ void good() {
+ CompletableFuture>[] futures = accounts.stream()
+ .map(account -> CompletableFuture.supplyAsync(() -> isAccountBlocked(account), asyncPool)) // good
+ .toArray(CompletableFuture[]::new);
+ }
+}
+]]>
+
+
+
+
+
+`take()` stalls indefinitely in case of hanging threads and consumes a thread.
+
+Solution: use `poll()` with a timeout value and handle the timeout.
+
+ 2
+
+
+
+
+
+
+
+
+ void collectAllCollectionReplyFromThreads(CompletionService> completionService) {
+ try {
+ Future> futureLocal = completionService.take(); // bad
+ Future> futuresGood = completionService.poll(3, TimeUnit.SECONDS); // good
+ responseCollector.addAll(futuresGood.get(10, TimeUnit.SECONDS)); // good
+ } catch (InterruptedException | ExecutionException e) {
+ LOGGER.error("Error in Thread : {}", e);
+ } catch (TimeoutException e) {
+ LOGGER.error("Timeout in Thread : {}", e);
+ }
+}
+]]>
+
+
+
+
+
+Stalls indefinitely in case of stalled Callable(s) and consumes threads.
+
+Solution: Provide a timeout to the invokeAll/invokeAny method and handle the timeout.
+
+ 2
+
+
+
+
+
+
+
+
+> executeTasksBad(Collection> tasks, ExecutorService executor) throws Exception {
+ return executor.invokeAll(tasks); // bad, no timeout
+ }
+ private List> executeTasksGood(Collection> tasks, ExecutorService executor) throws Exception {
+ return executor.invokeAll(tasks, OUR_TIMEOUT_IN_MILLIS, TimeUnit.MILLISECONDS); // good
+ }
+}
+]]>
+
+
+
+
+
+Stalls indefinitely in case of hanging threads and consumes a thread.
+
+Solution: Provide a timeout value and handle the timeout.
+
+ 2
+
+
+
+
+
+
+
+
+ complFuture) throws Exception {
+ return complFuture.get(); // bad
+}
+
+public static String good(CompletableFuture complFuture) throws Exception {
+ return complFuture.get(10, TimeUnit.SECONDS); // good
+}
+]]>
+
+
+
+
+
+
+Apache HttpClient with its connection pool and timeouts should be setup once and then used for many requests.
+It is quite expensive to create and can only provide the benefits of pooling when reused in all requests for that connection.
+
+Solution: Create/build HttpClient with proper connection pooling and timeouts once, and then use it for requests.
+
+ 3
+
+
+
+
+
+
+
+
+ connectBad(Object req) {
+ HttpEntity
+
+
+
+
+Problem: Gson creation is relatively expensive. A JMH benchmark shows a 24x improvement reusing one instance.
+
+Solution: Since Gson objects are thread-safe after creation, they can be shared between threads.
+So reuse created instances from a static field.
+Pay attention to use thread-safe (custom) adapters and serializers.
+
+ 3
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.github/workflows/antora-build.yml b/.github/workflows/antora-build.yml
index a7b47f5b..0fdb00ce 100644
--- a/.github/workflows/antora-build.yml
+++ b/.github/workflows/antora-build.yml
@@ -21,12 +21,12 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Install Node.js
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: 18
+ node-version: 24
- name: Install Antora and the Antora Lunr Extension
run: npm i antora @antora/lunr-extension
@@ -38,7 +38,7 @@ jobs:
uses: actions/configure-pages@v5
- name: Upload artifact
- uses: actions/upload-pages-artifact@v3
+ uses: actions/upload-pages-artifact@v4
with:
name: site
path: docs/site
diff --git a/.github/workflows/broken-links.yml b/.github/workflows/broken-links.yml
index e2f3597a..2675c8b6 100644
--- a/.github/workflows/broken-links.yml
+++ b/.github/workflows/broken-links.yml
@@ -13,23 +13,23 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- run: mv .github/.lycheeignore .lycheeignore
- name: Link Checker
id: lychee
- uses: lycheeverse/lychee-action@82202e5e9c2f4ef1a55a3d02563e1cb6041e5332 # v2
+ uses: lycheeverse/lychee-action@a8c4c7cb88f0c7386610c35eb25108e448569cb0 # v2
with:
fail: false # Don't fail on broken links, create an issue instead
- name: Find already existing issue
id: find-issue
run: |
- echo "number=$(gh issue list -l 'bug' -l 'automated' -L 1 -S 'in:title \"Link Checker Report\"' -s 'open' --json 'number' --jq '.[].number')" >> $GITHUB_OUTPUT
+ echo "number=$(gh issue list -l 'bug' -l 'automated' -L 1 -S 'in:title "Link Checker Report"' -s 'open' --json 'number' --jq '.[].number')" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ github.token }}
-
+
- name: Close issue if everything is fine
if: steps.lychee.outputs.exit_code == 0 && steps.find-issue.outputs.number != ''
run: gh issue close -r 'not planned' ${{ steps.find-issue.outputs.number }}
@@ -38,7 +38,7 @@ jobs:
- name: Create Issue From File
if: steps.lychee.outputs.exit_code != 0
- uses: peter-evans/create-issue-from-file@e8ef132d6df98ed982188e460ebb3b5d4ef3a9cd # v5
+ uses: peter-evans/create-issue-from-file@fca9117c27cdc29c6c4db3b86c48e4115a786710 # v6
with:
issue-number: ${{ steps.find-issue.outputs.number }}
title: Link Checker Report
diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml
index 06e811af..f678d6b2 100644
--- a/.github/workflows/check-build.yml
+++ b/.github/workflows/check-build.yml
@@ -20,32 +20,36 @@ on:
- 'assets/**'
env:
- PRIMARY_MAVEN_MODULE: ${{ github.event.repository.name }}
DEMO_MAVEN_MODULE: ${{ github.event.repository.name }}-demo
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 30
-
strategy:
matrix:
- java: [17, 21]
+ java: [17, 21, 25]
distribution: [temurin]
-
steps:
- - uses: actions/checkout@v4
-
+ - uses: actions/checkout@v6
+
- name: Set up JDK
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
distribution: ${{ matrix.distribution }}
java-version: ${{ matrix.java }}
- cache: 'maven'
-
+
+ - name: Cache Maven
+ uses: actions/cache@v5
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-mvn-build-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-mvn-build-
+
- name: Build with Maven
run: ./mvnw -B clean package
-
+
- name: Check for uncommited changes
run: |
if [[ "$(git status --porcelain)" != "" ]]; then
@@ -65,7 +69,7 @@ jobs:
fi
- name: Upload demo files
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v6
with:
name: demo-files-java-${{ matrix.java }}
path: ${{ env.DEMO_MAVEN_MODULE }}/target/${{ env.DEMO_MAVEN_MODULE }}.jar
@@ -75,21 +79,34 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' || !startsWith(github.head_ref, 'renovate/') }}
timeout-minutes: 15
-
strategy:
matrix:
java: [17]
distribution: [temurin]
-
steps:
- - uses: actions/checkout@v4
-
+ - uses: actions/checkout@v6
+
- name: Set up JDK
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
distribution: ${{ matrix.distribution }}
java-version: ${{ matrix.java }}
- cache: 'maven'
+
+ - name: Cache Maven
+ uses: actions/cache@v5
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-mvn-checkstyle-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-mvn-checkstyle-
+
+ - name: CheckStyle Cache
+ uses: actions/cache@v5
+ with:
+ path: '**/target/checkstyle-cachefile'
+ key: ${{ runner.os }}-checkstyle-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-checkstyle-
- name: Run Checkstyle
run: ./mvnw -B checkstyle:check -P checkstyle -T2C
@@ -98,28 +115,41 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' || !startsWith(github.head_ref, 'renovate/') }}
timeout-minutes: 15
-
strategy:
matrix:
java: [17]
distribution: [temurin]
-
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Set up JDK
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
distribution: ${{ matrix.distribution }}
java-version: ${{ matrix.java }}
- cache: 'maven'
+
+ - name: Cache Maven
+ uses: actions/cache@v5
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-mvn-pmd-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-mvn-pmd-
+
+ - name: PMD Cache
+ uses: actions/cache@v5
+ with:
+ path: '**/target/pmd/pmd.cache'
+ key: ${{ runner.os }}-pmd-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-pmd-
- name: Run PMD
run: ./mvnw -B test pmd:aggregate-pmd-no-fork pmd:check -P pmd -DskipTests -T2C
- name: Upload report
if: always()
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v6
with:
name: pmd-report
if-no-files-found: ignore
@@ -130,12 +160,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: Install Node.js
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: 18
+ node-version: 24
- name: Install Antora and the Antora Lunr Extension
run: npm i antora @antora/lunr-extension
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 5f8fb83e..3f55399a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -11,20 +11,30 @@ permissions:
contents: write
pull-requests: write
+# DO NOT RESTORE CACHE for critical release steps to prevent a (extremely unlikely) scenario
+# where a supply chain attack could be achieved due to poisoned cache
jobs:
check-code:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- - uses: actions/checkout@v4
-
+ - uses: actions/checkout@v6
+
- name: Set up JDK
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'temurin'
- cache: 'maven'
-
+
+ # Try to reuse existing cache from check-build
+ - name: Try restore Maven Cache
+ uses: actions/cache/restore@v5
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-mvn-build-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-mvn-build-
+
- name: Build with Maven
run: ./mvnw -B clean package -T2C
@@ -53,16 +63,16 @@ jobs:
outputs:
upload_url: ${{ steps.create-release.outputs.upload_url }}
steps:
- - uses: actions/checkout@v4
-
+ - uses: actions/checkout@v6
+
- name: Configure Git
run: |
git config --global user.email "actions@github.com"
git config --global user.name "GitHub Actions"
-
+
- name: Un-SNAP
run: ./mvnw -B versions:set -DremoveSnapshot -DprocessAllModules -DgenerateBackupPoms=false
-
+
- name: Get version
id: version
run: |
@@ -70,7 +80,7 @@ jobs:
echo "release=$version" >> $GITHUB_OUTPUT
echo "releasenumber=${version//[!0-9]/}" >> $GITHUB_OUTPUT
working-directory: ${{ env.PRIMARY_MAVEN_MODULE }}
-
+
- name: Commit and Push
run: |
git add -A
@@ -78,10 +88,10 @@ jobs:
git push origin
git tag v${{ steps.version.outputs.release }}
git push origin --tags
-
+
- name: Create Release
id: create-release
- uses: shogo82148/actions-create-release@4661dc54f7b4b564074e9fbf73884d960de569a3 # v1
+ uses: shogo82148/actions-create-release@559c27ce7eb834825e2b55927c64f6d1bd1db716 # v1
with:
tag_name: v${{ steps.version.outputs.release }}
release_name: v${{ steps.version.outputs.release }}
@@ -105,8 +115,8 @@ jobs:
needs: [prepare-release]
timeout-minutes: 60
steps:
- - uses: actions/checkout@v4
-
+ - uses: actions/checkout@v6
+
- name: Init Git and pull
run: |
git config --global user.email "actions@github.com"
@@ -114,7 +124,7 @@ jobs:
git pull
- name: Set up JDK
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with: # running setup-java overwrites the settings.xml
distribution: 'temurin'
java-version: '17'
@@ -122,7 +132,7 @@ jobs:
server-password: PACKAGES_CENTRAL_TOKEN
gpg-passphrase: MAVEN_GPG_PASSPHRASE
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Only import once
-
+
- name: Publish to GitHub Packages Central
run: ../mvnw -B deploy -P publish -DskipTests -DaltDeploymentRepository=github-central::https://maven.pkg.github.com/xdev-software/central
working-directory: ${{ env.PRIMARY_MAVEN_MODULE }}
@@ -131,7 +141,7 @@ jobs:
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
- name: Set up JDK
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with: # running setup-java again overwrites the settings.xml
distribution: 'temurin'
java-version: '17'
@@ -153,8 +163,8 @@ jobs:
needs: [prepare-release]
timeout-minutes: 15
steps:
- - uses: actions/checkout@v4
-
+ - uses: actions/checkout@v6
+
- name: Init Git and pull
run: |
git config --global user.email "actions@github.com"
@@ -162,11 +172,19 @@ jobs:
git pull
- name: Setup - Java
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'temurin'
- cache: 'maven'
+
+ # Try to reuse existing cache from check-build
+ - name: Try restore Maven Cache
+ uses: actions/cache/restore@v5
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-mvn-build-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-mvn-build-
- name: Build site
run: ../mvnw -B compile site -DskipTests -T2C
@@ -184,8 +202,8 @@ jobs:
needs: [publish-maven]
timeout-minutes: 10
steps:
- - uses: actions/checkout@v4
-
+ - uses: actions/checkout@v6
+
- name: Init Git and pull
run: |
git config --global user.email "actions@github.com"
@@ -200,7 +218,7 @@ jobs:
git add -A
git commit -m "Preparing for next development iteration"
git push origin
-
+
- name: pull-request
env:
GH_TOKEN: ${{ github.token }}
diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml
index dc672877..6471ce7c 100644
--- a/.github/workflows/sync-labels.yml
+++ b/.github/workflows/sync-labels.yml
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
sparse-checkout: .github/labels.yml
diff --git a/.github/workflows/test-deploy.yml b/.github/workflows/test-deploy.yml
index 046be633..2d13d77a 100644
--- a/.github/workflows/test-deploy.yml
+++ b/.github/workflows/test-deploy.yml
@@ -11,10 +11,10 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Set up JDK
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with: # running setup-java overwrites the settings.xml
distribution: 'temurin'
java-version: '17'
@@ -22,16 +22,16 @@ jobs:
server-password: PACKAGES_CENTRAL_TOKEN
gpg-passphrase: MAVEN_GPG_PASSPHRASE
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Only import once
-
+
- name: Publish to GitHub Packages Central
run: ../mvnw -B deploy -P publish -DskipTests -DaltDeploymentRepository=github-central::https://maven.pkg.github.com/xdev-software/central
working-directory: ${{ env.PRIMARY_MAVEN_MODULE }}
env:
PACKAGES_CENTRAL_TOKEN: ${{ secrets.PACKAGES_CENTRAL_TOKEN }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
-
+
- name: Set up JDK
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with: # running setup-java again overwrites the settings.xml
distribution: 'temurin'
java-version: '17'
diff --git a/.github/workflows/update-from-template.yml b/.github/workflows/update-from-template.yml
index 65f56b0d..f447710f 100644
--- a/.github/workflows/update-from-template.yml
+++ b/.github/workflows/update-from-template.yml
@@ -36,14 +36,14 @@ jobs:
update_branch_merged_commit: ${{ steps.manage-branches.outputs.update_branch_merged_commit }}
create_update_branch_merged_pr: ${{ steps.manage-branches.outputs.create_update_branch_merged_pr }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
# Required because otherwise there are always changes detected when executing diff/rev-list
fetch-depth: 0
# If no PAT is used the following error occurs on a push:
# refusing to allow a GitHub App to create or update workflow `.github/workflows/xxx.yml` without `workflows` permission
token: ${{ secrets.UPDATE_FROM_TEMPLATE_PAT }}
-
+
- name: Init Git
run: |
git config --global user.email "111048771+xdev-gh-bot@users.noreply.github.com"
@@ -183,14 +183,14 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
# Required because otherwise there are always changes detected when executing diff/rev-list
fetch-depth: 0
# If no PAT is used the following error occurs on a push:
# refusing to allow a GitHub App to create or update workflow `.github/workflows/xxx.yml` without `workflows` permission
token: ${{ secrets.UPDATE_FROM_TEMPLATE_PAT }}
-
+
- name: Init Git
run: |
git config --global user.email "111048771+xdev-gh-bot@users.noreply.github.com"
diff --git a/.gitignore b/.gitignore
index d0c98eb4..46bc4aea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,6 +55,8 @@ spring-data-eclipse-store-jpa/storage-h2.trace.db
!.idea/saveactions_settings.xml
!.idea/checkstyle-idea.xml
!.idea/externalDependencies.xml
+!.idea/pmd-x.xml
+!.idea/PMDPlugin.xml
!.idea/inspectionProfiles/
.idea/inspectionProfiles/*
diff --git a/.idea/PMDPlugin.xml b/.idea/PMDPlugin.xml
new file mode 100644
index 00000000..0936e518
--- /dev/null
+++ b/.idea/PMDPlugin.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml
index b52c3e2f..ec555b58 100644
--- a/.idea/checkstyle-idea.xml
+++ b/.idea/checkstyle-idea.xml
@@ -1,7 +1,7 @@
- 10.21.0
+ 11.0.0JavaOnlyWithTeststruetrue
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 19681faa..21e0aff9 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -96,4 +96,4 @@
-
+
\ No newline at end of file
diff --git a/.idea/pmd-x.xml b/.idea/pmd-x.xml
new file mode 100644
index 00000000..260e454b
--- /dev/null
+++ b/.idea/pmd-x.xml
@@ -0,0 +1,27 @@
+
+
+
+ false
+ true
+ true
+ SUPPORTED_ONLY_WITH_TESTS
+
+
+
\ No newline at end of file
diff --git a/.idea/saveactions_settings.xml b/.idea/saveactions_settings.xml
index 848c311a..12a4f040 100644
--- a/.idea/saveactions_settings.xml
+++ b/.idea/saveactions_settings.xml
@@ -5,6 +5,7 @@
+
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
index 6a6b8b2c..8dea6c22 100644
--- a/.mvn/wrapper/maven-wrapper.properties
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -1,17 +1,3 @@
-# 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
-#
-# 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.
-distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
+wrapperVersion=3.3.4
+distributionType=only-script
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 669d756b..04518043 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -19,7 +19,7 @@ We also encourage you to read the [contribution instructions by GitHub](https://
### Software Requirements
You should have the following things installed:
* Git
-* Java 21 - should be as unmodified as possible (Recommended: [Eclipse Adoptium](https://adoptium.net/temurin/releases/))
+* Java 25 - should be as unmodified as possible (Recommended: [Eclipse Adoptium](https://adoptium.net/temurin/releases/))
* Maven (Note that the [Maven Wrapper](https://maven.apache.org/wrapper/) is shipped with the repo)
### Recommended setup
diff --git a/README.md b/README.md
index dfedc700..e48c30f9 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,8 @@ instructions** are in the documentation](https://xdev-software.github.io/spring-
| ``2.4.1`` | ``17+`` | ``3.4.0`` | ``2.1.0`` |
| ``2.5.0`` | ``17+`` | ``3.4.1`` | ``2.1.0`` |
| ``2.5.1-2.5.2`` | ``17+`` | ``3.4.2`` | ``2.1.1`` |
-| ``>= 2.5.3`` | ``17+`` | ``3.5.3`` | ``2.1.3`` |
+| ``2.5.3`` | ``17+`` | ``3.5.3`` | ``2.1.3`` |
+| ``>= 2.5.4`` | ``17+`` | ``3.5.9`` | ``2.1.3`` |
## Demo
diff --git a/docs/antora.yml b/docs/antora.yml
index 781a0082..75f6c219 100644
--- a/docs/antora.yml
+++ b/docs/antora.yml
@@ -1,14 +1,14 @@
name: ROOT
title: Spring-Data-Eclipse-Store
version: master
-display_version: '2.5.3'
+display_version: '2.5.4'
start_page: index.adoc
nav:
- modules/ROOT/nav.adoc
asciidoc:
attributes:
product-name: 'Spring-Data-Eclipse-Store'
- display-version: '2.5.3'
- maven-version: '2.5.3'
+ display-version: '2.5.4'
+ maven-version: '2.5.4'
page-editable: false
page-out-of-support: false
diff --git a/docs/package.json b/docs/package.json
index a8b88ac2..6c2165df 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -3,7 +3,7 @@
"@antora/lunr-extension": "^1.0.0-alpha.8"
},
"devDependencies": {
- "@antora/cli": "3.1.10",
- "@antora/site-generator": "3.1.10"
+ "@antora/cli": "3.1.14",
+ "@antora/site-generator": "3.1.14"
}
}
\ No newline at end of file
diff --git a/mvnw b/mvnw
index 08303327..bd8896bf 100755
--- a/mvnw
+++ b/mvnw
@@ -19,7 +19,7 @@
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
-# Apache Maven Wrapper startup batch script, version 3.3.0
+# Apache Maven Wrapper startup batch script, version 3.3.4
#
# Optional ENV vars
# -----------------
@@ -97,14 +97,25 @@ die() {
exit 1
}
+trim() {
+ # MWRAPPER-139:
+ # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
+ # Needed for removing poorly interpreted newline sequences when running in more
+ # exotic environments such as mingw bash on Windows.
+ printf "%s" "${1}" | tr -d '[:space:]'
+}
+
+scriptDir="$(dirname "$0")"
+scriptName="$(basename "$0")"
+
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
while IFS="=" read -r key value; do
case "${key-}" in
- distributionUrl) distributionUrl="${value-}" ;;
- distributionSha256Sum) distributionSha256Sum="${value-}" ;;
+ distributionUrl) distributionUrl=$(trim "${value-}") ;;
+ distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
esac
-done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
-[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
+[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
case "${distributionUrl##*/}" in
maven-mvnd-*bin.*)
@@ -122,7 +133,7 @@ maven-mvnd-*bin.*)
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
;;
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
-*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
+*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
esac
# apply MVNW_REPOURL and calculate MAVEN_HOME
@@ -131,7 +142,8 @@ esac
distributionUrlName="${distributionUrl##*/}"
distributionUrlNameMain="${distributionUrlName%.*}"
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
-MAVEN_HOME="$HOME/.m2/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
+MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
+MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
exec_maven() {
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
@@ -199,7 +211,7 @@ elif set_java_home; then
public static void main( String[] args ) throws Exception
{
setDefault( new Downloader() );
- java.nio.file.Files.copy( new java.net.URL( args[0] ).openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
+ java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
}
}
END
@@ -218,7 +230,7 @@ if [ -n "${distributionSha256Sum-}" ]; then
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
elif command -v sha256sum >/dev/null; then
- if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
distributionSha256Result=true
fi
elif command -v shasum >/dev/null; then
@@ -243,8 +255,41 @@ if command -v unzip >/dev/null; then
else
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
fi
-printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
-mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
+
+# Find the actual extracted directory name (handles snapshots where filename != directory name)
+actualDistributionDir=""
+
+# First try the expected directory name (for regular distributions)
+if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
+ if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
+ actualDistributionDir="$distributionUrlNameMain"
+ fi
+fi
+
+# If not found, search for any directory with the Maven executable (for snapshots)
+if [ -z "$actualDistributionDir" ]; then
+ # enable globbing to iterate over items
+ set +f
+ for dir in "$TMP_DOWNLOAD_DIR"/*; do
+ if [ -d "$dir" ]; then
+ if [ -f "$dir/bin/$MVN_CMD" ]; then
+ actualDistributionDir="$(basename "$dir")"
+ break
+ fi
+ fi
+ done
+ set -f
+fi
+
+if [ -z "$actualDistributionDir" ]; then
+ verbose "Contents of $TMP_DOWNLOAD_DIR:"
+ verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
+ die "Could not find Maven distribution directory in extracted archive"
+fi
+
+verbose "Found extracted Maven distribution directory: $actualDistributionDir"
+printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
+mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
clean || :
exec_maven "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
index 136e686a..92450f93 100644
--- a/mvnw.cmd
+++ b/mvnw.cmd
@@ -19,7 +19,7 @@
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
-@REM Apache Maven Wrapper startup batch script, version 3.3.0
+@REM Apache Maven Wrapper startup batch script, version 3.3.4
@REM
@REM Optional ENV vars
@REM MVNW_REPOURL - repo url base for downloading maven distribution
@@ -40,7 +40,7 @@
@SET __MVNW_ARG0_NAME__=
@SET MVNW_USERNAME=
@SET MVNW_PASSWORD=
-@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
+@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
@echo Cannot start maven from wrapper >&2 && exit /b 1
@GOTO :EOF
: end batch / begin powershell #>
@@ -73,13 +73,30 @@ switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
if ($env:MVNW_REPOURL) {
- $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
- $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
+ $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
+ $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
}
$distributionUrlName = $distributionUrl -replace '^.*/',''
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
-$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
-$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
+
+$MAVEN_M2_PATH = "$HOME/.m2"
+if ($env:MAVEN_USER_HOME) {
+ $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
+}
+
+if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
+ New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
+}
+
+$MAVEN_WRAPPER_DISTS = $null
+if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
+ $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
+} else {
+ $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
+}
+
+$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
+$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
@@ -131,7 +148,33 @@ if ($distributionSha256Sum) {
# unzip and move
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
-Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
+
+# Find the actual extracted directory name (handles snapshots where filename != directory name)
+$actualDistributionDir = ""
+
+# First try the expected directory name (for regular distributions)
+$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
+$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
+if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
+ $actualDistributionDir = $distributionUrlNameMain
+}
+
+# If not found, search for any directory with the Maven executable (for snapshots)
+if (!$actualDistributionDir) {
+ Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
+ $testPath = Join-Path $_.FullName "bin/$MVN_CMD"
+ if (Test-Path -Path $testPath -PathType Leaf) {
+ $actualDistributionDir = $_.Name
+ }
+ }
+}
+
+if (!$actualDistributionDir) {
+ Write-Error "Could not find Maven distribution directory in extracted archive"
+}
+
+Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
+Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
try {
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
} catch {
diff --git a/pom.xml b/pom.xml
index 10856453..966b7850 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
UTF-8
- 3.5.3
+ 3.5.92.1.32.1.32.1.3
@@ -107,7 +107,7 @@
com.puppycrawl.toolscheckstyle
- 10.26.1
+ 12.3.0
@@ -132,8 +132,9 @@
org.apache.maven.pluginsmaven-pmd-plugin
- 3.27.0
+ 3.28.0
+ truetruetrue
@@ -150,12 +151,12 @@
net.sourceforge.pmdpmd-core
- 7.15.0
+ 7.20.0net.sourceforge.pmdpmd-java
- 7.15.0
+ 7.20.0
diff --git a/renovate.json5 b/renovate.json5
index 485ab8c0..0e5dead6 100644
--- a/renovate.json5
+++ b/renovate.json5
@@ -30,6 +30,16 @@
"maven"
],
"groupName": "org.eclipse.store-serializer"
+ },
+ {
+ "description": "Group @antora",
+ "matchPackagePatterns": [
+ "^@antora\/"
+ ],
+ "datasources": [
+ "npm"
+ ],
+ "groupName": "@antora"
}
]
}
diff --git a/spring-data-eclipse-store-benchmark/pom.xml b/spring-data-eclipse-store-benchmark/pom.xml
index c4fd6e2a..1794065d 100644
--- a/spring-data-eclipse-store-benchmark/pom.xml
+++ b/spring-data-eclipse-store-benchmark/pom.xml
@@ -56,7 +56,7 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 3.14.0
+ 3.14.1${maven.compiler.release}
diff --git a/spring-data-eclipse-store-demo/pom.xml b/spring-data-eclipse-store-demo/pom.xml
index 92d48b83..48574eb1 100644
--- a/spring-data-eclipse-store-demo/pom.xml
+++ b/spring-data-eclipse-store-demo/pom.xml
@@ -71,7 +71,7 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 3.14.0
+ 3.14.1${maven.compiler.release}
@@ -95,7 +95,7 @@
org.apache.maven.pluginsmaven-surefire-plugin
- 3.5.3
+ 3.5.4
--add-opens java.base/java.util=ALL-UNNAMED
diff --git a/spring-data-eclipse-store-demo/src/main/java/software/xdev/spring/data/eclipse/store/demo/lazy/LazyDemoApplication.java b/spring-data-eclipse-store-demo/src/main/java/software/xdev/spring/data/eclipse/store/demo/lazy/LazyDemoApplication.java
index 7fcd5ec8..30579b87 100644
--- a/spring-data-eclipse-store-demo/src/main/java/software/xdev/spring/data/eclipse/store/demo/lazy/LazyDemoApplication.java
+++ b/spring-data-eclipse-store-demo/src/main/java/software/xdev/spring/data/eclipse/store/demo/lazy/LazyDemoApplication.java
@@ -26,6 +26,7 @@
@SpringBootApplication
public class LazyDemoApplication implements CommandLineRunner
{
+ // CPD-OFF
private static final Logger LOG = LoggerFactory.getLogger(LazyDemoApplication.class);
private final CustomerRepository customerRepository;
private final PetRepository petRepository;
@@ -64,4 +65,5 @@ public void run(final String... args)
LOG.info("Pets found with findAll():");
this.petRepository.findAll().forEach(p -> LOG.info(p.toString()));
}
+ // CPD-ON
}
diff --git a/spring-data-eclipse-store-jpa/pom.xml b/spring-data-eclipse-store-jpa/pom.xml
index 9dde82f5..f3d1a8ce 100644
--- a/spring-data-eclipse-store-jpa/pom.xml
+++ b/spring-data-eclipse-store-jpa/pom.xml
@@ -73,7 +73,7 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 3.14.0
+ 3.14.1${maven.compiler.release}
diff --git a/spring-data-eclipse-store/pom.xml b/spring-data-eclipse-store/pom.xml
index db67a72b..14523f28 100644
--- a/spring-data-eclipse-store/pom.xml
+++ b/spring-data-eclipse-store/pom.xml
@@ -51,7 +51,7 @@
UTF-8
- 3.5.3
+ 3.5.92.1.32.1.39.0.1.Final
@@ -278,7 +278,7 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 3.14.0
+ 3.14.1${javaVersion}${javaVersion}
@@ -291,7 +291,7 @@
org.apache.maven.pluginsmaven-javadoc-plugin
- 3.11.2
+ 3.12.0attach-javadocs
@@ -309,7 +309,7 @@
org.apache.maven.pluginsmaven-surefire-plugin
- 3.5.3
+ 3.5.4
--add-exports java.base/jdk.internal.misc=ALL-UNNAMED
@@ -322,7 +322,7 @@
org.apache.maven.pluginsmaven-source-plugin
- 3.3.1
+ 3.4.0attach-sources
@@ -343,7 +343,7 @@
org.codehaus.mojoflatten-maven-plugin
- 1.7.1
+ 1.7.3ossrh
@@ -389,7 +389,7 @@
org.sonatype.centralcentral-publishing-maven-plugin
- 0.8.0
+ 0.9.0truesonatype-central-portal
@@ -411,7 +411,7 @@
com.puppycrawl.toolscheckstyle
- 10.26.1
+ 12.3.0
@@ -436,8 +436,9 @@
org.apache.maven.pluginsmaven-pmd-plugin
- 3.27.0
+ 3.28.0
+ truetruetrue
@@ -453,12 +454,12 @@
net.sourceforge.pmdpmd-core
- 7.15.0
+ 7.20.0net.sourceforge.pmdpmd-java
- 7.15.0
+ 7.20.0
diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/antlr/HSqlQueryExecutor.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/antlr/HSqlQueryExecutor.java
index cff88bb1..94803d3a 100644
--- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/antlr/HSqlQueryExecutor.java
+++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/antlr/HSqlQueryExecutor.java
@@ -37,6 +37,8 @@
public class HSqlQueryExecutor
{
+ protected static final DateTimeFormatter LOCAL_DATE_REPLACE_DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
private final SQLParser parser;
private final EntityListProvider entityListProvider;
private final Class domainClass;
@@ -80,7 +82,7 @@ private String replacePlaceholders(final String sqlValue, final Object[] paramet
}
if(parameters[i] instanceof final LocalDate localDate)
{
- value = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+ value = localDate.format(LOCAL_DATE_REPLACE_DTF);
}
stringWithReplacedValues = stringWithReplacedValues.replaceAll(placeholder, value);
}
diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/criteria/CriteriaByExample.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/criteria/CriteriaByExample.java
index 15ec9418..1830bbf8 100644
--- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/criteria/CriteriaByExample.java
+++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/criteria/CriteriaByExample.java
@@ -17,10 +17,12 @@
import java.lang.reflect.Field;
import java.util.Collection;
+import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.WeakHashMap;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@@ -38,6 +40,7 @@
*/
public class CriteriaByExample implements Criteria
{
+ private static final Map REGEX_EXAMPLE_CACHE = Collections.synchronizedMap(new WeakHashMap<>());
private final Predicate predicate;
public CriteriaByExample(final Example example)
@@ -143,7 +146,9 @@ private boolean createPredicateForStringMatcher(
case STARTING -> String::startsWith;
case ENDING -> String::endsWith;
case CONTAINING -> String::contains;
- case REGEX -> (v, example) -> Pattern.compile(example).matcher(v).find();
+ case REGEX -> (v, example) -> REGEX_EXAMPLE_CACHE.computeIfAbsent(example, Pattern::compile)
+ .matcher(v)
+ .find();
default -> null;
};
diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/root/RootDataV2.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/root/RootDataV2.java
index fe5d4c4f..1148f52b 100644
--- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/root/RootDataV2.java
+++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/root/RootDataV2.java
@@ -28,6 +28,7 @@
@SuppressWarnings({"java:S119", "unchecked"})
public class RootDataV2
{
+ // CPD-OFF
private final Map> entityLists;
public RootDataV2()
@@ -87,4 +88,5 @@ public Object getObjectsToStoreAfterNewLastId(final Class> entityClass)
{
return this.entityLists.get(this.getEntityName(entityClass));
}
+ // CPD-ON
}
diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/version/VersionManager.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/version/VersionManager.java
index 1845e72a..dffbedb2 100644
--- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/version/VersionManager.java
+++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/version/VersionManager.java
@@ -59,7 +59,6 @@ public void incrementVersion(final T entity)
* Checks if the two entities are valid for merge. If they have the same version, everything is ok. But if the
* workingCopy-version is null or different from the original-version, an Exception is thrown.
*/
- @SuppressWarnings("PMD.AvoidRethrowingException")
public void ensureSameVersion(final T workingCopy, final T original)
{
if(this.versionField.isPresent() && original != null)
diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/HsqlTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/HsqlTest.java
index 620afbc7..0dfda3b9 100644
--- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/HsqlTest.java
+++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/HsqlTest.java
@@ -35,6 +35,7 @@
import software.xdev.spring.data.eclipse.store.integration.isolated.IsolatedTestAnnotations;
+@SuppressWarnings("checkstyle:TodoComment")
@IsolatedTestAnnotations
@ContextConfiguration(classes = {HsqlTestConfiguration.class})
class HsqlTest
diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/MyEntityRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/MyEntityRepository.java
index 2e9b7ae6..07b6af92 100644
--- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/MyEntityRepository.java
+++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/MyEntityRepository.java
@@ -22,6 +22,7 @@
import software.xdev.spring.data.eclipse.store.repository.Query;
+@SuppressWarnings("checkstyle:TodoComment")
public interface MyEntityRepository extends ListCrudRepository
{
// Simple Select
diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreatorAndOrTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreatorAndOrTest.java
index 2d55db8c..b2eaf020 100644
--- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreatorAndOrTest.java
+++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreatorAndOrTest.java
@@ -31,6 +31,7 @@
import software.xdev.spring.data.eclipse.store.helper.TestData;
+@SuppressWarnings("checkstyle:TodoComment")
class EclipseStoreQueryCreatorAndOrTest
{
static Stream generateDataWithCountOfFirstNameAndLastName()