Skip to content

Commit 8fb238e

Browse files
committed
Merge pull request spring-projects#41611 from ivamly
* spring-projectsgh-41611: Polish "Add rule to prevent calls to Objects.requireNonNull()" Add rule to prevent calls to Objects.requireNonNull() Closes spring-projectsgh-41611
2 parents 1a8e9c1 + 4ee24bf commit 8fb238e

File tree

8 files changed

+148
-6
lines changed

8 files changed

+148
-6
lines changed

buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java

+26-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022-2023 the original author or authors.
2+
* Copyright 2022-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,7 +22,10 @@
2222
import java.net.URLEncoder;
2323
import java.nio.file.Files;
2424
import java.nio.file.StandardOpenOption;
25+
import java.util.Collections;
2526
import java.util.List;
27+
import java.util.Objects;
28+
import java.util.function.Supplier;
2629
import java.util.stream.Collectors;
2730

2831
import com.tngtech.archunit.base.DescribedPredicate;
@@ -47,6 +50,7 @@
4750
import org.gradle.api.file.FileCollection;
4851
import org.gradle.api.file.FileTree;
4952
import org.gradle.api.provider.ListProperty;
53+
import org.gradle.api.provider.Property;
5054
import org.gradle.api.tasks.IgnoreEmptyDirectories;
5155
import org.gradle.api.tasks.Input;
5256
import org.gradle.api.tasks.InputFiles;
@@ -63,19 +67,23 @@
6367
*
6468
* @author Andy Wilkinson
6569
* @author Yanming Zhou
70+
* @author Ivan Malutin
6671
*/
6772
public abstract class ArchitectureCheck extends DefaultTask {
6873

6974
private FileCollection classes;
7075

7176
public ArchitectureCheck() {
7277
getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName()));
78+
getProhibitObjectsRequireNonNull().convention(true);
7379
getRules().addAll(allPackagesShouldBeFreeOfTangles(),
7480
allBeanPostProcessorBeanMethodsShouldBeStaticAndHaveParametersThatWillNotCausePrematureInitialization(),
7581
allBeanFactoryPostProcessorBeanMethodsShouldBeStaticAndHaveNoParameters(),
7682
noClassesShouldCallStepVerifierStepVerifyComplete(),
7783
noClassesShouldConfigureDefaultStepVerifierTimeout(), noClassesShouldCallCollectorsToList(),
7884
noClassesShouldCallURLEncoderWithStringEncoding(), noClassesShouldCallURLDecoderWithStringEncoding());
85+
getRules().addAll(getProhibitObjectsRequireNonNull()
86+
.map((prohibit) -> prohibit ? noClassesShouldCallObjectsRequireNonNull() : Collections.emptyList()));
7987
getRuleDescriptions().set(getRules().map((rules) -> rules.stream().map(ArchRule::getDescription).toList()));
8088
}
8189

@@ -208,6 +216,18 @@ private ArchRule noClassesShouldCallURLDecoderWithStringEncoding() {
208216
.because("java.net.URLDecoder.decode(String s, Charset charset) should be used instead");
209217
}
210218

219+
private List<ArchRule> noClassesShouldCallObjectsRequireNonNull() {
220+
return List.of(
221+
ArchRuleDefinition.noClasses()
222+
.should()
223+
.callMethod(Objects.class, "requireNonNull", Object.class, String.class)
224+
.because("org.springframework.utils.Assert.notNull(Object, String) should be used instead"),
225+
ArchRuleDefinition.noClasses()
226+
.should()
227+
.callMethod(Objects.class, "requireNonNull", Object.class, Supplier.class)
228+
.because("org.springframework.utils.Assert.notNull(Object, Supplier) should be used instead"));
229+
}
230+
211231
public void setClasses(FileCollection classes) {
212232
this.classes = classes;
213233
}
@@ -236,8 +256,12 @@ final FileTree getInputClasses() {
236256
@Internal
237257
public abstract ListProperty<ArchRule> getRules();
238258

259+
@Internal
260+
public abstract Property<Boolean> getProhibitObjectsRequireNonNull();
261+
239262
@Input
240-
// The rules themselves can't be an input as they aren't serializable so we use their
263+
// The rules themselves can't be an input as they aren't serializable so we use
264+
// their
241265
// descriptions instead
242266
abstract ListProperty<String> getRuleDescriptions();
243267

buildSrc/src/test/java/org/springframework/boot/build/architecture/ArchitectureCheckTests.java

+26-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,6 +36,7 @@
3636
* Tests for {@link ArchitectureCheck}.
3737
*
3838
* @author Andy Wilkinson
39+
* @author Ivan Malutin
3940
*/
4041
class ArchitectureCheckTests {
4142

@@ -121,6 +122,30 @@ void whenBeanFactoryPostProcessorBeanMethodIsStaticAndHasNoParametersTaskSucceed
121122
});
122123
}
123124

