From 09c0714730f482560b7e2cbb04cb05b7b97aea86 Mon Sep 17 00:00:00 2001 From: Scott Frederick Date: Tue, 9 Jul 2024 18:13:40 -0500 Subject: [PATCH] Register ProtocolResolvers with the application context by default Any ProtocolResolvers that are registered in a spring.factories file will be added to the application context using an ApplicationContextInitializer. Closes gh-41433 --- ...ResolverApplicationContextInitializer.java | 43 +++++++++++ .../main/resources/META-INF/spring.factories | 1 + .../SpringApplicationBuilderTests.java | 6 +- ...ionContextInitializerIntegrationTests.java | 77 +++++++++++++++++++ ...verApplicationContextInitializerTests.java | 48 ++++++++++++ 5 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/io/ProtocolResolverApplicationContextInitializer.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/io/ProtocolResolverApplicationContextInitializerIntegrationTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/io/ProtocolResolverApplicationContextInitializerTests.java diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/io/ProtocolResolverApplicationContextInitializer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/io/ProtocolResolverApplicationContextInitializer.java new file mode 100644 index 00000000000..615dcee9f3e --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/io/ProtocolResolverApplicationContextInitializer.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.io; + +import java.util.List; + +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.io.ProtocolResolver; +import org.springframework.core.io.support.SpringFactoriesLoader; + +/** + * {@link ApplicationContextInitializer} that adds all {@link ProtocolResolver}s + * registered in a {@code spring.factories} file. + * + * @author Scott Frederick + */ +class ProtocolResolverApplicationContextInitializer + implements ApplicationContextInitializer { + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + SpringFactoriesLoader loader = SpringFactoriesLoader + .forDefaultResourceLocation(applicationContext.getClassLoader()); + List protocolResolvers = loader.load(ProtocolResolver.class); + protocolResolvers.forEach(applicationContext::addProtocolResolver); + } + +} diff --git a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories index af78eab2294..194863fb9e0 100644 --- a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories @@ -36,6 +36,7 @@ org.springframework.boot.diagnostics.FailureAnalyzers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ +org.springframework.boot.io.ProtocolResolverApplicationContextInitializer,\ org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java index c78c6fc3453..c5341c8fd94 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java @@ -265,7 +265,7 @@ class SpringApplicationBuilderTests { SpringApplicationBuilder application = new SpringApplicationBuilder(ExampleConfig.class) .web(WebApplicationType.NONE); this.context = application.run(); - assertThat(application.application().getInitializers()).hasSize(4); + assertThat(application.application().getInitializers()).hasSize(5); } @Test @@ -274,7 +274,7 @@ class SpringApplicationBuilderTests { .child(ChildConfig.class) .web(WebApplicationType.NONE); this.context = application.run(); - assertThat(application.application().getInitializers()).hasSize(5); + assertThat(application.application().getInitializers()).hasSize(6); } @Test @@ -284,7 +284,7 @@ class SpringApplicationBuilderTests { .initializers((ConfigurableApplicationContext applicationContext) -> { }); this.context = application.run(); - assertThat(application.application().getInitializers()).hasSize(5); + assertThat(application.application().getInitializers()).hasSize(6); } @Test diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/io/ProtocolResolverApplicationContextInitializerIntegrationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/io/ProtocolResolverApplicationContextInitializerIntegrationTests.java new file mode 100644 index 00000000000..1673edbb58d --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/io/ProtocolResolverApplicationContextInitializerIntegrationTests.java @@ -0,0 +1,77 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.io; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for resolving configuration properties using + * {@code ProtocolResolver}s. + * + * @author Scott Frederick + */ +class ProtocolResolverApplicationContextInitializerIntegrationTests { + + @Test + void base64ResourceResolves() throws IOException { + SpringApplication application = new SpringApplication(TestConfiguration.class); + application.setDefaultProperties(Map.of("test.resource", "base64:dGVzdC12YWx1ZQ==")); + application.setWebApplicationType(WebApplicationType.NONE); + ConfigurableApplicationContext context = application.run(); + TestProperties propertiesBean = context.getBean(TestProperties.class); + Resource resource = propertiesBean.getResource(); + assertThat(resource).isNotNull(); + assertThat(resource.exists()).isTrue(); + assertThat(resource.getContentAsString(Charset.defaultCharset())).isEqualTo("test-value"); + } + + @Configuration(proxyBeanMethods = false) + @EnableConfigurationProperties(TestProperties.class) + static class TestConfiguration { + + } + + @ConfigurationProperties(prefix = "test") + static class TestProperties { + + Resource resource; + + Resource getResource() { + return this.resource; + } + + void setResource(Resource resource) { + this.resource = resource; + } + + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/io/ProtocolResolverApplicationContextInitializerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/io/ProtocolResolverApplicationContextInitializerTests.java new file mode 100644 index 00000000000..b7d8bad54a5 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/io/ProtocolResolverApplicationContextInitializerTests.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.io; + +import java.util.Collection; + +import org.junit.jupiter.api.Test; + +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.ProtocolResolver; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ProtocolResolverApplicationContextInitializer}. + * + * @author Scott Frederick + */ +class ProtocolResolverApplicationContextInitializerTests { + + @Test + void initializeAddsProtocolResolversToApplicationContext() { + try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext()) { + ProtocolResolverApplicationContextInitializer initializer = new ProtocolResolverApplicationContextInitializer(); + initializer.initialize(context); + assertThat(context).isInstanceOf(DefaultResourceLoader.class); + Collection protocolResolvers = ((DefaultResourceLoader) context).getProtocolResolvers(); + assertThat(protocolResolvers).hasExactlyElementsOfTypes(Base64ProtocolResolver.class); + } + } + +}