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 requestEntity = new HttpEntity<>(req); + + HttpClient httpClient = HttpClientBuilder.create().setMaxConnPerRoute(10).build(); // bad + return remoteCall(httpClient, requestEntity); + } +} +]]> + + + + + +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.0 JavaOnlyWithTests true true 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 @@