Skip to content

Commit e5928f7

Browse files
committed
Merge branch '3.5.x'
Closes gh-48355
2 parents 811ddcd + 6bdf47b commit e5928f7

File tree

21 files changed

+796
-68
lines changed

21 files changed

+796
-68
lines changed

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

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import java.util.ArrayList;
2727
import java.util.Collections;
2828
import java.util.List;
29-
import java.util.Map;
3029
import java.util.concurrent.Callable;
3130
import java.util.function.Supplier;
3231
import java.util.stream.Stream;
@@ -74,27 +73,26 @@
7473
*/
7574
public abstract class ArchitectureCheck extends DefaultTask {
7675

77-
static final String CONDITIONAL_ON_CLASS = "ConditionalOnClass";
78-
79-
static final String DEPRECATED_CONFIGURATION_PROPERTY = "DeprecatedConfigurationProperty";
80-
81-
private static final String CONDITIONAL_ON_CLASS_ANNOTATION = "org.springframework.boot.autoconfigure.condition.ConditionalOnClass";
82-
83-
private static final String DEPRECATED_CONFIGURATION_PROPERTY_ANNOTATION = "org.springframework.boot.context.properties.DeprecatedConfigurationProperty";
84-
8576
private FileCollection classes;
8677

8778
public ArchitectureCheck() {
8879
getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName()));
89-
getAnnotationClasses().convention(Map.of(CONDITIONAL_ON_CLASS, CONDITIONAL_ON_CLASS_ANNOTATION,
90-
DEPRECATED_CONFIGURATION_PROPERTY, DEPRECATED_CONFIGURATION_PROPERTY_ANNOTATION));
80+
getAnnotationClasses().convention(ArchitectureCheckAnnotation.asMap());
9181
getRules().addAll(getProhibitObjectsRequireNonNull().convention(true)
9282
.map(whenTrue(ArchitectureRules::noClassesShouldCallObjectsRequireNonNull)));
9383
getRules().addAll(ArchitectureRules.standard());
94-
getRules().addAll(whenMainSources(() -> ArchitectureRules
95-
.beanMethods(annotationClassFor(CONDITIONAL_ON_CLASS, CONDITIONAL_ON_CLASS_ANNOTATION))));
96-
getRules().addAll(whenMainSources(() -> ArchitectureRules.configurationProperties(
97-
annotationClassFor(DEPRECATED_CONFIGURATION_PROPERTY, DEPRECATED_CONFIGURATION_PROPERTY_ANNOTATION))));
84+
getRules().addAll(whenMainSources(() -> ArchitectureRules.beanMethods(ArchitectureCheckAnnotation
85+
.classFor(getAnnotationClasses().get(), ArchitectureCheckAnnotation.CONDITIONAL_ON_CLASS))));
86+
getRules().addAll(whenMainSources(() -> ArchitectureRules.conditionalOnMissingBean(ArchitectureCheckAnnotation
87+
.classFor(getAnnotationClasses().get(), ArchitectureCheckAnnotation.CONDITIONAL_ON_MISSING_BEAN))));
88+
getRules().addAll(whenMainSources(() -> ArchitectureRules.configurationProperties(ArchitectureCheckAnnotation
89+
.classFor(getAnnotationClasses().get(), ArchitectureCheckAnnotation.CONFIGURATION_PROPERTIES))));
90+
getRules().addAll(whenMainSources(
91+
() -> ArchitectureRules.configurationPropertiesBinding(ArchitectureCheckAnnotation.classFor(
92+
getAnnotationClasses().get(), ArchitectureCheckAnnotation.CONFIGURATION_PROPERTIES_BINDING))));
93+
getRules().addAll(whenMainSources(
94+
() -> ArchitectureRules.configurationPropertiesDeprecation(ArchitectureCheckAnnotation.classFor(
95+
getAnnotationClasses().get(), ArchitectureCheckAnnotation.DEPRECATED_CONFIGURATION_PROPERTY))));
9896
getRules().addAll(and(getNullMarkedEnabled(), isMainSourceSet()).map(whenTrue(() -> Collections.singletonList(
9997
ArchitectureRules.packagesShouldBeAnnotatedWithNullMarked(getNullMarkedIgnoredPackages().get())))));
10098
getRuleDescriptions().set(getRules().map(this::asDescriptions));
@@ -120,10 +118,6 @@ private List<String> asDescriptions(List<ArchRule> rules) {
120118
return rules.stream().map(ArchRule::getDescription).toList();
121119
}
122120

