diff --git a/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java b/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java index ae88a3e8c8d..ed1cb29a69e 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 the original author or authors. + * Copyright 2022-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.file.Files; import java.nio.file.StandardOpenOption; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.function.Supplier; @@ -49,6 +50,7 @@ import org.gradle.api.file.DirectoryProperty; import org.gradle.api.file.FileCollection; import org.gradle.api.file.FileTree; import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; import org.gradle.api.tasks.IgnoreEmptyDirectories; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFiles; @@ -65,6 +67,7 @@ import org.gradle.api.tasks.TaskAction; * * @author Andy Wilkinson * @author Yanming Zhou + * @author Ivan Malutin */ public abstract class ArchitectureCheck extends DefaultTask { @@ -72,14 +75,15 @@ public abstract class ArchitectureCheck extends DefaultTask { public ArchitectureCheck() { getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName())); + getProhibitObjectsRequireNonNull().convention(true); getRules().addAll(allPackagesShouldBeFreeOfTangles(), allBeanPostProcessorBeanMethodsShouldBeStaticAndHaveParametersThatWillNotCausePrematureInitialization(), allBeanFactoryPostProcessorBeanMethodsShouldBeStaticAndHaveNoParameters(), noClassesShouldCallStepVerifierStepVerifyComplete(), noClassesShouldConfigureDefaultStepVerifierTimeout(), noClassesShouldCallCollectorsToList(), - noClassesShouldCallURLEncoderWithStringEncoding(), noClassesShouldCallURLDecoderWithStringEncoding(), - noClassesShouldCallObjectsRequireNonNullWithMessage(), - noClassesShouldCallObjectsRequireNonNullWithSupplier()); + noClassesShouldCallURLEncoderWithStringEncoding(), noClassesShouldCallURLDecoderWithStringEncoding()); + getRules().addAll(getProhibitObjectsRequireNonNull() + .map((prohibit) -> prohibit ? noClassesShouldCallObjectsRequireNonNull() : Collections.emptyList())); getRuleDescriptions().set(getRules().map((rules) -> rules.stream().map(ArchRule::getDescription).toList())); } @@ -212,18 +216,16 @@ public abstract class ArchitectureCheck extends DefaultTask { .because("java.net.URLDecoder.decode(String s, Charset charset) should be used instead"); } - private ArchRule noClassesShouldCallObjectsRequireNonNullWithMessage() { - return ArchRuleDefinition.noClasses() - .should() - .callMethod(Objects.class, "requireNonNull", Object.class, String.class) - .because("Use org.springframework.utils.Assert.notNull(Object, String) should be used instead"); - } - - private ArchRule noClassesShouldCallObjectsRequireNonNullWithSupplier() { - return ArchRuleDefinition.noClasses() - .should() - .callMethod(Objects.class, "requireNonNull", Object.class, Supplier.class) - .because("Use org.springframework.utils.Assert.notNull(Object, Supplier) should be used instead"); + private List noClassesShouldCallObjectsRequireNonNull() { + return List.of( + ArchRuleDefinition.noClasses() + .should() + .callMethod(Objects.class, "requireNonNull", Object.class, String.class) + .because("org.springframework.utils.Assert.notNull(Object, String) should be used instead"), + ArchRuleDefinition.noClasses() + .should() + .callMethod(Objects.class, "requireNonNull", Object.class, Supplier.class) + .because("org.springframework.utils.Assert.notNull(Object, Supplier) should be used instead")); } public void setClasses(FileCollection classes) { @@ -254,6 +256,9 @@ public abstract class ArchitectureCheck extends DefaultTask { @Internal public abstract ListProperty getRules(); + @Internal + public abstract Property getProhibitObjectsRequireNonNull(); + @Input // The rules themselves can't be an input as they aren't serializable so we use // their diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/ArchitectureCheckTests.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/ArchitectureCheckTests.java index b10d8009e7c..598d8d2fbe2 100644 --- a/buildSrc/src/test/java/org/springframework/boot/build/architecture/ArchitectureCheckTests.java +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/ArchitectureCheckTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * Tests for {@link ArchitectureCheck}. * * @author Andy Wilkinson + * @author Ivan Malutin */ class ArchitectureCheckTests { @@ -122,18 +123,18 @@ class ArchitectureCheckTests { } @Test - void whenClassCallsObjectsRequireNonNullWithMessageTaskFailsAndWritesReport() throws Exception { - prepareTask("objects/requireNonNullWithMessage", (architectureCheck) -> { - assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture); - assertThat(failureReport(architectureCheck)).isNotEmpty(); + void whenClassDoesNotCallObjectsRequireNonNullTaskSucceedsAndWritesAnEmptyReport() throws Exception { + prepareTask("objects/noRequireNonNull", (architectureCheck) -> { + architectureCheck.checkArchitecture(); + assertThat(failureReport(architectureCheck)).isEmpty(); }); } @Test - void whenClassDoesNotCallObjectsRequireNonNullWithMessageTaskSucceedsAndWritesAnEmptyReport() throws Exception { - prepareTask("objects/noRequireNonNullWithMessage", (architectureCheck) -> { - architectureCheck.checkArchitecture(); - assertThat(failureReport(architectureCheck)).isEmpty(); + void whenClassCallsObjectsRequireNonNullWithMessageTaskFailsAndWritesReport() throws Exception { + prepareTask("objects/requireNonNullWithString", (architectureCheck) -> { + assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture); + assertThat(failureReport(architectureCheck)).isNotEmpty(); }); } @@ -145,14 +146,6 @@ class ArchitectureCheckTests { }); } - @Test - void whenClassDoesNotCallObjectsRequireNonNullWithSupplierTaskSucceedsAndWritesAnEmptyReport() throws Exception { - prepareTask("objects/noRequireNonNullWithSupplier", (architectureCheck) -> { - architectureCheck.checkArchitecture(); - assertThat(failureReport(architectureCheck)).isEmpty(); - }); - } - private void prepareTask(String classes, Callback callback) throws Exception { File projectDir = new File(this.temp, "project"); projectDir.mkdirs(); diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNullWithMessage/NoRequireNonNullWithMessageUsage.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNull/NoRequireNonNull.java similarity index 65% rename from buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNullWithMessage/NoRequireNonNullWithMessageUsage.java rename to buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNull/NoRequireNonNull.java index 066faa77465..96542f00908 100644 --- a/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNullWithMessage/NoRequireNonNullWithMessageUsage.java +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNull/NoRequireNonNull.java @@ -14,24 +14,19 @@ * limitations under the License. */ -package org.springframework.boot.build.architecture.objects.noRequireNonNullWithMessage; +package org.springframework.boot.build.architecture.objects.noRequireNonNull; + +import java.util.Collections; import org.springframework.util.Assert; -/** - * This class uses `Assert.notNull(Object, String)` instead of - * `Objects.requireNonNull(Object, String)`, and should pass the architecture check. - * - * @author Ivan Malutin - */ -public class NoRequireNonNullWithMessageUsage { +class NoRequireNonNull { - /** - * Example method that uses `Assert.notNull(Object, String)`, which should not be - * flagged by the architecture check. - */ - public void exampleMethod() { + void exampleMethod() { Assert.notNull(new Object(), "Object must not be null"); + // Compilation of a method reference generates code that uses + // Objects.requireNonNull(Object). Check that it doesn't cause a failure. + Collections.emptyList().forEach(System.out::println); } } diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNullWithSupplier/NoRequireNonNullWithSupplierUsage.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNullWithSupplier/NoRequireNonNullWithSupplierUsage.java deleted file mode 100644 index 36ab315e410..00000000000 --- a/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNullWithSupplier/NoRequireNonNullWithSupplierUsage.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.build.architecture.objects.noRequireNonNullWithSupplier; - -import org.springframework.util.Assert; - -/** - * This class uses `Assert.notNull(Object, Supplier)` instead of - * `Objects.requireNonNull(Object, Supplier)`, and should pass the architecture check. - * - * @author Ivan Malutin - */ -public class NoRequireNonNullWithSupplierUsage { - - /** - * Example method that uses `Assert.notNull(Object, Supplier)`, which should not be - * flagged by the architecture check. - */ - public void exampleMethod() { - Assert.notNull(new Object(), () -> "Object must not be null"); - } - -} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithMessage/RequireNonNullWithMessageUsage.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithString/RequireNonNullWithString.java similarity index 64% rename from buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithMessage/RequireNonNullWithMessageUsage.java rename to buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithString/RequireNonNullWithString.java index 9e7ee8241f1..583cf65cbd7 100644 --- a/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithMessage/RequireNonNullWithMessageUsage.java +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithString/RequireNonNullWithString.java @@ -14,23 +14,13 @@ * limitations under the License. */ -package org.springframework.boot.build.architecture.objects.requireNonNullWithMessage; +package org.springframework.boot.build.architecture.objects.requireNonNullWithString; import java.util.Objects; -/** - * This class is intentionally designed to test the use of `Objects.requireNonNull(Object, - * String)`, which should trigger a failure in the architecture check. - * - * @author Ivan Malutin - */ -public class RequireNonNullWithMessageUsage { +class RequireNonNullWithString { - /** - * Example method that uses `Objects.requireNonNull(Object, String)`, which should be - * flagged by the architecture check. - */ - public void exampleMethod() { + void exampleMethod() { Objects.requireNonNull(new Object(), "Object cannot be null"); } diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithSupplier/RequireNonNullWithSupplierUsage.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithSupplier/RequireNonNullWithSupplier.java similarity index 66% rename from buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithSupplier/RequireNonNullWithSupplierUsage.java rename to buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithSupplier/RequireNonNullWithSupplier.java index 5f6529b22f1..bc5989d31af 100644 --- a/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithSupplier/RequireNonNullWithSupplierUsage.java +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithSupplier/RequireNonNullWithSupplier.java @@ -18,19 +18,9 @@ package org.springframework.boot.build.architecture.objects.requireNonNullWithSu import java.util.Objects; -/** - * This class is intentionally designed to test the use of `Objects.requireNonNull(Object, - * Supplier)`, which should trigger a failure in the architecture check. - * - * @author Ivan Malutin - */ -public class RequireNonNullWithSupplierUsage { +class RequireNonNullWithSupplier { - /** - * Example method that uses `Objects.requireNonNull(Object, Supplier)`, which should - * be flagged by the architecture check. - */ - public void exampleMethod() { + void exampleMethod() { Objects.requireNonNull(new Object(), () -> "Object cannot be null"); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/build.gradle index e04b7b35ceb..d45e33698c5 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/build.gradle @@ -21,3 +21,7 @@ dependencies { testRuntimeOnly("org.bouncycastle:bcprov-jdk18on:1.78.1") testRuntimeOnly("org.springframework:spring-webmvc") } + +tasks.named("checkArchitectureMain").configure { + prohibitObjectsRequireNonNull = false +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-loader/build.gradle index 2bdc365e07d..0784082e601 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/build.gradle @@ -21,3 +21,7 @@ dependencies { testRuntimeOnly("org.bouncycastle:bcprov-jdk18on:1.78.1") testRuntimeOnly("org.springframework:spring-webmvc") } + +tasks.named("checkArchitectureMain").configure { + prohibitObjectsRequireNonNull = false +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java index 3abd1067907..4b843c7d62c 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java @@ -24,7 +24,6 @@ import java.util.ArrayList; import java.util.EnumMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; import com.jayway.jsonpath.JsonPath; @@ -616,8 +615,8 @@ class ValueObjectBinderTests { private final Object value; ExampleFailingConstructorBean(String name, String value) { - Objects.requireNonNull(name, "'name' must be not null."); - Objects.requireNonNull(value, "'value' must be not null."); + Assert.notNull(name, "'name' must be not null."); + Assert.notNull(value, "'value' must be not null."); this.name = name; this.value = value; }