├── .java-version ├── .mvn ├── maven.config ├── wrapper │ ├── maven-wrapper.jar │ ├── maven-wrapper.properties │ └── MavenWrapperDownloader.java ├── jvm.config └── extensions.xml ├── config ├── logging.properties ├── owasp-suppressions.xml ├── pmd │ └── custom-rules.xml ├── checkstyle │ ├── sun_checks.xml │ ├── checkstyle.xml │ └── google_checks.xml └── ide │ └── intellij-java-google-style.xml ├── .gitattributes ├── images ├── try.png ├── changes.png ├── table-of-contents.png ├── modern-agile-wheel-english.png ├── public-domain.svg ├── jacoco.svg ├── cc0.svg └── Wikibooks-contribute-icon.svg ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── src ├── test │ └── java │ │ └── demo │ │ ├── package-info.java │ │ ├── ApplicationIT.java │ │ ├── TheFooTest.java │ │ └── TerminalContext.java ├── lombok.config ├── main │ └── java │ │ └── demo │ │ ├── package-info.java │ │ ├── Application.java │ │ └── TheFoo.java └── integrationTest │ └── java │ └── demo │ ├── package-info.java │ └── ApplicationTest.java ├── dot-envrc-template ├── .editorconfig ├── .gitignore ├── gradle.properties ├── settings.gradle ├── .github ├── dependabot.yml └── workflows │ ├── ci-earthly-gradle.yml │ └── ci-earthly-maven.yml ├── Earthfile ├── CONTRIBUTING.md ├── gradlew.bat ├── run-with-maven.sh ├── run-with-gradle.sh ├── coverage.sh ├── mvnw.cmd ├── LICENSE.md ├── gradlew ├── mvnw ├── README.md └── pom.xml /.java-version: -------------------------------------------------------------------------------- 1 | 21 2 | -------------------------------------------------------------------------------- /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | --strict-checksums 2 | -------------------------------------------------------------------------------- /config/logging.properties: -------------------------------------------------------------------------------- 1 | .level = WARNING 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.bat text eol=crlf 3 | *.cmd text eol=crlf 4 | -------------------------------------------------------------------------------- /images/try.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binkley/modern-java-practices/HEAD/images/try.png -------------------------------------------------------------------------------- /images/changes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binkley/modern-java-practices/HEAD/images/changes.png -------------------------------------------------------------------------------- /images/table-of-contents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binkley/modern-java-practices/HEAD/images/table-of-contents.png -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binkley/modern-java-practices/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binkley/modern-java-practices/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /images/modern-agile-wheel-english.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binkley/modern-java-practices/HEAD/images/modern-agile-wheel-english.png -------------------------------------------------------------------------------- /.mvn/jvm.config: -------------------------------------------------------------------------------- 1 | --add-opens java.base/java.lang=ALL-UNNAMED 2 | -Djava.util.logging.config.file=config/logging.properties 3 | -XX:+UseCodeCacheFlushing 4 | -XX:TieredStopAtLevel=1 5 | -------------------------------------------------------------------------------- /src/test/java/demo/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Tests for 3 | * Modern 4 | * Java Practices. 5 | */ 6 | package demo; 7 | -------------------------------------------------------------------------------- /src/lombok.config: -------------------------------------------------------------------------------- 1 | config.stopBubbling = true 2 | lombok.addLombokGeneratedAnnotation = true 3 | lombok.anyConstructor.addConstructorProperties = true 4 | lombok.extern.findbugs.addSuppressFBWarnings = true 5 | -------------------------------------------------------------------------------- /src/main/java/demo/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Demonstrations for 3 | * Modern 4 | * Java Practices. 5 | */ 6 | package demo; 7 | -------------------------------------------------------------------------------- /dot-envrc-template: -------------------------------------------------------------------------------- 1 | # For Earthly workaround 2 | export EARTHLY_DISABLE_REMOTE_REGISTRY_PROXY=true 3 | # For OWASP 4 | export OWASP_NVD_API_KEY='XXX' 5 | # To make Maven happier 6 | export JAVA_HOME="$(jenv javahome)" 7 | -------------------------------------------------------------------------------- /src/integrationTest/java/demo/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Integration tests for 3 | * Modern 4 | * Java Practices. 5 | */ 6 | package demo; 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [**] 4 | # Unless you are a Windows-only source code base, best to use linefeeds (`\n`) 5 | end_of_line = lf 6 | # Most teams will prefer 100 or 120 line limits, or no limits at all 7 | max_line_length = 80 8 | -------------------------------------------------------------------------------- /.mvn/extensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | fr.jcgay.maven 5 | maven-profiler 6 | 3.2 7 | 8 | 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Assumes use of IntelliJ -- adjust to the editors you use 2 | *.iml 3 | /.idea/ 4 | 5 | # Gradle -- pick this or Maven 6 | /.gradle/ 7 | /build/ 8 | 9 | # Maven -- pick this or Gradle 10 | /target/ 11 | 12 | # When running GitHub actions locally 13 | /workflow-artifacts/ 14 | 15 | # DO NOT commit, push, or share. The env vars may hold secrets. 16 | /.envrc 17 | -------------------------------------------------------------------------------- /images/public-domain.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/java/demo/Application.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import lombok.Generated; 4 | 5 | import static java.lang.System.out; 6 | 7 | /** Production class for integration test demonstration. */ 8 | @Generated // Lie to JaCoCo and PIT 9 | public final class Application { 10 | /** 11 | * Main entry point. 12 | * 13 | * @param args the command-line arguments 14 | */ 15 | public static void main(final String... args) { 16 | out.println(new TheFoo("I AM FOOCUTUS OF BORG")); 17 | } 18 | 19 | // This is a "utility" class, ie, no instances, only static methods 20 | private Application() { } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/demo/ApplicationIT.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static demo.TerminalContext.captureTerminal; 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | /** 9 | * Integration test for the application. 10 | */ 11 | @SuppressWarnings({ 12 | // PMD does not understand FooIT.java as a test class 13 | "PMD.AtLeastOneConstructor", 14 | }) 15 | class ApplicationIT { 16 | /** 17 | * Use case: the application runs normally. 18 | */ 19 | @Test 20 | void shouldRun() { 21 | final var out = captureTerminal(Application::main); 22 | 23 | assertThat(out).isEqualTo("TheFoo(label=I AM FOOCUTUS OF BORG)\n"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/integrationTest/java/demo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static demo.TerminalContext.captureTerminal; 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | /** 9 | * Just an example of Unit test. 10 | */ 11 | @SuppressWarnings({ 12 | "PMD.AtLeastOneConstructor", 13 | "PMD.SignatureDeclareThrowsException" 14 | }) 15 | class ApplicationTest { 16 | 17 | /** 18 | * Use case: the application runs normally. 19 | **/ 20 | @Test 21 | void shouldRun() throws Exception { 22 | final var out = captureTerminal(() -> { 23 | Application.main("Hello", "world!"); 24 | }); 25 | 26 | assertThat(out).isEqualTo("TheFoo(label=I AM FOOCUTUS OF BORG)\n"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Gradle 2 | org.gradle.jvmargs=--add-opens java.base/java.lang=ALL-UNNAMED 3 | # Versions -- see gradle/libs.versions.toml 4 | checkstyleVersion=10.17.0 5 | coverageBranches=1.00 6 | coverageInstructions=1.00 7 | coverageLines=1.00 8 | # Use Enforcer 0.14.0 or higher which fixes a clash with DependencyCheck 9 | enforcerPluginVersion=0.14.0 10 | findsecbugsPluginVersion=1.13.0 11 | # Hey! This is important. This property sets your Gradle version. 12 | gradleWrapperVersion=8.9 13 | jacocoVersion=0.8.12 14 | jdkVersion=21 15 | mainClass=demo.Application 16 | pitestJUnit5PluginVersion=1.2.1 17 | pitestPluginVersion=1.15.0 18 | # Cannot name this "pitestVersion" -- the plugin has a property of the same 19 | # name, so this property needs to have a distinct name to satisfy Gradle 20 | pitestToolVersion=1.17.0 21 | pmdVersion=7.13.0 22 | spotbugsPluginVersion=6.0.15 23 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | // Note: 2 | // When you see "$fooVersion", those are versions pulled from 3 | // "gradle.properties". 4 | 5 | // TODO: Migrate these to use the version catalog (like other plugins in 6 | // "build.gradle"). 7 | pluginManagement { 8 | plugins { 9 | id "com.github.spotbugs" version "$spotbugsPluginVersion" 10 | id "info.solidsoft.pitest" version "$pitestPluginVersion" 11 | } 12 | } 13 | 14 | buildscript { 15 | repositories { 16 | gradlePluginPortal() 17 | mavenCentral() 18 | } 19 | 20 | dependencies { 21 | classpath "org.kordamp.gradle:enforcer-gradle-plugin:$enforcerPluginVersion" 22 | } 23 | } 24 | 25 | apply plugin: "org.kordamp.gradle.enforcer" 26 | 27 | // See https://kordamp.org/enforcer-gradle-plugin/#_rules for the kinds of 28 | // rules you can set up. 29 | enforce { 30 | rule(enforcer.rules.RequireGradleVersion) { r -> 31 | r.version = "$gradleWrapperVersion" 32 | } 33 | } 34 | 35 | rootProject.name = "modern-java-practices" 36 | -------------------------------------------------------------------------------- /images/jacoco.svg: -------------------------------------------------------------------------------- 1 | coverage100% -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gradle" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | groups: 8 | gradle-minors: 9 | applies-to: version-updates 10 | update-types: 11 | - "minor" 12 | - "patch" 13 | 14 | - package-ecosystem: "maven" 15 | directory: "/" 16 | schedule: 17 | interval: "daily" 18 | groups: 19 | maven-minors: 20 | applies-to: version-updates 21 | update-types: 22 | - "minor" 23 | - "patch" 24 | 25 | - package-ecosystem: "github-actions" 26 | directory: "/" 27 | schedule: 28 | interval: "daily" 29 | groups: 30 | gha-core: 31 | applies-to: version-updates 32 | update-types: 33 | - "minor" 34 | - "patch" 35 | patterns: 36 | - "actions/*" 37 | gha-third-party: 38 | applies-to: version-updates 39 | update-types: 40 | - "minor" 41 | - "patch" 42 | patterns: 43 | - "*" 44 | exclude-patterns: 45 | - "actions/*" 46 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=source 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar 21 | -------------------------------------------------------------------------------- /src/main/java/demo/TheFoo.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Getter; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.ToString; 7 | 8 | import javax.annotation.Nonnull; 9 | import java.util.regex.Pattern; 10 | 11 | import static java.util.regex.Pattern.CASE_INSENSITIVE; 12 | import static java.util.regex.Pattern.compile; 13 | 14 | /** Demonstration class. */ 15 | @Getter 16 | @EqualsAndHashCode 17 | @RequiredArgsConstructor 18 | @ToString 19 | public final class TheFoo { 20 | /** Check for the Borg. */ 21 | private static final Pattern BORG = compile("BORG", CASE_INSENSITIVE); 22 | 23 | /** The foo. */ 24 | @Nonnull 25 | private final String label; 26 | 27 | /** 28 | * Is there danger? 29 | * 30 | * @return if the ship should go to red alert 31 | */ 32 | public boolean isRedAlert() { 33 | return checkRedAlert(label); 34 | } 35 | 36 | /** 37 | * Checks text for the presence of the Borg. 38 | * 39 | * @param text the text to check 40 | * 41 | * @return {@code true} if Borg is found 42 | */ 43 | public static boolean checkRedAlert(final String text) { 44 | return BORG.matcher(text).find(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Earthfile: -------------------------------------------------------------------------------- 1 | VERSION 0.8 2 | FROM eclipse-temurin:21-jdk-jammy 3 | WORKDIR /code 4 | 5 | # build-with-gradle builds and tests with Gradle, and saves the build/ directory 6 | build-with-gradle: 7 | COPY gradlew . 8 | COPY gradle gradle 9 | COPY gradle.properties . 10 | COPY settings.gradle . 11 | COPY build.gradle . 12 | COPY config config 13 | COPY src src 14 | RUN --secret OWASP_NVD_API_KEY ./gradlew clean build 15 | 16 | # For CI so that GitHub can copy artifacts: 17 | # Just copy everything rather than maintain a whitelist of files/dirs. 18 | SAVE ARTIFACT --keep-ts build AS LOCAL build 19 | 20 | # run-with-gradle runs the demo program with Gradle, building if needed 21 | run-with-gradle: 22 | FROM +build-with-gradle 23 | COPY run-with-gradle.sh . 24 | RUN ./run-with-gradle.sh 25 | 26 | # build-with-maven builds and tests with Maven, and saves the target/ directory 27 | build-with-maven: 28 | COPY mvnw . 29 | COPY .mvn .mvn 30 | COPY pom.xml . 31 | COPY config config 32 | COPY src src 33 | RUN --secret OWASP_NVD_API_KEY ./mvnw --no-transfer-progress clean verify 34 | 35 | # For CI so that GitHub can copy artifacts: 36 | # Just copy everything rather than maintain a whitelist of files/dirs. 37 | SAVE ARTIFACT --keep-ts target AS LOCAL target 38 | 39 | # run-with-maven runs the demo program with Maven, building if needed 40 | run-with-maven: 41 | FROM +build-with-maven 42 | COPY run-with-maven.sh . 43 | RUN ./run-with-maven.sh 44 | 45 | # build executes both +build-with-gradle and +build-with-maven targets 46 | build: 47 | BUILD +build-with-gradle 48 | BUILD +build-with-maven 49 | 50 | # run executes both +run-with-gradle and +run-with-maven targets 51 | run: 52 | BUILD +run-with-gradle 53 | BUILD +run-with-maven 54 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Maintainance 2 | 3 | Notes for maintaining this repository: 4 | 5 | Use the helper scripts, and see that they: 6 | 1. **Work.**
7 | Working scripts is the #1 thing to help others. 8 | 2. **Work in CI and make sense there.**
9 | A top goal for you: your local build should mirror the build in CI. 10 | Check the GitHub actions. 11 | 3. **Make sense for the project.**
12 | An example is moving from Batect to Earthly. 13 | Scripts should migrate from (the archived) Batect. 14 | 15 | # Contributing 16 | 17 | Several scripts in the project root are to aid in contributing. 18 | 19 | - [`build-as-ci-does.sh`](./build-as-ci-does.sh) — 20 | Recreate locally the build used by CI in GitHub actions using Batect and 21 | Earthly (both use Docker): 22 | * Run a clean full build for Gradle 23 | * Run the Gradle-built demo program 24 | * Run a clean full build for Maven 25 | * Run the Maven-built demo program 26 | Helpful when CI has steps that local developers do not, and you want to 27 | reproduce or explore locally a CI problem. The script should match the 28 | actions your CI takes on pushes (this project uses GitHub actions). 29 | - [`coverage`](./coverage) 30 | Checks if the local code passes at given levels of code coverage. 31 | The script is focused on Maven, but with edits would do the same for Gradle. 32 | This supports the ["ratchet" 33 | pattern](https://robertgreiner.com/continuous-code-improvement-using-ratcheting/). 34 | - [`run-with-gradle.sh`](./run-with-gradle.sh) 35 | If you are a Gradle project, you will likely rename this to just `run` or 36 | similar. 37 | - [`run-with-maven.sh`](./run-with-maven.sh) 38 | If you are a Maven project, you will likely rename this to just `run` or 39 | similar. 40 | - Use the 41 | [`wiki-outline.py`](https://raw.githubusercontent.com/wiki/binkley/modern-java-practices/etc/wiki-outline.py) 42 | script in the wiki repo to get a Table-of-Contents view from the command 43 | line. 44 | -------------------------------------------------------------------------------- /config/owasp-suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | CVE-2023-2976 11 | 12 | 13 | 19 | CVE-2022-4244 20 | 21 | 22 | 28 | CVE-2022-4245 29 | 30 | 31 | 37 | CVE-2023-45960 38 | 39 | 40 | 45 | CVE-2015-0897 46 | 47 | 48 | -------------------------------------------------------------------------------- /images/cc0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 13 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /config/pmd/custom-rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | Custom rules to use on the project 8 | The source of rule sets 9 | https://github.com/pmd/pmd/tree/master/pmd-java/src/main/resources/category/java 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/test/java/demo/TheFooTest.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | import org.junit.jupiter.api.Test; 5 | import org.mockito.MockedStatic; 6 | 7 | import static demo.TheFoo.checkRedAlert; 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 10 | import static org.mockito.ArgumentMatchers.eq; 11 | import static org.mockito.Mockito.mockStatic; 12 | 13 | /** 14 | * An example of test class. 15 | */ 16 | @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") 17 | @SuppressWarnings({ 18 | "PMD.TooManyStaticImports", 19 | "PMD.AtLeastOneConstructor" 20 | }) 21 | class TheFooTest { 22 | @Test 23 | void shouldKnowTheFoo() { 24 | final var theFoo = "I am the Foo!"; 25 | final var didTheFoo = new TheFoo(theFoo); 26 | 27 | assertThat(didTheFoo.getLabel()).isEqualTo(theFoo); 28 | } 29 | 30 | @Test 31 | void shouldBeSameFooAnObject() { 32 | final var theFoo = "I am the Foo!"; 33 | final var didTheFoo = new TheFoo(theFoo); 34 | final var anotherFoo = new TheFoo(theFoo); 35 | 36 | assertThat(didTheFoo).isEqualTo(anotherFoo); 37 | } 38 | 39 | @Test 40 | void shouldBeSameFooAsAKey() { 41 | final var theFoo = "I am the Foo!"; 42 | final var didTheFoo = new TheFoo(theFoo); 43 | final var anotherFoo = new TheFoo(theFoo); 44 | 45 | assertThat(didTheFoo.hashCode()).isEqualTo(anotherFoo.hashCode()); 46 | } 47 | 48 | @SuppressWarnings("ConstantConditions") 49 | @Test 50 | void shouldComplainWhenInvalid() { 51 | assertThatThrownBy(() -> new TheFoo(null)) 52 | .isInstanceOf(NullPointerException.class); 53 | } 54 | 55 | @Test 56 | void shouldRedAlert() { 57 | assertThat(new TheFoo("We are the Borg.").isRedAlert()) 58 | .isTrue(); 59 | } 60 | 61 | @Test 62 | void shouldNotRedAlert() { 63 | assertThat(new TheFoo("My debt is repaid.").isRedAlert()) 64 | .isFalse(); 65 | } 66 | 67 | @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") 68 | @Test 69 | void shouldRedAlertAsStaticMock() { 70 | final var label = "Some label, *mumble, mumble*. "; 71 | 72 | try (MockedStatic mockFoo = mockStatic(TheFoo.class)) { 73 | mockFoo.when(() -> checkRedAlert(eq(label))).thenReturn(true); 74 | 75 | assertThat(new TheFoo(label).isRedAlert()) 76 | .isTrue(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | com-diffplug-spotless-plugin = "7.0.0.BETA4" 3 | com-dorongold-task-tree-plugin = "4.0.0" 4 | com-github-andygoossens-gradle-modernizer-plugin = "1.11.0" 5 | com-github-ben-manes-versions-plugin = "0.51.0" 6 | com-github-spotbugs = "4.8.6" 7 | com-google-code-findbugs = "3.0.1" 8 | com-h3xstream-findsecbugs-findsecbugs-plugin = "1.13.0" 9 | com-puppycrawl-tools-checkstyle = "10.20.0" 10 | nl-littlerobots-version-catalog-update-plugin = "0.8.5" 11 | org-assertj = "3.26.3" 12 | org-gaul-modernizer = "2.9.0" 13 | org-jacoco = "0.8.12" 14 | org-junit-jupiter = "5.11.1" 15 | org-kordamp-gradle-jdeps-plugin = "0.20.0" 16 | org-mockito = "5.13.0" 17 | org-owasp-dependencycheck-plugin = "11.1.0" 18 | org-pitest-pitest-command-line = "1.17.3" 19 | org-pitest-pitest-junit5-plugin = "1.2.1" 20 | org-projectlombok = "1.18.36" 21 | 22 | [libraries] 23 | com-github-spotbugs-spotbugs-annotations = { module = "com.github.spotbugs:spotbugs-annotations", version.ref = "com-github-spotbugs" } 24 | com-google-code-findbugs-findbugs-annotations = { module = "com.google.code.findbugs:findbugs-annotations", version.ref = "com-google-code-findbugs" } 25 | com-h3xstream-findsecbugs-findsecbugs-plugin = { module = "com.h3xstream.findsecbugs:findsecbugs-plugin", version.ref = "com-h3xstream-findsecbugs-findsecbugs-plugin" } 26 | com-puppycrawl-tools-checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "com-puppycrawl-tools-checkstyle" } 27 | org-apache-bcel = "org.apache.bcel:bcel:6.10.0" 28 | org-assertj-assertj-core = { module = "org.assertj:assertj-core", version.ref = "org-assertj" } 29 | org-gaul-modernizer-maven-annotations = { module = "org.gaul:modernizer-maven-annotations", version.ref = "org-gaul-modernizer" } 30 | org-jacoco-org-jacoco-agent = { module = "org.jacoco:org.jacoco.agent", version.ref = "org-jacoco" } 31 | org-jacoco-org-jacoco-ant = { module = "org.jacoco:org.jacoco.ant", version.ref = "org-jacoco" } 32 | org-junit-jupiter-junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "org-junit-jupiter" } 33 | org-junit-jupiter-junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "org-junit-jupiter" } 34 | org-mockito-mockito-core = { module = "org.mockito:mockito-core", version.ref = "org-mockito" } 35 | org-pitest-pitest-command-line = { module = "org.pitest:pitest-command-line", version.ref = "org-pitest-pitest-command-line" } 36 | org-pitest-pitest-junit5-plugin = { module = "org.pitest:pitest-junit5-plugin", version.ref = "org-pitest-pitest-junit5-plugin" } 37 | org-projectlombok-lombok = { module = "org.projectlombok:lombok", version.ref = "org-projectlombok" } 38 | 39 | [plugins] 40 | com-diffplug-spotless-plugin = { id = "com.diffplug.spotless", version.ref = "com-diffplug-spotless-plugin" } 41 | com-dorongold-task-tree-plugin = { id = "com.dorongold.task-tree", version.ref = "com-dorongold-task-tree-plugin" } 42 | com-github-andygoossens-gradle-modernizer-plugin = { id = "com.github.andygoossens.gradle-modernizer-plugin", version.ref = "com-github-andygoossens-gradle-modernizer-plugin" } 43 | com-github-ben-manes-versions-plugin = { id = "com.github.ben-manes.versions", version.ref = "com-github-ben-manes-versions-plugin" } 44 | nl-littlerobots-version-catalog-update-plugin = { id = "nl.littlerobots.version-catalog-update", version.ref = "nl-littlerobots-version-catalog-update-plugin" } 45 | org-kordamp-gradle-jdeps-plugin = { id = "org.kordamp.gradle.jdeps", version.ref = "org-kordamp-gradle-jdeps-plugin" } 46 | org-owasp-dependencycheck-plugin = { id = "org.owasp.dependencycheck", version.ref = "org-owasp-dependencycheck-plugin" } 47 | -------------------------------------------------------------------------------- /run-with-maven.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # shellcheck disable=SC2214,SC2215 3 | 4 | if ((BASH_VERSINFO[0] < 4)) 5 | then 6 | cat <&2 7 | $0: This script requires Bash 4.0 or higher. 8 | If you are on Mac, the native /bin/bash is from 2006 or older; consider using a 9 | more recent Bash from Homebrew. 10 | EOM 11 | exit 3 12 | fi 13 | 14 | export PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]:+${FUNCNAME[0]}():} ' 15 | 16 | # Edit these to suit 17 | readonly package=demo 18 | readonly artifactId=modern-java-practices 19 | readonly version=0-SNAPSHOT 20 | jvm_flags=() 21 | 22 | # No editable parts below here 23 | 24 | set -e 25 | set -u 26 | set -o pipefail 27 | 28 | readonly progname="${0##*/}" 29 | 30 | printf -v pbold "\e[1m" 31 | printf -v pred "\e[31m" 32 | printf -v preset "\e[0m" 33 | 34 | function print-help() { 35 | cat <&2 111 | exit 2 112 | fi 113 | readonly jar=target/$artifactId-$version-jar-with-dependencies.jar 114 | 115 | case "$alt_class" in 116 | '') jvm_flags=("${jvm_flags[@]}" -jar "$jar") ;; 117 | *) 118 | readonly runtime_classname="$(runtime-classname "$package.$alt_class")" 119 | jvm_flags=("${jvm_flags[@]}" -cp "$jar" "$runtime_classname") 120 | ;; 121 | esac 122 | 123 | rebuild-if-needed 124 | 125 | expected='TheFoo(label=I AM FOOCUTUS OF BORG)' 126 | captured_output=$(exec java "${jvm_flags[@]}" "$@") 127 | case $captured_output in 128 | $expected ) echo "$captured_output" ;; 129 | * ) 130 | echo "$pbold$pred$0: ERROR: expected: $expected, actual: $captured_output$preset" 131 | false 132 | ;; 133 | esac 134 | -------------------------------------------------------------------------------- /run-with-gradle.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # shellcheck disable=SC2214,SC2215 3 | 4 | if ((BASH_VERSINFO[0] < 4)) 5 | then 6 | cat <&2 7 | $0: This script requires Bash 4.0 or higher. 8 | If you are on Mac, the native /bin/bash is from 2006 or older; consider using a 9 | more recent Bash from Homebrew. 10 | EOM 11 | exit 3 12 | fi 13 | 14 | export PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]:+${FUNCNAME[0]}():} ' 15 | 16 | # Edit these to suit 17 | readonly package=hm.binkley.md 18 | readonly artifactId=modern-java-practices 19 | readonly version=0-SNAPSHOT 20 | jvm_flags=() 21 | 22 | # No editable parts below here 23 | 24 | set -e 25 | set -u 26 | set -o pipefail 27 | 28 | readonly progname="${0##*/}" 29 | 30 | printf -v pbold "\e[1m" 31 | printf -v pred "\e[31m" 32 | printf -v preset "\e[0m" 33 | 34 | function print-help() { 35 | cat <&2 118 | exit 2 119 | fi 120 | readonly jar=build/libs/$artifactId-$version.jar 121 | 122 | case "$alt_class" in 123 | '') jvm_flags=("${jvm_flags[@]}" -jar "$jar") ;; 124 | *) 125 | readonly runtime_classname="$(runtime-classname "$package.$alt_class")" 126 | jvm_flags=("${jvm_flags[@]}" -cp "$jar" "$runtime_classname") 127 | ;; 128 | esac 129 | 130 | rebuild-if-needed 131 | 132 | expected='TheFoo(label=I AM FOOCUTUS OF BORG)' 133 | captured_output=$(exec java "${jvm_flags[@]}" "$@") 134 | case $captured_output in 135 | $expected ) echo "$captured_output" ;; 136 | * ) 137 | echo "$pbold$pred$0: ERROR: expected: $expected, actual: $captured_output$preset" 138 | false 139 | ;; 140 | esac 141 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.net.Authenticator; 23 | import java.net.PasswordAuthentication; 24 | import java.net.URI; 25 | import java.net.URL; 26 | import java.nio.file.Files; 27 | import java.nio.file.Path; 28 | import java.nio.file.Paths; 29 | import java.nio.file.StandardCopyOption; 30 | import java.util.concurrent.ThreadLocalRandom; 31 | 32 | public final class MavenWrapperDownloader { 33 | private static final String WRAPPER_VERSION = "3.3.2"; 34 | 35 | private static final boolean VERBOSE = Boolean.parseBoolean(System.getenv("MVNW_VERBOSE")); 36 | 37 | public static void main(String[] args) { 38 | log("Apache Maven Wrapper Downloader " + WRAPPER_VERSION); 39 | 40 | if (args.length != 2) { 41 | System.err.println(" - ERROR wrapperUrl or wrapperJarPath parameter missing"); 42 | System.exit(1); 43 | } 44 | 45 | try { 46 | log(" - Downloader started"); 47 | final URL wrapperUrl = URI.create(args[0]).toURL(); 48 | final String jarPath = args[1].replace("..", ""); // Sanitize path 49 | final Path wrapperJarPath = Paths.get(jarPath).toAbsolutePath().normalize(); 50 | downloadFileFromURL(wrapperUrl, wrapperJarPath); 51 | log("Done"); 52 | } catch (IOException e) { 53 | System.err.println("- Error downloading: " + e.getMessage()); 54 | if (VERBOSE) { 55 | e.printStackTrace(); 56 | } 57 | System.exit(1); 58 | } 59 | } 60 | 61 | private static void downloadFileFromURL(URL wrapperUrl, Path wrapperJarPath) 62 | throws IOException { 63 | log(" - Downloading to: " + wrapperJarPath); 64 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 65 | final String username = System.getenv("MVNW_USERNAME"); 66 | final char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 67 | Authenticator.setDefault(new Authenticator() { 68 | @Override 69 | protected PasswordAuthentication getPasswordAuthentication() { 70 | return new PasswordAuthentication(username, password); 71 | } 72 | }); 73 | } 74 | Path temp = wrapperJarPath 75 | .getParent() 76 | .resolve(wrapperJarPath.getFileName() + "." 77 | + Long.toUnsignedString(ThreadLocalRandom.current().nextLong()) + ".tmp"); 78 | try (InputStream inStream = wrapperUrl.openStream()) { 79 | Files.copy(inStream, temp, StandardCopyOption.REPLACE_EXISTING); 80 | Files.move(temp, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING); 81 | } finally { 82 | Files.deleteIfExists(temp); 83 | } 84 | log(" - Downloader complete"); 85 | } 86 | 87 | private static void log(String msg) { 88 | if (VERBOSE) { 89 | System.out.println(msg); 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]:+${FUNCNAME[0]}():} ' 4 | 5 | set -e 6 | set -u 7 | set -o pipefail 8 | 9 | readonly progname="${0##*/}" 10 | 11 | function print-help() { 12 | cat <&2 58 | return 1 59 | fi 60 | } 61 | 62 | full=none 63 | mutation=false 64 | open=false 65 | run=true 66 | # shellcheck disable=SC2214 67 | while getopts :f:hmox-: opt; do 68 | [[ $opt == - ]] && opt=${OPTARG%%=*} OPTARG=${OPTARG#*=} 69 | # shellcheck disable=SC2213 70 | case $opt in 71 | f | full-coverage) 72 | case "$OPTARG" in 73 | unit) full="$OPTARG" ;; 74 | all | mutation) mutation=true full="$OPTARG" ;; 75 | *) 76 | bad-option-argument "-f" "$OPTARG" 77 | exit 2 78 | ;; 79 | esac 80 | ;; 81 | h | help) 82 | print-help 83 | exit 0 84 | ;; 85 | m | with-mutation) mutation=true ;; 86 | o | open-report) open=true ;; 87 | x | no-rerun) open=true run=false ;; 88 | \?) 89 | bad-option "$OPTARG" 90 | exit 2 91 | ;; 92 | :) 93 | bad-option-argument "$OPTARG" 94 | exit 2 95 | ;; 96 | esac 97 | done 98 | shift $((OPTIND - 1)) 99 | 100 | case "$full" in 101 | none) flags='' ;; 102 | all) 103 | flags='-Dcoverage.lines=100 -Dcoverage.branches=100 -Dcoverage.instructions=100 -Dcoverage.mutation=100' 104 | ;; 105 | mutation) 106 | flags='-Dcoverage.mutation=100' 107 | ;; 108 | unit) 109 | flags='-Dcoverage.lines=100 -Dcoverage.branches=100 -Dcoverage.instructions=100' 110 | ;; 111 | esac 112 | 113 | ((rc = 0)) || true 114 | if $run; then 115 | if $mutation; then 116 | targets='-DwithHistory clean verify' 117 | else 118 | targets='clean test jacoco:report jacoco:check' 119 | fi 120 | 121 | # shellcheck disable=SC2086 122 | ./mvnw $flags "$@" $targets || ((rc += $?)) || true 123 | else 124 | # Refresh coverage report when editing limits in pom.xml without rebuild 125 | ./mvnw $flags "$@" jacoco:report jacoco:check || ((rc += $?)) || true 126 | fi 127 | 128 | if $open; then 129 | if require-file target/site/jacoco/index.html; then 130 | (cd target/site/jacoco && open index.html) 131 | else 132 | ((++rc)) 133 | fi 134 | if $mutation && require-file target/pit-reports/index.html; then 135 | (cd target/pit-reports && open index.html) 136 | else 137 | ((++rc)) 138 | fi 139 | fi 140 | 141 | # shellcheck disable=SC2086 142 | exit $rc 143 | -------------------------------------------------------------------------------- /src/test/java/demo/TerminalContext.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.PrintStream; 5 | 6 | import static java.nio.charset.StandardCharsets.UTF_8; 7 | 8 | /** 9 | * Replaces writing to STDOUT/STDERR with capture to an array of bytes all for 10 | * testing. 11 | *