123-
private String annotationClassFor(String name, String defaultValue) {
124-
return getAnnotationClasses().get().getOrDefault(name, defaultValue);
125-
}
126-
127121
@TaskAction
128122
void checkArchitecture() throws Exception {
129123
withCompileClasspath(() -> {
@@ -223,7 +217,7 @@ final FileTree getInputClasses() {
223217
@Internal
224218
abstract SetProperty<String> getNullMarkedIgnoredPackages();
225219

226-
@Input
220+
@Internal
227221
abstract MapProperty<String, String> getAnnotationClasses();
228222

229223
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2012-present 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;
18+
19+
import java.util.Map;
20+
21+
/**
22+
* Annotations used in architecture checks, which can be overridden in architecture rule
23+
* tests.
24+
*
25+
* @author Scott Frederick
26+
* @author Stephane Nicoll
27+
*/
28+
public enum ArchitectureCheckAnnotation {
29+
30+
/**
31+
* Condition on class check.
32+
*/
33+
CONDITIONAL_ON_CLASS,
34+
35+
/**
36+
* Condition on missing bean check.
37+
*/
38+
CONDITIONAL_ON_MISSING_BEAN,
39+
40+
/**
41+
* Configuration properties bean.
42+
*/
43+
CONFIGURATION_PROPERTIES,
44+
45+
/**
46+
* Deprecated configuration property.
47+
*/
48+
DEPRECATED_CONFIGURATION_PROPERTY,
49+
50+
/**
51+
* Custom implementation to bind configuration properties.
52+
*/
53+
CONFIGURATION_PROPERTIES_BINDING;
54+
55+
private static final Map<String, String> annotationNameToClassName = Map.of(CONDITIONAL_ON_CLASS.name(),
56+
"org.springframework.boot.autoconfigure.condition.ConditionalOnClass", CONDITIONAL_ON_MISSING_BEAN.name(),
57+
"org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean",
58+
CONFIGURATION_PROPERTIES.name(), "org.springframework.boot.context.properties.ConfigurationProperties",
59+
DEPRECATED_CONFIGURATION_PROPERTY.name(),
60+
"org.springframework.boot.context.properties.DeprecatedConfigurationProperty",
61+
CONFIGURATION_PROPERTIES_BINDING.name(),
62+
"org.springframework.boot.context.properties.ConfigurationPropertiesBinding");
63+
64+
static Map<String, String> asMap() {
65+
return annotationNameToClassName;
66+
}
67+
68+
static String classFor(Map<String, String> annotationProperty, ArchitectureCheckAnnotation annotation) {
69+
String name = annotation.name();
70+
return annotationProperty.getOrDefault(name, asMap().get(name));
71+
}
72+
73+
}

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

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -104,25 +104,35 @@ static List<ArchRule> standard() {
104104
rules.add(noClassesShouldLoadResourcesUsingResourceUtils());
105105
rules.add(noClassesShouldCallStringToUpperCaseWithoutLocale());
106106
rules.add(noClassesShouldCallStringToLowerCaseWithoutLocale());
107-
rules.add(conditionalOnMissingBeanShouldNotSpecifyOnlyATypeThatIsTheSameAsMethodReturnType());
108107
rules.add(enumSourceShouldNotHaveValueThatIsTheSameAsTypeOfMethodsFirstParameter());
109-
rules.add(classLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute());
110-
rules.add(methodLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute());
111108
rules.add(conditionsShouldNotBePublic());
112-
rules.add(allConfigurationPropertiesBindingBeanMethodsShouldBeStatic());
113109
rules.add(autoConfigurationClassesShouldBePublicAndFinal());
114110
rules.add(autoConfigurationClassesShouldHaveNoPublicMembers());
115111
rules.add(testAutoConfigurationClassesShouldBePackagePrivateAndFinal());
116112
return List.copyOf(rules);
117113
}
118114

119-
static List<ArchRule> beanMethods(String annotationName) {
115+
static List<ArchRule> beanMethods(String annotationClass) {
120116
return List.of(allBeanMethodsShouldReturnNonPrivateType(),
121-
allBeanMethodsShouldNotHaveConditionalOnClassAnnotation(annotationName));
117+
allBeanMethodsShouldNotHaveConditionalOnClassAnnotation(annotationClass));
122118
}
123119

124-
static List<ArchRule> configurationProperties(String annotationName) {
125-
return List.of(allDeprecatedConfigurationPropertiesShouldIncludeSince(annotationName));
120+
static List<ArchRule> conditionalOnMissingBean(String annotationClass) {
121+
return List
122+
.of(conditionalOnMissingBeanShouldNotSpecifyOnlyATypeThatIsTheSameAsMethodReturnType(annotationClass));
123+
}
124+
125+
static List<ArchRule> configurationProperties(String annotationClass) {
126+
return List.of(classLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute(annotationClass),
127+
methodLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute(annotationClass));
128+
}
129+
130+
static List<ArchRule> configurationPropertiesBinding(String annotationClass) {
131+
return List.of(allConfigurationPropertiesBindingBeanMethodsShouldBeStatic(annotationClass));
132+
}
133+
134+
static List<ArchRule> configurationPropertiesDeprecation(String annotationClass) {
135+
return List.of(allDeprecatedConfigurationPropertiesShouldIncludeSince(annotationClass));
126136
}
127137

128138
private static ArchRule allBeanMethodsShouldReturnNonPrivateType() {
@@ -267,9 +277,10 @@ private static ArchRule noClassesShouldCallStringToLowerCaseWithoutLocale() {
267277
.because(shouldUse("String.toLowerCase(Locale.ROOT)"));
268278
}
269279

270-
private static ArchRule conditionalOnMissingBeanShouldNotSpecifyOnlyATypeThatIsTheSameAsMethodReturnType() {
271-
return methodsThatAreAnnotatedWith("org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean")
272-
.should(notSpecifyOnlyATypeThatIsTheSameAsTheMethodReturnType())
280+
private static ArchRule conditionalOnMissingBeanShouldNotSpecifyOnlyATypeThatIsTheSameAsMethodReturnType(
281+
String annotation) {
282+
return methodsThatAreAnnotatedWith(annotation)
283+
.should(notSpecifyOnlyATypeThatIsTheSameAsTheMethodReturnType(annotation))
273284
.allowEmptyShould(true);
274285
}
275286

@@ -279,10 +290,10 @@ static ArchRule packagesShouldBeAnnotatedWithNullMarked(Set<String> ignoredPacka
279290
.allowEmptyShould(true);
280291
}
281292

282-
private static ArchCondition<? super JavaMethod> notSpecifyOnlyATypeThatIsTheSameAsTheMethodReturnType() {
293+
private static ArchCondition<? super JavaMethod> notSpecifyOnlyATypeThatIsTheSameAsTheMethodReturnType(
294+
String annotation) {
283295
return check("not specify only a type that is the same as the method's return type", (item, events) -> {
284-
JavaAnnotation<JavaMethod> conditionalAnnotation = item
285-
.getAnnotationOfType("org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean");
296+
JavaAnnotation<JavaMethod> conditionalAnnotation = item.getAnnotationOfType(annotation);
286297
Map<String, Object> properties = conditionalAnnotation.getProperties();
287298
if (!hasProperty("type", properties) && !hasProperty("name", properties)) {
288299
conditionalAnnotation.get("value").ifPresent((value) -> {
@@ -300,7 +311,7 @@ private static boolean hasProperty(String name, Map<String, Object> properties)
300311
if (property == null) {
301312
return false;
302313
}
303-
return !property.getClass().isArray() || ((Object[]) property).length > 0;
314+
return (property.getClass().isArray()) ? ((Object[]) property).length > 0 : !property.toString().isEmpty();
304315
}
305316

306317
private static ArchRule enumSourceShouldNotHaveValueThatIsTheSameAsTypeOfMethodsFirstParameter() {
@@ -329,33 +340,37 @@ private static void notSpecifyOnlyATypeThatIsTheSameAsTheMethodParameterType(Jav
329340
});
330341
}
331342

332-
private static ArchRule classLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute() {
343+
private static ArchRule classLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute(
344+
String annotationClass) {
333345
return ArchRuleDefinition.classes()
334346
.that()
335-
.areAnnotatedWith("org.springframework.boot.context.properties.ConfigurationProperties")
336-
.should(notSpecifyOnlyPrefixAttributeOfConfigurationProperties())
347+
.areAnnotatedWith(annotationClass)
348+
.should(notSpecifyOnlyPrefixAttributeOfConfigurationProperties(annotationClass))
337349
.allowEmptyShould(true);
338350
}
339351

340-
private static ArchRule methodLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute() {
352+
private static ArchRule methodLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute(
353+
String annotationClass) {
341354
return ArchRuleDefinition.methods()
342355
.that()
343-
.areAnnotatedWith("org.springframework.boot.context.properties.ConfigurationProperties")
344-
.should(notSpecifyOnlyPrefixAttributeOfConfigurationProperties())
356+
.areAnnotatedWith(annotationClass)
357+
.should(notSpecifyOnlyPrefixAttributeOfConfigurationProperties(annotationClass))
345358
.allowEmptyShould(true);
346359
}
347360

348-
private static ArchCondition<? super HasAnnotations<?>> notSpecifyOnlyPrefixAttributeOfConfigurationProperties() {
349-
return check("not specify only prefix attribute of @ConfigurationProperties",
350-
ArchitectureRules::notSpecifyOnlyPrefixAttributeOfConfigurationProperties);
361+
private static ArchCondition<? super HasAnnotations<?>> notSpecifyOnlyPrefixAttributeOfConfigurationProperties(
362+
String annotationClass) {
363+
return check("not specify only prefix attribute of @ConfigurationProperties", (item,
364+
events) -> notSpecifyOnlyPrefixAttributeOfConfigurationProperties(annotationClass, item, events));
351365
}
352366

353-
private static void notSpecifyOnlyPrefixAttributeOfConfigurationProperties(HasAnnotations<?> item,
354-
ConditionEvents events) {
355-
JavaAnnotation<?> configurationPropertiesAnnotation = item
356-
.getAnnotationOfType("org.springframework.boot.context.properties.ConfigurationProperties");
367+
private static void notSpecifyOnlyPrefixAttributeOfConfigurationProperties(String annotationClass,
368+
HasAnnotations<?> item, ConditionEvents events) {
369+
JavaAnnotation<?> configurationPropertiesAnnotation = item.getAnnotationOfType(annotationClass);
357370
Map<String, Object> properties = configurationPropertiesAnnotation.getProperties();
358-
if (properties.size() == 1 && properties.containsKey("prefix")) {
371+
if (hasProperty("prefix", properties) && !hasProperty("value", properties)
372+
&& properties.get("ignoreInvalidFields").equals(false)
373+
&& properties.get("ignoreUnknownFields").equals(true)) {
359374
addViolation(events, item, configurationPropertiesAnnotation.getDescription()
360375
+ " should specify implicit 'value' attribute other than explicit 'prefix' attribute");
361376
}
@@ -375,9 +390,9 @@ private static ArchRule conditionsShouldNotBePublic() {
375390
.allowEmptyShould(true);
376391
}
377392

378-
private static ArchRule allConfigurationPropertiesBindingBeanMethodsShouldBeStatic() {
393+
private static ArchRule allConfigurationPropertiesBindingBeanMethodsShouldBeStatic(String annotationClass) {
379394
return methodsThatAreAnnotatedWith("org.springframework.context.annotation.Bean").and()
380-
.areAnnotatedWith("org.springframework.boot.context.properties.ConfigurationPropertiesBinding")
395+
.areAnnotatedWith(annotationClass)
381396
.should()
382397
.beStatic()
383398
.allowEmptyShould(true);

0 commit comments

Comments
 (0)