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 6d019f689145..d54858ba37b6 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 @@ -26,7 +26,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.concurrent.Callable; import java.util.function.Supplier; import java.util.stream.Stream; @@ -60,6 +59,8 @@ import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.VerificationException; +import org.springframework.boot.build.architecture.ArchitectureCheckAnnotations.Annotation; + /** * {@link Task} that checks for architecture problems. * @@ -72,27 +73,26 @@ */ public abstract class ArchitectureCheck extends DefaultTask { - static final String CONDITIONAL_ON_CLASS = "ConditionalOnClass"; - - static final String DEPRECATED_CONFIGURATION_PROPERTY = "DeprecatedConfigurationProperty"; - - private static final String CONDITIONAL_ON_CLASS_ANNOTATION = "org.springframework.boot.autoconfigure.condition.ConditionalOnClass"; - - private static final String DEPRECATED_CONFIGURATION_PROPERTY_ANNOTATION = "org.springframework.boot.context.properties.DeprecatedConfigurationProperty"; - private FileCollection classes; public ArchitectureCheck() { getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName())); - getAnnotationClasses().convention(Map.of(CONDITIONAL_ON_CLASS, CONDITIONAL_ON_CLASS_ANNOTATION, - DEPRECATED_CONFIGURATION_PROPERTY, DEPRECATED_CONFIGURATION_PROPERTY_ANNOTATION)); + getAnnotationClasses().convention(ArchitectureCheckAnnotations.asMap()); getRules().addAll(getProhibitObjectsRequireNonNull().convention(true) .map(whenTrue(ArchitectureRules::noClassesShouldCallObjectsRequireNonNull))); getRules().addAll(ArchitectureRules.standard()); - getRules().addAll(whenMainSources(() -> ArchitectureRules - .beanMethods(annotationClassFor(CONDITIONAL_ON_CLASS, CONDITIONAL_ON_CLASS_ANNOTATION)))); - getRules().addAll(whenMainSources(() -> ArchitectureRules.configurationProperties( - annotationClassFor(DEPRECATED_CONFIGURATION_PROPERTY, DEPRECATED_CONFIGURATION_PROPERTY_ANNOTATION)))); + getRules().addAll(whenMainSources(() -> ArchitectureRules.beanMethods( + ArchitectureCheckAnnotations.classFor(getAnnotationClasses().get(), Annotation.CONDITIONAL_ON_CLASS)))); + getRules().addAll(whenMainSources(() -> ArchitectureRules.conditionalOnMissingBean(ArchitectureCheckAnnotations + .classFor(getAnnotationClasses().get(), Annotation.CONDITIONAL_ON_MISSING_BEAN)))); + getRules().addAll(whenMainSources(() -> ArchitectureRules.configurationProperties(ArchitectureCheckAnnotations + .classFor(getAnnotationClasses().get(), Annotation.CONFIGURATION_PROPERTIES)))); + getRules() + .addAll(whenMainSources(() -> ArchitectureRules.configurationPropertiesBinding(ArchitectureCheckAnnotations + .classFor(getAnnotationClasses().get(), Annotation.CONFIGURATION_PROPERTIES_BINDING)))); + getRules().addAll( + whenMainSources(() -> ArchitectureRules.configurationPropertiesDeprecation(ArchitectureCheckAnnotations + .classFor(getAnnotationClasses().get(), Annotation.DEPRECATED_CONFIGURATION_PROPERTY)))); getRuleDescriptions().set(getRules().map(this::asDescriptions)); } @@ -110,10 +110,6 @@ private List asDescriptions(List rules) { return rules.stream().map(ArchRule::getDescription).toList(); } - private String annotationClassFor(String name, String defaultValue) { - return getAnnotationClasses().get().getOrDefault(name, defaultValue); - } - @TaskAction void checkArchitecture() throws Exception { withCompileClasspath(() -> { @@ -204,7 +200,7 @@ final FileTree getInputClasses() { @Input // Use descriptions as input since rules aren't serializable abstract ListProperty getRuleDescriptions(); - @Input + @Internal abstract MapProperty getAnnotationClasses(); } diff --git a/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheckAnnotations.java b/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheckAnnotations.java new file mode 100644 index 000000000000..cd0f6c6d12f8 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheckAnnotations.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012-present 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; + +import java.util.Map; + +/** + * Annotations used in architecture checks, which can be overridden in architecture rule + * tests. + * + * @author Scott Frederick + */ +public final class ArchitectureCheckAnnotations { + + enum Annotation { + + CONDITIONAL_ON_CLASS, CONDITIONAL_ON_MISSING_BEAN, CONFIGURATION_PROPERTIES, DEPRECATED_CONFIGURATION_PROPERTY, + CONFIGURATION_PROPERTIES_BINDING + + } + + private ArchitectureCheckAnnotations() { + } + + private static final Map annotationNameToClassName = Map.of(Annotation.CONDITIONAL_ON_CLASS.name(), + "org.springframework.boot.autoconfigure.condition.ConditionalOnClass", + Annotation.CONDITIONAL_ON_MISSING_BEAN.name(), + "org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean", + Annotation.CONFIGURATION_PROPERTIES.name(), + "org.springframework.boot.context.properties.ConfigurationProperties", + Annotation.DEPRECATED_CONFIGURATION_PROPERTY.name(), + "org.springframework.boot.context.properties.DeprecatedConfigurationProperty", + Annotation.CONFIGURATION_PROPERTIES_BINDING.name(), + "org.springframework.boot.context.properties.ConfigurationPropertiesBinding"); + + static Map asMap() { + return annotationNameToClassName; + } + + static String classFor(Map annotationProperty, Annotation annotation) { + String name = annotation.name(); + return annotationProperty.getOrDefault(name, asMap().get(name)); + } + +} diff --git a/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureRules.java b/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureRules.java index 17f0e585306a..9d6e8d8d7983 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureRules.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureRules.java @@ -92,22 +92,32 @@ static List standard() { rules.add(noClassesShouldLoadResourcesUsingResourceUtils()); rules.add(noClassesShouldCallStringToUpperCaseWithoutLocale()); rules.add(noClassesShouldCallStringToLowerCaseWithoutLocale()); - rules.add(conditionalOnMissingBeanShouldNotSpecifyOnlyATypeThatIsTheSameAsMethodReturnType()); rules.add(enumSourceShouldNotHaveValueThatIsTheSameAsTypeOfMethodsFirstParameter()); - rules.add(classLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute()); - rules.add(methodLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute()); rules.add(conditionsShouldNotBePublic()); - rules.add(allConfigurationPropertiesBindingBeanMethodsShouldBeStatic()); return List.copyOf(rules); } - static List beanMethods(String annotationName) { + static List beanMethods(String annotationClass) { return List.of(allBeanMethodsShouldReturnNonPrivateType(), - allBeanMethodsShouldNotHaveConditionalOnClassAnnotation(annotationName)); + allBeanMethodsShouldNotHaveConditionalOnClassAnnotation(annotationClass)); } - static List configurationProperties(String annotationName) { - return List.of(allDeprecatedConfigurationPropertiesShouldIncludeSince(annotationName)); + static List conditionalOnMissingBean(String annotationClass) { + return List + .of(conditionalOnMissingBeanShouldNotSpecifyOnlyATypeThatIsTheSameAsMethodReturnType(annotationClass)); + } + + static List configurationProperties(String annotationClass) { + return List.of(classLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute(annotationClass), + methodLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute(annotationClass)); + } + + static List configurationPropertiesBinding(String annotationClass) { + return List.of(allConfigurationPropertiesBindingBeanMethodsShouldBeStatic(annotationClass)); + } + + static List configurationPropertiesDeprecation(String annotationClass) { + return List.of(allDeprecatedConfigurationPropertiesShouldIncludeSince(annotationClass)); } private static ArchRule allBeanMethodsShouldReturnNonPrivateType() { @@ -247,16 +257,17 @@ private static ArchRule noClassesShouldCallStringToLowerCaseWithoutLocale() { .because(shouldUse("String.toLowerCase(Locale.ROOT)")); } - private static ArchRule conditionalOnMissingBeanShouldNotSpecifyOnlyATypeThatIsTheSameAsMethodReturnType() { - return methodsThatAreAnnotatedWith("org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean") - .should(notSpecifyOnlyATypeThatIsTheSameAsTheMethodReturnType()) + private static ArchRule conditionalOnMissingBeanShouldNotSpecifyOnlyATypeThatIsTheSameAsMethodReturnType( + String annotation) { + return methodsThatAreAnnotatedWith(annotation) + .should(notSpecifyOnlyATypeThatIsTheSameAsTheMethodReturnType(annotation)) .allowEmptyShould(true); } - private static ArchCondition notSpecifyOnlyATypeThatIsTheSameAsTheMethodReturnType() { + private static ArchCondition notSpecifyOnlyATypeThatIsTheSameAsTheMethodReturnType( + String annotation) { return check("not specify only a type that is the same as the method's return type", (item, events) -> { - JavaAnnotation conditionalAnnotation = item - .getAnnotationOfType("org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean"); + JavaAnnotation conditionalAnnotation = item.getAnnotationOfType(annotation); Map properties = conditionalAnnotation.getProperties(); if (!hasProperty("type", properties) && !hasProperty("name", properties)) { conditionalAnnotation.get("value").ifPresent((value) -> { @@ -274,7 +285,7 @@ private static boolean hasProperty(String name, Map properties) if (property == null) { return false; } - return !property.getClass().isArray() || ((Object[]) property).length > 0; + return (property.getClass().isArray()) ? ((Object[]) property).length > 0 : !property.toString().isEmpty(); } private static ArchRule enumSourceShouldNotHaveValueThatIsTheSameAsTypeOfMethodsFirstParameter() { @@ -303,33 +314,37 @@ private static void notSpecifyOnlyATypeThatIsTheSameAsTheMethodParameterType(Jav }); } - private static ArchRule classLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute() { + private static ArchRule classLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute( + String annotationClass) { return ArchRuleDefinition.classes() .that() - .areAnnotatedWith("org.springframework.boot.context.properties.ConfigurationProperties") - .should(notSpecifyOnlyPrefixAttributeOfConfigurationProperties()) + .areAnnotatedWith(annotationClass) + .should(notSpecifyOnlyPrefixAttributeOfConfigurationProperties(annotationClass)) .allowEmptyShould(true); } - private static ArchRule methodLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute() { + private static ArchRule methodLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute( + String annotationClass) { return ArchRuleDefinition.methods() .that() - .areAnnotatedWith("org.springframework.boot.context.properties.ConfigurationProperties") - .should(notSpecifyOnlyPrefixAttributeOfConfigurationProperties()) + .areAnnotatedWith(annotationClass) + .should(notSpecifyOnlyPrefixAttributeOfConfigurationProperties(annotationClass)) .allowEmptyShould(true); } - private static ArchCondition> notSpecifyOnlyPrefixAttributeOfConfigurationProperties() { - return check("not specify only prefix attribute of @ConfigurationProperties", - ArchitectureRules::notSpecifyOnlyPrefixAttributeOfConfigurationProperties); + private static ArchCondition> notSpecifyOnlyPrefixAttributeOfConfigurationProperties( + String annotationClass) { + return check("not specify only prefix attribute of @ConfigurationProperties", (item, + events) -> notSpecifyOnlyPrefixAttributeOfConfigurationProperties(annotationClass, item, events)); } - private static void notSpecifyOnlyPrefixAttributeOfConfigurationProperties(HasAnnotations item, - ConditionEvents events) { - JavaAnnotation configurationPropertiesAnnotation = item - .getAnnotationOfType("org.springframework.boot.context.properties.ConfigurationProperties"); + private static void notSpecifyOnlyPrefixAttributeOfConfigurationProperties(String annotationClass, + HasAnnotations item, ConditionEvents events) { + JavaAnnotation configurationPropertiesAnnotation = item.getAnnotationOfType(annotationClass); Map properties = configurationPropertiesAnnotation.getProperties(); - if (properties.size() == 1 && properties.containsKey("prefix")) { + if (hasProperty("prefix", properties) && !hasProperty("value", properties) + && properties.get("ignoreInvalidFields").equals(false) + && properties.get("ignoreUnknownFields").equals(true)) { addViolation(events, item, configurationPropertiesAnnotation.getDescription() + " should specify implicit 'value' attribute other than explicit 'prefix' attribute"); } @@ -349,9 +364,9 @@ private static ArchRule conditionsShouldNotBePublic() { .allowEmptyShould(true); } - private static ArchRule allConfigurationPropertiesBindingBeanMethodsShouldBeStatic() { + private static ArchRule allConfigurationPropertiesBindingBeanMethodsShouldBeStatic(String annotationClass) { return methodsThatAreAnnotatedWith("org.springframework.context.annotation.Bean").and() - .areAnnotatedWith("org.springframework.boot.context.properties.ConfigurationPropertiesBinding") + .areAnnotatedWith(annotationClass) .should() .beStatic() .allowEmptyShould(true); 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 c83355c85eb7..da386e2f1e7a 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 @@ -43,7 +43,11 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; +import org.springframework.boot.build.architecture.ArchitectureCheckAnnotations.Annotation; import org.springframework.boot.build.architecture.annotations.TestConditionalOnClass; +import org.springframework.boot.build.architecture.annotations.TestConditionalOnMissingBean; +import org.springframework.boot.build.architecture.annotations.TestConfigurationProperties; +import org.springframework.boot.build.architecture.annotations.TestConfigurationPropertiesBinding; import org.springframework.boot.build.architecture.annotations.TestDeprecatedConfigurationProperty; import org.springframework.util.ClassUtils; import org.springframework.util.FileSystemUtils; @@ -204,6 +208,29 @@ void whenClassCallsObjectsRequireNonNullWithSupplierAndProhibitObjectsRequireNon build(this.gradleBuild.withProhibitObjectsRequireNonNull(false), task); } + @ParameterizedTest(name = "{0}") + @EnumSource(Task.class) + void whenClassCallsCollectorsToListShouldFailAndWriteReport(Task task) throws IOException { + prepareTask(task, "collectors/toList"); + buildAndFail(this.gradleBuild, task, "because java.util.stream.Stream.toList() should be used instead"); + } + + @ParameterizedTest(name = "{0}") + @EnumSource(Task.class) + void whenClassCallsUrlEncoderWithStringEncodingShouldFailAndWriteReport(Task task) throws IOException { + prepareTask(task, "url/encode"); + buildAndFail(this.gradleBuild, task, + "because java.net.URLEncoder.encode(String s, Charset charset) should be used instead"); + } + + @ParameterizedTest(name = "{0}") + @EnumSource(Task.class) + void whenClassCallsUrlDecoderWithStringEncodingShouldFailAndWriteReport(Task task) throws IOException { + prepareTask(task, "url/decode"); + buildAndFail(this.gradleBuild, task, + "because java.net.URLDecoder.decode(String s, Charset charset) should be used instead"); + } + @ParameterizedTest(name = "{0}") @EnumSource(Task.class) void whenClassCallsStringToUpperCaseWithoutLocaleShouldFailAndWriteReport(Task task) throws IOException { @@ -232,6 +259,81 @@ void whenClassCallsStringToUpperCaseWithLocaleShouldSucceedAndWriteEmptyReport(T build(this.gradleBuild, task); } + @Test + void whenConditionalOnMissingBeanWithTypeSameAsMethodReturnTypeShouldFailAndWriteReport() throws IOException { + prepareTask(Task.CHECK_ARCHITECTURE_MAIN, "conditionalonmissingbean/valueonly", "annotations"); + buildAndFail(this.gradleBuild.withDependencies(SPRING_CONTEXT).withConditionalOnMissingBeanAnnotation(), + Task.CHECK_ARCHITECTURE_MAIN, + "should not specify only a value that is the same as the method's return type"); + } + + @ParameterizedTest(name = "{0}") + @EnumSource(Task.class) + void whenConditionalOnMissingBeanWithTypeAttributeShouldSucceedAndWriteEmptyReport(Task task) throws IOException { + prepareTask(task, "conditionalonmissingbean/withtype", "annotations"); + build(this.gradleBuild.withDependencies(SPRING_CONTEXT), task); + } + + @ParameterizedTest(name = "{0}") + @EnumSource(Task.class) + void whenConditionalOnMissingBeanWithNameAttributeShouldSucceedAndWriteEmptyReport(Task task) throws IOException { + prepareTask(task, "conditionalonmissingbean/withname", "annotations"); + build(this.gradleBuild.withDependencies(SPRING_CONTEXT), task); + } + + @Test + void whenClassLevelConfigurationPropertiesContainsOnlyPrefixShouldFailAndWriteReport() throws IOException { + prepareTask(Task.CHECK_ARCHITECTURE_MAIN, "configurationproperties/classprefixonly", "annotations"); + buildAndFail(this.gradleBuild.withDependencies(SPRING_CONTEXT).withConfigurationPropertiesAnnotation(), + Task.CHECK_ARCHITECTURE_MAIN, + "should specify implicit 'value' attribute other than explicit 'prefix' attribute"); + } + + @Test + void whenClassLevelConfigurationPropertiesContainsPrefixAndIgnoreShouldSucceedAndWriteEmptyReport() + throws IOException { + prepareTask(Task.CHECK_ARCHITECTURE_MAIN, "configurationproperties/classprefixandignore", "annotations"); + build(this.gradleBuild.withDependencies(SPRING_CONTEXT).withConfigurationPropertiesAnnotation(), + Task.CHECK_ARCHITECTURE_MAIN); + } + + @Test + void whenClassLevelConfigurationPropertiesContainsOnlyValueShouldSucceedAndWriteEmptyReport() throws IOException { + prepareTask(Task.CHECK_ARCHITECTURE_MAIN, "configurationproperties/classvalueonly", "annotations"); + build(this.gradleBuild.withDependencies(SPRING_CONTEXT).withConfigurationPropertiesAnnotation(), + Task.CHECK_ARCHITECTURE_MAIN); + } + + @Test + void whenMethodLevelConfigurationPropertiesContainsOnlyPrefixShouldFailAndWriteReport() throws IOException { + prepareTask(Task.CHECK_ARCHITECTURE_MAIN, "configurationproperties/methodprefixonly", "annotations"); + buildAndFail(this.gradleBuild.withDependencies(SPRING_CONTEXT).withConfigurationPropertiesAnnotation(), + Task.CHECK_ARCHITECTURE_MAIN, + "should specify implicit 'value' attribute other than explicit 'prefix' attribute"); + } + + @Test + void whenMethodLevelConfigurationPropertiesContainsPrefixAndIgnoreShouldSucceedAndWriteEmptyReport() + throws IOException { + prepareTask(Task.CHECK_ARCHITECTURE_MAIN, "configurationproperties/methodprefixandignore", "annotations"); + build(this.gradleBuild.withDependencies(SPRING_CONTEXT).withConfigurationPropertiesAnnotation(), + Task.CHECK_ARCHITECTURE_MAIN); + } + + @Test + void whenMethodLevelConfigurationPropertiesContainsOnlyValueShouldSucceedAndWriteEmptyReport() throws IOException { + prepareTask(Task.CHECK_ARCHITECTURE_MAIN, "configurationproperties/methodvalueonly", "annotations"); + build(this.gradleBuild.withDependencies(SPRING_CONTEXT).withConfigurationPropertiesAnnotation(), + Task.CHECK_ARCHITECTURE_MAIN); + } + + @Test + void whenConfigurationPropertiesBindingBeanMethodIsNotStaticShouldFailAndWriteReport() throws IOException { + prepareTask(Task.CHECK_ARCHITECTURE_MAIN, "configurationproperties/bindingnonstatic", "annotations"); + buildAndFail(this.gradleBuild.withDependencies(SPRING_CONTEXT).withConfigurationPropertiesBindingAnnotation(), + Task.CHECK_ARCHITECTURE_MAIN, "does not have modifier STATIC"); + } + @ParameterizedTest(name = "{0}") @EnumSource(Task.class) void whenBeanPostProcessorBeanMethodIsNotStaticWithExternalClassShouldFailAndWriteReport(Task task) @@ -304,8 +406,7 @@ void whenEnumSourceValueIsSameAsTypeOfMethodsFirstParameterShouldFailAndWriteRep @Test void whenConditionalOnClassUsedOnBeanMethodsWithMainSourcesShouldFailAndWriteReport() throws IOException { prepareTask(Task.CHECK_ARCHITECTURE_MAIN, "conditionalonclass", "annotations"); - GradleBuild gradleBuild = this.gradleBuild.withDependencies(SPRING_CONTEXT) - .withConditionalOnClassAnnotation(TestConditionalOnClass.class.getName()); + GradleBuild gradleBuild = this.gradleBuild.withDependencies(SPRING_CONTEXT).withConditionalOnClassAnnotation(); buildAndFail(gradleBuild, Task.CHECK_ARCHITECTURE_MAIN, "because @ConditionalOnClass on @Bean methods is ineffective - it doesn't prevent" + " the method signature from being loaded. Such condition need to be placed" @@ -315,16 +416,15 @@ void whenConditionalOnClassUsedOnBeanMethodsWithMainSourcesShouldFailAndWriteRep @Test void whenConditionalOnClassUsedOnBeanMethodsWithTestSourcesShouldSucceedAndWriteEmptyReport() throws IOException { prepareTask(Task.CHECK_ARCHITECTURE_TEST, "conditionalonclass", "annotations"); - GradleBuild gradleBuild = this.gradleBuild.withDependencies(SPRING_CONTEXT) - .withConditionalOnClassAnnotation(TestConditionalOnClass.class.getName()); + GradleBuild gradleBuild = this.gradleBuild.withDependencies(SPRING_CONTEXT).withConditionalOnClassAnnotation(); build(gradleBuild, Task.CHECK_ARCHITECTURE_TEST); } @Test void whenDeprecatedConfigurationPropertyIsMissingSinceShouldFailAndWriteReport() throws IOException { - prepareTask(Task.CHECK_ARCHITECTURE_MAIN, "configurationproperties", "annotations"); + prepareTask(Task.CHECK_ARCHITECTURE_MAIN, "configurationproperties/deprecatedsince", "annotations"); GradleBuild gradleBuild = this.gradleBuild.withDependencies(SPRING_CONTEXT) - .withDeprecatedConfigurationPropertyAnnotation(TestDeprecatedConfigurationProperty.class.getName()); + .withDeprecatedConfigurationPropertyAnnotation(); buildAndFail(gradleBuild, Task.CHECK_ARCHITECTURE_MAIN, "should include a non-empty 'since' attribute of @DeprecatedConfigurationProperty", "DeprecatedConfigurationPropertySince.getProperty"); @@ -429,20 +529,37 @@ GradleBuild withProhibitObjectsRequireNonNull(Boolean prohibitObjectsRequireNonN return this; } - GradleBuild withConditionalOnClassAnnotation(String annotationClass) { - for (Task task : Task.values()) { - configureTask(task, (configuration) -> configuration - .withAnnotation(ArchitectureCheck.CONDITIONAL_ON_CLASS, annotationClass)); - } + GradleBuild withConditionalOnClassAnnotation() { + configureTasks(Annotation.CONDITIONAL_ON_CLASS.name(), TestConditionalOnClass.class.getName()); + return this; + } + + GradleBuild withConditionalOnMissingBeanAnnotation() { + configureTasks(Annotation.CONDITIONAL_ON_MISSING_BEAN.name(), TestConditionalOnMissingBean.class.getName()); + return this; + } + + GradleBuild withConfigurationPropertiesAnnotation() { + configureTasks(Annotation.CONFIGURATION_PROPERTIES.name(), TestConfigurationProperties.class.getName()); return this; } - GradleBuild withDeprecatedConfigurationPropertyAnnotation(String annotationClass) { + GradleBuild withConfigurationPropertiesBindingAnnotation() { + configureTasks(Annotation.CONFIGURATION_PROPERTIES_BINDING.name(), + TestConfigurationPropertiesBinding.class.getName()); + return this; + } + + GradleBuild withDeprecatedConfigurationPropertyAnnotation() { + configureTasks(Annotation.DEPRECATED_CONFIGURATION_PROPERTY.name(), + TestDeprecatedConfigurationProperty.class.getName()); + return this; + } + + private void configureTasks(String annotationName, String annotationClass) { for (Task task : Task.values()) { - configureTask(task, (configuration) -> configuration - .withAnnotation(ArchitectureCheck.DEPRECATED_CONFIGURATION_PROPERTY, annotationClass)); + configureTask(task, (configuration) -> configuration.withAnnotation(annotationName, annotationClass)); } - return this; } private void configureTask(Task task, UnaryOperator configurer) { diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/annotations/TestConditionalOnMissingBean.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/annotations/TestConditionalOnMissingBean.java new file mode 100644 index 000000000000..9c8b918a9104 --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/annotations/TestConditionalOnMissingBean.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012-present 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.annotations; + +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * {@code @ConditionalOnMissingBean} analogue for architecture checks. + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface TestConditionalOnMissingBean { + + Class[] value() default {}; + + String[] type() default {}; + + Class[] ignored() default {}; + + String[] ignoredType() default {}; + + Class[] annotation() default {}; + + String[] name() default {}; + + Class[] parameterizedContainer() default {}; + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/annotations/TestConfigurationProperties.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/annotations/TestConfigurationProperties.java new file mode 100644 index 000000000000..c17ae3bcaac5 --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/annotations/TestConfigurationProperties.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-present 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.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Indexed; + +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Indexed +public @interface TestConfigurationProperties { + + @AliasFor("prefix") + String value() default ""; + + @AliasFor("value") + String prefix() default ""; + + boolean ignoreInvalidFields() default false; + + boolean ignoreUnknownFields() default true; + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/annotations/TestConfigurationPropertiesBinding.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/annotations/TestConfigurationPropertiesBinding.java new file mode 100644 index 000000000000..afa7d0252130 --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/annotations/TestConfigurationPropertiesBinding.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-present 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.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface TestConfigurationPropertiesBinding { + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/collectors/toList/CollectorsToList.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/collectors/toList/CollectorsToList.java new file mode 100644 index 000000000000..a371b7e34641 --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/collectors/toList/CollectorsToList.java @@ -0,0 +1,29 @@ +/* + * Copyright 2012-present 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.collectors.toList; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class CollectorsToList { + + void exampleMethod() { + List strings = Stream.of("a", "b", "c").collect(Collectors.toList()); + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/conditionalonmissingbean/valueonly/TypeSameAsMethodReturnType.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/conditionalonmissingbean/valueonly/TypeSameAsMethodReturnType.java new file mode 100644 index 000000000000..13d505f1c306 --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/conditionalonmissingbean/valueonly/TypeSameAsMethodReturnType.java @@ -0,0 +1,30 @@ +/* + * Copyright 2012-present 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.conditionalonmissingbean.valueonly; + +import org.springframework.boot.build.architecture.annotations.TestConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; + +class TypeSameAsMethodReturnType { + + @Bean + @TestConditionalOnMissingBean(String.class) + String helloWorld() { + return "Hello World"; + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/conditionalonmissingbean/withname/WithNameAttribute.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/conditionalonmissingbean/withname/WithNameAttribute.java new file mode 100644 index 000000000000..4ab41bc9c54b --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/conditionalonmissingbean/withname/WithNameAttribute.java @@ -0,0 +1,30 @@ +/* + * Copyright 2012-present 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.conditionalonmissingbean.withname; + +import org.springframework.boot.build.architecture.annotations.TestConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; + +class WithNameAttribute { + + @Bean + @TestConditionalOnMissingBean(name = "myBean") + String helloWorld() { + return "Hello World"; + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/conditionalonmissingbean/withtype/WithTypeAttribute.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/conditionalonmissingbean/withtype/WithTypeAttribute.java new file mode 100644 index 000000000000..7ea7f3cae5c7 --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/conditionalonmissingbean/withtype/WithTypeAttribute.java @@ -0,0 +1,30 @@ +/* + * Copyright 2012-present 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.conditionalonmissingbean.withtype; + +import org.springframework.boot.build.architecture.annotations.TestConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; + +class WithTypeAttribute { + + @Bean + @TestConditionalOnMissingBean(type = "String") + String helloWorld() { + return "Hello World"; + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/bindingnonstatic/BindingMethodNonStatic.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/bindingnonstatic/BindingMethodNonStatic.java new file mode 100644 index 000000000000..10dfbfd899e3 --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/bindingnonstatic/BindingMethodNonStatic.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012-present 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.configurationproperties.bindingnonstatic; + +import java.util.List; + +import org.springframework.boot.build.architecture.annotations.TestConfigurationPropertiesBinding; +import org.springframework.context.annotation.Bean; + +public class BindingMethodNonStatic { + + @Bean + @TestConfigurationPropertiesBinding + public List binder() { + return List.of("hello", "world"); + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/classprefixandignore/ConfigurationPropertiesWithPrefixAndIgnore.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/classprefixandignore/ConfigurationPropertiesWithPrefixAndIgnore.java new file mode 100644 index 000000000000..381f1c6a8080 --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/classprefixandignore/ConfigurationPropertiesWithPrefixAndIgnore.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012-present 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.configurationproperties.classprefixandignore; + +import org.springframework.boot.build.architecture.annotations.TestConfigurationProperties; + +@TestConfigurationProperties(prefix = "testing", ignoreUnknownFields = false) +public class ConfigurationPropertiesWithPrefixAndIgnore { + + private String property; + + public String getProperty() { + return this.property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/classprefixonly/ConfigurationPropertiesWithPrefixOnly.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/classprefixonly/ConfigurationPropertiesWithPrefixOnly.java new file mode 100644 index 000000000000..566489d79c36 --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/classprefixonly/ConfigurationPropertiesWithPrefixOnly.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012-present 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.configurationproperties.classprefixonly; + +import org.springframework.boot.build.architecture.annotations.TestConfigurationProperties; + +@TestConfigurationProperties(prefix = "testing") +public class ConfigurationPropertiesWithPrefixOnly { + + private String property; + + public String getProperty() { + return this.property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/classvalueonly/ConfigurationPropertiesWithValueOnly.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/classvalueonly/ConfigurationPropertiesWithValueOnly.java new file mode 100644 index 000000000000..eedce786208f --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/classvalueonly/ConfigurationPropertiesWithValueOnly.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012-present 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.configurationproperties.classvalueonly; + +import org.springframework.boot.build.architecture.annotations.TestConfigurationProperties; + +@TestConfigurationProperties("testing") +public class ConfigurationPropertiesWithValueOnly { + + private String property; + + public String getProperty() { + return this.property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/DeprecatedConfigurationPropertySince.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/deprecatedsince/DeprecatedConfigurationPropertySince.java similarity index 97% rename from buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/DeprecatedConfigurationPropertySince.java rename to buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/deprecatedsince/DeprecatedConfigurationPropertySince.java index 3cc3185dd168..60030a7003a2 100644 --- a/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/DeprecatedConfigurationPropertySince.java +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/deprecatedsince/DeprecatedConfigurationPropertySince.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.build.architecture.configurationproperties; +package org.springframework.boot.build.architecture.configurationproperties.deprecatedsince; import org.springframework.boot.build.architecture.annotations.TestDeprecatedConfigurationProperty; diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/methodprefixandignore/ConfigurationPropertiesWithPrefixAndIgnore.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/methodprefixandignore/ConfigurationPropertiesWithPrefixAndIgnore.java new file mode 100644 index 000000000000..ed1c8dc4594b --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/methodprefixandignore/ConfigurationPropertiesWithPrefixAndIgnore.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012-present 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.configurationproperties.methodprefixandignore; + +import org.springframework.boot.build.architecture.annotations.TestConfigurationProperties; + +public class ConfigurationPropertiesWithPrefixAndIgnore { + + private String property; + + @TestConfigurationProperties(prefix = "testing", ignoreInvalidFields = true) + public String getProperty() { + return this.property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/methodprefixonly/ConfigurationPropertiesWithPrefixOnly.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/methodprefixonly/ConfigurationPropertiesWithPrefixOnly.java new file mode 100644 index 000000000000..2d68a1946e46 --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/methodprefixonly/ConfigurationPropertiesWithPrefixOnly.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012-present 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.configurationproperties.methodprefixonly; + +import org.springframework.boot.build.architecture.annotations.TestConfigurationProperties; + +public class ConfigurationPropertiesWithPrefixOnly { + + private String property; + + @TestConfigurationProperties(prefix = "testing") + public String getProperty() { + return this.property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/methodvalueonly/ConfigurationPropertiesWithValueOnly.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/methodvalueonly/ConfigurationPropertiesWithValueOnly.java new file mode 100644 index 000000000000..f0005e06f6e9 --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/configurationproperties/methodvalueonly/ConfigurationPropertiesWithValueOnly.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012-present 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.configurationproperties.methodvalueonly; + +import org.springframework.boot.build.architecture.annotations.TestConfigurationProperties; + +public class ConfigurationPropertiesWithValueOnly { + + private String property; + + @TestConfigurationProperties("testing") + public String getProperty() { + return this.property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/url/decode/UrlDecodeWithStringEncoding.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/url/decode/UrlDecodeWithStringEncoding.java new file mode 100644 index 000000000000..3622651f66ac --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/url/decode/UrlDecodeWithStringEncoding.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-present 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.url.decode; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; + +class UrlDecodeWithStringEncoding { + + void exampleMethod() throws UnsupportedEncodingException { + URLDecoder.decode("https://example.com", "UTF-8"); + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/url/encode/UrlEncodeWithStringEncoding.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/url/encode/UrlEncodeWithStringEncoding.java new file mode 100644 index 000000000000..9fe337e0424f --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/url/encode/UrlEncodeWithStringEncoding.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-present 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.url.encode; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +class UrlEncodeWithStringEncoding { + + void exampleMethod() throws UnsupportedEncodingException { + URLEncoder.encode("https://example.com", "UTF-8"); + } + +}