12 | * One does not usually test STDOUT/STDERR output unless working on a terminal 13 | * program. 14 | * This example project does so to demonstrate integration testing. 15 | * We do not want to focus on complex alternatives (databases, dependency 16 | * injection, remote REST calls, et al) but it is important to demonstrate 17 | * contrast between unit tests and integration tests in the 18 | * build. 19 | *

20 | * Note: Mucking with UTF-8/character encodings is required in this 21 | * code even though our tests are all ASCII, and users wanting a richer API 22 | * can turn to {@code system-lambda} or similar libraries. Anytime you work 23 | * with text for humans, keep UTF and encodings in mind. 24 | *

25 | * Note: Use of this test helper means: do not run integration 26 | * tests in parallel: their output to the terminal will become interleaved. 27 | * This is a concern always and anytime you access STDOUT/STDERR. 28 | * Note: A recommended pattern is that a class initializes as much as 29 | * possible in the fields so that the constructor has minimal work. 30 | * 31 | * @see TerminalContext#captureTerminal(Runnable) the main entry point for this 32 | * class 33 | * @see system-lambda 35 | * for more sophisticated handling of STDOUT/STDERR 36 | */ 37 | @SuppressWarnings({ 38 | // We follow JVM statics for out and err which confuses PMD 39 | "PMD.FinalFieldCouldBeStatic", 40 | // The code is too complex for PMD to follow because of JVM statics 41 | "PMD.CloseResource", 42 | // File issue with PMD, and suppress in config. 43 | // Why is long Javadoc triggering this? 44 | "PMD.CommentSize"}) 45 | public final class TerminalContext implements AutoCloseable { 46 | /** 47 | * Combine STDOUT/STDERR into a single buffer usable in tests. 48 | *

