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 8015cdaa10d..6fb0390036b 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 @@ -34,6 +34,7 @@ import com.tngtech.archunit.core.domain.JavaCall; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaClass.Predicates; import com.tngtech.archunit.core.domain.JavaMethod; +import com.tngtech.archunit.core.domain.JavaModifier; import com.tngtech.archunit.core.domain.JavaParameter; import com.tngtech.archunit.core.domain.JavaType; import com.tngtech.archunit.core.domain.properties.CanBeAnnotated; @@ -94,6 +95,7 @@ final class ArchitectureRules { rules.add(enumSourceShouldNotSpecifyOnlyATypeThatIsTheSameAsMethodParameterType()); rules.add(classLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute()); rules.add(methodLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute()); + rules.add(conditionsShouldNotBePublic()); return List.copyOf(rules); } @@ -290,6 +292,20 @@ final class ArchitectureRules { } } + private static ArchRule conditionsShouldNotBePublic() { + String springBootCondition = "org.springframework.boot.autoconfigure.condition.SpringBootCondition"; + return ArchRuleDefinition.noClasses() + .that() + .areAssignableTo(springBootCondition) + .and() + .doNotHaveModifier(JavaModifier.ABSTRACT) + .and() + .areNotAnnotatedWith(Deprecated.class) + .should() + .bePublic() + .allowEmptyShould(true); + } + private static boolean containsOnlySingleType(JavaType[] types, JavaType type) { return types.length == 1 && type.equals(types[0]); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyListCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyListCondition.java index 413de3b31fc..1ab148c9f2b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyListCondition.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyListCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 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. @@ -34,7 +34,7 @@ import org.springframework.core.type.AnnotatedTypeMetadata; * @author Stephane Nicoll * @since 2.0.5 */ -public class OnPropertyListCondition extends SpringBootCondition { +public abstract class OnPropertyListCondition extends SpringBootCondition { private static final Bindable> STRING_LIST = Bindable.listOf(String.class); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/ClientsConfiguredCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/ClientsConfiguredCondition.java index d4d9df519ee..6bd518524aa 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/ClientsConfiguredCondition.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/ClientsConfiguredCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2025 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. @@ -35,7 +35,10 @@ import org.springframework.core.type.AnnotatedTypeMetadata; * * @author Madhura Bhave * @since 2.1.0 + * @deprecated since 3.5.0 for removal in 3.7.0 in favor of + * {@link ConditionalOnOAuth2ClientRegistrationProperties @ConditionalOnOAuth2ClientRegistrationConfigured} */ +@Deprecated(since = "3.5.0", forRemoval = true) public class ClientsConfiguredCondition extends SpringBootCondition { private static final Bindable> STRING_REGISTRATION_MAP = Bindable diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/ConditionalOnOAuth2ClientRegistrationProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/ConditionalOnOAuth2ClientRegistrationProperties.java new file mode 100644 index 00000000000..af76acda8c3 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/ConditionalOnOAuth2ClientRegistrationProperties.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012-2025 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.autoconfigure.security.oauth2.client; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Conditional; + +/** + * Condition that matches if any {@code spring.security.oauth2.client.registration} + * properties are defined. + * + * @author Andy Wilkinson + * @since 3.5.0 + */ +@SuppressWarnings("removal") +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Documented +@Conditional(ClientsConfiguredCondition.class) +public @interface ConditionalOnOAuth2ClientRegistrationProperties { + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/reactive/ReactiveOAuth2ClientConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/reactive/ReactiveOAuth2ClientConfigurations.java index 8eb3871b937..8ab3b87a183 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/reactive/ReactiveOAuth2ClientConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/reactive/ReactiveOAuth2ClientConfigurations.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2025 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,11 +22,10 @@ import java.util.List; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; -import org.springframework.boot.autoconfigure.security.oauth2.client.ClientsConfiguredCondition; +import org.springframework.boot.autoconfigure.security.oauth2.client.ConditionalOnOAuth2ClientRegistrationProperties; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesMapper; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService; @@ -48,7 +47,7 @@ import static org.springframework.security.config.Customizer.withDefaults; class ReactiveOAuth2ClientConfigurations { @Configuration(proxyBeanMethods = false) - @Conditional(ClientsConfiguredCondition.class) + @ConditionalOnOAuth2ClientRegistrationProperties @ConditionalOnMissingBean(ReactiveClientRegistrationRepository.class) static class ReactiveClientRegistrationRepositoryConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/servlet/OAuth2ClientRegistrationRepositoryConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/servlet/OAuth2ClientRegistrationRepositoryConfiguration.java index 09855253ad1..edcb424d7f2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/servlet/OAuth2ClientRegistrationRepositoryConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/servlet/OAuth2ClientRegistrationRepositoryConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2025 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. @@ -20,12 +20,11 @@ import java.util.ArrayList; import java.util.List; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.security.oauth2.client.ClientsConfiguredCondition; +import org.springframework.boot.autoconfigure.security.oauth2.client.ConditionalOnOAuth2ClientRegistrationProperties; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesMapper; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; @@ -39,7 +38,7 @@ import org.springframework.security.oauth2.client.registration.InMemoryClientReg */ @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(OAuth2ClientProperties.class) -@Conditional(ClientsConfiguredCondition.class) +@ConditionalOnOAuth2ClientRegistrationProperties class OAuth2ClientRegistrationRepositoryConfiguration { @Bean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ConditionalOnIssuerLocationJwtDecoder.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ConditionalOnIssuerLocationJwtDecoder.java new file mode 100644 index 00000000000..2a8b8fe29ce --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ConditionalOnIssuerLocationJwtDecoder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-2025 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.autoconfigure.security.oauth2.resource; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Conditional; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; + +/** + * Condition that matches when an {@link NimbusJwtDecoder#withIssuerLocation + * issuer-location-based JWT decoder} should be used. + * + * @author Andy Wilkinson + * @since 3.5.0 + */ +@SuppressWarnings("removal") +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Documented +@Conditional(IssuerUriCondition.class) +public @interface ConditionalOnIssuerLocationJwtDecoder { + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ConditionalOnPublicKeyJwtDecoder.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ConditionalOnPublicKeyJwtDecoder.java new file mode 100644 index 00000000000..ef54bd01275 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ConditionalOnPublicKeyJwtDecoder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-2025 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.autoconfigure.security.oauth2.resource; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Conditional; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; + +/** + * Condition that matches when a {@link NimbusJwtDecoder#withPublicKey public-key-based + * JWT decoder} should be used. + * + * @author Andy Wilkinson + * @since 3.5.0 + */ +@SuppressWarnings("removal") +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Documented +@Conditional(KeyValueCondition.class) +public @interface ConditionalOnPublicKeyJwtDecoder { + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/IssuerUriCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/IssuerUriCondition.java index d2e5c10763f..867860041d8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/IssuerUriCondition.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/IssuerUriCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2025 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. @@ -30,7 +30,10 @@ import org.springframework.util.StringUtils; * * @author Artsiom Yudovin * @since 2.1.0 + * @deprecated since 3.5.0 for removal in 3.7.0 in favor of + * {@link ConditionalOnIssuerLocationJwtDecoder @ConditionalOnIssuerLocationJwtDecoder} */ +@Deprecated(since = "3.5.0", forRemoval = true) public class IssuerUriCondition extends SpringBootCondition { @Override @@ -38,10 +41,10 @@ public class IssuerUriCondition extends SpringBootCondition { ConditionMessage.Builder message = ConditionMessage.forCondition("OpenID Connect Issuer URI Condition"); Environment environment = context.getEnvironment(); String issuerUri = environment.getProperty("spring.security.oauth2.resourceserver.jwt.issuer-uri"); - String jwkSetUri = environment.getProperty("spring.security.oauth2.resourceserver.jwt.jwk-set-uri"); if (!StringUtils.hasText(issuerUri)) { return ConditionOutcome.noMatch(message.didNotFind("issuer-uri property").atAll()); } + String jwkSetUri = environment.getProperty("spring.security.oauth2.resourceserver.jwt.jwk-set-uri"); if (StringUtils.hasText(jwkSetUri)) { return ConditionOutcome.noMatch(message.found("jwk-set-uri property").items(jwkSetUri)); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/KeyValueCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/KeyValueCondition.java index 90b5d71cb0d..54354793b40 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/KeyValueCondition.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/KeyValueCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2025 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. @@ -29,7 +29,10 @@ import org.springframework.util.StringUtils; * * @author Madhura Bhave * @since 2.2.0 + * @deprecated since 3.5.0 for removal in 3.7.0 in favor of + * {@link ConditionalOnPublicKeyJwtDecoder @ConditionalOnPublicKeyJwtDecoder} */ +@Deprecated(since = "3.5.0", forRemoval = true) public class KeyValueCondition extends SpringBootCondition { @Override @@ -41,11 +44,11 @@ public class KeyValueCondition extends SpringBootCondition { if (!StringUtils.hasText(publicKeyLocation)) { return ConditionOutcome.noMatch(message.didNotFind("public-key-location property").atAll()); } - String issuerUri = environment.getProperty("spring.security.oauth2.resourceserver.jwt.issuer-uri"); String jwkSetUri = environment.getProperty("spring.security.oauth2.resourceserver.jwt.jwk-set-uri"); if (StringUtils.hasText(jwkSetUri)) { return ConditionOutcome.noMatch(message.found("jwk-set-uri property").items(jwkSetUri)); } + String issuerUri = environment.getProperty("spring.security.oauth2.resourceserver.jwt.issuer-uri"); if (StringUtils.hasText(issuerUri)) { return ConditionOutcome.noMatch(message.found("issuer-uri property").items(issuerUri)); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java index f305eecb9a6..2dbbb1476c7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java @@ -30,8 +30,8 @@ import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.security.oauth2.resource.IssuerUriCondition; -import org.springframework.boot.autoconfigure.security.oauth2.resource.KeyValueCondition; +import org.springframework.boot.autoconfigure.security.oauth2.resource.ConditionalOnIssuerLocationJwtDecoder; +import org.springframework.boot.autoconfigure.security.oauth2.resource.ConditionalOnPublicKeyJwtDecoder; import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.context.annotation.Bean; @@ -130,7 +130,7 @@ class ReactiveOAuth2ResourceServerJwkConfiguration { } @Bean - @Conditional(KeyValueCondition.class) + @ConditionalOnPublicKeyJwtDecoder NimbusReactiveJwtDecoder jwtDecoderByPublicKeyValue() throws Exception { RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA") .generatePublic(new X509EncodedKeySpec(getKeySpec(this.properties.readPublicKey()))); @@ -158,7 +158,7 @@ class ReactiveOAuth2ResourceServerJwkConfiguration { } @Bean - @Conditional(IssuerUriCondition.class) + @ConditionalOnIssuerLocationJwtDecoder SupplierReactiveJwtDecoder jwtDecoderByIssuerUri( ObjectProvider customizers) { return new SupplierReactiveJwtDecoder(() -> { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java index 7f475b67e8c..a4776565c91 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java @@ -31,8 +31,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.security.ConditionalOnDefaultWebSecurity; -import org.springframework.boot.autoconfigure.security.oauth2.resource.IssuerUriCondition; -import org.springframework.boot.autoconfigure.security.oauth2.resource.KeyValueCondition; +import org.springframework.boot.autoconfigure.security.oauth2.resource.ConditionalOnIssuerLocationJwtDecoder; +import org.springframework.boot.autoconfigure.security.oauth2.resource.ConditionalOnPublicKeyJwtDecoder; import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.context.annotation.Bean; @@ -129,7 +129,7 @@ class OAuth2ResourceServerJwtConfiguration { } @Bean - @Conditional(KeyValueCondition.class) + @ConditionalOnPublicKeyJwtDecoder JwtDecoder jwtDecoderByPublicKeyValue() throws Exception { RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA") .generatePublic(new X509EncodedKeySpec(getKeySpec(this.properties.readPublicKey()))); @@ -157,7 +157,7 @@ class OAuth2ResourceServerJwtConfiguration { } @Bean - @Conditional(IssuerUriCondition.class) + @ConditionalOnIssuerLocationJwtDecoder SupplierJwtDecoder jwtDecoderByIssuerUri(ObjectProvider customizers) { return new SupplierJwtDecoder(() -> { String issuerUri = this.properties.getIssuerUri(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/OnDatabaseInitializationCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/OnDatabaseInitializationCondition.java index 032a0473147..0cdf101fbff 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/OnDatabaseInitializationCondition.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/OnDatabaseInitializationCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2025 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. @@ -35,7 +35,7 @@ import org.springframework.util.StringUtils; * @since 2.6.2 * @see DatabaseInitializationMode */ -public class OnDatabaseInitializationCondition extends SpringBootCondition { +public abstract class OnDatabaseInitializationCondition extends SpringBootCondition { private final String name; @@ -48,7 +48,7 @@ public class OnDatabaseInitializationCondition extends SpringBootCondition { * @param name the name of the component * @param propertyNames the properties to check (in order) */ - public OnDatabaseInitializationCondition(String name, String... propertyNames) { + protected OnDatabaseInitializationCondition(String name, String... propertyNames) { this.name = name; this.propertyNames = propertyNames; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/sql/init/OnDatabaseInitializationConditionTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/sql/init/OnDatabaseInitializationConditionTests.java index fd44e19a449..b05a0bb4adb 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/sql/init/OnDatabaseInitializationConditionTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/sql/init/OnDatabaseInitializationConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2025 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,7 +36,7 @@ class OnDatabaseInitializationConditionTests { @Test void getMatchOutcomeWithPropertyNoSetMatches() { - OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode"); + OnDatabaseInitializationCondition condition = new OnTestDatabaseInitializationCondition("test.init-mode"); ConditionOutcome outcome = condition .getMatchOutcome(mockConditionContext(TestPropertyValues.of("test.another", "noise")), null); assertThat(outcome.isMatch()).isTrue(); @@ -44,7 +44,7 @@ class OnDatabaseInitializationConditionTests { @Test void getMatchOutcomeWithPropertySetToAlwaysMatches() { - OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode"); + OnDatabaseInitializationCondition condition = new OnTestDatabaseInitializationCondition("test.init-mode"); ConditionOutcome outcome = condition .getMatchOutcome(mockConditionContext(TestPropertyValues.of("test.init-mode=always")), null); assertThat(outcome.isMatch()).isTrue(); @@ -52,7 +52,7 @@ class OnDatabaseInitializationConditionTests { @Test void getMatchOutcomeWithPropertySetToEmbeddedMatches() { - OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode"); + OnDatabaseInitializationCondition condition = new OnTestDatabaseInitializationCondition("test.init-mode"); ConditionOutcome outcome = condition .getMatchOutcome(mockConditionContext(TestPropertyValues.of("test.init-mode=embedded")), null); assertThat(outcome.isMatch()).isTrue(); @@ -60,7 +60,7 @@ class OnDatabaseInitializationConditionTests { @Test void getMatchOutcomeWithPropertySetToNeverDoesNotMatch() { - OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode"); + OnDatabaseInitializationCondition condition = new OnTestDatabaseInitializationCondition("test.init-mode"); ConditionOutcome outcome = condition .getMatchOutcome(mockConditionContext(TestPropertyValues.of("test.init-mode=never")), null); assertThat(outcome.isMatch()).isFalse(); @@ -68,7 +68,7 @@ class OnDatabaseInitializationConditionTests { @Test void getMatchOutcomeWithPropertySetToEmptyStringIsIgnored() { - OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode"); + OnDatabaseInitializationCondition condition = new OnTestDatabaseInitializationCondition("test.init-mode"); ConditionOutcome outcome = condition .getMatchOutcome(mockConditionContext(TestPropertyValues.of("test.init-mode")), null); assertThat(outcome.isMatch()).isTrue(); @@ -76,7 +76,7 @@ class OnDatabaseInitializationConditionTests { @Test void getMatchOutcomeWithMultiplePropertiesUsesFirstSet() { - OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode", + OnDatabaseInitializationCondition condition = new OnTestDatabaseInitializationCondition("test.init-mode", "test.schema-mode", "test.init-schema-mode"); ConditionOutcome outcome = condition .getMatchOutcome(mockConditionContext(TestPropertyValues.of("test.init-schema-mode=embedded")), null); @@ -86,7 +86,7 @@ class OnDatabaseInitializationConditionTests { @Test void getMatchOutcomeHasDedicatedDescription() { - OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode"); + OnDatabaseInitializationCondition condition = new OnTestDatabaseInitializationCondition("test.init-mode"); ConditionOutcome outcome = condition .getMatchOutcome(mockConditionContext(TestPropertyValues.of("test.init-mode=embedded")), null); assertThat(outcome.getMessage()).isEqualTo("TestDatabase Initialization test.init-mode is EMBEDDED"); @@ -94,7 +94,7 @@ class OnDatabaseInitializationConditionTests { @Test void getMatchOutcomeHasWhenPropertyIsNotSetHasDefaultDescription() { - OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode"); + OnDatabaseInitializationCondition condition = new OnTestDatabaseInitializationCondition("test.init-mode"); ConditionOutcome outcome = condition.getMatchOutcome(mockConditionContext(TestPropertyValues.empty()), null); assertThat(outcome.getMessage()).isEqualTo("TestDatabase Initialization default value is EMBEDDED"); } @@ -107,4 +107,12 @@ class OnDatabaseInitializationConditionTests { return conditionContext; } + static class OnTestDatabaseInitializationCondition extends OnDatabaseInitializationCondition { + + OnTestDatabaseInitializationCondition(String... properties) { + super("Test", properties); + } + + } + } diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/ConditionalOnEnabledDevTools.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/ConditionalOnEnabledDevTools.java new file mode 100644 index 00000000000..c035ae58d26 --- /dev/null +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/ConditionalOnEnabledDevTools.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012-2025 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.devtools.autoconfigure; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Conditional; + +/** + * {@link Conditional @Conditional} that matches when DevTools is enabled. + * + * @author Andy Wilkinson + * @since 3.5.0 + */ +@SuppressWarnings("removal") +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Documented +@Conditional(OnEnabledDevToolsCondition.class) +public @interface ConditionalOnEnabledDevTools { + +} diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsDataSourceAutoConfiguration.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsDataSourceAutoConfiguration.java index 24f0a2f80ba..770f88a3510 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsDataSourceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsDataSourceAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2025 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. @@ -58,7 +58,8 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; * @since 1.3.3 */ @ConditionalOnClass(DataSource.class) -@Conditional({ OnEnabledDevToolsCondition.class, DevToolsDataSourceCondition.class }) +@ConditionalOnEnabledDevTools +@Conditional(DevToolsDataSourceCondition.class) @AutoConfiguration(after = DataSourceAutoConfiguration.class) @Import(DatabaseShutdownExecutorEntityManagerFactoryDependsOnPostProcessor.class) public class DevToolsDataSourceAutoConfiguration { diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsR2dbcAutoConfiguration.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsR2dbcAutoConfiguration.java index ce5be593743..7c3e72f650d 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsR2dbcAutoConfiguration.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsR2dbcAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2025 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. @@ -49,7 +49,8 @@ import org.springframework.core.type.MethodMetadata; * @since 2.5.6 */ @ConditionalOnClass(ConnectionFactory.class) -@Conditional({ OnEnabledDevToolsCondition.class, DevToolsConnectionFactoryCondition.class }) +@ConditionalOnEnabledDevTools +@Conditional(DevToolsConnectionFactoryCondition.class) @AutoConfiguration(after = R2dbcAutoConfiguration.class) public class DevToolsR2dbcAutoConfiguration { diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/OnEnabledDevToolsCondition.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/OnEnabledDevToolsCondition.java index 1a405ab9931..026f1ad67b0 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/OnEnabledDevToolsCondition.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/OnEnabledDevToolsCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 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. @@ -28,7 +28,10 @@ import org.springframework.core.type.AnnotatedTypeMetadata; * * @author Madhura Bhave * @since 2.2.0 + * @deprecated since 3.5.0 for removal in 3.7.0 in favor of + * {@link ConditionalOnEnabledDevTools @ConditionalOnEnabledDevTools} */ +@Deprecated(since = "3.5.0", forRemoval = true) public class OnEnabledDevToolsCondition extends SpringBootCondition { @Override diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfiguration.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfiguration.java index 9a10aac0ddb..5ecf350ed4d 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfiguration.java @@ -45,7 +45,6 @@ import org.springframework.boot.devtools.restart.server.HttpRestartServer; import org.springframework.boot.devtools.restart.server.HttpRestartServerHandler; import org.springframework.boot.devtools.restart.server.SourceDirectoryUrlFilter; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.log.LogMessage; @@ -61,7 +60,7 @@ import org.springframework.http.server.ServerHttpRequest; * @since 1.3.0 */ @AutoConfiguration(after = SecurityAutoConfiguration.class) -@Conditional(OnEnabledDevToolsCondition.class) +@ConditionalOnEnabledDevTools @ConditionalOnProperty("spring.devtools.remote.secret") @ConditionalOnClass({ Filter.class, ServerHttpRequest.class }) @Import(RemoteDevtoolsSecurityConfiguration.class) diff --git a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/OnEnabledDevToolsConditionTests.java b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/OnEnabledDevToolsConditionTests.java index c0047b93ad3..adc80b11107 100644 --- a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/OnEnabledDevToolsConditionTests.java +++ b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/OnEnabledDevToolsConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 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. @@ -65,6 +65,7 @@ class OnEnabledDevToolsConditionTests { static class TestConfiguration { @Bean + @SuppressWarnings("removal") @Conditional(OnEnabledDevToolsCondition.class) String test() { return "hello";