From 372b5be77e5b1adbc494bc00698e8d7535ccfdc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Thu, 25 May 2023 22:07:14 +0200 Subject: [PATCH] update to Gradle 8.1.1, Java 17 --- build.gradle | 38 +- gradle.properties | 7 - gradle/compile/java.gradle | 24 +- gradle/publish/forgejo.gradle | 16 + gradle/publish/ivy.gradle | 27 + .../maven.gradle} | 25 +- gradle/publish/sonatype.gradle | 11 + gradle/publishing/sonatype.gradle | 11 - gradle/quality/checkstyle.gradle | 19 + gradle/quality/checkstyle.xml | 333 ++ gradle/quality/cyclonedx.gradle | 11 + gradle/quality/pmd.gradle | 17 + .../pmd/category/java/bestpractices.xml | 1650 ++++++++ .../pmd/category/java/categories.properties | 10 + .../quality/pmd/category/java/codestyle.xml | 2176 +++++++++++ gradle/quality/pmd/category/java/design.xml | 1657 ++++++++ .../pmd/category/java/documentation.xml | 144 + .../quality/pmd/category/java/errorprone.xml | 3383 +++++++++++++++++ .../pmd/category/java/multithreading.xml | 393 ++ .../quality/pmd/category/java/performance.xml | 1006 +++++ gradle/quality/pmd/category/java/security.xml | 65 + gradle/quality/sonarqube.gradle | 37 + gradle/quality/spotbugs.gradle | 52 +- gradle/test/junit5.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 62076 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 288 +- gradlew.bat | 15 +- settings.gradle | 50 +- .../com/google/inject/name/NamedImpl.java | 2 +- .../com/google/inject/spi/ElementSource.java | 83 - .../java/com/google/inject/spi/Elements.java | 58 +- .../com/google/inject/spi/ModuleSource.java | 82 +- .../java/com/google/inject/BinderTest.java | 9 +- .../inject/BoundInstanceInjectionTest.java | 163 +- .../google/inject/CircularDependencyTest.java | 1 + .../inject/internal/WeakKeySetTest.java | 362 +- .../inject/internal/WeakKeySetUtils.java | 31 +- .../com/google/inject/name/NamesTest.java | 10 +- .../google/inject/spi/ElementSourceTest.java | 131 +- .../com/google/inject/spi/ElementsTest.java | 6 - .../google/inject/spi/ModuleSourceTest.java | 68 +- .../google/inject/spi/SpiBindingsTest.java | 6 - 43 files changed, 11579 insertions(+), 903 deletions(-) create mode 100644 gradle/publish/forgejo.gradle create mode 100644 gradle/publish/ivy.gradle rename gradle/{publishing/publication.gradle => publish/maven.gradle} (70%) create mode 100644 gradle/publish/sonatype.gradle delete mode 100644 gradle/publishing/sonatype.gradle create mode 100644 gradle/quality/checkstyle.gradle create mode 100644 gradle/quality/checkstyle.xml create mode 100644 gradle/quality/cyclonedx.gradle create mode 100644 gradle/quality/pmd.gradle create mode 100644 gradle/quality/pmd/category/java/bestpractices.xml create mode 100644 gradle/quality/pmd/category/java/categories.properties create mode 100644 gradle/quality/pmd/category/java/codestyle.xml create mode 100644 gradle/quality/pmd/category/java/design.xml create mode 100644 gradle/quality/pmd/category/java/documentation.xml create mode 100644 gradle/quality/pmd/category/java/errorprone.xml create mode 100644 gradle/quality/pmd/category/java/multithreading.xml create mode 100644 gradle/quality/pmd/category/java/performance.xml create mode 100644 gradle/quality/pmd/category/java/security.xml create mode 100644 gradle/quality/sonarqube.gradle diff --git a/build.gradle b/build.gradle index 52325a9..a547c48 100644 --- a/build.gradle +++ b/build.gradle @@ -1,22 +1,28 @@ plugins { - id "de.marcphilipp.nexus-publish" version "0.4.0" - id "io.codearte.nexus-staging" version "0.21.1" + //id "checkstyle" + //id "pmd" + id 'maven-publish' + id 'signing' + id "io.github.gradle-nexus.publish-plugin" version "1.3.0" + //id "com.github.spotbugs" version "5.0.14" + id "org.cyclonedx.bom" version "1.7.2" + //id "org.xbib.gradle.plugin.asciidoctor" version "3.0.0" } wrapper { - gradleVersion = "${project.property('gradle.wrapper.version')}" + gradleVersion = libs.versions.gradle.get() distributionType = Wrapper.DistributionType.ALL } ext { - user = 'jprante' + user = 'joerg' name = 'guice' description = 'Guice implementation with named modules for Java 11+' inceptionYear = '2012' - url = 'https://github.com/' + user + '/' + name - scmUrl = 'https://github.com/' + user + '/' + name - scmConnection = 'scm:git:git://github.com/' + user + '/' + name + '.git' - scmDeveloperConnection = 'scm:git:ssh://git@github.com:' + user + '/' + name + '.git' + url = 'https://xbib.org/' + user + '/' + name + scmUrl = 'https://xbib.org/' + user + '/' + name + scmConnection = 'scm:git:git://xbib.org/' + user + '/' + name + '.git' + scmDeveloperConnection = 'scm:git:ssh://forgejo@xbib.org:' + user + '/' + name + '.git' issueManagementSystem = 'Github' issueManagementUrl = ext.scmUrl + '/issues' licenseName = 'The Apache License, Version 2.0' @@ -29,13 +35,17 @@ apply from: rootProject.file('gradle/ide/idea.gradle') apply from: rootProject.file('gradle/repositories/maven.gradle') apply from: rootProject.file('gradle/compile/java.gradle') apply from: rootProject.file('gradle/test/junit5.gradle') -apply from: rootProject.file('gradle/publishing/publication.gradle') -apply from: rootProject.file('gradle/publishing/sonatype.gradle') +apply from: rootProject.file('gradle/quality/cyclonedx.gradle') +//apply from: rootProject.file('gradle/quality/spotbugs.gradle') +//apply from: rootProject.file('gradle/quality/checkstyle.gradle') +//apply from: rootProject.file('gradle/quality/pmd.gradle') +apply from: rootProject.file('gradle/publish/sonatype.gradle') +apply from: rootProject.file('gradle/publish/forgejo.gradle') dependencies { - api "org.xbib:javax-inject:${project.property('javax-inject.version')}" - api "org.xbib:guava:${project.property('guava.version')}" - testImplementation "junit:junit:${project.property('junit4.version')}" + api libs.javax.inject + api libs.guava + testImplementation libs.junit4 // Helper for com.google.common.testing.GcFinalization, com.google.common.testing.EqualsTester - testImplementation "com.google.guava:guava-testlib:30.1-jre" + testImplementation libs.guava.testlib } diff --git a/gradle.properties b/gradle.properties index 41c09b0..6042065 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,10 +3,3 @@ name = guice version = 5.0.1.0 org.gradle.warning.mode = ALL -gradle.wrapper.version = 6.6.1 -javax-inject.version = 1 -guava.version = 30.1 -# test -junit.version = 5.7.2 -junit4.version = 4.13.2 -log4j.version = 2.14.1 diff --git a/gradle/compile/java.gradle b/gradle/compile/java.gradle index 54f1f87..5e99d19 100644 --- a/gradle/compile/java.gradle +++ b/gradle/compile/java.gradle @@ -3,16 +3,18 @@ apply plugin: 'java-library' java { modularity.inferModulePath.set(true) + withSourcesJar() + withJavadocJar() } compileJava { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } compileTestJava { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } jar { @@ -21,20 +23,6 @@ jar { } } -task sourcesJar(type: Jar, dependsOn: classes) { - classifier 'sources' - from sourceSets.main.allSource -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier 'javadoc' - from javadoc.destinationDir -} - -artifacts { - archives sourcesJar, javadocJar -} - tasks.withType(JavaCompile) { options.encoding('UTF-8') options.compilerArgs << '-Xlint:all' diff --git a/gradle/publish/forgejo.gradle b/gradle/publish/forgejo.gradle new file mode 100644 index 0000000..b99b2fb --- /dev/null +++ b/gradle/publish/forgejo.gradle @@ -0,0 +1,16 @@ +if (project.hasProperty('forgeJoToken')) { + publishing { + repositories { + maven { + url 'https://xbib.org/api/packages/joerg/maven' + credentials(HttpHeaderCredentials) { + name = "Authorization" + value = "token ${project.property('forgeJoToken')}" + } + authentication { + header(HttpHeaderAuthentication) + } + } + } + } +} diff --git a/gradle/publish/ivy.gradle b/gradle/publish/ivy.gradle new file mode 100644 index 0000000..71aa155 --- /dev/null +++ b/gradle/publish/ivy.gradle @@ -0,0 +1,27 @@ +apply plugin: 'ivy-publish' + +publishing { + repositories { + ivy { + url = "https://xbib.org/repo" + } + } + publications { + ivy(IvyPublication) { + from components.java + descriptor { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + author { + name = 'Jörg Prante' + url = 'https://xbib.org/joerg' + } + descriptor.description { + text = rootProject.ext.description + } + } + } + } +} \ No newline at end of file diff --git a/gradle/publishing/publication.gradle b/gradle/publish/maven.gradle similarity index 70% rename from gradle/publishing/publication.gradle rename to gradle/publish/maven.gradle index 2e2b2c0..867e23a 100644 --- a/gradle/publishing/publication.gradle +++ b/gradle/publish/maven.gradle @@ -1,13 +1,10 @@ -apply plugin: "de.marcphilipp.nexus-publish" - publishing { publications { - mavenJava(MavenPublication) { + "${project.name}"(MavenPublication) { from components.java - artifact sourcesJar - artifact javadocJar pom { + artifactId = project.name name = project.name description = rootProject.ext.description url = rootProject.ext.url @@ -19,10 +16,10 @@ publishing { } developers { developer { - id = 'jprante' + id = 'joerg' name = 'Jörg Prante' email = 'joergprante@gmail.com' - url = 'https://github.com/jprante' + url = 'https://xbib.org/joerg' } } scm { @@ -49,18 +46,6 @@ publishing { if (project.hasProperty("signing.keyId")) { apply plugin: 'signing' signing { - sign publishing.publications.mavenJava - } -} - -if (project.hasProperty("ossrhUsername")) { - nexusPublishing { - repositories { - sonatype { - username = project.property('ossrhUsername') - password = project.property('ossrhPassword') - packageGroup = "org.xbib" - } - } + sign publishing.publications."${project.name}" } } diff --git a/gradle/publish/sonatype.gradle b/gradle/publish/sonatype.gradle new file mode 100644 index 0000000..5d739de --- /dev/null +++ b/gradle/publish/sonatype.gradle @@ -0,0 +1,11 @@ +if (project.hasProperty('ossrhUsername') && project.hasProperty('ossrhPassword')) { + nexusPublishing { + repositories { + sonatype { + username = project.property('ossrhUsername') + password = project.property('ossrhPassword') + packageGroup = "org.xbib" + } + } + } +} diff --git a/gradle/publishing/sonatype.gradle b/gradle/publishing/sonatype.gradle deleted file mode 100644 index e1813f3..0000000 --- a/gradle/publishing/sonatype.gradle +++ /dev/null @@ -1,11 +0,0 @@ - -if (project.hasProperty('ossrhUsername') && project.hasProperty('ossrhPassword')) { - - apply plugin: 'io.codearte.nexus-staging' - - nexusStaging { - username = project.property('ossrhUsername') - password = project.property('ossrhPassword') - packageGroup = "org.xbib" - } -} diff --git a/gradle/quality/checkstyle.gradle b/gradle/quality/checkstyle.gradle new file mode 100644 index 0000000..85b8bd8 --- /dev/null +++ b/gradle/quality/checkstyle.gradle @@ -0,0 +1,19 @@ + +apply plugin: 'checkstyle' + +tasks.withType(Checkstyle) { + ignoreFailures = true + reports { + xml.getRequired().set(true) + html.getRequired().set(true) + } +} + +checkstyle { + configFile = rootProject.file('gradle/quality/checkstyle.xml') + ignoreFailures = true + showViolations = true + checkstyleMain { + source = sourceSets.main.allSource + } +} diff --git a/gradle/quality/checkstyle.xml b/gradle/quality/checkstyle.xml new file mode 100644 index 0000000..66a9aae --- /dev/null +++ b/gradle/quality/checkstyle.xml @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gradle/quality/cyclonedx.gradle b/gradle/quality/cyclonedx.gradle new file mode 100644 index 0000000..a6bf41b --- /dev/null +++ b/gradle/quality/cyclonedx.gradle @@ -0,0 +1,11 @@ +cyclonedxBom { + includeConfigs = [ 'runtimeClasspath' ] + skipConfigs = [ 'compileClasspath', 'testCompileClasspath' ] + projectType = "library" + schemaVersion = "1.4" + destination = file("build/reports") + outputName = "bom" + outputFormat = "json" + includeBomSerialNumber = true + componentVersion = "2.0.0" +} diff --git a/gradle/quality/pmd.gradle b/gradle/quality/pmd.gradle new file mode 100644 index 0000000..55fcfda --- /dev/null +++ b/gradle/quality/pmd.gradle @@ -0,0 +1,17 @@ + +apply plugin: 'pmd' + +tasks.withType(Pmd) { + ignoreFailures = true + reports { + xml.getRequired().set(true) + html.getRequired().set(true) + } +} + +pmd { + ignoreFailures = true + consoleOutput = false + toolVersion = "6.51.0" + ruleSetFiles = rootProject.files('gradle/quality/pmd/category/java/bestpractices.xml') +} diff --git a/gradle/quality/pmd/category/java/bestpractices.xml b/gradle/quality/pmd/category/java/bestpractices.xml new file mode 100644 index 0000000..6bf15a0 --- /dev/null +++ b/gradle/quality/pmd/category/java/bestpractices.xml @@ -0,0 +1,1650 @@ + + + + + + Rules which enforce generally accepted best practices. + + + + + The abstract class does not contain any abstract methods. An abstract class suggests + an incomplete implementation, which is to be completed by subclasses implementing the + abstract methods. If the class is intended to be used as a base class only (not to be instantiated + directly) a protected constructor can be provided prevent direct instantiation. + + 3 + + + + + + + + + + + + + + + Instantiation by way of private constructors from outside of the constructor's class often causes the + generation of an accessor. A factory method, or non-privatization of the constructor can eliminate this + situation. The generated class file is actually an interface. It gives the accessing class the ability + to invoke a new hidden package scope constructor that takes the interface as a supplementary parameter. + This turns a private constructor effectively into one with package scope, and is challenging to discern. + + 3 + + + + + + + + When accessing a private field / method from another class, the Java compiler will generate a accessor methods + with package-private visibility. This adds overhead, and to the dex method count on Android. This situation can + be avoided by changing the visibility of the field / method from private to package-private. + + 3 + + + + + + + + Constructors and methods receiving arrays should clone objects and store the copy. + This prevents future changes from the user from affecting the original array. + + 3 + + + + + + + + Avoid printStackTrace(); use a logger call instead. + + 3 + + + + + + + + + + + + + + + Reassigning loop variables can lead to hard-to-find bugs. Prevent or limit how these variables can be changed. + + In foreach-loops, configured by the `foreachReassign` property: + - `deny`: Report any reassignment of the loop variable in the loop body. _This is the default._ + - `allow`: Don't check the loop variable. + - `firstOnly`: Report any reassignments of the loop variable, except as the first statement in the loop body. + _This is useful if some kind of normalization or clean-up of the value before using is permitted, but any other change of the variable is not._ + + In for-loops, configured by the `forReassign` property: + - `deny`: Report any reassignment of the control variable in the loop body. _This is the default._ + - `allow`: Don't check the control variable. + - `skip`: Report any reassignments of the control variable, except conditional increments/decrements (`++`, `--`, `+=`, `-=`). + _This prevents accidental reassignments or unconditional increments of the control variable._ + + 3 + + + + + + + + Reassigning values to incoming parameters is not recommended. Use temporary local variables instead. + + 2 + + + + + + + + StringBuffers/StringBuilders can grow considerably, and so may become a source of memory leaks + if held within objects with long lifetimes. + + 3 + + + + + + + + + + + + + + + Application with hard-coded IP addresses can become impossible to deploy in some cases. + Externalizing IP adresses is preferable. + + 3 + + + + + + + + Always check the return values of navigation methods (next, previous, first, last) of a ResultSet. + If the value return is 'false', it should be handled properly. + + 3 + + + + + + + + Avoid constants in interfaces. Interfaces should define types, constants are implementation details + better placed in classes or enums. See Effective Java, item 19. + + 3 + + + + + + + + + + + + + + + + By convention, the default label should be the last label in a switch statement. + + 3 + + + + + + + + + + + + + + + Reports loops that can be safely replaced with the foreach syntax. The rule considers loops over + lists, arrays and iterators. A loop is safe to replace if it only uses the index variable to + access an element of the list or array, only has one update statement, and loops through *every* + element of the list or array left to right. + + 3 + + l) { + for (int i = 0; i < l.size(); i++) { // pre Java 1.5 + System.out.println(l.get(i)); + } + + for (String s : l) { // post Java 1.5 + System.out.println(s); + } + } +} +]]> + + + + + + Having a lot of control variables in a 'for' loop makes it harder to see what range of values + the loop iterates over. By default this rule allows a regular 'for' loop with only one variable. + + 3 + + + + //ForInit/LocalVariableDeclaration[count(VariableDeclarator) > $maximumVariables] + + + + + + + + + + Whenever using a log level, one should check if the loglevel is actually enabled, or + otherwise skip the associate String creation and manipulation. + + 2 + + + + + + + + In JUnit 3, test suites are indicated by the suite() method. In JUnit 4, suites are indicated + through the @RunWith(Suite.class) annotation. + + 3 + + + + + + + + + + + + + + + In JUnit 3, the tearDown method was used to clean up all data entities required in running tests. + JUnit 4 skips the tearDown method and executes all methods annotated with @After after running each test. + JUnit 5 introduced @AfterEach and @AfterAll annotations to execute methods after each test or after all tests in the class, respectively. + + 3 + + + + + + + + + + + + + + + In JUnit 3, the setUp method was used to set up all data entities required in running tests. + JUnit 4 skips the setUp method and executes all methods annotated with @Before before all tests. + JUnit 5 introduced @BeforeEach and @BeforeAll annotations to execute methods before each test or before all tests in the class, respectively. + + 3 + + + + + + + + + + + + + + + In JUnit 3, the framework executed all methods which started with the word test as a unit test. + In JUnit 4, only methods annotated with the @Test annotation are executed. + In JUnit 5, one of the following annotations should be used for tests: @Test, @RepeatedTest, @TestFactory, @TestTemplate or @ParameterizedTest. + + 3 + + + + + + + + + + + + + + + + + JUnit assertions should include an informative message - i.e., use the three-argument version of + assertEquals(), not the two-argument version. + + 3 + + + + + + + + Unit tests should not contain too many asserts. Many asserts are indicative of a complex test, for which + it is harder to verify correctness. Consider breaking the test scenario into multiple, shorter test scenarios. + Customize the maximum number of assertions used by this Rule to suit your needs. + + This rule checks for JUnit4, JUnit5 and TestNG Tests, as well as methods starting with "test". + + 3 + + + + + $maximumAsserts] +]]> + + + + + + + + + + + JUnit tests should include at least one assertion. This makes the tests more robust, and using assert + with messages provide the developer a clearer idea of what the test does. + + 3 + + + + + + + + In JUnit4, use the @Test(expected) annotation to denote tests that should throw exceptions. + + 3 + + + + + + + + The use of implementation types (i.e., HashSet) as object references limits your ability to use alternate + implementations in the future as requirements change. Whenever available, referencing objects + by their interface types (i.e, Set) provides much more flexibility. + + 3 + + list = new ArrayList<>(); + + public HashSet getFoo() { + return new HashSet(); + } + + // preferred approach + private List list = new ArrayList<>(); + + public Set getFoo() { + return new HashSet(); + } +} +]]> + + + + + + Exposing internal arrays to the caller violates object encapsulation since elements can be + removed or replaced outside of the object that owns it. It is safer to return a copy of the array. + + 3 + + + + + + + + + Annotating overridden methods with @Override ensures at compile time that + the method really overrides one, which helps refactoring and clarifies intent. + + 3 + + + + + + + + Java allows the use of several variables declaration of the same type on one line. However, it + can lead to quite messy code. This rule looks for several declarations on the same line. + + 4 + + + + 1] + [$strictMode or count(distinct-values(VariableDeclarator/@BeginLine)) != count(VariableDeclarator)] +| +//FieldDeclaration + [count(VariableDeclarator) > 1] + [$strictMode or count(distinct-values(VariableDeclarator/@BeginLine)) != count(VariableDeclarator)] +]]> + + + + + + + + + + + + + Position literals first in comparisons, if the second argument is null then NullPointerExceptions + can be avoided, they will just return false. + + 3 + + + + + + + + + + + + + + + Position literals first in comparisons, if the second argument is null then NullPointerExceptions + can be avoided, they will just return false. + + 3 + + + + + + + + + + + + + + + Throwing a new exception from a catch block without passing the original exception into the + new exception will cause the original stack trace to be lost making it difficult to debug + effectively. + + 3 + + + + + + + + Consider replacing Enumeration usages with the newer java.util.Iterator + + 3 + + + + + + + + + + + + + + + Consider replacing Hashtable usage with the newer java.util.Map if thread safety is not required. + + 3 + + + //Type/ReferenceType/ClassOrInterfaceType[@Image='Hashtable'] + + + + + + + + + + Consider replacing Vector usages with the newer java.util.ArrayList if expensive thread-safe operations are not required. + + 3 + + + //Type/ReferenceType/ClassOrInterfaceType[@Image='Vector'] + + + + + + + + + + All switch statements should include a default option to catch any unspecified values. + + 3 + + + + + + + + + + + + + + References to System.(out|err).print are usually intended for debugging purposes and can remain in + the codebase even in production code. By using a logger one can enable/disable this behaviour at + will (and by priority) and avoid clogging the Standard out log. + + 2 + + + + + + + + + + + + + + + Avoid passing parameters to methods or constructors without actually referencing them in the method body. + + 3 + + + + + + + + Avoid unused import statements to prevent unwanted dependencies. + This rule will also find unused on demand imports, i.e. import com.foo.*. + + 4 + + + + + + + + Detects when a local variable is declared and/or assigned, but not used. + + 3 + + + + + + + + Detects when a private field is declared and/or assigned a value, but not used. + + 3 + + + + + + + + Unused Private Method detects when a private method is declared but is unused. + + 3 + + + + + + + + This rule detects JUnit assertions in object equality. These assertions should be made by more specific methods, like assertEquals. + + 3 + + + + + + + + + + + + + + + This rule detects JUnit assertions in object references equality. These assertions should be made by + more specific methods, like assertNull, assertNotNull. + + 3 + + + + + + + + + + + + + + + This rule detects JUnit assertions in object references equality. These assertions should be made + by more specific methods, like assertSame, assertNotSame. + + 3 + + + + + + + + + + + + + + + When asserting a value is the same as a literal or Boxed boolean, use assertTrue/assertFalse, instead of assertEquals. + + 3 + + + + + + + + + + + + + + + The isEmpty() method on java.util.Collection is provided to determine if a collection has any elements. + Comparing the value of size() to 0 does not convey intent as well as the isEmpty() method. + + 3 + + + + + + + + Java 7 introduced the try-with-resources statement. This statement ensures that each resource is closed at the end + of the statement. It avoids the need of explicitly closing the resources in a finally block. Additionally exceptions + are better handled: If an exception occurred both in the `try` block and `finally` block, then the exception from + the try block was suppressed. With the `try`-with-resources statement, the exception thrown from the try-block is + preserved. + + 3 + + + + + + + + + + + + + + + + + Java 5 introduced the varargs parameter declaration for methods and constructors. This syntactic + sugar provides flexibility for users of these methods and constructors, allowing them to avoid + having to deal with the creation of an array. + + 4 + + + + + + + + + + + + + + diff --git a/gradle/quality/pmd/category/java/categories.properties b/gradle/quality/pmd/category/java/categories.properties new file mode 100644 index 0000000..8ef5eac --- /dev/null +++ b/gradle/quality/pmd/category/java/categories.properties @@ -0,0 +1,10 @@ + +rulesets.filenames=\ + category/java/bestpractices.xml,\ + category/java/codestyle.xml,\ + category/java/design.xml,\ + category/java/documentation.xml,\ + category/java/errorprone.xml,\ + category/java/multithreading.xml,\ + category/java/performance.xml,\ + category/java/security.xml diff --git a/gradle/quality/pmd/category/java/codestyle.xml b/gradle/quality/pmd/category/java/codestyle.xml new file mode 100644 index 0000000..186ea4b --- /dev/null +++ b/gradle/quality/pmd/category/java/codestyle.xml @@ -0,0 +1,2176 @@ + + + + + + Rules which enforce a specific coding style. + + + + + Abstract classes should be named 'AbstractXXX'. + + This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced + by {% rule java/codestyle/ClassNamingConventions %}. + + 3 + + + + + + + + + + + + + + + + + + 3 + + + + + + + + Avoid using dollar signs in variable/method/class/interface names. + + 3 + + + + + + + Avoid using final local variables, turn them into fields. + 3 + + + + + + + + + + + + + + + Prefixing parameters by 'in' or 'out' pollutes the name of the parameters and reduces code readability. + To indicate whether or not a parameter will be modify in a method, its better to document method + behavior with Javadoc. + + This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced + by the more general rule {% rule java/codestyle/FormalParameterNamingConventions %}. + + 4 + + + + + + + + + + + + + + + + + + Do not use protected fields in final classes since they cannot be subclassed. + Clarify your intent by using private or package access modifiers instead. + + 3 + + + + + + + + + + + + + + + Do not use protected methods in most final classes since they cannot be subclassed. This should + only be allowed in final classes that extend other classes with protected methods (whose + visibility cannot be reduced). Clarify your intent by using private or package access modifiers instead. + + 3 + + + + + + + + + + + + + + + Unnecessary reliance on Java Native Interface (JNI) calls directly reduces application portability + and increases the maintenance burden. + + 2 + + + //Name[starts-with(@Image,'System.loadLibrary')] + + + + + + + + + + Methods that return boolean results should be named as predicate statements to denote this. + I.e, 'isReady()', 'hasValues()', 'canCommit()', 'willFail()', etc. Avoid the use of the 'get' + prefix for these methods. + + 4 + + + + + + + + + + + + + + + + It is a good practice to call super() in a constructor. If super() is not called but + another constructor (such as an overloaded constructor) is called, this rule will not report it. + + 3 + + + + 0 ] +/ClassOrInterfaceBody + /ClassOrInterfaceBodyDeclaration + /ConstructorDeclaration[ count (.//ExplicitConstructorInvocation)=0 ] +]]> + + + + + + + + + + + Configurable naming conventions for type declarations. This rule reports + type declarations which do not match the regex that applies to their + specific kind (e.g. enum or interface). Each regex can be configured through + properties. + + By default this rule uses the standard Java naming convention (Pascal case), + and reports utility class names not ending with 'Util'. + + 1 + + + + + + + + To avoid mistakes if we want that a Method, Constructor, Field or Nested class have a default access modifier + we must add a comment at the beginning of it's declaration. + By default the comment must be /* default */ or /* package */, if you want another, you have to provide a regular expression. + This rule ignores by default all cases that have a @VisibleForTesting annotation. Use the + property "ignoredAnnotations" to customize the recognized annotations. + + 3 + + + + + + + + Avoid negation within an "if" expression with an "else" clause. For example, rephrase: + `if (x != y) diff(); else same();` as: `if (x == y) same(); else diff();`. + + Most "if (x != y)" cases without an "else" are often return cases, so consistent use of this + rule makes the code easier to read. Also, this resolves trivial ordering problems, such + as "does the error case go first?" or "does the common case go first?". + + 3 + + + + + + + + Enforce a policy for braces on control statements. It is recommended to use braces on 'if ... else' + statements and loop statements, even if they are optional. This usually makes the code clearer, and + helps prepare the future when you need to add another statement. That said, this rule lets you control + which statements are required to have braces via properties. + + From 6.2.0 on, this rule supersedes WhileLoopMustUseBraces, ForLoopMustUseBraces, IfStmtMustUseBraces, + and IfElseStmtMustUseBraces. + + 3 + + + + + + + + + + + + + 1 + or (some $stmt (: in only the block statements until the next label :) + in following-sibling::BlockStatement except following-sibling::SwitchLabel[1]/following-sibling::BlockStatement + satisfies not($stmt/Statement/Block))] + ]]> + + + + + + + + + + Use explicit scoping instead of accidental usage of default package private level. + The rule allows methods and fields annotated with Guava's @VisibleForTesting. + + 3 + + + + + + + + + + + + Avoid importing anything from the package 'java.lang'. These classes are automatically imported (JLS 7.5.3). + + 4 + + + + + + + + Duplicate or overlapping import statements should be avoided. + + 4 + + + + + + + + Empty or auto-generated methods in an abstract class should be tagged as abstract. This helps to remove their inapproprate + usage by developers who should be implementing their own versions in the concrete subclasses. + + 1 + + + + + + + + + + + + + + No need to explicitly extend Object. + 4 + + + + + + + + + + + + + + + Fields should be declared at the top of the class, before any method declarations, constructors, initializers or inner classes. + + 3 + + + + + + + + + Configurable naming conventions for field declarations. This rule reports variable declarations + which do not match the regex that applies to their specific kind ---e.g. constants (static final), + enum constant, final field. Each regex can be configured through properties. + + By default this rule uses the standard Java naming convention (Camel case), and uses the ALL_UPPER + convention for constants and enum constants. + + 1 + + + + + + + + Some for loops can be simplified to while loops, this makes them more concise. + + 3 + + + + + + + + + + + + + + + Avoid using 'for' statements without using curly braces. If the code formatting or + indentation is lost then it becomes difficult to separate the code being controlled + from the rest. + + This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced + by the rule {% rule java/codestyle/ControlStatementBraces %}. + + 3 + + + //ForStatement[not(Statement/Block)] + + + + + + + + + + Configurable naming conventions for formal parameters of methods and lambdas. + This rule reports formal parameters which do not match the regex that applies to their + specific kind (e.g. lambda parameter, or final formal parameter). Each regex can be + configured through properties. + + By default this rule uses the standard Java naming convention (Camel case). + + 1 + + lambda1 = s_str -> { }; + + // lambda parameters with an explicit type can be configured separately + Consumer lambda1 = (String str) -> { }; + + } + + } + ]]> + + + + + + Names for references to generic values should be limited to a single uppercase letter. + + 4 + + + + 1 + or + string:upper-case(@Image) != @Image +] +]]> + + + + + extends BaseDao { + // This is ok... +} + +public interface GenericDao { + // Also this +} + +public interface GenericDao { + // 'e' should be an 'E' +} + +public interface GenericDao { + // 'EF' is not ok. +} +]]> + + + + + + + Identical `catch` branches use up vertical space and increase the complexity of code without + adding functionality. It's better style to collapse identical branches into a single multi-catch + branch. + + 3 + + + + + + + + Avoid using if..else statements without using surrounding braces. If the code formatting + or indentation is lost then it becomes difficult to separate the code being controlled + from the rest. + + This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced + by the rule {% rule java/codestyle/ControlStatementBraces %}. + + 3 + + + + + + + + + + + + + + + Avoid using if statements without using braces to surround the code block. If the code + formatting or indentation is lost then it becomes difficult to separate the code being + controlled from the rest. + + This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced + by the rule {% rule java/codestyle/ControlStatementBraces %}. + + 3 + + + + + + + + + + + + + + + This rule finds Linguistic Naming Antipatterns. It checks for fields, that are named, as if they should + be boolean but have a different type. It also checks for methods, that according to their name, should + return a boolean, but don't. Further, it checks, that getters return something and setters won't. + Finally, it checks that methods, that start with "to" - so called transform methods - actually return + something, since according to their name, they should convert or transform one object into another. + There is additionally an option, to check for methods that contain "To" in their name - which are + also transform methods. However, this is disabled by default, since this detection is prone to + false positives. + + For more information, see [Linguistic Antipatterns - What They Are and How + Developers Perceive Them](https://doi.org/10.1007/s10664-014-9350-8). + + 3 + + + + + + + + The Local Home interface of a Session EJB should be suffixed by 'LocalHome'. + + 4 + + + + + + + + + + + + + + + The Local Interface of a Session EJB should be suffixed by 'Local'. + + 4 + + + + + + + + + + + + + + + A local variable assigned only once can be declared final. + + 3 + + + + + + + + Configurable naming conventions for local variable declarations and other locally-scoped + variables. This rule reports variable declarations which do not match the regex that applies to their + specific kind (e.g. final variable, or catch-clause parameter). Each regex can be configured through + properties. + + By default this rule uses the standard Java naming convention (Camel case). + + 1 + + + + + + + + Fields, formal arguments, or local variable names that are too long can make the code difficult to follow. + + 3 + + + + + $minimum] +]]> + + + + + + + + + + + The EJB Specification states that any MessageDrivenBean or SessionBean should be suffixed by 'Bean'. + + 4 + + + + + + + + + + + + + + + A method argument that is never re-assigned within the method can be declared final. + + 3 + + + + + + + + Configurable naming conventions for method declarations. This rule reports + method declarations which do not match the regex that applies to their + specific kind (e.g. JUnit test or native method). Each regex can be + configured through properties. + + By default this rule uses the standard Java naming convention (Camel case). + + 1 + + + + + + + + Detects when a non-field has a name starting with 'm_'. This usually denotes a field and could be confusing. + + This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced + by the more general rule + {% rule java/codestyle/LocalVariableNamingConventions %}. + + 3 + + + + + + + + + + + + + + + Detects when a class or interface does not have a package definition. + + 3 + + + //ClassOrInterfaceDeclaration[count(preceding::PackageDeclaration) = 0] + + + + + + + + + + Since Java 1.7, numeric literals can use underscores to separate digits. This rule enforces that + numeric literals above a certain length use these underscores to increase readability. + + The rule only supports decimal (base 10) literals for now. The acceptable length under which literals + are not required to have underscores is configurable via a property. Even under that length, underscores + that are misplaced (not making groups of 3 digits) are reported. + + 3 + + + + + + + + + + + + + + + + + A method should have only one exit point, and that should be the last statement in the method. + + 3 + + 0) { + return "hey"; // first exit + } + return "hi"; // second exit + } +} +]]> + + + + + + Detects when a package definition contains uppercase characters. + + 3 + + + //PackageDeclaration/Name[lower-case(@Image)!=@Image] + + + + + + + + + + Checks for variables that are defined before they might be used. A reference is deemed to be premature if it is created right before a block of code that doesn't use it that also has the ability to return or throw an exception. + + 3 + + + + + + + + Remote Interface of a Session EJB should not have a suffix. + + 4 + + + + + + + + + + + + + + + A Remote Home interface type of a Session EJB should be suffixed by 'Home'. + + 4 + + + + + + + + + + + + + + + Short Classnames with fewer than e.g. five characters are not recommended. + + 4 + + + + + + + + + + + + + + + + Method names that are very short are not helpful to the reader. + + 3 + + + + + + + + + + + + + + + + Fields, local variables, or parameter names that are very short are not helpful to the reader. + + 3 + + + + + + + + + + + + + + + + + Field names using all uppercase characters - Sun's Java naming conventions indicating constants - should + be declared as final. + + This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced + by the more general rule {% rule java/codestyle/FieldNamingConventions %}. + + 3 + + + + + + + + + + + + + + + If you overuse the static import feature, it can make your program unreadable and + unmaintainable, polluting its namespace with all the static members you import. + Readers of your code (including you, a few months after you wrote it) will not know + which class a static member comes from (Sun 1.5 Language Guide). + + 3 + + + + + $maximumStaticImports] +]]> + + + + + + + + + + + + Avoid the use of value in annotations when it's the only element. + + 3 + + + + + + + + + This rule detects when a constructor is not necessary; i.e., when there is only one constructor and the + constructor is identical to the default constructor. The default constructor should has same access + modifier as the declaring class. In an enum type, the default constructor is implicitly private. + + 3 + + + + + + + + Import statements allow the use of non-fully qualified names. The use of a fully qualified name + which is covered by an import statement is redundant. Consider using the non-fully qualified name. + + 4 + + + + + + + + Avoid the creation of unnecessary local variables + + 3 + + + + + + + + Fields in interfaces and annotations are automatically `public static final`, and methods are `public abstract`. + Classes, interfaces or annotations nested in an interface or annotation are automatically `public static` + (all nested interfaces and annotations are automatically static). + Nested enums are automatically `static`. + For historical reasons, modifiers which are implied by the context are accepted by the compiler, but are superfluous. + + 3 + + + + + + + + Avoid the use of unnecessary return statements. + + 3 + + + + + + + + Use the diamond operator to let the type be inferred automatically. With the Diamond operator it is possible + to avoid duplication of the type parameters. + Instead, the compiler is now able to infer the parameter types for constructor calls, + which makes the code also more readable. + + 3 + + + + + + + + + strings = new ArrayList(); // unnecessary duplication of type parameters +List stringsWithDiamond = new ArrayList<>(); // using the diamond operator is more concise +]]> + + + + + Useless parentheses should be removed. + 4 + + + + 1] + /PrimaryPrefix/Expression + [not(./CastExpression)] + [not(./ConditionalExpression)] + [not(./AdditiveExpression)] + [not(./AssignmentOperator)] +| +//Expression[not(parent::PrimaryPrefix)]/PrimaryExpression[count(*)=1] + /PrimaryPrefix/Expression +| +//Expression/ConditionalAndExpression/PrimaryExpression/PrimaryPrefix/Expression[ + count(*)=1 and + count(./CastExpression)=0 and + count(./EqualityExpression/MultiplicativeExpression)=0 and + count(./ConditionalExpression)=0 and + count(./ConditionalOrExpression)=0] +| +//Expression/ConditionalOrExpression/PrimaryExpression/PrimaryPrefix/Expression[ + count(*)=1 and + not(./CastExpression) and + not(./ConditionalExpression) and + not(./EqualityExpression/MultiplicativeExpression)] +| +//Expression/ConditionalExpression/PrimaryExpression/PrimaryPrefix/Expression[ + count(*)=1 and + not(./CastExpression) and + not(./EqualityExpression)] +| +//Expression/AdditiveExpression[not(./PrimaryExpression/PrimaryPrefix/Literal[@StringLiteral='true'])] + /PrimaryExpression[1]/PrimaryPrefix/Expression[ + count(*)=1 and + not(./CastExpression) and + not(./AdditiveExpression[@Image = '-']) and + not(./ShiftExpression) and + not(./RelationalExpression) and + not(./InstanceOfExpression) and + not(./EqualityExpression) and + not(./AndExpression) and + not(./ExclusiveOrExpression) and + not(./InclusiveOrExpression) and + not(./ConditionalAndExpression) and + not(./ConditionalOrExpression) and + not(./ConditionalExpression)] +| +//Expression/EqualityExpression/PrimaryExpression/PrimaryPrefix/Expression[ + count(*)=1 and + not(./CastExpression) and + not(./AndExpression) and + not(./InclusiveOrExpression) and + not(./ExclusiveOrExpression) and + not(./ConditionalExpression) and + not(./ConditionalAndExpression) and + not(./ConditionalOrExpression) and + not(./EqualityExpression)] +]]> + + + + + + + + + + + Reports qualified this usages in the same class. + + 3 + + + + + + + + + + + + + + + A variable naming conventions rule - customize this to your liking. Currently, it + checks for final variables that should be fully capitalized and non-final variables + that should not include underscores. + + This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced + by the more general rules {% rule java/codestyle/FieldNamingConventions %}, + {% rule java/codestyle/FormalParameterNamingConventions %}, and + {% rule java/codestyle/LocalVariableNamingConventions %}. + + 1 + + + + + + + + Avoid using 'while' statements without using braces to surround the code block. If the code + formatting or indentation is lost then it becomes difficult to separate the code being + controlled from the rest. + + This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced + by the rule {% rule java/codestyle/ControlStatementBraces %}. + + 3 + + + //WhileStatement[not(Statement/Block)] + + + + + + + + diff --git a/gradle/quality/pmd/category/java/design.xml b/gradle/quality/pmd/category/java/design.xml new file mode 100644 index 0000000..ded3d80 --- /dev/null +++ b/gradle/quality/pmd/category/java/design.xml @@ -0,0 +1,1657 @@ + + + + + + Rules that help you discover design issues. + + + + + If an abstract class does not provides any methods, it may be acting as a simple data container + that is not meant to be instantiated. In this case, it is probably better to use a private or + protected constructor in order to prevent instantiation than make the class misleadingly abstract. + + 1 + + + + + + + + + + + + + + + Avoid catching generic exceptions such as NullPointerException, RuntimeException, Exception in try-catch block + + 3 + + + + + + + + + + + + + + + Avoid creating deeply nested if-then statements since they are harder to read and error-prone to maintain. + + 3 + + y) { + if (y>z) { + if (z==x) { + // !! too deep + } + } + } + } +} +]]> + + + + + + Catch blocks that merely rethrow a caught exception only add to code size and runtime complexity. + + 3 + + + + + + + + + + + + + + + Catch blocks that merely rethrow a caught exception wrapped inside a new instance of the same type only add to + code size and runtime complexity. + + 3 + + + + + + + + + + + + + + + *Effective Java, 3rd Edition, Item 72: Favor the use of standard exceptions* +> +>Arguably, every erroneous method invocation boils down to an illegal argument or state, +but other exceptions are standardly used for certain kinds of illegal arguments and states. +If a caller passes null in some parameter for which null values are prohibited, convention dictates that +NullPointerException be thrown rather than IllegalArgumentException. + +To implement that, you are encouraged to use `java.util.Objects.requireNonNull()` +(introduced in Java 1.7). This method is designed primarily for doing parameter +validation in methods and constructors with multiple parameters. + +Your parameter validation could thus look like the following: +``` +public class Foo { + private String exampleValue; + + void setExampleValue(String exampleValue) { + // check, throw and assignment in a single standard call + this.exampleValue = Objects.requireNonNull(exampleValue, "exampleValue must not be null!"); + } + } +``` +]]> + + 1 + + + + + + + + + + + + + + + Avoid throwing certain exception types. Rather than throw a raw RuntimeException, Throwable, + Exception, or Error, use a subclassed exception or error instead. + + 1 + + + + + + + + + + + + + + + A class with only private constructors should be final, unless the private constructor + is invoked by a inner class. + + 1 + + + + = 1 ] +[count(./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration[(@Public = 'true') or (@Protected = 'true') or (@PackagePrivate = 'true')]) = 0 ] +[not(.//ClassOrInterfaceDeclaration)] +]]> + + + + + + + + + + + Sometimes two consecutive 'if' statements can be consolidated by separating their conditions with a boolean short-circuit operator. + + 3 + + + + + + + + + + + + + + + This rule counts the number of unique attributes, local variables, and return types within an object. + A number higher than the specified threshold can indicate a high degree of coupling. + + 3 + + + + + + + = 10. +Additionnally, classes with many methods of moderate complexity get reported as well once the total of their +methods' complexities reaches 80, even if none of the methods was directly reported. + +Reported methods should be broken down into several smaller methods. Reported classes should probably be broken down +into subcomponents.]]> + + 3 + + + + + + + + Data Classes are simple data holders, which reveal most of their state, and + without complex functionality. The lack of functionality may indicate that + their behaviour is defined elsewhere, which is a sign of poor data-behaviour + proximity. By directly exposing their internals, Data Classes break encapsulation, + and therefore reduce the system's maintainability and understandability. Moreover, + classes tend to strongly rely on their data representation, which makes for a brittle + design. + + Refactoring a Data Class should focus on restoring a good data-behaviour proximity. In + most cases, that means moving the operations defined on the data back into the class. + In some other cases it may make sense to remove entirely the class and move the data + into the former client classes. + + 3 + + + + + + + + Errors are system exceptions. Do not extend them. + + 3 + + + + + + + + + + + + + + + Using Exceptions as form of flow control is not recommended as they obscure true exceptions when debugging. + Either add the necessary validation or use an alternate control structure. + + 3 + + + + + + + + Excessive class file lengths are usually indications that the class may be burdened with excessive + responsibilities that could be provided by external classes or functions. In breaking these methods + apart the code becomes more manageable and ripe for reuse. + + 3 + + + + + + + + A high number of imports can indicate a high degree of coupling within an object. This rule + counts the number of unique imports and reports a violation if the count is above the + user-specified threshold. + + 3 + + + + + + + + When methods are excessively long this usually indicates that the method is doing more than its + name/signature might suggest. They also become challenging for others to digest since excessive + scrolling causes readers to lose focus. + Try to reduce the method length by creating helper methods and removing any copy/pasted code. + + 3 + + + + + + + + Methods with numerous parameters are a challenge to maintain, especially if most of them share the + same datatype. These situations usually denote the need for new objects to wrap the numerous parameters. + + 3 + + + + + + + + Classes with large numbers of public methods and attributes require disproportionate testing efforts + since combinational side effects grow rapidly and increase risk. Refactoring these classes into + smaller ones not only increases testability and reliability but also allows new variations to be + developed easily. + + 3 + + + + + + + + If a final field is assigned to a compile-time constant, it could be made static, thus saving overhead + in each object at runtime. + + 3 + + + + + + + + + + + + + + + The God Class rule detects the God Class design flaw using metrics. God classes do too many things, + are very big and overly complex. They should be split apart to be more object-oriented. + The rule uses the detection strategy described in "Object-Oriented Metrics in Practice". + The violations are reported against the entire class. + + See also the references: + + Michele Lanza and Radu Marinescu. Object-Oriented Metrics in Practice: + Using Software Metrics to Characterize, Evaluate, and Improve the Design + of Object-Oriented Systems. Springer, Berlin, 1 edition, October 2006. Page 80. + + 3 + + + + + Identifies private fields whose values never change once object initialization ends either in the declaration + of the field or by a constructor. This helps in converting existing classes to becoming immutable ones. + + 3 + + + + + + + + The Law of Demeter is a simple rule, that says "only talk to friends". It helps to reduce coupling between classes + or objects. + + See also the references: + + * Andrew Hunt, David Thomas, and Ward Cunningham. The Pragmatic Programmer. From Journeyman to Master. Addison-Wesley Longman, Amsterdam, October 1999.; + * K.J. Lieberherr and I.M. Holland. Assuring good style for object-oriented programs. Software, IEEE, 6(5):38–48, 1989.; + * <http://www.ccs.neu.edu/home/lieber/LoD.html> + * <http://en.wikipedia.org/wiki/Law_of_Demeter> + + 3 + + + + + + + + Use opposite operator instead of negating the whole expression with a logic complement operator. + + 3 + + + + + + + + + = + return false; + } + + return true; +} +]]> + + + + + + Avoid using classes from the configured package hierarchy outside of the package hierarchy, + except when using one of the configured allowed classes. + + 3 + + + + + + + + Complexity directly affects maintenance costs is determined by the number of decision points in a method + plus one for the method entry. The decision points include 'if', 'while', 'for', and 'case labels' calls. + Generally, numbers ranging from 1-4 denote low complexity, 5-7 denote moderate complexity, 8-10 denote + high complexity, and 11+ is very high complexity. Modified complexity treats switch statements as a single + decision point. + + This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced + by the rule {% rule java/design/CyclomaticComplexity %}. + + 3 + + + + + + + + This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines + of code for a given constructor. NCSS ignores comments, and counts actual statements. Using this algorithm, + lines of code that are split are counted as one. + + This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced + by the rule {% rule java/design/NcssCount %}. + + 3 + + + + + + + + This rule uses the NCSS (Non-Commenting Source Statements) metric to determine the number of lines + of code in a class, method or constructor. NCSS ignores comments, blank lines, and only counts actual + statements. For more details on the calculation, see the documentation of + the [NCSS metric](/pmd_java_metrics_index.html#non-commenting-source-statements-ncss). + + 3 + + + + + + + + This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines + of code for a given method. NCSS ignores comments, and counts actual statements. Using this algorithm, + lines of code that are split are counted as one. + + This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced + by the rule {% rule java/design/NcssCount %}. + + 3 + + + + + + + + This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines + of code for a given type. NCSS ignores comments, and counts actual statements. Using this algorithm, + lines of code that are split are counted as one. + + This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced + by the rule {% rule java/design/NcssCount %}. + + 3 + + + + + + + + The NPath complexity of a method is the number of acyclic execution paths through that method. + While cyclomatic complexity counts the number of decision points in a method, NPath counts the number of + full paths from the beginning to the end of the block of the method. That metric grows exponentially, as + it multiplies the complexity of statements in the same block. For more details on the calculation, see the + documentation of the [NPath metric](/pmd_java_metrics_index.html#npath-complexity-npath). + + A threshold of 200 is generally considered the point where measures should be taken to reduce + complexity and increase readability. + + 3 + + + + + + + + A method/constructor shouldn't explicitly throw the generic java.lang.Exception, since it + is unclear which exceptions that can be thrown from the methods. It might be + difficult to document and understand such vague interfaces. Use either a class + derived from RuntimeException or a checked exception. + + 3 + + + + + + + + + + 3 + + + + + + + + + + + + + + + Avoid negation in an assertTrue or assertFalse test. + + For example, rephrase: + + assertTrue(!expr); + + as: + + assertFalse(expr); + + + 3 + + + + + + + + + + + + + + + Avoid unnecessary comparisons in boolean expressions, they serve no purpose and impacts readability. + + 3 + + + + + + + + + + + + + + + Avoid unnecessary if-then-else statements when returning a boolean. The result of + the conditional test can be returned instead. + + 3 + + + + + + + + No need to check for null before an instanceof; the instanceof keyword returns false when given a null argument. + + 3 + + + + + + + + + + + + + + + Fields whose scopes are limited to just single methods do not rely on the containing + object to provide them to other methods. They may be better implemented as local variables + within those methods. + + 3 + + + + + + + + Complexity directly affects maintenance costs is determined by the number of decision points in a method + plus one for the method entry. The decision points include 'if', 'while', 'for', and 'case labels' calls. + Generally, numbers ranging from 1-4 denote low complexity, 5-7 denote moderate complexity, 8-10 denote + high complexity, and 11+ is very high complexity. + + This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced + by the rule {% rule java/design/CyclomaticComplexity %}. + + 3 + + + + + + + + A high ratio of statements to labels in a switch statement implies that the switch statement + is overloaded. Consider moving the statements into new methods or creating subclasses based + on the switch variable. + + 3 + + + + + + + + Classes that have too many fields can become unwieldy and could be redesigned to have fewer fields, + possibly through grouping related fields in new objects. For example, a class with individual + city/state/zip fields could park them within a single Address field. + + 3 + + + + + + + + A class with too many methods is probably a good suspect for refactoring, in order to reduce its + complexity and find a way to have more fine grained objects. + + 3 + + + + + + $maxmethods + ] +]]> + + + + + + + + The overriding method merely calls the same method defined in a superclass. + + 3 + + + + + + + + When you write a public method, you should be thinking in terms of an API. If your method is public, it means other class + will use it, therefore, you want (or need) to offer a comprehensive and evolutive API. If you pass a lot of information + as a simple series of Strings, you may think of using an Object to represent all those information. You'll get a simpler + API (such as doWork(Workload workload), rather than a tedious series of Strings) and more importantly, if you need at some + point to pass extra data, you'll be able to do so by simply modifying or extending Workload without any modification to + your API. + + 3 + + + + 3 +] +]]> + + + + + + + + + + + For classes that only have static methods, consider making them utility classes. + Note that this doesn't apply to abstract classes, since their subclasses may + well include non-static methods. Also, if you want this class to be a utility class, + remember to add a private constructor to prevent instantiation. + (Note, that this use was known before PMD 5.1.0 as UseSingleton). + + 3 + + + + + + diff --git a/gradle/quality/pmd/category/java/documentation.xml b/gradle/quality/pmd/category/java/documentation.xml new file mode 100644 index 0000000..34b351a --- /dev/null +++ b/gradle/quality/pmd/category/java/documentation.xml @@ -0,0 +1,144 @@ + + + + + + Rules that are related to code documentation. + + + + + A rule for the politically correct... we don't want to offend anyone. + + 3 + + + + + + + + Denotes whether comments are required (or unwanted) for specific language elements. + + 3 + + + + + + + + Determines whether the dimensions of non-header comments found are within the specified limits. + + 3 + + + + + + + + Uncommented Empty Constructor finds instances where a constructor does not + contain statements, but there is no comment. By explicitly commenting empty + constructors it is easier to distinguish between intentional (commented) + and unintentional empty constructors. + + 3 + + + + + + + + + + + + + + + + Uncommented Empty Method Body finds instances where a method body does not contain + statements, but there is no comment. By explicitly commenting empty method bodies + it is easier to distinguish between intentional (commented) and unintentional + empty methods. + + 3 + + + + + + + + + + + + + \ No newline at end of file diff --git a/gradle/quality/pmd/category/java/errorprone.xml b/gradle/quality/pmd/category/java/errorprone.xml new file mode 100644 index 0000000..5ee4e89 --- /dev/null +++ b/gradle/quality/pmd/category/java/errorprone.xml @@ -0,0 +1,3383 @@ + + + + + + Rules to detect constructs that are either broken, extremely confusing or prone to runtime errors. + + + + + Avoid assignments in operands; this can make code more complicated and harder to read. + + 3 + + + + + + + + Identifies a possible unsafe usage of a static field. + + 3 + + + + + + + + Methods such as getDeclaredConstructors(), getDeclaredConstructor(Class[]) and setAccessible(), + as the interface PrivilegedAction, allow for the runtime alteration of variable, class, or + method visibility, even if they are private. This violates the principle of encapsulation. + + 3 + + + + + + + + + + + + + + + Use of the term 'assert' will conflict with newer versions of Java since it is a reserved word. + + 2 + + + //VariableDeclaratorId[@Image='assert'] + + + + + + + + + + Using a branching statement as the last message of a loop may be a bug, and/or is confusing. + Ensure that the usage is not a bug, or consider using another approach. + + 2 + + 25) { + break; + } +} +]]> + + + + + + The method Object.finalize() is called by the garbage collector on an object when garbage collection determines + that there are no more references to the object. It should not be invoked by application logic. + + Note that Oracle has declared Object.finalize() as deprecated since JDK 9. + + 3 + + + + + + + + Code should never throw NullPointerExceptions under normal circumstances. A catch block may hide the + original error, causing other, more subtle problems later on. + + 3 + + + + + + + + + + + + + + + Catching Throwable errors is not recommended since its scope is very broad. It includes runtime issues such as + OutOfMemoryError that should be exposed and managed separately. + + 3 + + + + + + + + One might assume that the result of "new BigDecimal(0.1)" is exactly equal to 0.1, but it is actually + equal to .1000000000000000055511151231257827021181583404541015625. + This is because 0.1 cannot be represented exactly as a double (or as a binary fraction of any finite + length). Thus, the long value that is being passed in to the constructor is not exactly equal to 0.1, + appearances notwithstanding. + + The (String) constructor, on the other hand, is perfectly predictable: 'new BigDecimal("0.1")' is + exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the + (String) constructor be used in preference to this one. + + 3 + + + + + + + + + + + + + + + Code containing duplicate String literals can usually be improved by declaring the String as a constant field. + + 3 + + + + + + + + Use of the term 'enum' will conflict with newer versions of Java since it is a reserved word. + + 2 + + + //VariableDeclaratorId[@Image='enum'] + + + + + + + + + + It can be confusing to have a field name with the same name as a method. While this is permitted, + having information (field) and actions (method) is not clear naming. Developers versed in + Smalltalk often prefer this approach as the methods denote accessor methods. + + 3 + + + + + + + + It is somewhat confusing to have a field name matching the declaring class name. + This probably means that type and/or field names should be chosen more carefully. + + 3 + + + + + + + + Each caught exception type should be handled in its own catch clause. + + 3 + + + + + + + + + + + + + + + Avoid using hard-coded literals in conditional statements. By declaring them as static variables + or private members with descriptive names maintainability is enhanced. By default, the literals "-1" and "0" are ignored. + More exceptions can be defined with the property "ignoreMagicNumbers". + + 3 + + + + + + + + + + + = 0) { } // alternative approach + + if (aDouble > 0.0) {} // magic number 0.0 + if (aDouble >= Double.MIN_VALUE) {} // preferred approach +} +]]> + + + + + + Statements in a catch block that invoke accessors on the exception without using the information + only add to code size. Either remove the invocation, or use the return result. + + 2 + + + + + + + + + + + + + + + The use of multiple unary operators may be problematic, and/or confusing. + Ensure that the intended usage is not a bug, or consider simplifying the expression. + + 2 + + + + + + + + Integer literals should not start with zero since this denotes that the rest of literal will be + interpreted as an octal value. + + 3 + + + + + + + + Avoid equality comparisons with Double.NaN. Due to the implicit lack of representation + precision when comparing floating point numbers these are likely to cause logic errors. + + 3 + + + + + + + + + + + + + + + If a class is a bean, or is referenced by a bean directly or indirectly it needs to be serializable. + Member variables need to be marked as transient, static, or have accessor methods in the class. Marking + variables as transient is the safest and easiest modification. Accessor methods should follow the Java + naming conventions, i.e. for a variable named foo, getFoo() and setFoo() accessor methods should be provided. + + 3 + + + + + + + + The null check is broken since it will throw a NullPointerException itself. + It is likely that you used || instead of && or vice versa. + + 2 + + + + + + + Super should be called at the start of the method + 3 + + + + + + + + + + + + + + + Super should be called at the end of the method + + 3 + + + + + + + + + + + + + + + The skip() method may skip a smaller number of bytes than requested. Check the returned value to find out if it was the case or not. + + 3 + + + + + + + + When deriving an array of a specific class from your Collection, one should provide an array of + the same class as the parameter of the toArray() method. Doing otherwise you will will result + in a ClassCastException. + + 3 + + + + + + + + + + + + + + + The java Manual says "By convention, classes that implement this interface should override + Object.clone (which is protected) with a public method." + + 3 + + + + + + + + + + + + + + + The method clone() should only be implemented if the class implements the Cloneable interface with the exception of + a final method that only throws CloneNotSupportedException. + + The rule can also detect, if the class implements or extends a Cloneable class. + + 3 + + + + + + + + If a class implements cloneable the return type of the method clone() must be the class name. That way, the caller + of the clone method doesn't need to cast the returned clone to the correct type. + + Note: This is only possible with Java 1.5 or higher. + + 3 + + + + + + + + + + + + + + + The method clone() should throw a CloneNotSupportedException. + + 3 + + + + + + + + + + + + + + + Ensure that resources (like Connection, Statement, and ResultSet objects) are always closed after use. + + 3 + + + + + + + + Use equals() to compare object references; avoid comparing them with ==. + + 3 + + + + + + + + Calling overridable methods during construction poses a risk of invoking methods on an incompletely + constructed object and can be difficult to debug. + It may leave the sub-class unable to construct its superclass or forced to replicate the construction + process completely within itself, losing the ability to call super(). If the default constructor + contains a call to an overridable method, the subclass may be completely uninstantiable. Note that + this includes method calls throughout the control flow graph - i.e., if a constructor Foo() calls a + private method bar() that calls a public method buz(), this denotes a problem. + + 1 + + + + + + + The dataflow analysis tracks local definitions, undefinitions and references to variables on different paths on the data flow. + From those informations there can be found various problems. + + 1. UR - Anomaly: There is a reference to a variable that was not defined before. This is a bug and leads to an error. + 2. DU - Anomaly: A recently defined variable is undefined. These anomalies may appear in normal source text. + 3. DD - Anomaly: A recently defined variable is redefined. This is ominous but don't have to be a bug. + + 5 + + dd-anomaly + foo(buz); + buz = 2; +} // buz is undefined when leaving scope -> du-anomaly +]]> + + + + + + Calls to System.gc(), Runtime.getRuntime().gc(), and System.runFinalization() are not advised. Code should have the + same behavior whether the garbage collection is disabled using the option -Xdisableexplicitgc or not. + Moreover, "modern" jvms do a very good job handling garbage collections. If memory usage issues unrelated to memory + leaks develop within an application, it should be dealt with JVM options rather than within the code itself. + + 2 + + + + + + + + + + + + + + + Web applications should not call System.exit(), since only the web container or the + application server should stop the JVM. This rule also checks for the equivalent call Runtime.getRuntime().exit(). + + 3 + + + + + + + + + + + + + + + Extend Exception or RuntimeException instead of Throwable. + + 3 + + + + + + + + + + + + + + + Use Environment.getExternalStorageDirectory() instead of "/sdcard" + + 3 + + + //Literal[starts-with(@Image,'"/sdcard')] + + + + + + + + + + Throwing exceptions within a 'finally' block is confusing since they may mask other exceptions + or code defects. + Note: This is a PMD implementation of the Lint4j rule "A throw in a finally block" + + 4 + + + //FinallyStatement[descendant::ThrowStatement] + + + + + + + + + + Avoid importing anything from the 'sun.*' packages. These packages are not portable and are likely to change. + + 4 + + + + + + + + Don't use floating point for loop indices. If you must use floating point, use double + unless you're certain that float provides enough precision and you have a compelling + performance need (space or time). + + 3 + + + + + + + + + + + + + + + Empty Catch Block finds instances where an exception is caught, but nothing is done. + In most circumstances, this swallows an exception which should either be acted on + or reported. + + 3 + + + + + + + + + + + + + + + + + Empty finalize methods serve no purpose and should be removed. Note that Oracle has declared Object.finalize() as deprecated since JDK 9. + + 3 + + + + + + + + + + + + + + + Empty finally blocks serve no purpose and should be removed. + + 3 + + + + + + + + + + + + + + + Empty If Statement finds instances where a condition is checked but nothing is done about it. + + 3 + + + + + + + + + + + + + + + Empty initializers serve no purpose and should be removed. + + 3 + + + //Initializer/Block[count(*)=0] + + + + + + + + + + Empty block statements serve no purpose and should be removed. + + 3 + + + //BlockStatement/Statement/Block[count(*) = 0] + + + + + + + + + + An empty statement (or a semicolon by itself) that is not used as the sole body of a 'for' + or 'while' loop is probably a bug. It could also be a double semicolon, which has no purpose + and should be removed. + + 3 + + + + + + + + + + + + + + + Empty switch statements serve no purpose and should be removed. + + 3 + + + //SwitchStatement[count(*) = 1] + + + + + + + + + + Empty synchronized blocks serve no purpose and should be removed. + + 3 + + + //SynchronizedStatement/Block[1][count(*) = 0] + + + + + + + + + + Avoid empty try blocks - what's the point? + + 3 + + + + + + + + + + + + + + + Empty While Statement finds all instances where a while statement does nothing. + If it is a timing loop, then you should use Thread.sleep() for it; if it is + a while loop that does a lot in the exit expression, rewrite it to make it clearer. + + 3 + + + + + + + + + + + + + + + Tests for null should not use the equals() method. The '==' operator should be used instead. + + 1 + + + + + + + + + + + + + + + If the finalize() is implemented, its last action should be to call super.finalize. Note that Oracle has declared Object.finalize() as deprecated since JDK 9. + + 3 + + + + + + + + + + + + + + + + If the finalize() is implemented, it should do something besides just calling super.finalize(). Note that Oracle has declared Object.finalize() as deprecated since JDK 9. + + 3 + + + + + + + + + + + + + + + Methods named finalize() should not have parameters. It is confusing and most likely an attempt to + overload Object.finalize(). It will not be called by the VM. + + Note that Oracle has declared Object.finalize() as deprecated since JDK 9. + + 3 + + + + 0]] +]]> + + + + + + + + + + + When overriding the finalize(), the new method should be set as protected. If made public, + other classes may invoke it at inappropriate times. + + Note that Oracle has declared Object.finalize() as deprecated since JDK 9. + + 3 + + + + + + + + + + + + + + + Avoid idempotent operations - they have no effect. + + 3 + + + + + + + + There is no need to import a type that lives in the same package. + + 3 + + + + + + + + Avoid instantiating an object just to call getClass() on it; use the .class public member instead. + + 4 + + + + + + + + + + + + + + + Check for messages in slf4j loggers with non matching number of arguments and placeholders. + + 5 + + + + + + + + Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional. + + 3 + + + + + + + + + + + + + + Some JUnit framework methods are easy to misspell. + + 3 + + + + + + + + + + + + + + + The suite() method in a JUnit test needs to be both public and static. + + 3 + + + + + + + + + + + + + + + In most cases, the Logger reference can be declared as static and final. + + 2 + + + + + + + + + + + + + + + Non-constructor methods should not have the same name as the enclosing class. + + 3 + + + + + + + + The null check here is misplaced. If the variable is null a NullPointerException will be thrown. + Either the check is useless (the variable will never be "null") or it is incorrect. + + 3 + + + + + + + + + + + + + + + + + + Switch statements without break or return statements for each case option + may indicate problematic behaviour. Empty cases are ignored as these indicate an intentional fall-through. + + 3 + + + + + + + + + + + + + + + Serializable classes should provide a serialVersionUID field. + The serialVersionUID field is also needed for abstract base classes. Each individual class in the inheritance + chain needs an own serialVersionUID field. See also [Should an abstract class have a serialVersionUID](https://stackoverflow.com/questions/893259/should-an-abstract-class-have-a-serialversionuid). + + 3 + + + + + + + + + + + + + + + A class that has private constructors and does not have any static methods or fields cannot be used. + + 3 + + + + + + + + + + + + + + + Normally only one logger is used in each class. + + 2 + + + + + + + + A non-case label (e.g. a named break/continue label) was present in a switch statement. + This legal, but confusing. It is easy to mix up the case labels and the non-case labels. + + 3 + + + //SwitchStatement//BlockStatement/Statement/LabeledStatement + + + + + + + + + + A non-static initializer block will be called any time a constructor is invoked (just prior to + invoking the constructor). While this is a valid language construct, it is rarely used and is + confusing. + + 3 + + + + + + + + + + + + + + + Assigning a "null" to a variable (outside of its declaration) is usually bad form. Sometimes, this type + of assignment is an indication that the programmer doesn't completely understand what is going on in the code. + + NOTE: This sort of assignment may used in some cases to dereference objects and encourage garbage collection. + + 3 + + + + + + + + Override both public boolean Object.equals(Object other), and public int Object.hashCode(), or override neither. Even if you are inheriting a hashCode() from a parent class, consider implementing hashCode and explicitly delegating to your superclass. + + 3 + + + + + + + + Object clone() should be implemented with super.clone(). + + 2 + + + + 0 +] +]]> + + + + + + + + + + + A logger should normally be defined private static final and be associated with the correct class. + Private final Log log; is also allowed for rare cases where loggers need to be passed around, + with the restriction that the logger needs to be passed into the constructor. + + 3 + + + + + + + + + + + + + + + + For any method that returns an array, it is a better to return an empty array rather than a + null reference. This removes the need for null checking all results and avoids inadvertent + NullPointerExceptions. + + 1 + + + + + + + + + + + + + + + Avoid returning from a finally block, this can discard exceptions. + + 3 + + + + //FinallyStatement//ReturnStatement except //FinallyStatement//(MethodDeclaration|LambdaExpression)//ReturnStatement + + + + + + + + + + Be sure to specify a Locale when creating SimpleDateFormat instances to ensure that locale-appropriate + formatting is used. + + 3 + + + + + + + + + + + + + + + Some classes contain overloaded getInstance. The problem with overloaded getInstance methods + is that the instance created using the overloaded method is not cached and so, + for each call and new objects will be created for every invocation. + + 2 + + + + + + + + Some classes contain overloaded getInstance. The problem with overloaded getInstance methods + is that the instance created using the overloaded method is not cached and so, + for each call and new objects will be created for every invocation. + + 2 + + + + + + + + According to the J2EE specification, an EJB should not have any static fields + with write access. However, static read-only fields are allowed. This ensures proper + behavior especially when instances are distributed by the container on several JREs. + + 3 + + + + + + + + + + + + + + + Individual character values provided as initialization arguments will be converted into integers. + This can lead to internal buffer sizes that are larger than expected. Some examples: + + ``` + new StringBuffer() // 16 + new StringBuffer(6) // 6 + new StringBuffer("hello world") // 11 + 16 = 27 + new StringBuffer('A') // chr(A) = 65 + new StringBuffer("A") // 1 + 16 = 17 + + new StringBuilder() // 16 + new StringBuilder(6) // 6 + new StringBuilder("hello world") // 11 + 16 = 27 + new StringBuilder('C') // chr(C) = 67 + new StringBuilder("A") // 1 + 16 = 17 + ``` + + 4 + + + + + + + + + + + + + + + The method name and parameter number are suspiciously close to equals(Object), which can denote an + intention to override the equals(Object) method. + + 2 + + + + + + + + + + + + + + + The method name and return type are suspiciously close to hashCode(), which may denote an intention + to override the hashCode() method. + + 3 + + + + + + + + A suspicious octal escape sequence was found inside a String literal. + The Java language specification (section 3.10.6) says an octal + escape sequence inside a literal String shall consist of a backslash + followed by: + + OctalDigit | OctalDigit OctalDigit | ZeroToThree OctalDigit OctalDigit + + Any octal escape sequence followed by non-octal digits can be confusing, + e.g. "\038" is interpreted as the octal escape sequence "\03" followed by + the literal character "8". + + 3 + + + + + + + + Test classes end with the suffix Test. Having a non-test class with that name is not a good practice, + since most people will assume it is a test case. Test classes have test methods named testXXX. + + 3 + + + + + + + + Do not use "if" statements whose conditionals are always true or always false. + + 3 + + + + + + + + + + + + + + + A JUnit test assertion with a boolean literal is unnecessary since it always will evaluate to the same thing. + Consider using flow control (in case of assertTrue(false) or similar) or simply removing + statements like assertTrue(true) and assertFalse(false). If you just want a test to halt after finding + an error, use the fail() method and provide an indication message of why it did. + + 3 + + + + + + + + + + + + + + + Using equalsIgnoreCase() is faster than using toUpperCase/toLowerCase().equals() + + 3 + + + + + + + + Avoid the use temporary objects when converting primitives to Strings. Use the static conversion methods + on the wrapper classes instead. + + 3 + + + + + + + + After checking an object reference for null, you should invoke equals() on that object rather than passing it to another object's equals() method. + + 3 + + + + + + + + + + + + + + + To make sure the full stacktrace is printed out, use the logging statement with two arguments: a String and a Throwable. + + 3 + + + + + + + + + + + + + + + Using '==' or '!=' to compare strings only works if intern version is used on both sides. + Use the equals() method instead. + + 3 + + + + + + + + + + + + + + + An operation on an Immutable object (String, BigDecimal or BigInteger) won't change the object itself + since the result of the operation is a new object. Therefore, ignoring the operation result is an error. + + 3 + + + + + + + + When doing String.toLowerCase()/toUpperCase() conversions, use Locales to avoids problems with languages that + have unusual conventions, i.e. Turkish. + + 3 + + + + + + + + + + + + + + + In J2EE, the getClassLoader() method might not work as expected. Use + Thread.currentThread().getContextClassLoader() instead. + + 3 + + + //PrimarySuffix[@Image='getClassLoader'] + + + + + + + + diff --git a/gradle/quality/pmd/category/java/multithreading.xml b/gradle/quality/pmd/category/java/multithreading.xml new file mode 100644 index 0000000..d3e8327 --- /dev/null +++ b/gradle/quality/pmd/category/java/multithreading.xml @@ -0,0 +1,393 @@ + + + + + + Rules that flag issues when dealing with multiple threads of execution. + + + + + Method-level synchronization can cause problems when new code is added to the method. + Block-level synchronization helps to ensure that only the code that needs synchronization + gets it. + + 3 + + + //MethodDeclaration[@Synchronized='true'] + + + + + + + + + + Avoid using java.lang.ThreadGroup; although it is intended to be used in a threaded environment + it contains methods that are not thread-safe. + + 3 + + + + + + + + + + + + + + + Use of the keyword 'volatile' is generally used to fine tune a Java application, and therefore, requires + a good expertise of the Java Memory Model. Moreover, its range of action is somewhat misknown. Therefore, + the volatile keyword should not be used for maintenance purpose and portability. + + 2 + + + //FieldDeclaration[contains(@Volatile,'true')] + + + + + + + + + + The J2EE specification explicitly forbids the use of threads. + + 3 + + + //ClassOrInterfaceType[@Image = 'Thread' or @Image = 'Runnable'] + + + + + + + + + + Explicitly calling Thread.run() method will execute in the caller's thread of control. Instead, call Thread.start() for the intended behavior. + + 4 + + + + + + + + + + + + + + + Partially created objects can be returned by the Double Checked Locking pattern when used in Java. + An optimizing JRE may assign a reference to the baz variable before it calls the constructor of the object the + reference points to. + + Note: With Java 5, you can make Double checked locking work, if you declare the variable to be `volatile`. + + For more details refer to: <http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html> + or <http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html> + + 1 + + + + + + + + Non-thread safe singletons can result in bad state changes. Eliminate + static singletons if possible by instantiating the object directly. Static + singletons are usually not needed as only a single instance exists anyway. + Other possible fixes are to synchronize the entire method or to use an + [initialize-on-demand holder class](https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom). + + Refrain from using the double-checked locking pattern. The Java Memory Model doesn't + guarantee it to work unless the variable is declared as `volatile`, adding an uneeded + performance penalty. [Reference](http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html) + + See Effective Java, item 48. + + 3 + + + + + + + + SimpleDateFormat instances are not synchronized. Sun recommends using separate format instances + for each thread. If multiple threads must access a static formatter, the formatter must be + synchronized either on method or block level. + + This rule has been deprecated in favor of the rule {% rule UnsynchronizedStaticFormatter %}. + + 3 + + + + + + + + Instances of `java.text.Format` are generally not synchronized. + Sun recommends using separate format instances for each thread. + If multiple threads must access a static formatter, the formatter must be + synchronized either on method or block level. + + 3 + + + + + + + + Since Java5 brought a new implementation of the Map designed for multi-threaded access, you can + perform efficient map reads without blocking other threads. + + 3 + + + + + + + + + + + + + + + Thread.notify() awakens a thread monitoring the object. If more than one thread is monitoring, then only + one is chosen. The thread chosen is arbitrary; thus its usually safer to call notifyAll() instead. + + 3 + + + + + + + + + + + + + \ No newline at end of file diff --git a/gradle/quality/pmd/category/java/performance.xml b/gradle/quality/pmd/category/java/performance.xml new file mode 100644 index 0000000..1ce2d8d --- /dev/null +++ b/gradle/quality/pmd/category/java/performance.xml @@ -0,0 +1,1006 @@ + + + + + + Rules that flag suboptimal code. + + + + + The conversion of literals to strings by concatenating them with empty strings is inefficient. + It is much better to use one of the type-specific toString() methods instead. + + 3 + + + + + + + + + + + + + + + Avoid concatenating characters as strings in StringBuffer/StringBuilder.append methods. + + 3 + + + + + + + + Instead of manually copying data between two arrays, use the efficient Arrays.copyOf or System.arraycopy method instead. + + 3 + + + + + + + + + + + + + + + The FileInputStream and FileOutputStream classes contains a finalizer method which will cause garbage + collection pauses. + See [JDK-8080225](https://bugs.openjdk.java.net/browse/JDK-8080225) for details. + + The FileReader and FileWriter constructors instantiate FileInputStream and FileOutputStream, + again causing garbage collection issues while finalizer methods are called. + + * Use `Files.newInputStream(Paths.get(fileName))` instead of `new FileInputStream(fileName)`. + * Use `Files.newOutputStream(Paths.get(fileName))` instead of `new FileOutputStream(fileName)`. + * Use `Files.newBufferedReader(Paths.get(fileName))` instead of `new FileReader(fileName)`. + * Use `Files.newBufferedWriter(Paths.get(fileName))` instead of `new FileWriter(fileName)`. + + Please note, that the `java.nio` API does not throw a `FileNotFoundException` anymore, instead + it throws a `NoSuchFileException`. If your code dealt explicitly with a `FileNotFoundException`, + then this needs to be adjusted. Both exceptions are subclasses of `IOException`, so catching + that one covers both. + + 1 + + + + + + + + + + + + + + + New objects created within loops should be checked to see if they can created outside them and reused. + + 3 + + + + + + + + Java uses the 'short' type to reduce memory usage, not to optimize calculation. In fact, the JVM does not have any + arithmetic capabilities for the short type: the JVM must convert the short into an int, do the proper calculation + and convert the int back to a short. Thus any storage gains found through use of the 'short' type may be offset by + adverse impacts on performance. + + 1 + + + + + + + + + + + + + + + Don't create instances of already existing BigInteger (BigInteger.ZERO, BigInteger.ONE) and + for Java 1.5 onwards, BigInteger.TEN and BigDecimal (BigDecimal.ZERO, BigDecimal.ONE, BigDecimal.TEN) + + 3 + + + + + + + + Avoid instantiating Boolean objects; you can reference Boolean.TRUE, Boolean.FALSE, or call Boolean.valueOf() instead. + Note that new Boolean() is deprecated since JDK 9 for that reason. + + 2 + + + + + + + + Calling new Byte() causes memory allocation that can be avoided by the static Byte.valueOf(). + It makes use of an internal cache that recycles earlier instances making it more memory efficient. + Note that new Byte() is deprecated since JDK 9 for that reason. + + 2 + + + + + + + + + + + + + + + Consecutive calls to StringBuffer/StringBuilder .append should be chained, reusing the target object. This can improve the performance + by producing a smaller bytecode, reducing overhead and improving inlining. A complete analysis can be found [here](https://github.com/pmd/pmd/issues/202#issuecomment-274349067) + + 3 + + + + + + + + Consecutively calling StringBuffer/StringBuilder.append(...) with literals should be avoided. + Since the literals are constants, they can already be combined into a single String literal and this String + can be appended in a single method call. + + 3 + + + + + + + + + + 3 + + 0) { + doSomething(); + } +} +]]> + + + + + + Avoid concatenating non-literals in a StringBuffer constructor or append() since intermediate buffers will + need to be be created and destroyed by the JVM. + + 3 + + + + + + + + Failing to pre-size a StringBuffer or StringBuilder properly could cause it to re-size many times + during runtime. This rule attempts to determine the total number the characters that are actually + passed into StringBuffer.append(), but represents a best guess "worst case" scenario. An empty + StringBuffer/StringBuilder constructor initializes the object to 16 characters. This default + is assumed if the length of the constructor can not be determined. + + 3 + + + + + + + + Calling new Integer() causes memory allocation that can be avoided by the static Integer.valueOf(). + It makes use of an internal cache that recycles earlier instances making it more memory efficient. + Note that new Integer() is deprecated since JDK 9 for that reason. + + 2 + + + + + + + + + + + + + + + Calling new Long() causes memory allocation that can be avoided by the static Long.valueOf(). + It makes use of an internal cache that recycles earlier instances making it more memory efficient. + Note that new Long() is deprecated since JDK 9 for that reason. + + 2 + + + + + + + + + + + + + + + Calls to a collection's `toArray(E[])` method should specify a target array of zero size. This allows the JVM + to optimize the memory allocation and copying as much as possible. + + Previous versions of this rule (pre PMD 6.0.0) suggested the opposite, but current JVM implementations + perform always better, when they have full control over the target array. And allocation an array via + reflection is nowadays as fast as the direct allocation. + + See also [Arrays of Wisdom of the Ancients](https://shipilev.net/blog/2016/arrays-wisdom-ancients/) + + Note: If you don't need an array of the correct type, then the simple `toArray()` method without an array + is faster, but returns only an array of type `Object[]`. + + 3 + + + + + + + + + foos = getFoos(); + +// much better; this one allows the jvm to allocate an array of the correct size and effectively skip +// the zeroing, since each array element will be overridden anyways +Foo[] fooArray = foos.toArray(new Foo[0]); + +// inefficient, the array needs to be zeroed out by the jvm before it is handed over to the toArray method +Foo[] fooArray = foos.toArray(new Foo[foos.size()]); +]]> + + + + + + Java will initialize fields with known default values so any explicit initialization of those same defaults + is redundant and results in a larger class file (approximately three additional bytecode instructions per field). + + 3 + + + + + + + + Since it passes in a literal of length 1, calls to (string).startsWith can be rewritten using (string).charAt(0) + at the expense of some readability. + + 3 + + + + + + + + + + + + + + + Calling new Short() causes memory allocation that can be avoided by the static Short.valueOf(). + It makes use of an internal cache that recycles earlier instances making it more memory efficient. + Note that new Short() is deprecated since JDK 9 for that reason. + + 2 + + + + + + + + + + + + + + + Avoid instantiating String objects; this is usually unnecessary since they are immutable and can be safely shared. + + 2 + + + + + + + + Avoid calling toString() on objects already known to be string instances; this is unnecessary. + + 3 + + + + + + + + Switch statements are intended to be used to support complex branching behaviour. Using a switch for only a few + cases is ill-advised, since switches are not as easy to understand as if-then statements. In these cases use the + if-then statement to increase code readability. + + 3 + + + + + + + + + + + + + + + + Most wrapper classes provide static conversion methods that avoid the need to create intermediate objects + just to create the primitive forms. Using these avoids the cost of creating objects that also need to be + garbage-collected later. + + 3 + + + + + + + + ArrayList is a much better Collection implementation than Vector if thread-safe operation is not required. + + 3 + + + + 0] + //AllocationExpression/ClassOrInterfaceType + [@Image='Vector' or @Image='java.util.Vector'] +]]> + + + + + + + + + + + (Arrays.asList(...)) if that is inconvenient for you (e.g. because of concurrent access). +]]> + + 3 + + + + + + + + + l= new ArrayList<>(100); + for (int i=0; i< 100; i++) { + l.add(ints[i]); + } + for (int i=0; i< 100; i++) { + l.add(a[i].toString()); // won't trigger the rule + } + } +} +]]> + + + + + + Use String.indexOf(char) when checking for the index of a single character; it executes faster. + + 3 + + + + + + + + No need to call String.valueOf to append to a string; just use the valueOf() argument directly. + + 3 + + + + + + + + The use of the '+=' operator for appending strings causes the JVM to create and use an internal StringBuffer. + If a non-trivial number of these concatenations are being used then the explicit use of a StringBuilder or + threadsafe StringBuffer is recommended to avoid this. + + 3 + + + + + + + + Use StringBuffer.length() to determine StringBuffer length rather than using StringBuffer.toString().equals("") + or StringBuffer.toString().length() == ... + + 3 + + + + + + + + + diff --git a/gradle/quality/pmd/category/java/security.xml b/gradle/quality/pmd/category/java/security.xml new file mode 100644 index 0000000..dbad352 --- /dev/null +++ b/gradle/quality/pmd/category/java/security.xml @@ -0,0 +1,65 @@ + + + + + + Rules that flag potential security flaws. + + + + + Do not use hard coded values for cryptographic operations. Please store keys outside of source code. + + 3 + + + + + + + + Do not use hard coded initialization vector in cryptographic operations. Please use a randomly generated IV. + + 3 + + + + + + diff --git a/gradle/quality/sonarqube.gradle b/gradle/quality/sonarqube.gradle new file mode 100644 index 0000000..fe66cd0 --- /dev/null +++ b/gradle/quality/sonarqube.gradle @@ -0,0 +1,37 @@ + +subprojects { + + sonarqube { + properties { + property "sonar.projectName", "${project.group} ${project.name}" + property "sonar.sourceEncoding", "UTF-8" + property "sonar.tests", "src/test/java" + property "sonar.scm.provider", "git" + property "sonar.junit.reportsPath", "build/test-results/test/" + } + } + + + tasks.withType(Pmd) { + ignoreFailures = true + reports { + xml.enabled = true + html.enabled = true + } + } + + + spotbugs { + effort = "max" + reportLevel = "low" + //includeFilter = file("findbugs-exclude.xml") + } + + tasks.withType(com.github.spotbugs.SpotBugsTask) { + ignoreFailures = true + reports { + xml.enabled = false + html.enabled = true + } + } +} \ No newline at end of file diff --git a/gradle/quality/spotbugs.gradle b/gradle/quality/spotbugs.gradle index 8e671e2..2e5b0cd 100644 --- a/gradle/quality/spotbugs.gradle +++ b/gradle/quality/spotbugs.gradle @@ -1,53 +1,15 @@ -/* + +apply plugin: 'com.github.spotbugs' + spotbugs { effort = "max" reportLevel = "low" + ignoreFailures = true } -tasks.withType(com.github.spotbugs.SpotBugsTask) { - ignoreFailures = true +spotbugsMain { reports { - xml.enabled = false - html.enabled = true + xml.getRequired().set(false) + html.getRequired().set(true) } } - -pmd { - toolVersion = '6.11.0' - ruleSets = ['category/java/bestpractices.xml'] -} - -tasks.withType(Pmd) { - ignoreFailures = true - reports { - xml.enabled = true - html.enabled = true - } -} - -checkstyle { - toolVersion = '8.26' - configFile = rootProject.file('config/checkstyle/checkstyle.xml') - ignoreFailures = true - checkstyleMain { - source = sourceSets.main.allSource - } -} - -tasks.withType(Checkstyle) { - ignoreFailures = true - reports { - xml.enabled = true - html.enabled = true - } -} - -sonarqube { - properties { - property "sonar.projectName", "${project.group} ${project.name}" - property "sonar.sourceEncoding", "UTF-8" - property "sonar.tests", "src/test/java" - property "sonar.scm.provider", "git" - } -} -*/ \ No newline at end of file diff --git a/gradle/test/junit5.gradle b/gradle/test/junit5.gradle index 10ad7d2..3a9a9dd 100644 --- a/gradle/test/junit5.gradle +++ b/gradle/test/junit5.gradle @@ -12,7 +12,7 @@ dependencies { test { useJUnitPlatform() - failFast = true + failFast = false testLogging { events 'STARTED', 'PASSED', 'FAILED', 'SKIPPED' } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..c1962a79e29d3e0ab67b14947c167a862655af9b 100644 GIT binary patch delta 42507 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 AFaQ7m delta 39488 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 SjTjh$z-I$+9LbEIUi}{|+iFq( diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 33682bb..8707e8b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0..aeb74cb 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,98 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +118,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +129,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +137,109 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index ac1b06f..6689b85 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/settings.gradle b/settings.gradle index 89f4110..cb29c87 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,49 @@ -rootProject.name = name +pluginManagement { + repositories { + mavenLocal() + mavenCentral { + metadataSources { + mavenPom() + artifact() + ignoreGradleMetadataRedirection() + } + } + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + versionCatalogs { + libs { + version('gradle', '8.1.1') + version('groovy', '3.0.10') + version('junit', '5.9.2') + library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit') + library('junit-jupiter-params', 'org.junit.jupiter', 'junit-jupiter-params').versionRef('junit') + library('junit-jupiter-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit') + library('hamcrest', 'org.hamcrest', 'hamcrest-library').version('2.2') + library('junit4', 'junit', 'junit').version('4.13.2') + library('javax.inject', 'org.xbib', 'javax-inject').version('1') + library('guava', 'org.xbib', 'guava').version('30.1') + library('guava.testlib', 'com.google.guava', 'guava-testlib').version('30.1-jre') + } + } +} + +/* +gradle.wrapper.version = 6.6.1 +javax-inject.version = 1 +guava.version = 30.1 +# test +junit.version = 5.7.2 +junit4.version = 4.13.2 +log4j.version = 2.14.1 + + api "org.xbib:javax-inject:${project.property('javax-inject.version')}" + api "org.xbib:guava:${project.property('guava.version')}" + testImplementation "junit:junit:${project.property('junit4.version')}" + // Helper for com.google.common.testing.GcFinalization, com.google.common.testing.EqualsTester + testImplementation "com.google.guava:guava-testlib:30.1-jre" + + + */ \ No newline at end of file diff --git a/src/main/java/com/google/inject/name/NamedImpl.java b/src/main/java/com/google/inject/name/NamedImpl.java index 265d74a..d23a69b 100644 --- a/src/main/java/com/google/inject/name/NamedImpl.java +++ b/src/main/java/com/google/inject/name/NamedImpl.java @@ -41,6 +41,6 @@ class NamedImpl implements Named { @Override public String toString() { - return "@" + Named.class.getName() + "(value=" + Annotations.memberValueString(value) + ")"; + return "@" + Named.class.getName() + "(" + Annotations.memberValueString(value) + ")"; } } diff --git a/src/main/java/com/google/inject/spi/ElementSource.java b/src/main/java/com/google/inject/spi/ElementSource.java index 754f054..575ad02 100644 --- a/src/main/java/com/google/inject/spi/ElementSource.java +++ b/src/main/java/com/google/inject/spi/ElementSource.java @@ -1,9 +1,6 @@ package com.google.inject.spi; import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.inject.internal.util.StackTraceElements; -import com.google.inject.internal.util.StackTraceElements.InMemoryStackTraceElement; import java.util.List; /** @@ -13,21 +10,6 @@ import java.util.List; * defines the Guice {@link Element element}. For example, if the element is created from a method * annotated by {@literal @Provides}, the declaring source of element would be the method itself. * - *

The {@link #getStackTrace()} refers to the sequence of calls ends at one of {@link - * com.google.inject.Binder} {@code bindXXX()} methods and eventually defines the element. Note that - * {@link #getStackTrace()} lists {@link StackTraceElement StackTraceElements} in reverse - * chronological order. The first element (index zero) is the last method call and the last element - * is the first method invocation. By default, the stack trace is not collected. The default - * behavior can be changed by setting the {@code guice_include_stack_traces} flag value. The value - * can be either {@code OFF}, {@code ONLY_FOR_DECLARING_SOURCE} or {@code COMPLETE}. Note that - * collecting stack traces for every binding can cause a performance hit when the injector is - * created. - * - *

The sequence of class names of {@link com.google.inject.Module modules} involved in the - * element creation can be retrieved by {@link #getModuleClassNames()}. Similar to {@link - * #getStackTrace()}, the order is reverse chronological. The first module (index 0) is the module - * that installs the {@link Element element}. The last module is the root module. - * *

In order to support the cases where a Guice {@link Element element} is created from another * Guice {@link Element element} (original) (e.g., by {@link Element#applyTo}), it also provides a * reference to the original element source ({@link #getOriginalElementSource()}). @@ -54,12 +36,6 @@ public final class ElementSource { /** The {@link ModuleSource source} of module creates the element. */ final ModuleSource moduleSource; - /** - * The partial call stack that starts at the last module {@link Module#Configure(Binder) - * configure(Binder)} call. The value is empty if stack trace collection is off. - */ - final InMemoryStackTraceElement[] partialCallStack; - /** * Refers to a single location in source code that causes the element creation. It can be any * object such as {@link Constructor}, {@link Method}, {@link Field}, {@link StackTraceElement}, @@ -78,24 +54,19 @@ public final class ElementSource { * any), otherwise {@code null}. * @param declaringSource the source (in)directly declared the element. * @param moduleSource the moduleSource when the element is bound - * @param partialCallStack the partial call stack from the top module to where the element is - * bound */ ElementSource( ElementSource originalSource, boolean trustedOriginalSource, Object declaringSource, ModuleSource moduleSource, - StackTraceElement[] partialCallStack, ModuleAnnotatedMethodScanner scanner) { Preconditions.checkNotNull(declaringSource, "declaringSource cannot be null."); Preconditions.checkNotNull(moduleSource, "moduleSource cannot be null."); - Preconditions.checkNotNull(partialCallStack, "partialCallStack cannot be null."); this.originalElementSource = originalSource; this.trustedOriginalElementSource = trustedOriginalSource; this.declaringSource = declaringSource; this.moduleSource = moduleSource; - this.partialCallStack = StackTraceElements.convertToInMemoryStackTraceElement(partialCallStack); this.scanner = scanner; } @@ -127,60 +98,6 @@ public final class ElementSource { return moduleSource.getModuleClassNames(); } - /** - * Returns the position of {@link com.google.inject.Module#configure configure(Binder)} method - * call in the {@link #getStackTrace stack trace} for modules that their classes returned by - * {@link #getModuleClassNames}. For example, if the stack trace looks like the following: - * - *

    - *
  1. {@code Binder.bind()} - *
  2. {@code ModuleTwo.configure()} - *
  3. {@code Binder.install()} - *
  4. {@code ModuleOne.configure()} - *
  5. {@code theRest(). - *
- * - *

1 and 3 are returned. - * - *

In the cases where stack trace is not available (i.e., the stack trace was not collected), - * it returns -1 for all module positions. - */ - public List getModuleConfigurePositionsInStackTrace() { - int size = moduleSource.size(); - Integer[] positions = new Integer[size]; - int chunkSize = partialCallStack.length; - positions[0] = chunkSize - 1; - ModuleSource current = moduleSource; - for (int cursor = 1; cursor < size; cursor++) { - chunkSize = current.getPartialCallStackSize(); - positions[cursor] = positions[cursor - 1] + chunkSize; - current = current.getParent(); - } - return ImmutableList.copyOf(positions); - } - - /** - * Returns the sequence of method calls that ends at one of {@link com.google.inject.Binder} - * {@code bindXXX()} methods and eventually defines the element. Note that {@link #getStackTrace} - * lists {@link StackTraceElement StackTraceElements} in reverse chronological order. The first - * element (index zero) is the last method call and the last element is the first method - * invocation. In the cases where stack trace is not available (i.e.,the stack trace was not - * collected), it returns an empty array. - */ - public StackTraceElement[] getStackTrace() { - int modulesCallStackSize = moduleSource.getStackTraceSize(); - int chunkSize = partialCallStack.length; - int size = moduleSource.getStackTraceSize() + chunkSize; - StackTraceElement[] callStack = new StackTraceElement[size]; - System.arraycopy( - StackTraceElements.convertToStackTraceElement(partialCallStack), - 0, - callStack, - 0, - chunkSize); - System.arraycopy(moduleSource.getStackTrace(), 0, callStack, chunkSize, modulesCallStackSize); - return callStack; - } /** Returns {@code getDeclaringSource().toString()} value. */ @Override diff --git a/src/main/java/com/google/inject/spi/Elements.java b/src/main/java/com/google/inject/spi/Elements.java index 5f0b07d..7d49d6b 100644 --- a/src/main/java/com/google/inject/spi/Elements.java +++ b/src/main/java/com/google/inject/spi/Elements.java @@ -603,23 +603,13 @@ public final class Elements { } private ModuleSource getModuleSource(Class module) { - StackTraceElement[] partialCallStack; - if (getIncludeStackTraceOption() == IncludeStackTraceOption.COMPLETE) { - partialCallStack = getPartialCallStack(new Throwable().getStackTrace()); - } else { - partialCallStack = new StackTraceElement[0]; - } if (moduleSource == null) { - return new ModuleSource(module, partialCallStack, permitMapConstruction.getPermitMap()); + return new ModuleSource(module, permitMapConstruction.getPermitMap()); } - return moduleSource.createChild(module, partialCallStack); + return moduleSource.createChild(module); } private ElementSource getElementSource() { - // Full call stack - StackTraceElement[] callStack = null; - // The call stack starts from current top module configure and ends at this method caller - StackTraceElement[] partialCallStack = new StackTraceElement[0]; // The element original source ElementSource originalSource = null; // The element declaring source @@ -628,21 +618,10 @@ public final class Elements { originalSource = (ElementSource) declaringSource; declaringSource = originalSource.getDeclaringSource(); } - IncludeStackTraceOption stackTraceOption = getIncludeStackTraceOption(); - if (stackTraceOption == IncludeStackTraceOption.COMPLETE - || (stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE - && declaringSource == null)) { - callStack = new Throwable().getStackTrace(); - } - if (stackTraceOption == IncludeStackTraceOption.COMPLETE) { - partialCallStack = getPartialCallStack(callStack); - } if (declaringSource == null) { - // So 'source' and 'originalSource' are null otherwise declaringSource has some value - if (stackTraceOption == IncludeStackTraceOption.COMPLETE - || stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE) { - // With the above conditions and assignments 'callStack' is non-null - StackTraceElement callingSource = sourceProvider.get(callStack); + IncludeStackTraceOption stackTraceOption = getIncludeStackTraceOption(); + if (stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE) { + StackTraceElement callingSource = sourceProvider.get(new Throwable().getStackTrace()); // If we've traversed past all reasonable sources and into our internal code, then we // don't know the source. if (callingSource @@ -653,37 +632,14 @@ public final class Elements { } else { declaringSource = callingSource; } - } else { // or if (stackTraceOption == IncludeStackTraceOptions.OFF) + } else { // As neither 'declaring source' nor 'call stack' is available use 'module source' declaringSource = sourceProvider.getFromClassNames(moduleSource.getModuleClassNames()); } } // Build the binding call stack return new ElementSource( - originalSource, - trustedSource, - declaringSource, - moduleSource, - partialCallStack, - scannerSource); - } - - /** - * Removes the {@link #moduleSource} call stack from the beginning of current call stack. It - * also removes the last two elements in order to make {@link #install(Module)} the last call in - * the call stack. - */ - private StackTraceElement[] getPartialCallStack(StackTraceElement[] callStack) { - int toSkip = 0; - if (moduleSource != null) { - toSkip = moduleSource.getStackTraceSize(); - } - // -1 for skipping 'getModuleSource' and 'getElementSource' calls - int chunkSize = callStack.length - toSkip - 1; - - StackTraceElement[] partialCallStack = new StackTraceElement[chunkSize]; - System.arraycopy(callStack, 1, partialCallStack, 0, chunkSize); - return partialCallStack; + originalSource, trustedSource, declaringSource, moduleSource, scannerSource); } /** Returns if the binder is in the module scanning phase. */ diff --git a/src/main/java/com/google/inject/spi/ModuleSource.java b/src/main/java/com/google/inject/spi/ModuleSource.java index 2e2a886..e0a595e 100644 --- a/src/main/java/com/google/inject/spi/ModuleSource.java +++ b/src/main/java/com/google/inject/spi/ModuleSource.java @@ -3,14 +3,11 @@ package com.google.inject.spi; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.inject.Module; -import com.google.inject.internal.util.StackTraceElements; -import com.google.inject.internal.util.StackTraceElements.InMemoryStackTraceElement; import java.util.List; /** * Associated to a {@link Module module}, provides the module class name, the parent module {@link - * ModuleSource source}, and the call stack that ends just before the module {@link - * Module#configure(Binder) configure(Binder)} method invocation. + * ModuleSource source}, and the call stack that ends just before the method invocation. */ final class ModuleSource { @@ -29,27 +26,14 @@ final class ModuleSource { */ private final BindingSourceRestriction.PermitMap permitMap; - /** - * The chunk of call stack that starts from the parent module {@link Module#configure(Binder) - * configure(Binder)} call and ends just before the module {@link Module#configure(Binder) - * configure(Binder)} method invocation. For a module without a parent module the chunk starts - * from the bottom of call stack. The array is non-empty if stack trace collection is on. - */ - private final InMemoryStackTraceElement[] partialCallStack; - /** * Creates a new {@link ModuleSource} with a {@literal null} parent. * * @param moduleClass the corresponding module - * @param partialCallStack the chunk of call stack that starts from the parent module {@link - * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link - * Module#configure(Binder) configure(Binder)} method invocation */ - ModuleSource( - Class moduleClass, - StackTraceElement[] partialCallStack, + ModuleSource(Class moduleClass, BindingSourceRestriction.PermitMap permitMap) { - this(null, moduleClass, partialCallStack, permitMap); + this(null, moduleClass, permitMap); } /** @@ -57,20 +41,14 @@ final class ModuleSource { * * @param parent the parent module {@link ModuleSource source} * @param moduleClass the corresponding module - * @param partialCallStack the chunk of call stack that starts from the parent module {@link - * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link - * Module#configure(Binder) configure(Binder)} method invocation */ private ModuleSource( ModuleSource parent, Class moduleClass, - StackTraceElement[] partialCallStack, BindingSourceRestriction.PermitMap permitMap) { Preconditions.checkNotNull(moduleClass, "module cannot be null."); - Preconditions.checkNotNull(partialCallStack, "partialCallStack cannot be null."); this.parent = parent; this.moduleClassName = moduleClass.getName(); - this.partialCallStack = StackTraceElements.convertToInMemoryStackTraceElement(partialCallStack); this.permitMap = permitMap; } @@ -83,31 +61,13 @@ final class ModuleSource { return moduleClassName; } - /** - * Returns the chunk of call stack that starts from the parent module {@link - * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link - * Module#configure(Binder) configure(Binder)} method invocation. The return array is non-empty - * only if stack trace collection is on. - */ - StackTraceElement[] getPartialCallStack() { - return StackTraceElements.convertToStackTraceElement(partialCallStack); - } - - /** Returns the size of partial call stack if stack trace collection is on otherwise zero. */ - int getPartialCallStackSize() { - return partialCallStack.length; - } - /** * Creates and returns a child {@link ModuleSource} corresponding to the {@link Module module}. * * @param moduleClass the corresponding module - * @param partialCallStack the chunk of call stack that starts from the parent module {@link - * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link - * Module#configure(Binder) configure(Binder)} method invocation */ - ModuleSource createChild(Class moduleClass, StackTraceElement[] partialCallStack) { - return new ModuleSource(this, moduleClass, partialCallStack, permitMap); + ModuleSource createChild(Class moduleClass) { + return new ModuleSource(this, moduleClass, permitMap); } /** Returns the parent module {@link ModuleSource source}. */ @@ -142,38 +102,6 @@ final class ModuleSource { return parent.size() + 1; } - /** - * Returns the size of call stack that ends just before the module {@link Module#configure(Binder) - * configure(Binder)} method invocation (see {@link #getStackTrace()}). - */ - int getStackTraceSize() { - if (parent == null) { - return partialCallStack.length; - } - return parent.getStackTraceSize() + partialCallStack.length; - } - - /** - * Returns the full call stack that ends just before the module {@link Module#configure(Binder) - * configure(Binder)} method invocation. The return array is non-empty if stack trace collection - * on. - */ - StackTraceElement[] getStackTrace() { - int stackTraceSize = getStackTraceSize(); - StackTraceElement[] callStack = new StackTraceElement[stackTraceSize]; - int cursor = 0; - ModuleSource current = this; - while (current != null) { - StackTraceElement[] chunk = - StackTraceElements.convertToStackTraceElement(current.partialCallStack); - int chunkSize = chunk.length; - System.arraycopy(chunk, 0, callStack, cursor, chunkSize); - current = current.parent; - cursor = cursor + chunkSize; - } - return callStack; - } - /** Returns the permit map created by the binder that installed this module. */ BindingSourceRestriction.PermitMap getPermitMap() { return permitMap; diff --git a/src/test/java/com/google/inject/BinderTest.java b/src/test/java/com/google/inject/BinderTest.java index 67ecd19..a22367d 100644 --- a/src/test/java/com/google/inject/BinderTest.java +++ b/src/test/java/com/google/inject/BinderTest.java @@ -124,9 +124,10 @@ public class BinderTest { } catch (CreationException e) { assertEquals(1, e.getErrorMessages().size()); assertContains(e.getMessage(), - "No implementation for java.lang.Runnable was bound.", - "for field at " + NeedsRunnable.class.getName(), ".runnable(", - "at " + getClass().getName(), getDeclaringSourcePart(getClass())); + "No implementation for Runnable was bound", + "for field runnable", + "at BinderTest", + getDeclaringSourcePart(getClass())); } } @@ -440,7 +441,7 @@ public class BinderTest { fail(); } catch (CreationException expected) { assertContains(expected.getMessage(), - "1) Binding to Provider is not allowed.", + "Binding to Provider is not allowed", "at " + BinderTest.class.getName(), getDeclaringSourcePart(getClass())); } } diff --git a/src/test/java/com/google/inject/BoundInstanceInjectionTest.java b/src/test/java/com/google/inject/BoundInstanceInjectionTest.java index dbf0f1a..a356cd6 100644 --- a/src/test/java/com/google/inject/BoundInstanceInjectionTest.java +++ b/src/test/java/com/google/inject/BoundInstanceInjectionTest.java @@ -8,10 +8,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import com.google.inject.name.Named; - -import org.junit.Test; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.junit.Test; public class BoundInstanceInjectionTest { @@ -19,80 +20,19 @@ public class BoundInstanceInjectionTest { public void testInstancesAreInjected() throws CreationException { final O o = new O(); - Injector injector = Guice.createInjector(new AbstractModule() { - protected void configure() { - bind(O.class).toInstance(o); - bind(int.class).toInstance(5); - } - }); + Injector injector = + Guice.createInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(O.class).toInstance(o); + bind(int.class).toInstance(5); + } + }); assertEquals(5, o.fromMethod); } - @Test - public void testProvidersAreInjected() throws CreationException { - Injector injector = Guice.createInjector(new AbstractModule() { - protected void configure() { - bind(O.class).toProvider(new Provider() { - @Inject - int i; - - public O get() { - O o = new O(); - o.setInt(i); - return o; - } - }); - bind(int.class).toInstance(5); - } - }); - - assertEquals(5, injector.getInstance(O.class).fromMethod); - } - - @Test - public void testMalformedInstance() { - try { - Guice.createInjector(new AbstractModule() { - protected void configure() { - bind(Object.class).toInstance(new MalformedInjectable()); - } - }); - fail(); - } catch (CreationException expected) { - // disabled because of double message - /*Asserts.assertContains(expected.getMessage(), - MalformedInjectable.class.getName(), - ".doublyAnnotated() has more than one ", - "annotation annotated with @BindingAnnotation: ", - Named.class.getName() + " and " + Another.class.getName());*/ - } - } - - @Test - public void testMalformedProvider() { - try { - Guice.createInjector(new AbstractModule() { - protected void configure() { - bind(String.class).toProvider(new MalformedProvider()); - } - }); - fail(); - } catch (CreationException expected) { - Asserts.assertContains(expected.getMessage(), - MalformedProvider.class.getName(), - ".doublyAnnotated() has more than one ", - "annotation annotated with @BindingAnnotation: ", - Named.class.getName() + " and " + Another.class.getName()); - } - } - - @BindingAnnotation - @Target({FIELD, PARAMETER, METHOD}) - @Retention(RUNTIME) - public @interface Another { - } - static class O { int fromMethod; @@ -102,19 +42,90 @@ public class BoundInstanceInjectionTest { } } + @Test + public void testProvidersAreInjected() throws CreationException { + Injector injector = + Guice.createInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(O.class) + .toProvider( + new Provider() { + @Inject int i; + + @Override + public O get() { + O o = new O(); + o.setInt(i); + return o; + } + }); + bind(int.class).toInstance(5); + } + }); + + assertEquals(5, injector.getInstance(O.class).fromMethod); + } + + @Test + public void testMalformedInstance() { + try { + Guice.createInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(Object.class).toInstance(new MalformedInjectable()); + } + }); + fail(); + } catch (CreationException expected) { + Logger.getAnonymousLogger().log(Level.INFO, expected.getMessage()); + Asserts.assertContains( + expected.getMessage(), + "BoundInstanceInjectionTest$MalformedInjectable.doublyAnnotated() has more than one ", + "annotation annotated with @BindingAnnotation: ", + "Named and BoundInstanceInjectionTest$Another"); + } + } + + @Test + public void testMalformedProvider() { + try { + Guice.createInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(String.class).toProvider(new MalformedProvider()); + } + }); + fail(); + } catch (CreationException expected) { + Asserts.assertContains( + expected.getMessage(), + "BoundInstanceInjectionTest$MalformedProvider.doublyAnnotated() has more than one ", + "annotation annotated with @BindingAnnotation: ", + "Named and BoundInstanceInjectionTest$Another"); + } + } + static class MalformedInjectable { @Inject - void doublyAnnotated(@Named("a") @Another String unused) { - } + void doublyAnnotated(@Named("a") @Another String unused) {} } static class MalformedProvider implements Provider { @Inject - void doublyAnnotated(@Named("a") @Another String s) { - } + void doublyAnnotated(@Named("a") @Another String s) {} + @Override public String get() { return "a"; } } + + @BindingAnnotation + @Target({FIELD, PARAMETER, METHOD}) + @Retention(RUNTIME) + public @interface Another {} } diff --git a/src/test/java/com/google/inject/CircularDependencyTest.java b/src/test/java/com/google/inject/CircularDependencyTest.java index 4aece99..3185995 100644 --- a/src/test/java/com/google/inject/CircularDependencyTest.java +++ b/src/test/java/com/google/inject/CircularDependencyTest.java @@ -184,6 +184,7 @@ public class CircularDependencyTest { } } + @Test public void testDisabledCircularDependency() { try { Guice.createInjector( diff --git a/src/test/java/com/google/inject/internal/WeakKeySetTest.java b/src/test/java/com/google/inject/internal/WeakKeySetTest.java index fb9cae1..fa44493 100644 --- a/src/test/java/com/google/inject/internal/WeakKeySetTest.java +++ b/src/test/java/com/google/inject/internal/WeakKeySetTest.java @@ -2,9 +2,9 @@ package com.google.inject.internal; import static com.google.inject.Asserts.awaitClear; import static com.google.inject.Asserts.awaitFullGc; -import static com.google.inject.internal.WeakKeySetUtils.assertBlacklisted; +import static com.google.inject.internal.WeakKeySetUtils.assertBanned; import static com.google.inject.internal.WeakKeySetUtils.assertInSet; -import static com.google.inject.internal.WeakKeySetUtils.assertNotBlacklisted; +import static com.google.inject.internal.WeakKeySetUtils.assertNotBanned; import static com.google.inject.internal.WeakKeySetUtils.assertNotInSet; import static com.google.inject.internal.WeakKeySetUtils.assertSourceNotInSet; import static org.junit.Assert.assertNotNull; @@ -28,18 +28,18 @@ import com.google.inject.spi.ScopeBinding; import com.google.inject.spi.StaticInjectionRequest; import com.google.inject.spi.TypeConverterBinding; import com.google.inject.spi.TypeListenerBinding; - -import org.junit.Before; -import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.ref.WeakReference; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; +import org.junit.Before; +import org.junit.Test; /** * Tests for {@link WeakKeySet}. - *

+ * * Multibinding specific tests can be found in MultibinderTest and MapBinderTest. */ public class WeakKeySetTest { @@ -47,22 +47,22 @@ public class WeakKeySetTest { private WeakKeySet set; @Before - public void setUp() { + public void setUp() throws Exception { set = new WeakKeySet(new Object()); } @Test public void testEviction() { - TestState state = new TestState(); + TestInjectorBindingData bindingData = new TestInjectorBindingData(); Key key = Key.get(Integer.class); Object source = new Object(); - WeakReference> weakKeyRef = new WeakReference>(key); + WeakReference> weakKeyRef = new WeakReference<>(key); - set.add(key, state, source); + set.add(key, bindingData, source); assertInSet(set, key, 1, source); - state = null; + bindingData = null; awaitFullGc(); @@ -75,16 +75,16 @@ public class WeakKeySetTest { @Test public void testEviction_nullSource() { - TestState state = new TestState(); + TestInjectorBindingData bindingData = new TestInjectorBindingData(); Key key = Key.get(Integer.class); Object source = null; - WeakReference> weakKeyRef = new WeakReference>(key); + WeakReference> weakKeyRef = new WeakReference<>(key); - set.add(key, state, source); + set.add(key, bindingData, source); assertInSet(set, key, 1, source); - state = null; + bindingData = null; awaitFullGc(); @@ -97,42 +97,43 @@ public class WeakKeySetTest { @Test public void testEviction_keyOverlap_2x() { - TestState state1 = new TestState(); - TestState state2 = new TestState(); + TestInjectorBindingData bindingData1 = new TestInjectorBindingData(); + TestInjectorBindingData bindingData2 = new TestInjectorBindingData(); Key key1 = Key.get(Integer.class); Key key2 = Key.get(Integer.class); Object source1 = new Object(); Object source2 = new Object(); - set.add(key1, state1, source1); + set.add(key1, bindingData1, source1); assertInSet(set, key1, 1, source1); - set.add(key2, state2, source2); + set.add(key2, bindingData2, source2); assertInSet(set, key2, 2, source1, source2); - WeakReference> weakKey1Ref = new WeakReference>(key1); - WeakReference> weakKey2Ref = new WeakReference>(key2); - WeakReference weakSource1Ref = new WeakReference(source1); - WeakReference weakSource2Ref = new WeakReference(source2); + WeakReference> weakKey1Ref = new WeakReference<>(key1); + WeakReference> weakKey2Ref = new WeakReference<>(key2); + WeakReference weakSource1Ref = new WeakReference<>(source1); + WeakReference weakSource2Ref = new WeakReference<>(source2); Key key = key1 = key2 = Key.get(Integer.class); - state1 = null; + bindingData1 = null; awaitFullGc(); assertSourceNotInSet(set, key, source1); assertInSet(set, key, 1, source2); - source1 = source2 = null; + // Clear source1 and source2 fields so the objects can be GCed. + Object unused = source1 = source2 = null; awaitClear(weakSource1Ref); // Key1 will be referenced as the key in the sources backingSet and won't be // GC'd. - // Should not be GC'd until state2 goes away. + // Should not be GC'd until bindingData2 goes away. assertNotNull(weakSource2Ref.get()); - state2 = null; + bindingData2 = null; awaitFullGc(); @@ -146,28 +147,29 @@ public class WeakKeySetTest { @Test public void testNoEviction_keyOverlap_2x() { - TestState state1 = new TestState(); - TestState state2 = new TestState(); + TestInjectorBindingData bindingData1 = new TestInjectorBindingData(); + TestInjectorBindingData bindingData2 = new TestInjectorBindingData(); Key key1 = Key.get(Integer.class); Key key2 = Key.get(Integer.class); Object source1 = new Object(); Object source2 = new Object(); - set.add(key1, state1, source1); + set.add(key1, bindingData1, source1); assertInSet(set, key1, 1, source1); - set.add(key2, state2, source2); + set.add(key2, bindingData2, source2); assertInSet(set, key2, 2, source1, source2); - WeakReference> weakKey1Ref = new WeakReference>(key1); - WeakReference> weakKey2Ref = new WeakReference>(key2); + WeakReference> weakKey1Ref = new WeakReference<>(key1); + WeakReference> weakKey2Ref = new WeakReference<>(key2); Key key = key1 = key2 = Key.get(Integer.class); awaitFullGc(); assertInSet(set, key, 2, source1, source2); - // Ensure the keys don't get GC'd when states are still referenced. key1 will be present in the + // Ensure the keys don't get GC'd when InjectorBindingData objects are still referenced. key1 + // will be present in the // as the map key but key2 could be GC'd if the implementation does something wrong. assertNotNull(weakKey1Ref.get()); assertNotNull(weakKey2Ref.get()); @@ -175,26 +177,26 @@ public class WeakKeySetTest { @Test public void testEviction_keyAndSourceOverlap_null() { - TestState state1 = new TestState(); - TestState state2 = new TestState(); + TestInjectorBindingData bindingData1 = new TestInjectorBindingData(); + TestInjectorBindingData bindingData2 = new TestInjectorBindingData(); Key key1 = Key.get(Integer.class); Key key2 = Key.get(Integer.class); Object source = null; - set.add(key1, state1, source); + set.add(key1, bindingData1, source); assertInSet(set, key1, 1, source); - set.add(key2, state2, source); + set.add(key2, bindingData2, source); // Same source so still only one value. assertInSet(set, key2, 1, source); assertInSet(set, key1, 1, source); - WeakReference> weakKey1Ref = new WeakReference>(key1); - WeakReference> weakKey2Ref = new WeakReference>(key2); - WeakReference weakSourceRef = new WeakReference(source); + WeakReference> weakKey1Ref = new WeakReference<>(key1); + WeakReference> weakKey2Ref = new WeakReference<>(key2); + WeakReference weakSourceRef = new WeakReference<>(source); Key key = key1 = key2 = Key.get(Integer.class); - state1 = null; + bindingData1 = null; awaitFullGc(); // Should still have a single source. @@ -206,7 +208,7 @@ public class WeakKeySetTest { // Key1 will be referenced as the key in the sources backingSet and won't be // GC'd. - state2 = null; + bindingData2 = null; awaitFullGc(); assertNotInSet(set, key); @@ -219,25 +221,25 @@ public class WeakKeySetTest { @Test public void testEviction_keyAndSourceOverlap_nonNull() { - TestState state1 = new TestState(); - TestState state2 = new TestState(); + TestInjectorBindingData bindingData1 = new TestInjectorBindingData(); + TestInjectorBindingData bindingData2 = new TestInjectorBindingData(); Key key1 = Key.get(Integer.class); Key key2 = Key.get(Integer.class); Object source = new Object(); - set.add(key1, state1, source); + set.add(key1, bindingData1, source); assertInSet(set, key1, 1, source); - set.add(key2, state2, source); + set.add(key2, bindingData2, source); // Same source so still only one value. assertInSet(set, key2, 1, source); - WeakReference> weakKey1Ref = new WeakReference>(key1); - WeakReference> weakKey2Ref = new WeakReference>(key2); - WeakReference weakSourceRef = new WeakReference(source); + WeakReference> weakKey1Ref = new WeakReference<>(key1); + WeakReference> weakKey2Ref = new WeakReference<>(key2); + WeakReference weakSourceRef = new WeakReference<>(source); Key key = key1 = key2 = Key.get(Integer.class); - state1 = null; + bindingData1 = null; awaitFullGc(); @@ -252,7 +254,7 @@ public class WeakKeySetTest { // Key1 will be referenced as the key in the sources backingSet and won't be // GC'd. - state2 = null; + bindingData2 = null; awaitFullGc(); @@ -266,9 +268,9 @@ public class WeakKeySetTest { @Test public void testEviction_keyOverlap_3x() { - TestState state1 = new TestState(); - TestState state2 = new TestState(); - TestState state3 = new TestState(); + TestInjectorBindingData bindingData1 = new TestInjectorBindingData(); + TestInjectorBindingData bindingData2 = new TestInjectorBindingData(); + TestInjectorBindingData bindingData3 = new TestInjectorBindingData(); Key key1 = Key.get(Integer.class); Key key2 = Key.get(Integer.class); Key key3 = Key.get(Integer.class); @@ -276,24 +278,24 @@ public class WeakKeySetTest { Object source2 = new Object(); Object source3 = new Object(); - set.add(key1, state1, source1); + set.add(key1, bindingData1, source1); assertInSet(set, key1, 1, source1); - set.add(key2, state2, source2); + set.add(key2, bindingData2, source2); assertInSet(set, key1, 2, source1, source2); - set.add(key3, state3, source3); + set.add(key3, bindingData3, source3); assertInSet(set, key1, 3, source1, source2, source3); - WeakReference> weakKey1Ref = new WeakReference>(key1); - WeakReference> weakKey2Ref = new WeakReference>(key2); - WeakReference> weakKey3Ref = new WeakReference>(key3); - WeakReference weakSource1Ref = new WeakReference(source1); - WeakReference weakSource2Ref = new WeakReference(source2); - WeakReference weakSource3Ref = new WeakReference(source3); + WeakReference> weakKey1Ref = new WeakReference<>(key1); + WeakReference> weakKey2Ref = new WeakReference<>(key2); + WeakReference> weakKey3Ref = new WeakReference<>(key3); + WeakReference weakSource1Ref = new WeakReference<>(source1); + WeakReference weakSource2Ref = new WeakReference<>(source2); + WeakReference weakSource3Ref = new WeakReference<>(source3); Key key = key1 = key2 = key3 = Key.get(Integer.class); - state1 = null; + bindingData1 = null; awaitFullGc(); assertSourceNotInSet(set, key, source1); @@ -304,7 +306,7 @@ public class WeakKeySetTest { // GC'd. awaitClear(weakSource1Ref); - state2 = null; + bindingData2 = null; awaitFullGc(); assertSourceNotInSet(set, key, source2); assertInSet(set, key, 1, source3); @@ -316,7 +318,7 @@ public class WeakKeySetTest { // Key1 will be referenced as the key in the sources backingSet and won't be // GC'd. - state3 = null; + bindingData3 = null; awaitFullGc(); assertNotInSet(set, key); @@ -329,125 +331,148 @@ public class WeakKeySetTest { @Test public void testWeakKeySet_integration() { - Injector parentInjector = Guice.createInjector(new AbstractModule() { - @Override - protected void configure() { - bind(Integer.class).toInstance(4); - } - }); - assertNotBlacklisted(parentInjector, Key.get(String.class)); + Injector parentInjector = + Guice.createInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(Integer.class).toInstance(4); + } + }); + assertNotBanned(parentInjector, Key.get(String.class)); - Injector childInjector = parentInjector.createChildInjector(new AbstractModule() { - @Override - protected void configure() { - bind(String.class).toInstance("bar"); - } - }); - WeakReference weakRef = new WeakReference(childInjector); - assertBlacklisted(parentInjector, Key.get(String.class)); + Injector childInjector = + parentInjector.createChildInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(String.class).toInstance("bar"); + } + }); + WeakReference weakRef = new WeakReference<>(childInjector); + assertBanned(parentInjector, Key.get(String.class)); - // Clear the ref, GC, and ensure that we are no longer blacklisting. + // Clear the ref, GC, and ensure that we are no longer banning. childInjector = null; awaitClear(weakRef); - assertNotBlacklisted(parentInjector, Key.get(String.class)); + assertNotBanned(parentInjector, Key.get(String.class)); } - @Test public void testWeakKeySet_integration_multipleChildren() { - Injector parentInjector = Guice.createInjector(new AbstractModule() { - @Override - protected void configure() { - bind(Integer.class).toInstance(4); - } - }); - assertNotBlacklisted(parentInjector, Key.get(String.class)); - assertNotBlacklisted(parentInjector, Key.get(Long.class)); + Injector parentInjector = + Guice.createInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(Integer.class).toInstance(4); + } + }); + assertNotBanned(parentInjector, Key.get(String.class)); + assertNotBanned(parentInjector, Key.get(Long.class)); - Injector childInjector1 = parentInjector.createChildInjector(new AbstractModule() { - @Override - protected void configure() { - bind(String.class).toInstance("foo"); - } - }); - WeakReference weakRef1 = new WeakReference(childInjector1); - assertBlacklisted(parentInjector, Key.get(String.class)); - assertNotBlacklisted(parentInjector, Key.get(Long.class)); + Injector childInjector1 = + parentInjector.createChildInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(String.class).toInstance("foo"); + } + }); + WeakReference weakRef1 = new WeakReference<>(childInjector1); + assertBanned(parentInjector, Key.get(String.class)); + assertNotBanned(parentInjector, Key.get(Long.class)); - Injector childInjector2 = parentInjector.createChildInjector(new AbstractModule() { - @Override - protected void configure() { - bind(Long.class).toInstance(6L); - } - }); - WeakReference weakRef2 = new WeakReference(childInjector2); - assertBlacklisted(parentInjector, Key.get(String.class)); - assertBlacklisted(parentInjector, Key.get(Long.class)); + Injector childInjector2 = + parentInjector.createChildInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(Long.class).toInstance(6L); + } + }); + WeakReference weakRef2 = new WeakReference<>(childInjector2); + assertBanned(parentInjector, Key.get(String.class)); + assertBanned(parentInjector, Key.get(Long.class)); - // Clear ref1, GC, and ensure that we still blacklist. + // Clear ref1, GC, and ensure that we still ban. childInjector1 = null; awaitClear(weakRef1); - assertNotBlacklisted(parentInjector, Key.get(String.class)); - assertBlacklisted(parentInjector, Key.get(Long.class)); + assertNotBanned(parentInjector, Key.get(String.class)); + assertBanned(parentInjector, Key.get(Long.class)); - // Clear the ref, GC, and ensure that we are no longer blacklisting. + // Clear the ref, GC, and ensure that we are no longer banning. childInjector2 = null; awaitClear(weakRef2); - assertNotBlacklisted(parentInjector, Key.get(String.class)); - assertNotBlacklisted(parentInjector, Key.get(Long.class)); + assertNotBanned(parentInjector, Key.get(String.class)); + assertNotBanned(parentInjector, Key.get(Long.class)); } @Test public void testWeakKeySet_integration_multipleChildren_overlappingKeys() { - Injector parentInjector = Guice.createInjector(new AbstractModule() { - @Override - protected void configure() { - bind(Integer.class).toInstance(4); - } - }); - assertNotBlacklisted(parentInjector, Key.get(String.class)); + Injector parentInjector = + Guice.createInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(Integer.class).toInstance(4); + } + }); + assertNotBanned(parentInjector, Key.get(String.class)); - Injector childInjector1 = parentInjector.createChildInjector(new AbstractModule() { - @Override - protected void configure() { - bind(String.class).toInstance("foo"); - } - }); - WeakReference weakRef1 = new WeakReference(childInjector1); - assertBlacklisted(parentInjector, Key.get(String.class)); + Injector childInjector1 = + parentInjector.createChildInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(String.class).toInstance("foo"); + } + }); + WeakReference weakRef1 = new WeakReference<>(childInjector1); + assertBanned(parentInjector, Key.get(String.class)); - Injector childInjector2 = parentInjector.createChildInjector(new AbstractModule() { - @Override - protected void configure() { - bind(String.class).toInstance("bar"); - } - }); - WeakReference weakRef2 = new WeakReference(childInjector2); - assertBlacklisted(parentInjector, Key.get(String.class)); + Injector childInjector2 = + parentInjector.createChildInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(String.class).toInstance("bar"); + } + }); + WeakReference weakRef2 = new WeakReference<>(childInjector2); + assertBanned(parentInjector, Key.get(String.class)); - // Clear ref1, GC, and ensure that we still blacklist. + // Clear ref1, GC, and ensure that we still ban. childInjector1 = null; awaitClear(weakRef1); - assertBlacklisted(parentInjector, Key.get(String.class)); + assertBanned(parentInjector, Key.get(String.class)); - // Clear the ref, GC, and ensure that we are no longer blacklisting. + // Clear the ref, GC, and ensure that we are no longer banning. childInjector2 = null; awaitClear(weakRef2); - assertNotBlacklisted(parentInjector, Key.get(String.class)); + assertNotBanned(parentInjector, Key.get(String.class)); } - private static class TestState implements State { - public State parent() { - return new TestState(); + private static class TestInjectorBindingData extends InjectorBindingData { + TestInjectorBindingData() { + super(Optional.empty()); } + @Override + public Optional parent() { + return Optional.of(new TestInjectorBindingData()); + } + + @Override public BindingImpl getExplicitBinding(Key key) { return null; } + @Override public Map, Binding> getExplicitBindingsThisLevel() { throw new UnsupportedOperationException(); } + @Override public void putBinding(Key key, BindingImpl binding) { throw new UnsupportedOperationException(); } @@ -492,66 +517,63 @@ public class WeakKeySetTest { throw new UnsupportedOperationException(); } + @Override public ScopeBinding getScopeBinding(Class scopingAnnotation) { return null; } + @Override public void putScopeBinding(Class annotationType, ScopeBinding scope) { throw new UnsupportedOperationException(); } + @Override public void addConverter(TypeConverterBinding typeConverterBinding) { throw new UnsupportedOperationException(); } - public TypeConverterBinding getConverter(String stringValue, TypeLiteral type, Errors errors, - Object source) { + @Override + public TypeConverterBinding getConverter( + String stringValue, TypeLiteral type, Errors errors, Object source) { throw new UnsupportedOperationException(); } + @Override public Iterable getConvertersThisLevel() { return ImmutableSet.of(); } + @Override public void addTypeListener(TypeListenerBinding typeListenerBinding) { throw new UnsupportedOperationException(); } - public List getTypeListenerBindings() { + @Override + public ImmutableList getTypeListenerBindings() { return ImmutableList.of(); } + @Override public void addProvisionListener(ProvisionListenerBinding provisionListenerBinding) { throw new UnsupportedOperationException(); } - public List getProvisionListenerBindings() { + @Override + public ImmutableList getProvisionListenerBindings() { return ImmutableList.of(); } + @Override public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) { throw new UnsupportedOperationException(); } - public List getScannerBindings() { + @Override + public ImmutableList getScannerBindings() { return ImmutableList.of(); } - public void blacklist(Key key, State state, Object source) { - } - - public boolean isBlacklisted(Key key) { - return true; - } - - public Set getSourcesForBlacklistedKey(Key key) { - throw new UnsupportedOperationException(); - } - - public Object lock() { - throw new UnsupportedOperationException(); - } - + @Override public Map, Scope> getScopes() { return ImmutableMap.of(); } @@ -562,17 +584,17 @@ public class WeakKeySetTest { } @Override - public List getTypeListenerBindingsThisLevel() { + public ImmutableList getTypeListenerBindingsThisLevel() { throw new UnsupportedOperationException(); } @Override - public List getProvisionListenerBindingsThisLevel() { + public ImmutableList getProvisionListenerBindingsThisLevel() { throw new UnsupportedOperationException(); } @Override - public List getScannerBindingsThisLevel() { + public ImmutableList getScannerBindingsThisLevel() { throw new UnsupportedOperationException(); } } diff --git a/src/test/java/com/google/inject/internal/WeakKeySetUtils.java b/src/test/java/com/google/inject/internal/WeakKeySetUtils.java index 465a2c6..381d694 100644 --- a/src/test/java/com/google/inject/internal/WeakKeySetUtils.java +++ b/src/test/java/com/google/inject/internal/WeakKeySetUtils.java @@ -1,14 +1,13 @@ package com.google.inject.internal; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import com.google.inject.Injector; import com.google.inject.Key; - import java.util.Set; /** @@ -16,15 +15,14 @@ import java.util.Set; */ public final class WeakKeySetUtils { - private WeakKeySetUtils() { + private WeakKeySetUtils() {} + + public static void assertBanned(Injector injector, Key key) { + assertBannedState(injector, key, true); } - public static void assertBlacklisted(Injector injector, Key key) { - assertBlacklistState(injector, key, true); - } - - public static void assertNotBlacklisted(Injector injector, Key key) { - assertBlacklistState(injector, key, false); + public static void assertNotBanned(Injector injector, Key key) { + assertBannedState(injector, key, false); } public static void assertNotInSet(WeakKeySet set, Key key) { @@ -39,8 +37,8 @@ public final class WeakKeySetUtils { assertNull(set.getSources(Key.get(Integer.class))); } - public static void assertInSet(WeakKeySet set, Key key, int expectedSources, - Object... sources) { + public static void assertInSet( + WeakKeySet set, Key key, int expectedSources, Object... sources) { assertTrue(set.contains(key)); assertEquals(expectedSources, set.getSources(key).size()); for (Object source : sources) { @@ -63,17 +61,17 @@ public final class WeakKeySetUtils { assertFalse(sources.contains(source)); } - private static void assertBlacklistState(Injector injector, Key key, boolean isBlacklisted) { - // if we're expecting it to not be blacklisted, loop around and wait for threads to run. - if (!isBlacklisted) { + private static void assertBannedState(Injector injector, Key key, boolean isBanned) { + // if we're expecting it to not be banned, loop around and wait for threads to run. + if (!isBanned) { for (int i = 0; i < 10; i++) { - if (!((InjectorImpl) injector).state.isBlacklisted(key)) { + if (!((InjectorImpl) injector).getJitBindingData().isBannedKey(key)) { break; } sleep(); } } - assertEquals(isBlacklisted, ((InjectorImpl) injector).state.isBlacklisted(key)); + assertEquals(isBanned, ((InjectorImpl) injector).getJitBindingData().isBannedKey(key)); } private static void sleep() { @@ -82,6 +80,5 @@ public final class WeakKeySetUtils { } catch (InterruptedException e) { throw new RuntimeException(e); } - Thread.yield(); } } diff --git a/src/test/java/com/google/inject/name/NamesTest.java b/src/test/java/com/google/inject/name/NamesTest.java index 947e237..e404641 100644 --- a/src/test/java/com/google/inject/name/NamesTest.java +++ b/src/test/java/com/google/inject/name/NamesTest.java @@ -10,7 +10,7 @@ import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; -import org.junit.BeforeClass; +import org.junit.Before; import org.junit.Test; import java.util.Map; import java.util.Properties; @@ -19,11 +19,11 @@ public class NamesTest { @Named("foo") private String foo; - private static Named namedFoo; + private Named namedFoo; - @BeforeClass - public static void setUp() throws Exception { - namedFoo = NamesTest.class.getDeclaredField("foo").getAnnotation(Named.class); + @Before + public void setUp() throws Exception { + namedFoo = getClass().getDeclaredField("foo").getAnnotation(Named.class); } @Test diff --git a/src/test/java/com/google/inject/spi/ElementSourceTest.java b/src/test/java/com/google/inject/spi/ElementSourceTest.java index 550ac1b..a734567 100644 --- a/src/test/java/com/google/inject/spi/ElementSourceTest.java +++ b/src/test/java/com/google/inject/spi/ElementSourceTest.java @@ -1,49 +1,30 @@ package com.google.inject.spi; -import static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; import com.google.inject.AbstractModule; import com.google.inject.Binder; import com.google.inject.Binding; import com.google.inject.BindingAnnotation; import com.google.inject.Module; - -import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.util.List; +import junit.framework.TestCase; -/** - * Tests for {@link ElementSource}. - */ -public class ElementSourceTest { +/** Tests for {@link ElementSource}. */ +public class ElementSourceTest extends TestCase { private static final StackTraceElement BINDER_INSTALL = - new StackTraceElement("com.google.inject.spi.Elements$RecordingBinder", "install", - "Unknown Source", 234 /* line number*/); + new StackTraceElement( + "com.google.inject.spi.Elements$RecordingBinder", + "install", + "Unknown Source", + 234 /* line number*/); - @Test - public void testCallStackSize() { - ModuleSource moduleSource = createModuleSource(); - StackTraceElement[] bindingCallStack = new StackTraceElement[3]; - bindingCallStack[0] = new StackTraceElement( - "com.google.inject.spi.Elements$RecordingBinder", "bind", "Unknown Source", 200); - bindingCallStack[1] = new StackTraceElement( - "com.google.inject.spi.Elements$RecordingBinder", "bind", "Unknown Source", 100); - bindingCallStack[2] = new StackTraceElement( - "com.google.inject.spi.moduleSourceTest$C", "configure", "Unknown Source", 100); - ElementSource elementSource = new ElementSource( - null /* No original element source */, "" /* Don't care */, moduleSource, bindingCallStack); - assertEquals(10 /* call stack size */, elementSource.getStackTrace().length); - } - - @Test - public void testGetCallStack_IntegrationTest() { + public void testGetCallStack_IntegrationTest() throws Exception { List elements = Elements.getElements(new A()); for (Element element : elements) { if (element instanceof Binding) { @@ -59,70 +40,7 @@ public class ElementSourceTest { assertEquals("com.google.inject.spi.ElementSourceTest$B", moduleClassNames.get(1)); // Module A assertEquals("com.google.inject.spi.ElementSourceTest$A", moduleClassNames.get(2)); - StackTraceElement[] callStack = elementSource.getStackTrace(); - switch (getIncludeStackTraceOption()) { - case OFF: - // Check declaring source - StackTraceElement stackTraceElement = - (StackTraceElement) elementSource.getDeclaringSource(); - assertEquals(new StackTraceElement( - "com.google.inject.spi.ElementSourceTest$C", "configure", null, -1), - stackTraceElement); - // Check call stack - assertEquals(0, callStack.length); - return; - case ONLY_FOR_DECLARING_SOURCE: - // Check call stack - assertEquals(0, callStack.length); - return; - case COMPLETE: - // Check call stack - int skippedCallStackSize = new Throwable().getStackTrace().length - 1; - assertEquals(skippedCallStackSize + 15, elementSource.getStackTrace().length); - assertEquals("com.google.inject.spi.Elements$RecordingBinder", - callStack[0].getClassName()); - assertEquals("com.google.inject.spi.Elements$RecordingBinder", - callStack[1].getClassName()); - assertEquals("com.google.inject.AbstractModule", - callStack[2].getClassName()); - // Module C - assertEquals("com.google.inject.spi.ElementSourceTest$C", - callStack[3].getClassName()); - assertEquals("configure", - callStack[3].getMethodName()); - assertEquals("Unknown Source", - callStack[3].getFileName()); - assertEquals("com.google.inject.AbstractModule", - callStack[4].getClassName()); - assertEquals("com.google.inject.spi.Elements$RecordingBinder", - callStack[5].getClassName()); - // Module B - assertEquals("com.google.inject.spi.ElementSourceTest$B", - callStack[6].getClassName()); - assertEquals("com.google.inject.spi.Elements$RecordingBinder", - callStack[7].getClassName()); - // Module A - assertEquals("com.google.inject.AbstractModule", - callStack[8].getClassName()); - assertEquals("com.google.inject.spi.ElementSourceTest$A", - callStack[9].getClassName()); - assertEquals("com.google.inject.AbstractModule", - callStack[10].getClassName()); - assertEquals("com.google.inject.spi.Elements$RecordingBinder", - callStack[11].getClassName()); - assertEquals("com.google.inject.spi.Elements", - callStack[12].getClassName()); - assertEquals("com.google.inject.spi.Elements", - callStack[13].getClassName()); - assertEquals("com.google.inject.spi.ElementSourceTest", - callStack[14].getClassName()); - // Check modules index - List indexes = elementSource.getModuleConfigurePositionsInStackTrace(); - assertEquals((int) indexes.get(0), 4); - assertEquals((int) indexes.get(1), 6); - assertEquals((int) indexes.get(2), 10); - return; - } + return; } } } @@ -131,29 +49,11 @@ public class ElementSourceTest { private ModuleSource createModuleSource() { // First module - StackTraceElement[] partialCallStack = new StackTraceElement[1]; - partialCallStack[0] = BINDER_INSTALL; - ModuleSource moduleSource = new ModuleSource(new A(), partialCallStack); + ModuleSource moduleSource = new ModuleSource(A.class, /* permitMap = */ null); // Second module - partialCallStack = new StackTraceElement[2]; - partialCallStack[0] = BINDER_INSTALL; - partialCallStack[1] = new StackTraceElement( - "com.google.inject.spi.moduleSourceTest$A", "configure", "Unknown Source", 100); - moduleSource = moduleSource.createChild(new B(), partialCallStack); + moduleSource = moduleSource.createChild(B.class); // Third module - partialCallStack = new StackTraceElement[4]; - partialCallStack[0] = BINDER_INSTALL; - partialCallStack[1] = new StackTraceElement("class1", "method1", "Class1.java", 1); - partialCallStack[2] = new StackTraceElement("class2", "method2", "Class2.java", 2); - partialCallStack[3] = new StackTraceElement( - "com.google.inject.spi.moduleSourceTest$B", "configure", "Unknown Source", 200); - return moduleSource.createChild(new C(), partialCallStack); - } - - @Retention(RUNTIME) - @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) - @BindingAnnotation - @interface SampleAnnotation { + return moduleSource.createChild(C.class); } private static class A extends AbstractModule { @@ -170,6 +70,11 @@ public class ElementSourceTest { } } + @Retention(RUNTIME) + @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) + @BindingAnnotation + @interface SampleAnnotation {} + private static class C extends AbstractModule { @Override public void configure() { diff --git a/src/test/java/com/google/inject/spi/ElementsTest.java b/src/test/java/com/google/inject/spi/ElementsTest.java index 750f075..407c8a0 100644 --- a/src/test/java/com/google/inject/spi/ElementsTest.java +++ b/src/test/java/com/google/inject/spi/ElementsTest.java @@ -3,7 +3,6 @@ package com.google.inject.spi; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.inject.Asserts.assertContains; import static com.google.inject.Asserts.getDeclaringSourcePart; -import static com.google.inject.Asserts.isIncludeStackTraceComplete; import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -1342,11 +1341,6 @@ public class ElementsTest { if (!(element instanceof Message)) { ElementSource source = (ElementSource) element.getSource(); assertFalse(source.getModuleClassNames().isEmpty()); - if (isIncludeStackTraceComplete()) { - assertTrue(source.getStackTrace().length > 0); - } else { - assertEquals(0, source.getStackTrace().length); - } } if (!(visitor instanceof ExternalFailureVisitor)) { assertContains(element.getSource().toString(), getDeclaringSourcePart(ElementsTest.class)); diff --git a/src/test/java/com/google/inject/spi/ModuleSourceTest.java b/src/test/java/com/google/inject/spi/ModuleSourceTest.java index a4e5bf1..5494630 100644 --- a/src/test/java/com/google/inject/spi/ModuleSourceTest.java +++ b/src/test/java/com/google/inject/spi/ModuleSourceTest.java @@ -1,27 +1,25 @@ package com.google.inject.spi; -import static org.junit.Assert.assertEquals; import com.google.inject.AbstractModule; import com.google.inject.Binder; import com.google.inject.Module; -import org.junit.Test; +import junit.framework.TestCase; -/** - * Tests for {@link ModuleSource}. - */ -public class ModuleSourceTest { +/** Tests for {@link ModuleSource}. */ +public class ModuleSourceTest extends TestCase { private static final StackTraceElement BINDER_INSTALL = - new StackTraceElement("com.google.inject.spi.Elements$RecordingBinder", "install", - "Unknown Source", 235 /* line number*/); + new StackTraceElement( + "com.google.inject.spi.Elements$RecordingBinder", + "install", + "Unknown Source", + 235 /* line number*/); - @Test public void testOneModule() { ModuleSource moduleSource = createWithSizeOne(); checkSizeOne(moduleSource); } - @Test public void testTwoModules() { ModuleSource moduleSource = createWithSizeTwo(); checkSizeTwo(moduleSource); @@ -29,7 +27,6 @@ public class ModuleSourceTest { checkSizeOne(moduleSource); } - @Test public void testThreeModules() { ModuleSource moduleSource = createWithSizeThree(); checkSizeThree(moduleSource); @@ -41,70 +38,28 @@ public class ModuleSourceTest { private void checkSizeOne(ModuleSource moduleSource) { assertEquals(1, moduleSource.size()); - assertEquals(1, moduleSource.getStackTraceSize()); - // Check call stack - StackTraceElement[] callStack = moduleSource.getStackTrace(); - assertEquals(BINDER_INSTALL, callStack[0]); } private void checkSizeTwo(ModuleSource moduleSource) { assertEquals(2, moduleSource.size()); - assertEquals(3, moduleSource.getStackTraceSize()); - // Check call stack - StackTraceElement[] callStack = moduleSource.getStackTrace(); - assertEquals(BINDER_INSTALL, callStack[0]); - assertEquals( - new StackTraceElement( - "com.google.inject.spi.moduleSourceTest$A", "configure", "Unknown Source", 100), - callStack[1]); - assertEquals(BINDER_INSTALL, callStack[2]); } private void checkSizeThree(ModuleSource moduleSource) { assertEquals(3, moduleSource.size()); - assertEquals(7, moduleSource.getStackTraceSize()); - // Check call stack - StackTraceElement[] callStack = moduleSource.getStackTrace(); - assertEquals(BINDER_INSTALL, callStack[0]); - assertEquals(new StackTraceElement("class1", "method1", "Unknown Source", 1), callStack[1]); - assertEquals(new StackTraceElement("class2", "method2", "Unknown Source", 2), callStack[2]); - assertEquals( - new StackTraceElement( - "com.google.inject.spi.moduleSourceTest$B", "configure", "Unknown Source", 200), - callStack[3]); - assertEquals(BINDER_INSTALL, callStack[4]); - - assertEquals( - new StackTraceElement( - "com.google.inject.spi.moduleSourceTest$A", "configure", "Unknown Source", 100), - callStack[5]); - assertEquals(BINDER_INSTALL, callStack[6]); } private ModuleSource createWithSizeOne() { - StackTraceElement[] partialCallStack = new StackTraceElement[1]; - partialCallStack[0] = BINDER_INSTALL; - return new ModuleSource(new A(), partialCallStack); + return new ModuleSource(A.class, null); } private ModuleSource createWithSizeTwo() { ModuleSource moduleSource = createWithSizeOne(); - StackTraceElement[] partialCallStack = new StackTraceElement[2]; - partialCallStack[0] = BINDER_INSTALL; - partialCallStack[1] = new StackTraceElement( - "com.google.inject.spi.moduleSourceTest$A", "configure", "moduleSourceTest.java", 100); - return moduleSource.createChild(new B(), partialCallStack); + return moduleSource.createChild(B.class); } private ModuleSource createWithSizeThree() { ModuleSource moduleSource = createWithSizeTwo(); - StackTraceElement[] partialCallStack = new StackTraceElement[4]; - partialCallStack[0] = BINDER_INSTALL; - partialCallStack[1] = new StackTraceElement("class1", "method1", "Class1.java", 1); - partialCallStack[2] = new StackTraceElement("class2", "method2", "Class2.java", 2); - partialCallStack[3] = new StackTraceElement( - "com.google.inject.spi.moduleSourceTest$B", "configure", "moduleSourceTest.java", 200); - return moduleSource.createChild(new C(), partialCallStack); + return moduleSource.createChild(C.class); } private static class A extends AbstractModule { @@ -122,6 +77,7 @@ public class ModuleSourceTest { } private static class C extends AbstractModule { + @Override public void configure() { } diff --git a/src/test/java/com/google/inject/spi/SpiBindingsTest.java b/src/test/java/com/google/inject/spi/SpiBindingsTest.java index 3ef3a47..39f1556 100644 --- a/src/test/java/com/google/inject/spi/SpiBindingsTest.java +++ b/src/test/java/com/google/inject/spi/SpiBindingsTest.java @@ -2,7 +2,6 @@ package com.google.inject.spi; import static com.google.inject.Asserts.assertContains; import static com.google.inject.Asserts.getDeclaringSourcePart; -import static com.google.inject.Asserts.isIncludeStackTraceComplete; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; @@ -431,11 +430,6 @@ public class SpiBindingsTest { assertContains(binding.getSource().toString(), getDeclaringSourcePart(getClass())); ElementSource source = (ElementSource) binding.getSource(); assertFalse(source.getModuleClassNames().isEmpty()); - if (isIncludeStackTraceComplete()) { - assertTrue(source.getStackTrace().length > 0); - } else { - assertEquals(0, source.getStackTrace().length); - } } public void checkInjector(Module module, ElementVisitor... visitors) {