125+
@Test
126+
void whenClassDoesNotCallObjectsRequireNonNullTaskSucceedsAndWritesAnEmptyReport() throws Exception {
127+
prepareTask("objects/noRequireNonNull", (architectureCheck) -> {
128+
architectureCheck.checkArchitecture();
129+
assertThat(failureReport(architectureCheck)).isEmpty();
130+
});
131+
}
132+
133+
@Test
134+
void whenClassCallsObjectsRequireNonNullWithMessageTaskFailsAndWritesReport() throws Exception {
135+
prepareTask("objects/requireNonNullWithString", (architectureCheck) -> {
136+
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture);
137+
assertThat(failureReport(architectureCheck)).isNotEmpty();
138+
});
139+
}
140+
141+
@Test
142+
void whenClassCallsObjectsRequireNonNullWithSupplierTaskFailsAndWritesReport() throws Exception {
143+
prepareTask("objects/requireNonNullWithSupplier", (architectureCheck) -> {
144+
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture);
145+
assertThat(failureReport(architectureCheck)).isNotEmpty();
146+
});
147+
}
148+
124149
private void prepareTask(String classes, Callback<ArchitectureCheck> callback) throws Exception {
125150
File projectDir = new File(this.temp, "project");
126151
projectDir.mkdirs();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.build.architecture.objects.noRequireNonNull;
18+
19+
import java.util.Collections;
20+
21+
import org.springframework.util.Assert;
22+
23+
class NoRequireNonNull {
24+
25+
void exampleMethod() {
26+
Assert.notNull(new Object(), "Object must not be null");
27+
// Compilation of a method reference generates code that uses
28+
// Objects.requireNonNull(Object). Check that it doesn't cause a failure.
29+
Collections.emptyList().forEach(System.out::println);
30+
}
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.build.architecture.objects.requireNonNullWithString;
18+
19+
import java.util.Objects;
20+
21+
class RequireNonNullWithString {
22+
23+
void exampleMethod() {
24+
Objects.requireNonNull(new Object(), "Object cannot be null");
25+
}
26+
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.build.architecture.objects.requireNonNullWithSupplier;
18+
19+
import java.util.Objects;
20+
21+
class RequireNonNullWithSupplier {
22+
23+
void exampleMethod() {
24+
Objects.requireNonNull(new Object(), () -> "Object cannot be null");
25+
}
26+
27+
}

spring-boot-project/spring-boot-tools/spring-boot-loader-classic/build.gradle

+4
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ dependencies {
2121
testRuntimeOnly("org.bouncycastle:bcprov-jdk18on:1.78.1")
2222
testRuntimeOnly("org.springframework:spring-webmvc")
2323
}
24+
25+
tasks.named("checkArchitectureMain").configure {
26+
prohibitObjectsRequireNonNull = false
27+
}

spring-boot-project/spring-boot-tools/spring-boot-loader/build.gradle

+4
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ dependencies {
2121
testRuntimeOnly("org.bouncycastle:bcprov-jdk18on:1.78.1")
2222
testRuntimeOnly("org.springframework:spring-webmvc")
2323
}
24+
25+
tasks.named("checkArchitectureMain").configure {
26+
prohibitObjectsRequireNonNull = false
27+
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import java.util.EnumMap;
2525
import java.util.List;
2626
import java.util.Map;
27-
import java.util.Objects;
2827
import java.util.Optional;
2928

3029
import com.jayway.jsonpath.JsonPath;
@@ -616,8 +615,8 @@ static class ExampleFailingConstructorBean {
616615
private final Object value;
617616

618617
ExampleFailingConstructorBean(String name, String value) {
619-
Objects.requireNonNull(name, "'name' must be not null.");
620-
Objects.requireNonNull(value, "'value' must be not null.");
618+
Assert.notNull(name, "'name' must be not null.");
619+
Assert.notNull(value, "'value' must be not null.");
621620
this.name = name;
622621
this.value = value;
623622
}

0 commit comments

Comments
 (0)