49 | * Note that this does not address UTF-8 concerns, so needs wrapping, 50 | * and additional state. 51 | */ 52 | private final ByteArrayOutputStream captureOutAndErr 53 | = new ByteArrayOutputStream(); 54 | /** 55 | * Remember the original STDOUT so we can restore after the test. 56 | */ 57 | private final PrintStream originalOut = System.out; 58 | /** 59 | * Remember the original STDERR so we can restore after the test. 60 | */ 61 | private final PrintStream originalErr = System.err; 62 | 63 | private TerminalContext() { 64 | final var outAndErr = new PrintStream(captureOutAndErr, true, UTF_8); 65 | System.setOut(outAndErr); // NOT thread-safe 66 | System.setErr(outAndErr); // NOT thread-safe 67 | } 68 | 69 | /** 70 | * Runs the call provided to capture STDOUT/STDERR such as {@code main}. 71 | * Use this in tests to check STDOUT/STDERR, and restore those after the 72 | * test completes. Note that outut is captured as UTF-8. 73 | * 74 | * @param call the method reference or lambda to execute and capture 75 | * STDOUT/STDERR from 76 | * @return the captured STDOUT and STDERR as a UTF-8 string, including 77 | * possibly interleaved output 78 | */ 79 | public static String captureTerminal(final Runnable call) { 80 | try (var context = new TerminalContext()) { 81 | call.run(); 82 | return context.toString(); 83 | } 84 | } 85 | 86 | @Override 87 | public void close() { 88 | final PrintStream wrapper = System.out; // Shared with System.err 89 | System.setOut(originalOut); 90 | System.setErr(originalErr); 91 | wrapper.close(); 92 | } 93 | 94 | @Override 95 | public String toString() { 96 | return captureOutAndErr.toString(UTF_8); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /.github/workflows/ci-earthly-gradle.yml: -------------------------------------------------------------------------------- 1 | name: Earthly/Gradle CI 2 | 3 | env: 4 | OWASP_NVD_API_KEY: ${{ secrets.OWASP_NVD_API_KEY }} 5 | # Workaround for: https://github.com/earthly/earthly/issues/4220 6 | EARTHLY_DISABLE_REMOTE_REGISTRY_PROXY: true 7 | 8 | # Ignore cross-build files with Maven 9 | on: 10 | push: 11 | paths-ignore: 12 | # Generic files to ignore for building 13 | - '**.md' 14 | - 'images/**' 15 | # These ignores are specific to a cross-build against Maven 16 | - 'pom.xml' 17 | - 'mvnw*' # Wraper scripts 18 | - '.mvn/**' 19 | - '.github/workflows/*maven*' 20 | # These ignores are specific to this project 21 | - 'run-with-maven.sh' 22 | pull_request: 23 | paths-ignore: 24 | # Generic files to ignore for building 25 | - '**.md' 26 | - 'images/**' 27 | # These ignores are specific to a cross-build against Maven 28 | - 'pom.xml' 29 | - 'mvnw*' # Wrapper scripts 30 | - '.mvn/**' 31 | - '.github/workflows/*maven*' 32 | # These ignores are specific to this project 33 | - 'run-with-maven.sh' 34 | 35 | jobs: 36 | build: 37 | name: Builds and tests 38 | runs-on: ubuntu-latest 39 | 40 | steps: 41 | - name: Use Earthly 42 | uses: earthly/actions-setup@v1 43 | with: 44 | github-token: ${{ secrets.GITHUB_TOKEN }} 45 | # The version in CI should match the version you use locally. 46 | # This is a manual step. 47 | # Using "latest" to force CI warnings if Earthfile is outdated 48 | version: "latest" 49 | 50 | - name: Clone repository 51 | uses: actions/checkout@v4 52 | 53 | - name: Restore Gradle caches 54 | uses: actions/cache@master 55 | env: 56 | cache-name: gradle-cache 57 | with: 58 | path: ~/.gradle 59 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{hashFiles('**/build.gradle') }} 60 | # TODO: See discussion: https://github.com/jeremylong/DependencyCheck/issues/2560#issuecomment-702098748 61 | restore-keys: | 62 | ${{ runner.os }}-build-${{ env.cache-name }}- 63 | ${{ runner.os }}-build- 64 | ${{ runner.os }}- 65 | 66 | - name: Build and test 67 | run: earthly --secret OWASP_NVD_API_KEY=$OWASP_NVD_API_KEY +build-with-gradle 68 | 69 | - name: Execute run script 70 | run: earthly --secret OWASP_NVD_API_KEY=$OWASP_NVD_API_KEY +run-with-gradle 71 | 72 | # Save artifacts for build 73 | # See Earthfile that copies these from container to local 74 | 75 | - name: Save Jar for main 76 | uses: actions/upload-artifact@v4 77 | with: 78 | name: jar-main 79 | if-no-files-found: error 80 | path: build/libs/modern-java-practices-0-SNAPSHOT.jar 81 | 82 | # Javadoc HTML and jars 83 | 84 | - name: Save Javadoc HTML for main 85 | uses: actions/upload-artifact@v4 86 | with: 87 | name: javadoc-html-main 88 | if-no-files-found: error 89 | path: build/docs/javadoc/ 90 | 91 | - name: Save Javadoc HTML Jar for main 92 | uses: actions/upload-artifact@v4 93 | with: 94 | name: javadoc-jar-main 95 | if-no-files-found: error 96 | path: build/libs/modern-java-practices-0-SNAPSHOT-javadoc.jar 97 | 98 | # Generate coverage report 99 | 100 | - name: Save coverage report 101 | uses: actions/upload-artifact@v4 102 | with: 103 | name: coverage-report 104 | if-no-files-found: error 105 | path: build/reports/jacoco/test/ 106 | 107 | - name: Generate coverage badge 108 | uses: cicirello/jacoco-badge-generator@v2 109 | with: 110 | badges-directory: images 111 | jacoco-csv-file: build/reports/jacoco/test/jacocoTestReport.csv 112 | # To avoid conflicts with the Maven build, only generate the badge 113 | # there. For Gradle only projects: REMOVE THIS LINE. 114 | generate-coverage-badge: false 115 | generate-branches-badge: false 116 | generate-summary: false 117 | 118 | -------------------------------------------------------------------------------- /images/Wikibooks-contribute-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 22 | 24 | image/svg+xml 25 | 27 | Wikibooks Community DE Icon Mitmachen 28 | 30 | 31 | 32 | HirnSpuk 33 | 34 | 35 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 51 | 52 | 53 | 58 | 65 | 72 | 79 | 86 | 93 | 100 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /.github/workflows/ci-earthly-maven.yml: -------------------------------------------------------------------------------- 1 | name: Earthly/Maven CI 2 | 3 | env: 4 | OWASP_NVD_API_KEY: ${{ secrets.OWASP_NVD_API_KEY }} 5 | # Workaround for: https://github.com/earthly/earthly/issues/4220 6 | EARTHLY_DISABLE_REMOTE_REGISTRY_PROXY: true 7 | 8 | # Ignore cross-build files with Gradle 9 | on: 10 | push: 11 | paths-ignore: 12 | # Generic files to ignore for building 13 | - '**.md' 14 | - 'images/**' 15 | # These ignores are specific to a cross-build against Gradle 16 | - 'build.gradle' 17 | - 'gradle/**' # Directory is distinct from files in GitHub ignores 18 | - 'gradle*' # Wrapper scripts and properties 19 | - '.gradle' 20 | - '.github/workflows/*gradle*' 21 | # These ignores are specific to this project 22 | - 'run-with-gradle.sh' 23 | pull_request: 24 | paths-ignore: 25 | # Generic files to ignore for building 26 | - '**.md' 27 | - 'images/**' 28 | # These ignores are specific to a cross-build against Gradle 29 | - 'build.gradle' 30 | - 'gradle/**' # Directory is distinct from files in GitHub ignores 31 | - 'gradle*' # Wrapper scripts and properties 32 | - '.gradle' 33 | - '.github/workflows/*gradle*' 34 | # These ignores are specific to this project 35 | - 'run-with-gradle.sh' 36 | workflow_dispatch: 37 | 38 | jobs: 39 | build: 40 | name: Builds and tests 41 | runs-on: ubuntu-latest 42 | 43 | steps: 44 | - name: Use Earthly 45 | uses: earthly/actions-setup@v1 46 | with: 47 | github-token: ${{ secrets.GITHUB_TOKEN }} 48 | # The version in CI should match the version you use locally. 49 | # This is a manual step. 50 | # Using "latest" to force CI warnings if Earthfile is outdated 51 | version: "latest" 52 | 53 | - name: Clone repository 54 | uses: actions/checkout@v4 55 | 56 | - name: Restore Maven caches 57 | uses: actions/cache@master 58 | env: 59 | cache-name: maven-cache 60 | with: 61 | path: ~/.m2 62 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/pom.xml') }} 63 | restore-keys: | 64 | ${{ runner.os }}-build-${{ env.cache-name }}- 65 | ${{ runner.os }}-build- 66 | ${{ runner.os }}- 67 | 68 | - name: Build and test 69 | run: earthly --secret OWASP_NVD_API_KEY=$OWASP_NVD_API_KEY +build-with-maven 70 | 71 | - name: Execute run script 72 | run: earthly --secret OWASP_NVD_API_KEY=$OWASP_NVD_API_KEY +run-with-maven 73 | 74 | # Save artifacts for build 75 | # See Earthfile that copies these from container to local 76 | 77 | - name: Save Jar for main 78 | uses: actions/upload-artifact@v4 79 | with: 80 | name: jar-main 81 | if-no-files-found: error 82 | path: target/modern-java-practices-0-SNAPSHOT.jar 83 | 84 | # Javadoc HTML and jars 85 | 86 | - name: Save Javadoc HTML for main 87 | uses: actions/upload-artifact@v4 88 | with: 89 | name: javadoc-html-main 90 | if-no-files-found: error 91 | path: target/reports/apidocs/ 92 | 93 | - name: Save Javadoc HTML Jar for main 94 | uses: actions/upload-artifact@v4 95 | with: 96 | name: javadoc-jar-main 97 | if-no-files-found: error 98 | path: target/modern-java-practices-0-SNAPSHOT-javadoc.jar 99 | 100 | - name: Save Javadoc HTML for unit tests 101 | uses: actions/upload-artifact@v4 102 | with: 103 | name: javadoc-html-unittests 104 | if-no-files-found: error 105 | path: target/reports/testapidocs/ 106 | 107 | - name: Save Javadoc HTML Jar for unit tests 108 | uses: actions/upload-artifact@v4 109 | with: 110 | name: javadoc-jar-unittests 111 | if-no-files-found: error 112 | path: target/modern-java-practices-0-SNAPSHOT-test-javadoc.jar 113 | 114 | # Coverage coverage report 115 | 116 | - name: Save coverage report 117 | uses: actions/upload-artifact@v4 118 | with: 119 | name: coverage-report 120 | if-no-files-found: error 121 | path: target/site/jacoco/ 122 | 123 | # Generate coverage badge for README 124 | # Gradle builds are similar but will require configuration the path for the 125 | # CSV JaCoCo report. 126 | 127 | - name: Generate coverage badge 128 | uses: cicirello/jacoco-badge-generator@v2 129 | with: 130 | badges-directory: images 131 | generate-branches-badge: false 132 | generate-summary: false 133 | 134 | # The saved badge does not appear when you pull code locally 135 | - name: Add coverage to CI repo 136 | # Limit frontpage badge to mainline code, not branches or PRs 137 | if: ${{ github.ref == 'refs/head/master' }} 138 | run: | 139 | cd images 140 | if [[ ! -z "$(git status --porcelain *.svg)" ]]; then 141 | git config --global user.name 'github-actions' 142 | git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' 143 | git add *.svg 144 | git commit -m 'Autogenerated coverage badge' 145 | git push 146 | fi 147 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Apache Maven Wrapper startup batch script, version 3.3.2 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM set title of command window 38 | title %0 39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 41 | 42 | @REM set %HOME% to equivalent of $HOME 43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 44 | 45 | @REM Execute a user defined script before this one 46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 50 | :skipRcPre 51 | 52 | @setlocal 53 | 54 | set ERROR_CODE=0 55 | 56 | @REM To isolate internal variables from possible post scripts, we use another setlocal 57 | @setlocal 58 | 59 | @REM ==== START VALIDATION ==== 60 | if not "%JAVA_HOME%" == "" goto OkJHome 61 | 62 | echo. >&2 63 | echo Error: JAVA_HOME not found in your environment. >&2 64 | echo Please set the JAVA_HOME variable in your environment to match the >&2 65 | echo location of your Java installation. >&2 66 | echo. >&2 67 | goto error 68 | 69 | :OkJHome 70 | if exist "%JAVA_HOME%\bin\java.exe" goto init 71 | 72 | echo. >&2 73 | echo Error: JAVA_HOME is set to an invalid directory. >&2 74 | echo JAVA_HOME = "%JAVA_HOME%" >&2 75 | echo Please set the JAVA_HOME variable in your environment to match the >&2 76 | echo location of your Java installation. >&2 77 | echo. >&2 78 | goto error 79 | 80 | @REM ==== END VALIDATION ==== 81 | 82 | :init 83 | 84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 85 | @REM Fallback to current working directory if not found. 86 | 87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 89 | 90 | set EXEC_DIR=%CD% 91 | set WDIR=%EXEC_DIR% 92 | :findBaseDir 93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 94 | cd .. 95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 96 | set WDIR=%CD% 97 | goto findBaseDir 98 | 99 | :baseDirFound 100 | set MAVEN_PROJECTBASEDIR=%WDIR% 101 | cd "%EXEC_DIR%" 102 | goto endDetectBaseDir 103 | 104 | :baseDirNotFound 105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 106 | cd "%EXEC_DIR%" 107 | 108 | :endDetectBaseDir 109 | 110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 111 | 112 | @setlocal EnableExtensions EnableDelayedExpansion 113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 115 | 116 | :endReadAdditionalConfig 117 | 118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" 123 | 124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | if "%MVNW_VERBOSE%" == "true" ( 132 | echo Found %WRAPPER_JAR% 133 | ) 134 | ) else ( 135 | if not "%MVNW_REPOURL%" == "" ( 136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" 137 | ) 138 | if "%MVNW_VERBOSE%" == "true" ( 139 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 140 | echo Downloading from: %WRAPPER_URL% 141 | ) 142 | 143 | powershell -Command "&{"^ 144 | "$webclient = new-object System.Net.WebClient;"^ 145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 147 | "}"^ 148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ 149 | "}" 150 | if "%MVNW_VERBOSE%" == "true" ( 151 | echo Finished downloading %WRAPPER_JAR% 152 | ) 153 | ) 154 | @REM End of extension 155 | 156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file 157 | SET WRAPPER_SHA_256_SUM="" 158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B 160 | ) 161 | IF NOT %WRAPPER_SHA_256_SUM%=="" ( 162 | powershell -Command "&{"^ 163 | "Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^ 164 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ 165 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ 166 | " Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ 167 | " Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ 168 | " Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ 169 | " exit 1;"^ 170 | "}"^ 171 | "}" 172 | if ERRORLEVEL 1 goto error 173 | ) 174 | 175 | @REM Provide a "standardized" way to retrieve the CLI args that will 176 | @REM work with both Windows and non-Windows executions. 177 | set MAVEN_CMD_LINE_ARGS=%* 178 | 179 | %MAVEN_JAVA_EXE% ^ 180 | %JVM_CONFIG_MAVEN_PROPS% ^ 181 | %MAVEN_OPTS% ^ 182 | %MAVEN_DEBUG_OPTS% ^ 183 | -classpath %WRAPPER_JAR% ^ 184 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 185 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 186 | if ERRORLEVEL 1 goto error 187 | goto end 188 | 189 | :error 190 | set ERROR_CODE=1 191 | 192 | :end 193 | @endlocal & set ERROR_CODE=%ERROR_CODE% 194 | 195 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 196 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 197 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 198 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 199 | :skipRcPost 200 | 201 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 202 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 203 | 204 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 205 | 206 | cmd /C exit /B %ERROR_CODE% 207 | -------------------------------------------------------------------------------- /config/checkstyle/sun_checks.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 33 | 34 | 35 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /config/checkstyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 33 | 34 | 35 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The license for this code base and the accompanying wiki pages has changed to 2 | CC0 (Creative Commons Zero 1.0 Universal) from Public Domain. 3 | This is to accomodate those legal jurisdictions not supporting dedication of 4 | rights to the public until author's death. 5 | No author of these works is a lawyer: Please consult with an accredited expert 6 | in your jurisdiction for legal advice. 7 | 8 | # Creative Commons Zero 1.0 Universal license 9 | 10 | CC0 1.0 Universal 11 | 12 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 13 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 14 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 15 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 16 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 17 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 18 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 19 | HEREUNDER. 20 | 21 | Statement of Purpose 22 | 23 | The laws of most jurisdictions throughout the world automatically confer 24 | exclusive Copyright and Related Rights (defined below) upon the creator 25 | and subsequent owner(s) (each and all, an "owner") of an original work of 26 | authorship and/or a database (each, a "Work"). 27 | 28 | Certain owners wish to permanently relinquish those rights to a Work for 29 | the purpose of contributing to a commons of creative, cultural and 30 | scientific works ("Commons") that the public can reliably and without fear 31 | of later claims of infringement build upon, modify, incorporate in other 32 | works, reuse and redistribute as freely as possible in any form whatsoever 33 | and for any purposes, including without limitation commercial purposes. 34 | These owners may contribute to the Commons to promote the ideal of a free 35 | culture and the further production of creative, cultural and scientific 36 | works, or to gain reputation or greater distribution for their Work in 37 | part through the use and efforts of others. 38 | 39 | For these and/or other purposes and motivations, and without any 40 | expectation of additional consideration or compensation, the person 41 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 42 | is an owner of Copyright and Related Rights in the Work, voluntarily 43 | elects to apply CC0 to the Work and publicly distribute the Work under its 44 | terms, with knowledge of his or her Copyright and Related Rights in the 45 | Work and the meaning and intended legal effect of CC0 on those rights. 46 | 47 | 1. Copyright and Related Rights. A Work made available under CC0 may be 48 | protected by copyright and related or neighboring rights ("Copyright and 49 | Related Rights"). Copyright and Related Rights include, but are not 50 | limited to, the following: 51 | 52 | i. the right to reproduce, adapt, distribute, perform, display, 53 | communicate, and translate a Work; 54 | ii. moral rights retained by the original author(s) and/or performer(s); 55 | iii. publicity and privacy rights pertaining to a person's image or 56 | likeness depicted in a Work; 57 | iv. rights protecting against unfair competition in regards to a Work, 58 | subject to the limitations in paragraph 4(a), below; 59 | v. rights protecting the extraction, dissemination, use and reuse of data 60 | in a Work; 61 | vi. database rights (such as those arising under Directive 96/9/EC of the 62 | European Parliament and of the Council of 11 March 1996 on the legal 63 | protection of databases, and under any national implementation 64 | thereof, including any amended or successor version of such 65 | directive); and 66 | vii. other similar, equivalent or corresponding rights throughout the 67 | world based on applicable law or treaty, and any national 68 | implementations thereof. 69 | 70 | 2. Waiver. To the greatest extent permitted by, but not in contravention 71 | of, applicable law, Affirmer hereby overtly, fully, permanently, 72 | irrevocably and unconditionally waives, abandons, and surrenders all of 73 | Affirmer's Copyright and Related Rights and associated claims and causes 74 | of action, whether now known or unknown (including existing as well as 75 | future claims and causes of action), in the Work (i) in all territories 76 | worldwide, (ii) for the maximum duration provided by applicable law or 77 | treaty (including future time extensions), (iii) in any current or future 78 | medium and for any number of copies, and (iv) for any purpose whatsoever, 79 | including without limitation commercial, advertising or promotional 80 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 81 | member of the public at large and to the detriment of Affirmer's heirs and 82 | successors, fully intending that such Waiver shall not be subject to 83 | revocation, rescission, cancellation, termination, or any other legal or 84 | equitable action to disrupt the quiet enjoyment of the Work by the public 85 | as contemplated by Affirmer's express Statement of Purpose. 86 | 87 | 3. Public License Fallback. Should any part of the Waiver for any reason 88 | be judged legally invalid or ineffective under applicable law, then the 89 | Waiver shall be preserved to the maximum extent permitted taking into 90 | account Affirmer's express Statement of Purpose. In addition, to the 91 | extent the Waiver is so judged Affirmer hereby grants to each affected 92 | person a royalty-free, non transferable, non sublicensable, non exclusive, 93 | irrevocable and unconditional license to exercise Affirmer's Copyright and 94 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 95 | maximum duration provided by applicable law or treaty (including future 96 | time extensions), (iii) in any current or future medium and for any number 97 | of copies, and (iv) for any purpose whatsoever, including without 98 | limitation commercial, advertising or promotional purposes (the 99 | "License"). The License shall be deemed effective as of the date CC0 was 100 | applied by Affirmer to the Work. Should any part of the License for any 101 | reason be judged legally invalid or ineffective under applicable law, such 102 | partial invalidity or ineffectiveness shall not invalidate the remainder 103 | of the License, and in such case Affirmer hereby affirms that he or she 104 | will not (i) exercise any of his or her remaining Copyright and Related 105 | Rights in the Work or (ii) assert any associated claims and causes of 106 | action with respect to the Work, in either case contrary to Affirmer's 107 | express Statement of Purpose. 108 | 109 | 4. Limitations and Disclaimers. 110 | 111 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 112 | surrendered, licensed or otherwise affected by this document. 113 | b. Affirmer offers the Work as-is and makes no representations or 114 | warranties of any kind concerning the Work, express, implied, 115 | statutory or otherwise, including without limitation warranties of 116 | title, merchantability, fitness for a particular purpose, non 117 | infringement, or the absence of latent or other defects, accuracy, or 118 | the present or absence of errors, whether or not discoverable, all to 119 | the greatest extent permissible under applicable law. 120 | c. Affirmer disclaims responsibility for clearing rights of other persons 121 | that may apply to the Work or any use thereof, including without 122 | limitation any person's Copyright and Related Rights in the Work. 123 | Further, Affirmer disclaims responsibility for obtaining any necessary 124 | consents, permissions or other rights required for any use of the 125 | Work. 126 | d. Affirmer understands and acknowledges that Creative Commons is not a 127 | party to this document and has no duty or obligation with respect to 128 | this CC0 or use of the Work. 129 | 130 | For more information, please refer to 131 | and to 132 | . 133 | 134 | # Former Public Domain license 135 | 136 | This is free and unencumbered software released into the public domain. 137 | 138 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute 139 | this software, either in source code form or as a compiled binary, for any 140 | purpose, commercial or non-commercial, and by any means. 141 | 142 | In jurisdictions that recognize copyright laws, the author or authors of this 143 | software dedicate any and all copyright interest in the software to the public 144 | domain. We make this dedication for the benefit of the public at large and to 145 | the detriment of our heirs and successors. We intend this dedication to be an 146 | overt act of relinquishment in perpetuity of all present and future rights to 147 | this software under copyright law. 148 | 149 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 150 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 151 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 152 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 153 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 154 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 155 | 156 | For more information, please refer to . 157 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s 90 | ' "$PWD" ) || exit 91 | 92 | # Use the maximum available, or set MAX_FD != -1 to use that value. 93 | MAX_FD=maximum 94 | 95 | warn () { 96 | echo "$*" 97 | } >&2 98 | 99 | die () { 100 | echo 101 | echo "$*" 102 | echo 103 | exit 1 104 | } >&2 105 | 106 | # OS specific support (must be 'true' or 'false'). 107 | cygwin=false 108 | msys=false 109 | darwin=false 110 | nonstop=false 111 | case "$( uname )" in #( 112 | CYGWIN* ) cygwin=true ;; #( 113 | Darwin* ) darwin=true ;; #( 114 | MSYS* | MINGW* ) msys=true ;; #( 115 | NONSTOP* ) nonstop=true ;; 116 | esac 117 | 118 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 119 | 120 | 121 | # Determine the Java command to use to start the JVM. 122 | if [ -n "$JAVA_HOME" ] ; then 123 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 124 | # IBM's JDK on AIX uses strange locations for the executables 125 | JAVACMD=$JAVA_HOME/jre/sh/java 126 | else 127 | JAVACMD=$JAVA_HOME/bin/java 128 | fi 129 | if [ ! -x "$JAVACMD" ] ; then 130 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 131 | 132 | Please set the JAVA_HOME variable in your environment to match the 133 | location of your Java installation." 134 | fi 135 | else 136 | JAVACMD=java 137 | if ! command -v java >/dev/null 2>&1 138 | then 139 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 140 | 141 | Please set the JAVA_HOME variable in your environment to match the 142 | location of your Java installation." 143 | fi 144 | fi 145 | 146 | # Increase the maximum file descriptors if we can. 147 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 148 | case $MAX_FD in #( 149 | max*) 150 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 151 | # shellcheck disable=SC2039,SC3045 152 | MAX_FD=$( ulimit -H -n ) || 153 | warn "Could not query maximum file descriptor limit" 154 | esac 155 | case $MAX_FD in #( 156 | '' | soft) :;; #( 157 | *) 158 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 159 | # shellcheck disable=SC2039,SC3045 160 | ulimit -n "$MAX_FD" || 161 | warn "Could not set maximum file descriptor limit to $MAX_FD" 162 | esac 163 | fi 164 | 165 | # Collect all arguments for the java command, stacking in reverse order: 166 | # * args from the command line 167 | # * the main class name 168 | # * -classpath 169 | # * -D...appname settings 170 | # * --module-path (only if needed) 171 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 172 | 173 | # For Cygwin or MSYS, switch paths to Windows format before running java 174 | if "$cygwin" || "$msys" ; then 175 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 176 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 177 | 178 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 179 | 180 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 181 | for arg do 182 | if 183 | case $arg in #( 184 | -*) false ;; # don't mess with options #( 185 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 186 | [ -e "$t" ] ;; #( 187 | *) false ;; 188 | esac 189 | then 190 | arg=$( cygpath --path --ignore --mixed "$arg" ) 191 | fi 192 | # Roll the args list around exactly as many times as the number of 193 | # args, so each arg winds up back in the position where it started, but 194 | # possibly modified. 195 | # 196 | # NB: a `for` loop captures its iteration list before it begins, so 197 | # changing the positional parameters here affects neither the number of 198 | # iterations, nor the values presented in `arg`. 199 | shift # remove old arg 200 | set -- "$@" "$arg" # push replacement arg 201 | done 202 | fi 203 | 204 | 205 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 206 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 207 | 208 | # Collect all arguments for the java command: 209 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 210 | # and any embedded shellness will be escaped. 211 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 212 | # treated as '${Hostname}' itself on the command line. 213 | 214 | set -- \ 215 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 216 | -classpath "$CLASSPATH" \ 217 | org.gradle.wrapper.GradleWrapperMain \ 218 | "$@" 219 | 220 | # Stop when "xargs" is not available. 221 | if ! command -v xargs >/dev/null 2>&1 222 | then 223 | die "xargs is not available" 224 | fi 225 | 226 | # Use "xargs" to parse quoted args. 227 | # 228 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 229 | # 230 | # In Bash we could simply go: 231 | # 232 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 233 | # set -- "${ARGS[@]}" "$@" 234 | # 235 | # but POSIX shell has neither arrays nor command substitution, so instead we 236 | # post-process each arg (as a line of input to sed) to backslash-escape any 237 | # character that might be a shell metacharacter, then use eval to reverse 238 | # that process (while maintaining the separation between arguments), and wrap 239 | # the whole thing up as a single "set" statement. 240 | # 241 | # This will of course break if any of these variables contains a newline or 242 | # an unmatched quote. 243 | # 244 | 245 | eval "set -- $( 246 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 247 | xargs -n1 | 248 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 249 | tr '\n' ' ' 250 | )" '"$@"' 251 | 252 | exec "$JAVACMD" "$@" 253 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.3.2 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | # e.g. to debug Maven itself, use 32 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | # ---------------------------------------------------------------------------- 35 | 36 | if [ -z "$MAVEN_SKIP_RC" ]; then 37 | 38 | if [ -f /usr/local/etc/mavenrc ]; then 39 | . /usr/local/etc/mavenrc 40 | fi 41 | 42 | if [ -f /etc/mavenrc ]; then 43 | . /etc/mavenrc 44 | fi 45 | 46 | if [ -f "$HOME/.mavenrc" ]; then 47 | . "$HOME/.mavenrc" 48 | fi 49 | 50 | fi 51 | 52 | # OS specific support. $var _must_ be set to either true or false. 53 | cygwin=false 54 | darwin=false 55 | mingw=false 56 | case "$(uname)" in 57 | CYGWIN*) cygwin=true ;; 58 | MINGW*) mingw=true ;; 59 | Darwin*) 60 | darwin=true 61 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 62 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 63 | if [ -z "$JAVA_HOME" ]; then 64 | if [ -x "/usr/libexec/java_home" ]; then 65 | JAVA_HOME="$(/usr/libexec/java_home)" 66 | export JAVA_HOME 67 | else 68 | JAVA_HOME="/Library/Java/Home" 69 | export JAVA_HOME 70 | fi 71 | fi 72 | ;; 73 | esac 74 | 75 | if [ -z "$JAVA_HOME" ]; then 76 | if [ -r /etc/gentoo-release ]; then 77 | JAVA_HOME=$(java-config --jre-home) 78 | fi 79 | fi 80 | 81 | # For Cygwin, ensure paths are in UNIX format before anything is touched 82 | if $cygwin; then 83 | [ -n "$JAVA_HOME" ] \ 84 | && JAVA_HOME=$(cygpath --unix "$JAVA_HOME") 85 | [ -n "$CLASSPATH" ] \ 86 | && CLASSPATH=$(cygpath --path --unix "$CLASSPATH") 87 | fi 88 | 89 | # For Mingw, ensure paths are in UNIX format before anything is touched 90 | if $mingw; then 91 | [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] \ 92 | && JAVA_HOME="$( 93 | cd "$JAVA_HOME" || ( 94 | echo "cannot cd into $JAVA_HOME." >&2 95 | exit 1 96 | ) 97 | pwd 98 | )" 99 | fi 100 | 101 | if [ -z "$JAVA_HOME" ]; then 102 | javaExecutable="$(which javac)" 103 | if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then 104 | # readlink(1) is not available as standard on Solaris 10. 105 | readLink=$(which readlink) 106 | if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then 107 | if $darwin; then 108 | javaHome="$(dirname "$javaExecutable")" 109 | javaExecutable="$(cd "$javaHome" && pwd -P)/javac" 110 | else 111 | javaExecutable="$(readlink -f "$javaExecutable")" 112 | fi 113 | javaHome="$(dirname "$javaExecutable")" 114 | javaHome=$(expr "$javaHome" : '\(.*\)/bin') 115 | JAVA_HOME="$javaHome" 116 | export JAVA_HOME 117 | fi 118 | fi 119 | fi 120 | 121 | if [ -z "$JAVACMD" ]; then 122 | if [ -n "$JAVA_HOME" ]; then 123 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then 124 | # IBM's JDK on AIX uses strange locations for the executables 125 | JAVACMD="$JAVA_HOME/jre/sh/java" 126 | else 127 | JAVACMD="$JAVA_HOME/bin/java" 128 | fi 129 | else 130 | JAVACMD="$( 131 | \unset -f command 2>/dev/null 132 | \command -v java 133 | )" 134 | fi 135 | fi 136 | 137 | if [ ! -x "$JAVACMD" ]; then 138 | echo "Error: JAVA_HOME is not defined correctly." >&2 139 | echo " We cannot execute $JAVACMD" >&2 140 | exit 1 141 | fi 142 | 143 | if [ -z "$JAVA_HOME" ]; then 144 | echo "Warning: JAVA_HOME environment variable is not set." >&2 145 | fi 146 | 147 | # traverses directory structure from process work directory to filesystem root 148 | # first directory with .mvn subdirectory is considered project base directory 149 | find_maven_basedir() { 150 | if [ -z "$1" ]; then 151 | echo "Path not specified to find_maven_basedir" >&2 152 | return 1 153 | fi 154 | 155 | basedir="$1" 156 | wdir="$1" 157 | while [ "$wdir" != '/' ]; do 158 | if [ -d "$wdir"/.mvn ]; then 159 | basedir=$wdir 160 | break 161 | fi 162 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 163 | if [ -d "${wdir}" ]; then 164 | wdir=$( 165 | cd "$wdir/.." || exit 1 166 | pwd 167 | ) 168 | fi 169 | # end of workaround 170 | done 171 | printf '%s' "$( 172 | cd "$basedir" || exit 1 173 | pwd 174 | )" 175 | } 176 | 177 | # concatenates all lines of a file 178 | concat_lines() { 179 | if [ -f "$1" ]; then 180 | # Remove \r in case we run on Windows within Git Bash 181 | # and check out the repository with auto CRLF management 182 | # enabled. Otherwise, we may read lines that are delimited with 183 | # \r\n and produce $'-Xarg\r' rather than -Xarg due to word 184 | # splitting rules. 185 | tr -s '\r\n' ' ' <"$1" 186 | fi 187 | } 188 | 189 | log() { 190 | if [ "$MVNW_VERBOSE" = true ]; then 191 | printf '%s\n' "$1" 192 | fi 193 | } 194 | 195 | BASE_DIR=$(find_maven_basedir "$(dirname "$0")") 196 | if [ -z "$BASE_DIR" ]; then 197 | exit 1 198 | fi 199 | 200 | MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 201 | export MAVEN_PROJECTBASEDIR 202 | log "$MAVEN_PROJECTBASEDIR" 203 | 204 | ########################################################################################## 205 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 206 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 207 | ########################################################################################## 208 | wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" 209 | if [ -r "$wrapperJarPath" ]; then 210 | log "Found $wrapperJarPath" 211 | else 212 | log "Couldn't find $wrapperJarPath, downloading it ..." 213 | 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" 216 | else 217 | wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" 218 | fi 219 | while IFS="=" read -r key value; do 220 | # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) 221 | safeValue=$(echo "$value" | tr -d '\r') 222 | case "$key" in wrapperUrl) 223 | wrapperUrl="$safeValue" 224 | break 225 | ;; 226 | esac 227 | done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 228 | log "Downloading from: $wrapperUrl" 229 | 230 | if $cygwin; then 231 | wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") 232 | fi 233 | 234 | if command -v wget >/dev/null; then 235 | log "Found wget ... using wget" 236 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" 237 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 238 | wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 239 | else 240 | wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 241 | fi 242 | elif command -v curl >/dev/null; then 243 | log "Found curl ... using curl" 244 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" 245 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 246 | curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 247 | else 248 | curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 249 | fi 250 | else 251 | log "Falling back to using Java to download" 252 | javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" 253 | javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" 254 | # For Cygwin, switch paths to Windows format before running javac 255 | if $cygwin; then 256 | javaSource=$(cygpath --path --windows "$javaSource") 257 | javaClass=$(cygpath --path --windows "$javaClass") 258 | fi 259 | if [ -e "$javaSource" ]; then 260 | if [ ! -e "$javaClass" ]; then 261 | log " - Compiling MavenWrapperDownloader.java ..." 262 | ("$JAVA_HOME/bin/javac" "$javaSource") 263 | fi 264 | if [ -e "$javaClass" ]; then 265 | log " - Running MavenWrapperDownloader.java ..." 266 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" 267 | fi 268 | fi 269 | fi 270 | fi 271 | ########################################################################################## 272 | # End of extension 273 | ########################################################################################## 274 | 275 | # If specified, validate the SHA-256 sum of the Maven wrapper jar file 276 | wrapperSha256Sum="" 277 | while IFS="=" read -r key value; do 278 | case "$key" in wrapperSha256Sum) 279 | wrapperSha256Sum=$value 280 | break 281 | ;; 282 | esac 283 | done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 284 | if [ -n "$wrapperSha256Sum" ]; then 285 | wrapperSha256Result=false 286 | if command -v sha256sum >/dev/null; then 287 | if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c >/dev/null 2>&1; then 288 | wrapperSha256Result=true 289 | fi 290 | elif command -v shasum >/dev/null; then 291 | if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c >/dev/null 2>&1; then 292 | wrapperSha256Result=true 293 | fi 294 | else 295 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 296 | echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." >&2 297 | exit 1 298 | fi 299 | if [ $wrapperSha256Result = false ]; then 300 | echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 301 | echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 302 | echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 303 | exit 1 304 | fi 305 | fi 306 | 307 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 308 | 309 | # For Cygwin, switch paths to Windows format before running java 310 | if $cygwin; then 311 | [ -n "$JAVA_HOME" ] \ 312 | && JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") 313 | [ -n "$CLASSPATH" ] \ 314 | && CLASSPATH=$(cygpath --path --windows "$CLASSPATH") 315 | [ -n "$MAVEN_PROJECTBASEDIR" ] \ 316 | && MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") 317 | fi 318 | 319 | # Provide a "standardized" way to retrieve the CLI args that will 320 | # work with both Windows and non-Windows executions. 321 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" 322 | export MAVEN_CMD_LINE_ARGS 323 | 324 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 325 | 326 | # shellcheck disable=SC2086 # safe args 327 | exec "$JAVACMD" \ 328 | $MAVEN_OPTS \ 329 | $MAVEN_DEBUG_OPTS \ 330 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 331 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 332 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 333 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Creative Commons 0 4 | 5 | 6 | **[Read the book!](https://github.com/binkley/modern-java-practices/wiki)** 7 | _(this jumps to the wiki)_
8 | _Jump to [the project card wall](https://github.com/users/binkley/projects/1) 9 | to see upcoming book and code changes (the card wall tracks Issues for the 10 | project)._ 11 | 12 | Some highlighted documentation pages: 13 | - [_Keep local consistent with 14 | CI_](https://github.com/binkley/modern-java-practices/wiki/Keep-local-consistent-with-CI) 15 | - [_Shift security 16 | left_](https://github.com/binkley/modern-java-practices/wiki/Shift-security-left) 17 | 18 | # If you clone this project 19 | 20 | Make sure you update the `OWASP_API_NVD_KEY` in your GitHub secrets. 21 | 22 | # Modern Java/JVM Build Practices 23 | 24 | 25 | Modern Agile 27 | 28 | 29 | [![Gradle build](https://github.com/binkley/modern-java-practices/actions/workflows/ci-earthly-gradle.yml/badge.svg)](https://github.com/binkley/modern-java-practices/actions) 30 | [![Maven build](https://github.com/binkley/modern-java-practices/actions/workflows/ci-earthly-maven.yml/badge.svg)](https://github.com/binkley/modern-java-practices/actions) 31 | [![CodeQL](https://github.com/binkley/modern-java-practices/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/binkley/modern-java-practices/actions/workflows/github-code-scanning/codeql) 32 | [![vulnerabilities](https://snyk.io/test/github/binkley/modern-java-practices/badge.svg)](https://snyk.io/test/github/binkley/modern-java-practices) 33 | [![coverage](https://github.com/binkley/modern-java-practices/raw/master/images/jacoco.svg)](https://github.com/binkley/modern-java-practices/actions/workflows/ci.yml) 34 | [![pull requests](https://img.shields.io/github/issues-pr/binkley/modern-java-practices.svg)](https://github.com/binkley/modern-java-practices/pulls) 35 | [![issues](https://img.shields.io/github/issues/binkley/modern-java-practices.svg)](https://github.com/binkley/modern-java-practices/issues/) 36 | [![license](https://img.shields.io/badge/License-CC0_1.0-lightgrey.svg)](https://creativecommons.org/public-domain/cc0/) 37 | 38 | > [!WARNING] 39 | > For those using the DependencyCheck plugins for Gradle or Maven, over the 40 | > July 1st weekend the upstream API for fetching security CVEs changed a major 41 | > version, and stopped supporting older versions of the data. 42 | > To get up to date, update to at least version 10.0.2 of either the Gradle or 43 | > Maven plugin. 44 | > 45 | > After the update, the first build will take a _very long time_, but should 46 | > perform normally afterwards. 47 | > And during the first week or so after this change, you may see multiple 48 | > connection failures as OWASP NVD is overloaded with projects all catching up 49 | > at the same time. 50 | > The Maven plugin shows progress as CVE records are pulled: to see progress 51 | > with the Gradle plugin, use the `--info` command-line flag. 52 | 53 | **Modern Java/JVM Build Practices** is an article-as-repo on building modern 54 | Java/JVM projects using 55 | [Gradle](https://docs.gradle.org/current/userguide/userguide.html) and 56 | [Maven](https://maven.apache.org/what-is-maven.html), and a _starter project_ 57 | in Java (the advice works for non-Java languages on the JVM though details may 58 | change). 59 | 60 | > [!IMPORTANT] 61 | > See [the wiki](https://github.com/binkley/modern-java-practices/wiki) for 62 | > all pages and sections. 63 | > This README is only introduction, motivation, and project status. 64 | > You can use the [table of contents](#table-of-contents) below to quickly 65 | > jump to bits that interest you. 66 | 67 | Regardless of what language(s) or build tool(s) you choose, and you 68 | should treat your build and your pipeline as worthy of your attention just as 69 | you would your project source code: 70 | _If it doesn't build right for customers as it does for developers, you have 71 | something to think about._ 72 | 73 | I'm showing you practices and tools that help you make your build and pipeline 74 | to production as _first-class_ the same as your own source code. 75 | An example of this philosophy for a non-Java language is [Clojure](https://www.clojure.org/guides/tools_build#_builds_are_programs). 76 | 77 | Your focus, and the focus of this article, is _best build practices_ and 78 | _project hygiene_, and helping you have local work that is identical in 79 | production. 80 | This project is _agnostic_ between Gradle and Maven: discussion in each section 81 | covers both tools (alphabetical order, Gradle before Maven). 82 | See [_My Final Take on Gradle (vs. 83 | Maven)_](https://blog.frankel.ch/final-take-gradle/) for an opinionated view 84 | (not my own). 85 | 86 | This is not a JVM starter for only Java: 87 | I use it for starting my Kotlin projects, and substitute complilation and code 88 | quality plugins. 89 | Any language on the JVM can find practices and tips. 90 | 91 | > [!NOTE] 92 | > _Scala_ and _Clojure_ have their own prefered build tools not covered here; 93 | > however, the advice and examples for your **build pipeline** are intended to 94 | > be just as helpful for those JVM languages. 95 | > Groovy and Kotlin can use the examples directly (they both tend towards the 96 | > _Gradle_ option on build tools). 97 | 98 | As a _guide_, this project focuses on: 99 | 100 | * A quick starter for JVM projects using Gradle or Maven. 101 | [Fork](https://github.com/binkley/modern-java-practices/fork) me, 102 | [clone](https://github.com/binkley/modern-java-practices.git) me, copy/paste 103 | freely! 104 | I am [Creative Commons Public Domain Dedication 105 | (CC0)](https://creativecommons.org/public-domain/cc0/). 106 | * Discuss—and illustrate (through code)—sensible default practices; 107 | highlight good build tools and plugins 108 | * Document pitfalls that turned up. 109 | Some were easy to address after Internet search; some were challenging 110 | (see "Tips" sections) 111 | * Do not be an "all-in-one" solution. You know your circumstances best. 112 | I hope this project helps you discover build improvements you love. 113 | Please share with others through 114 | [issues](https://github.com/binkley/modern-java-practices/issues) or 115 | [PRs](https://github.com/binkley/modern-java-practices/pulls) 116 | 117 | ### Two recurring themes 118 | 119 | * _Shift problems left_ — Find issues earlier in your build—before 120 | you see them in production 121 | * _Make developer life easier_ — Automate build tasks often done by 122 | hand: get your build to complain (_fail_) locally before sharing with your 123 | team, or fail in CI before deployment 124 | 125 | These can be summed up as a _Software supply chain_: ensuring reliable, 126 | trusted software from local development through ready-to-deploy: 127 | **Build with confidence**. 128 | 129 | But ... you **must** judge and measure the advice here against your own 130 | systems and processes. 131 | Some things (many or most things) may work for you: 132 | keep an eye for things that do not work for you. 133 | 134 | ### What is a _Starter_? 135 | 136 | A project starter has several goals: 137 | - Help a new project get up and running with minimal fuss. 138 | - Show examples of _best practices_. 139 | - Explain the _why_ for choices, and help you pick what makes most sense for 140 | your project. 141 | 142 | This starter project is focused on _build_: 143 | - Easy on-ramp for new folks to try out your project for themselves 144 | - Support new contributors to your project that they become productive quickly 145 | - Support current contributors in the build, get out of their way, and make 146 | everyday things easy 147 | 148 | This starter project has minimal dependencies. 149 | The focus is on Gradle and Maven plugins and configuration so that you and 150 | contributors can focus on the code, not on setting up the build. 151 | 152 | ### Summing up 153 | 154 | - _I'm not a great programmer; I'm just a good programmer with great habits._ 155 | — 156 | [Kent Beck](https://www.goodreads.com/quotes/532211-i-m-not-a-great-programmer-i-m-just-a-good-programmer) 157 | - _Make it work, make it right, make it fast_ 158 | — [C2 Wiki](http://wiki.c2.com/?MakeItWorkMakeItRightMakeItFast) 159 | 160 | > [!NOTE] 161 | > **NB** — This is a _living document_. 162 | > The project is frequently updated to pick up new dependency or plugin 163 | > versions, and improved practices; the `README.md` and 164 | > [wiki](https://github.com/binkley/modern-java-practices/wiki) update 165 | > recommendations. 166 | > This is part of what _great habits_ looks like: you do not just show love 167 | > for your developers and users, but enable them to feed back into projects 168 | > and help others. 169 | > See [_Reusing this 170 | > project_](https://github.com/binkley/modern-java-practices/wiki/Reusing-this-project) 171 | > for tips on pulling in updates. 172 | 173 | (Credit to Yegor Bugayenko for [_Elegant 174 | READMEs_](https://www.yegor256.com/2019/04/23/elegant-readme.html).) 175 | 176 | --- 177 | 178 | 179 | Run from a local script 181 | 182 | 183 | ## Try it 184 | 185 | You should "kick the tires" and get a feel for what parts of this project 186 | you'd like to pull into your own projects and builds. 187 | You run across lots of projects: 188 | Let's make this one helpful for you. 189 | 190 | After cloning or forking this project to your machine, try out the local build 191 | that makes sense for you: 192 | 193 | ```shell 194 | $ earthly +build-with-gradle # CI build with Earthly 195 | $ earthly +build-with-maven # CI build with Earthly 196 | $ ./gradlew build # Local-only build 197 | $ ./mvnw verify # Local-only build 198 | ``` 199 | 200 | Notice that you can run the build purely locally, or _in a container_? 201 | 202 | I want to convince you that running your builds in a container fixes the "it 203 | worked on my machine" problem, and show you how to pick up improvements for 204 | your build that helps you and others be _awesome_. 205 | 206 | > [!NOTE] 207 | > This project uses NVD to check for CVEs with your dependencies which can 208 | > take a long time to download. 209 | > You can speed up your build time by [requesting an NVD API 210 | > key](https://nvd.nist.gov/developers/request-an-api-key) (it can take quite 211 | > a while to fetch the CVEs list or update it, and may fail with 403 or 404 212 | > without a key). 213 | > 214 | > When you request a key, NVD sends you an email to confirm your identity, and 215 | > then share an API key web page. 216 | > See [_Shift security 217 | > left_](https://github.com/binkley/modern-java-practices/wiki/Shift-security-left) 218 | > for more details. 219 | 220 | See what the starter "run" program does: 221 | 222 | Both Gradle and Maven (after building if needed) should print: 223 | ``` 224 | TheFoo(label=I AM FOOCUTUS OF BORG) 225 | ``` 226 | 227 | A "starter" program is the simplest of all possible ["smoke 228 | tests"](https://en.wikipedia.org/wiki/Smoke_testing_(software)), meaning, 229 | the minimal things _just work_, and when you check other things, maybe smoke 230 | drifts from your computer as circuits burn out[^1]. 231 | 232 | [^1]: No, I'm just kidding. 233 | Amazon or Google or Microsoft cloud would have quite different problems than 234 | "white smoke" from computers[^2]. 235 | 236 | [^2]: Actually, this really happened me in a data center before the cloud when 237 | a power supply burned out. 238 | We rushed to use a fire extinguisher before the Halon system triggered. 239 | 240 | --- 241 | 242 | 243 | Changes 245 | 246 | 247 | ## Recent significant changes 248 | 249 | (For detailed changes in the example code, browse the [commit 250 | log](https://github.com/binkley/modern-java-practices/commits/master/).) 251 | 252 | - Move to a CC0 license from Public Domain. 253 | - Gradle: Bump to Gradle 8.9. 254 | - Migrate most of the `README.md` to the [GitHub project 255 | wiki](https://github.com/binkley/modern-java-practices/wiki). 256 | This is breaks up an overlong (14k+ words and growing) README into 257 | digestible sections. 258 | - Earthly and Batect: Remove support for Batect as the author has archived 259 | that project. 260 | Please use _Earthly_ for local containerized builds. 261 | So your local command line is: 262 | ```bash 263 | $ earthly +build-with-gradle 264 | # OR 265 | $ earthly +build-with-maven 266 | ``` 267 | I'll be researching other options, and updating to show those and examples. 268 | Advice remains the same: Run your local build in a container for 269 | reproducibility, and have CI do the same to exactly repeat your local 270 | builds. 271 | - JVM: Move to JDK 21. 272 | This project has no sample code relying on recent/modern versions of Java or 273 | the JVM; however, moving between versions _does_ need changes to build 274 | scripts and supporting files. 275 | Here is [the last commit using JDK 17](https://github.com/binkley/modern-java-practices/commit/039f6f45fade51da0c548bf5d61b8013423ab8b9) 276 | - Gradle: Build with Gradle 8.x. 277 | - Gradle: Bemove use of `testsets` plugin for integration testing in favor of 278 | [native Gradle 279 | support](https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests). 280 | This supports Gradle 8. 281 | 282 | --- 283 | 284 | 285 | Table of Contents 287 | 288 | 289 | ## Table of Contents 290 | 291 | The writing for this project is fully moved to the [wiki 292 | pages](https://github.com/binkley/modern-java-practices/wiki/). 293 | Use the sidebar navigation in the wiki to browse or jump to topics, or to 294 | follow in a reading order. 295 | You can also use the droplist control next to "Pages" for an alphabetical 296 | listing (including subheaders within pages), and for a search box. 297 | 298 | Lastly, the wiki pages are themselves a repo, and you can clone it using 299 | `git@github.com:binkley/modern-java-practices.wiki.git` as you can for any 300 | GitHub wiki. 301 | 302 | --- 303 | 304 | ## Contributing 305 | 306 | See [`CONTRIBUTING.md`](./CONTRIBUTING.md). 307 | Please [file issues](https://github.com/binkley/modern-java-practices/issues), 308 | or contribute [pull 309 | requests](https://github.com/binkley/modern-java-practices/pulls)! 310 | I'd love a conversation with you. 311 | 312 | --- 313 | 314 | ## Credits 315 | 316 | Special thanks to my co-author, [John Libby](https://github.com/jwlibby). 317 | 318 | And many thanks to all the contributions from: 319 | 320 | * [Dan Wallach](https://github.com/danwallach) for multiple email reviews and 321 | discussions on security 322 | * [Kristoffer Haugsbakk](https://github.com/LemmingAvalanche) 323 | * [Sam Gammon](https://github.com/sgammon) 324 | * [Sergei Bukharov](https://github.com/Bukharovsi) 325 | 326 | All suggestions and ideas welcome! 327 | Please [file an 328 | issue](https://github.com/binkley/modern-java-practices/issues). ☺ 329 | -------------------------------------------------------------------------------- /config/checkstyle/google_checks.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 59 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 76 | 77 | 78 | 80 | 81 | 82 | 88 | 89 | 90 | 91 | 94 | 95 | 96 | 97 | 98 | 102 | 103 | 104 | 105 | 106 | 108 | 109 | 110 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 129 | 131 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 179 | 180 | 181 | 183 | 185 | 186 | 187 | 188 | 190 | 191 | 192 | 193 | 195 | 196 | 197 | 198 | 200 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 210 | 211 | 212 | 213 | 215 | 216 | 217 | 218 | 220 | 221 | 222 | 223 | 225 | 226 | 227 | 228 | 230 | 231 | 232 | 233 | 235 | 236 | 237 | 238 | 240 | 241 | 242 | 243 | 245 | 247 | 249 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 280 | 281 | 282 | 285 | 286 | 287 | 288 | 294 | 295 | 296 | 297 | 301 | 302 | 303 | 304 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 319 | 320 | 321 | 322 | 323 | 324 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 340 | 341 | 342 | 343 | 346 | 347 | 348 | 349 | 350 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 364 | 365 | 366 | 367 | 368 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 4.0.0 8 | demo 9 | modern-java-practices 10 | 0-SNAPSHOT 11 | 12 | Modern Java/JVM build practices 13 | 14 | Advice on Modern Java/JVM Gradle and Maven builds. 15 | The advice is language-agnostic, but options are often specific for 16 | Java (such as plugins that review Java code quality). 17 | The goal: Make you build AWESOME, and help you with solid CI builds. 18 | 19 | https://github.com/binkley/modern-java-practices 20 | 21 | 22 | CC0 23 | https://creativecommons.org/public-domain/cc0/ 24 | repo 25 | 26 | A "No Rights Reserved" license suitable in most jurisdictions 27 | without waiting for expiration of rights under Public Domain. 28 | 29 | 30 | 31 | 32 | GitHub 33 | https://github.com/binkley/modern-java-practices/issues 34 | 35 | 36 | GitHub 37 | https://github.com/binkley/modern-java-practices/actions 38 | 39 | 40 | 41 | scm:git:git://github.com/binkley/modern-java-practices 42 | 43 | https://github.com/binkley/modern-java-practices 44 | 45 | 46 | jar 47 | 48 | 49 | 50 | binkley 51 | Brian Oxley 52 | binkley@alumni.rice.edu 53 | 54 | owner 55 | architect 56 | developer 57 | 58 | America/Chicago 59 | 60 | 61 | jwlibby 62 | John Libby 63 | jwlibby@gmail.com 64 | 65 | architect 66 | developer 67 | 68 | America/Los_Angeles 69 | 70 | 71 | 72 | 73 | 74 | 3.27.3 75 | 1.5 76 | 10.20.0 77 | 100 78 | 100 79 | 100 80 | 11.1.1 81 | 3.0.1 82 | 1.13.0 83 | 0.8.12 84 | UTF-8 85 | 21 86 | 5.11.1 87 | 1.18.34 88 | demo.Application 89 | 3.7.1 90 | 2.3 91 | 3.6.0 92 | 3.4.0 93 | 3.13.0 94 | 3.8.0 95 | 3.5.0 96 | 3.4.2 97 | 3.11.2 98 | 3.1.2 99 | 3.25.0 100 | ${project.build.directory} 101 | 3.5.2 102 | 3.3.2 103 | 3.9.9 104 | 5.14.2 105 | 2.9.0 106 | ${env.OWASP_NVD_API_KEY} 107 | false 108 | 111 | 1.2.1 112 | 1.17.3 113 | false 114 | 7.10.0 115 | ${java.charset} 116 | ${java.charset} 117 | 4.8.6.4 118 | 4.9.0 119 | 2.43.0 120 | 3.0.0 121 | 2.17.1 122 | true 123 | 124 | 125 | 126 | 127 | 128 | 129 | 131 | org.apache.logging.log4j 132 | log4j 133 | [2.16,) 134 | 135 | 136 | org.junit 137 | junit-bom 138 | ${junit.version} 139 | pom 140 | import 141 | 142 | 143 | 144 | 145 | 146 | 147 | org.projectlombok 148 | lombok 149 | ${lombok.version} 150 | true 151 | 152 | 153 | com.github.spotbugs 154 | spotbugs-annotations 155 | ${spotbugs.version} 156 | true 157 | 158 | 159 | com.google.code.findbugs 160 | findbugs-annotations 161 | ${findbugs-annotations.version} 162 | true 163 | 164 | 165 | 166 | org.junit.jupiter 167 | junit-jupiter 168 | test 169 | 170 | 171 | org.assertj 172 | assertj-core 173 | ${assertj.version} 174 | test 175 | 176 | 177 | org.mockito 178 | mockito-core 179 | ${mockito.version} 180 | test 181 | 182 | 183 | 184 | 185 | 186 | 187 | org.apache.maven.plugins 188 | maven-wrapper-plugin 189 | ${maven-wrapper-plugin.version} 190 | 191 | source 192 | ${maven.version} 193 | 194 | 195 | 196 | org.apache.maven.plugins 197 | maven-enforcer-plugin 198 | ${maven-enforcer-plugin.version} 199 | 200 | 201 | 202 | 203 | 204 | ${maven.version} 205 | 206 | 207 | 208 | 209 | 210 | 211 | enforce 212 | 213 | 214 | 215 | 216 | 217 | org.codehaus.mojo 218 | versions-maven-plugin 219 | ${versions-maven-plugin.version} 220 | 221 | false 222 | .*[-.](BETA|beta).* 223 | 224 | 225 | 226 | fr.jcgay.maven.plugins 227 | buildplan-maven-plugin 228 | ${buildplan-maven-plugin.version} 229 | 230 | 231 | org.apache.maven.plugins 232 | maven-clean-plugin 233 | ${maven-clean-plugin.version} 234 | 235 | 236 | org.apache.maven.plugins 237 | maven-dependency-plugin 238 | ${maven-dependency-plugin.version} 239 | 240 | 241 | org.apache.maven.plugins 242 | maven-compiler-plugin 243 | ${maven-compiler-plugin.version} 244 | 245 | 246 | 247 | org.projectlombok 248 | lombok 249 | ${lombok.version} 250 | 251 | 252 | 253 | -XDcompilePolicy=simple 254 | -Xlint:all,-processing 255 | 256 | true 257 | true 258 | ${jdk.version} 259 | 260 | 261 | 262 | com.diffplug.spotless 263 | spotless-maven-plugin 264 | ${spotless-maven-plugin.version} 265 | 266 | 267 | org.apache.maven.plugins 268 | maven-checkstyle-plugin 269 | ${maven-checkstyle-plugin.version} 270 | 271 | 272 | config/checkstyle/checkstyle.xml 273 | 274 | 275 | 276 | true 277 | true 278 | true 279 | 280 | 281 | 282 | 283 | validate-checkstyle 284 | validate 285 | 286 | check 287 | 288 | 289 | 290 | 291 | 292 | com.puppycrawl.tools 293 | checkstyle 294 | ${checkstyle.version} 295 | 296 | 297 | 298 | 299 | org.apache.maven.plugins 300 | maven-pmd-plugin 301 | 302 | true 303 | ${jdk.version} 304 | true 305 | 306 | config/pmd/custom-rules.xml 307 | 308 | 309 | 310 | 311 | 312 | check 313 | cpd-check 314 | 315 | 316 | 317 | 318 | 319 | org.apache.maven.plugins 320 | maven-surefire-plugin 321 | ${maven-testing-plugins.version} 322 | 323 | 326 | 327 | 328 | ${project.basedir}/config/logging.properties 329 | 330 | 331 | 332 | 333 | 334 | org.jacoco 335 | jacoco-maven-plugin 336 | ${jacoco-maven-plugin.version} 337 | 338 | 339 | 340 | 341 | 342 | LINE 343 | COVEREDRATIO 344 | ${coverage.lines}% 345 | 346 | 347 | BRANCH 348 | COVEREDRATIO 349 | ${coverage.branches}% 350 | 351 | 352 | INSTRUCTION 353 | COVEREDRATIO 354 | ${coverage.instructions}% 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | prepare-coverage 363 | 364 | prepare-agent 365 | 366 | 367 | 368 | coverage-report 369 | post-integration-test 370 | 371 | report 372 | 373 | 374 | 375 | check-coverage 376 | 377 | check 378 | 379 | 380 | 381 | 382 | 383 | com.github.spotbugs 384 | spotbugs-maven-plugin 385 | ${spotbugs-maven-plugin.version} 386 | 387 | Max 388 | true 389 | Low 390 | 391 | true 392 | true 393 | true 394 | 395 | 396 | 397 | com.h3xstream.findsecbugs 398 | findsecbugs-plugin 399 | ${findsecbugs-plugin.version} 400 | 401 | 402 | 403 | 404 | 405 | com.github.spotbugs 406 | spotbugs 407 | ${spotbugs.version} 408 | 409 | 410 | 411 | 412 | 413 | check 414 | 415 | 416 | 417 | 418 | 419 | org.gaul 420 | modernizer-maven-plugin 421 | ${modernizer.version} 422 | 423 | ${jdk.version} 424 | 425 | 426 | 427 | verify 428 | 429 | modernizer 430 | 431 | 432 | 433 | 434 | 435 | org.pitest 436 | pitest-maven 437 | ${pitest-maven.version} 438 | 439 | 440 | -XX:TieredStopAtLevel=1 441 | 442 | 100 443 | ${pitest.skip} 444 | false 445 | false 446 | NO_SPINNER 447 | true 448 | 449 | 450 | 451 | mutation-testing 452 | verify 453 | 454 | mutationCoverage 455 | 456 | 457 | 458 | 459 | 460 | org.pitest 461 | pitest-junit5-plugin 462 | ${pitest-junit5-plugin.version} 463 | 464 | 465 | 466 | 467 | org.apache.maven.plugins 468 | maven-javadoc-plugin 469 | ${maven-javadoc-plugin.version} 470 | 471 | 472 | javadoc-jars 473 | package 474 | 475 | jar 476 | test-jar 477 | 478 | 479 | 480 | 481 | 482 | 483 | org.apache.maven.plugins 484 | maven-jdeps-plugin 485 | ${maven-jdeps-plugin.version} 486 | 487 | ${jdk.version} 488 | 489 | 490 | 491 | 492 | jdkinternals 493 | test-jdkinternals 494 | 495 | 496 | 497 | 498 | 499 | org.owasp 500 | dependency-check-maven 501 | ${dependency-check-maven.version} 502 | 503 | 504 | false 505 | 0 506 | ${owasp.nvdApiKey} 507 | ${owasp.skip} 508 | 509 | 510 | 511 | 512 | check 513 | 514 | 515 | 516 | 517 | 518 | org.apache.maven.plugins 519 | maven-failsafe-plugin 520 | ${maven-testing-plugins.version} 521 | 522 | 525 | 526 | 527 | ${project.basedir}/config/logging.properties 528 | 529 | 530 | 531 | 532 | 533 | 534 | integration-test 535 | verify 536 | 537 | 538 | 539 | 540 | 541 | org.apache.maven.plugins 542 | maven-jar-plugin 543 | ${maven-jar-plugin.version} 544 | 545 | 546 | 547 | 548 | java.base/java.lang java.base/java.util java.base/java.lang.reflect 549 | 550 | 551 | 552 | 553 | 554 | 555 | org.apache.maven.plugins 556 | maven-assembly-plugin 557 | ${maven-assembly-plugin.version} 558 | 559 | 560 | 561 | ${main.class} 562 | 563 | 564 | 565 | jar-with-dependencies 566 | 567 | 568 | 569 | 570 | package 571 | 572 | single 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | org.apache.maven.plugins 583 | maven-pmd-plugin 584 | ${maven-pmd-plugin.version} 585 | 586 | 587 | net.sourceforge.pmd 588 | pmd-core 589 | ${pmd.version} 590 | 591 | 592 | net.sourceforge.pmd 593 | pmd-java 594 | ${pmd.version} 595 | 596 | 597 | net.sourceforge.pmd 598 | pmd-javascript 599 | ${pmd.version} 600 | 601 | 602 | net.sourceforge.pmd 603 | pmd-jsp 604 | ${pmd.version} 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | -------------------------------------------------------------------------------- /config/ide/intellij-java-google-style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 21 | 28 |

225 | 226 | 227 | 228 | xmlns:android 229 | 230 | ^$ 231 | 232 | 233 | 234 |
235 |
236 | 237 | 238 | 239 | xmlns:.* 240 | 241 | ^$ 242 | 243 | 244 | BY_NAME 245 | 246 |
247 |
248 | 249 | 250 | 251 | .*:id 252 | 253 | http://schemas.android.com/apk/res/android 254 | 255 | 256 | 257 |
258 |
259 | 260 | 261 | 262 | style 263 | 264 | ^$ 265 | 266 | 267 | 268 |
269 |
270 | 271 | 272 | 273 | .* 274 | 275 | ^$ 276 | 277 | 278 | BY_NAME 279 | 280 |
281 |
282 | 283 | 284 | 285 | .*:.*Style 286 | 287 | http://schemas.android.com/apk/res/android 288 | 289 | 290 | BY_NAME 291 | 292 |
293 |
294 | 295 | 296 | 297 | .*:layout_width 298 | 299 | http://schemas.android.com/apk/res/android 300 | 301 | 302 | 303 |
304 |
305 | 306 | 307 | 308 | .*:layout_height 309 | 310 | http://schemas.android.com/apk/res/android 311 | 312 | 313 | 314 |
315 |
316 | 317 | 318 | 319 | .*:layout_weight 320 | 321 | http://schemas.android.com/apk/res/android 322 | 323 | 324 | 325 |
326 |
327 | 328 | 329 | 330 | .*:layout_margin 331 | 332 | http://schemas.android.com/apk/res/android 333 | 334 | 335 | 336 |
337 |
338 | 339 | 340 | 341 | .*:layout_marginTop 342 | 343 | http://schemas.android.com/apk/res/android 344 | 345 | 346 | 347 |
348 |
349 | 350 | 351 | 352 | .*:layout_marginBottom 353 | 354 | http://schemas.android.com/apk/res/android 355 | 356 | 357 | 358 |
359 |
360 | 361 | 362 | 363 | .*:layout_marginStart 364 | 365 | http://schemas.android.com/apk/res/android 366 | 367 | 368 | 369 |
370 |
371 | 372 | 373 | 374 | .*:layout_marginEnd 375 | 376 | http://schemas.android.com/apk/res/android 377 | 378 | 379 | 380 |
381 |
382 | 383 | 384 | 385 | .*:layout_marginLeft 386 | 387 | http://schemas.android.com/apk/res/android 388 | 389 | 390 | 391 |
392 |
393 | 394 | 395 | 396 | .*:layout_marginRight 397 | 398 | http://schemas.android.com/apk/res/android 399 | 400 | 401 | 402 |
403 |
404 | 405 | 406 | 407 | .*:layout_.* 408 | 409 | http://schemas.android.com/apk/res/android 410 | 411 | 412 | BY_NAME 413 | 414 |
415 |
416 | 417 | 418 | 419 | .*:padding 420 | 421 | http://schemas.android.com/apk/res/android 422 | 423 | 424 | 425 |
426 |
427 | 428 | 429 | 430 | .*:paddingTop 431 | 432 | http://schemas.android.com/apk/res/android 433 | 434 | 435 | 436 |
437 |
438 | 439 | 440 | 441 | .*:paddingBottom 442 | 443 | http://schemas.android.com/apk/res/android 444 | 445 | 446 | 447 |
448 |
449 | 450 | 451 | 452 | .*:paddingStart 453 | 454 | http://schemas.android.com/apk/res/android 455 | 456 | 457 | 458 |
459 |
460 | 461 | 462 | 463 | .*:paddingEnd 464 | 465 | http://schemas.android.com/apk/res/android 466 | 467 | 468 | 469 |
470 |
471 | 472 | 473 | 474 | .*:paddingLeft 475 | 476 | http://schemas.android.com/apk/res/android 477 | 478 | 479 | 480 |
481 |
482 | 483 | 484 | 485 | .*:paddingRight 486 | 487 | http://schemas.android.com/apk/res/android 488 | 489 | 490 | 491 |
492 |
493 | 494 | 495 | 496 | .* 497 | http://schemas.android.com/apk/res/android 498 | 499 | 500 | BY_NAME 501 | 502 |
503 |
504 | 505 | 506 | 507 | .* 508 | http://schemas.android.com/apk/res-auto 509 | 510 | 511 | BY_NAME 512 | 513 |
514 |
515 | 516 | 517 | 518 | .* 519 | http://schemas.android.com/tools 520 | 521 | 522 | BY_NAME 523 | 524 |
525 |
526 | 527 | 528 | 529 | .* 530 | .* 531 | 532 | 533 | BY_NAME 534 | 535 |
536 | 537 | 538 | 539 | 540 | 553 | 554 | 581 | 582 | 598 | --------------------------------------------------------------------------------