From 02498ec1e335bc69506b41f677a0a4c54bd7c7e9 Mon Sep 17 00:00:00 2001 From: Dmytro Nosan Date: Fri, 21 Feb 2025 19:05:40 +0200 Subject: [PATCH] Update ApplicationResourceLoader to support FilteredReactiveWebContextResource Prior to this update, FilteredReactiveWebContextResource was not considered when preferFileResolution was set to true. This commit updates the ApplicationResourceLoader to include support for FilteredReactiveWebContextResource. Signed-off-by: Dmytro Nosan --- .../boot/io/ApplicationResourceLoader.java | 52 +++++++++++++------ .../io/ApplicationResourceLoaderTests.java | 21 ++++++++ 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/io/ApplicationResourceLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/io/ApplicationResourceLoader.java index de160fdaa3ff..50957a9d4f91 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/io/ApplicationResourceLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/io/ApplicationResourceLoader.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. @@ -132,8 +132,8 @@ public static ResourceLoader get(ResourceLoader resourceLoader) { * class loader at the time this call is made. * @param resourceLoader the delegate resource loader * @param preferFileResolution if file based resolution is preferred over - * {@code ServletContextResource} or {@link ClassPathResource} when no resource prefix - * is provided. + * {@code ServletContextResource}, {@code FilteredReactiveWebContextResource} or + * {@link ClassPathResource} when no resource prefix is provided. * @return a {@link ResourceLoader} instance * @since 3.4.1 */ @@ -212,28 +212,32 @@ private static class ProtocolResolvingResourceLoader implements ResourceLoader { private static final String SERVLET_CONTEXT_RESOURCE_CLASS_NAME = "org.springframework.web.context.support.ServletContextResource"; + private static final String FILTERED_REACTIVE_WEB_CONTEXT_RESOURCE_CLASS_NAME = "org.springframework.boot.web.reactive.context.FilteredReactiveWebContextResource"; + private final ResourceLoader resourceLoader; private final List protocolResolvers; private final boolean preferFileResolution; - private Class servletContextResourceClass; + private final Class servletContextResourceClass; + + private final Class filteredReactiveWebContextResourceClass; ProtocolResolvingResourceLoader(ResourceLoader resourceLoader, List protocolResolvers, boolean preferFileResolution) { this.resourceLoader = resourceLoader; this.protocolResolvers = protocolResolvers; this.preferFileResolution = preferFileResolution; - this.servletContextResourceClass = resolveServletContextResourceClass( + this.servletContextResourceClass = resolveClassName(SERVLET_CONTEXT_RESOURCE_CLASS_NAME, resourceLoader.getClass().getClassLoader()); + this.filteredReactiveWebContextResourceClass = resolveClassName( + FILTERED_REACTIVE_WEB_CONTEXT_RESOURCE_CLASS_NAME, resourceLoader.getClass().getClassLoader()); } - private static Class resolveServletContextResourceClass(ClassLoader classLoader) { - if (!ClassUtils.isPresent(SERVLET_CONTEXT_RESOURCE_CLASS_NAME, classLoader)) { - return null; - } - return ClassUtils.resolveClassName(SERVLET_CONTEXT_RESOURCE_CLASS_NAME, classLoader); + @Override + public ClassLoader getClassLoader() { + return this.resourceLoader.getClassLoader(); } @Override @@ -247,24 +251,38 @@ public Resource getResource(String location) { } } Resource resource = this.resourceLoader.getResource(location); - if (this.preferFileResolution - && (isClassPathResourceByPath(location, resource) || isServletResource(resource))) { - resource = new ApplicationResource(location); + if (shouldUseFileResolution(location, resource)) { + return new ApplicationResource(location); } return resource; } + private boolean shouldUseFileResolution(String location, Resource resource) { + if (!this.preferFileResolution) { + return false; + } + return isClassPathResourceByPath(location, resource) || isServletResource(resource) + || isFilteredReactiveWebContextResource(resource); + } + private boolean isClassPathResourceByPath(String location, Resource resource) { return (resource instanceof ClassPathResource) && !location.startsWith(CLASSPATH_URL_PREFIX); } private boolean isServletResource(Resource resource) { - return this.servletContextResourceClass != null && this.servletContextResourceClass.isInstance(resource); + return (this.servletContextResourceClass != null) && this.servletContextResourceClass.isInstance(resource); } - @Override - public ClassLoader getClassLoader() { - return this.resourceLoader.getClassLoader(); + private boolean isFilteredReactiveWebContextResource(Resource resource) { + return (this.filteredReactiveWebContextResourceClass != null) + && this.filteredReactiveWebContextResourceClass.isInstance(resource); + } + + private static Class resolveClassName(String clazz, ClassLoader classLoader) { + if (!ClassUtils.isPresent(clazz, classLoader)) { + return null; + } + return ClassUtils.resolveClassName(clazz, classLoader); } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/io/ApplicationResourceLoaderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/io/ApplicationResourceLoaderTests.java index 6da80ecfa923..87d34cdcd8e9 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/io/ApplicationResourceLoaderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/io/ApplicationResourceLoaderTests.java @@ -29,6 +29,7 @@ import org.springframework.boot.testsupport.classpath.resources.ResourcePath; import org.springframework.boot.testsupport.classpath.resources.WithResource; +import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.DefaultResourceLoader; @@ -37,6 +38,7 @@ import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.mock.web.MockServletContext; +import org.springframework.util.ClassUtils; import org.springframework.web.context.support.ServletContextResource; import org.springframework.web.context.support.ServletContextResourceLoader; @@ -209,6 +211,25 @@ void getResourceWithPreferFileResolutionWhenPathWithServletContextResource() thr assertThat(regularLoader.getResource("src/main/resources/a-file")).isInstanceOf(ServletContextResource.class); } + @Test + void getResourceWhenFilteredReactiveWebContextResourceWithPreferFileResolution() throws Exception { + ResourceLoader resourceLoader = ApplicationResourceLoader + .get(new AnnotationConfigReactiveWebApplicationContext(), true); + Resource resource = resourceLoader.getResource("src/main/resources/a-file"); + assertThat(resource).isInstanceOf(FileSystemResource.class); + assertThat(resource.getFile().getAbsoluteFile()) + .isEqualTo(new File("src/main/resources/a-file").getAbsoluteFile()); + } + + @Test + void getResourceWhenFilteredReactiveWebContextResource() { + ResourceLoader resourceLoader = ApplicationResourceLoader + .get(new AnnotationConfigReactiveWebApplicationContext(), false); + Resource resource = resourceLoader.getResource("src/main/resources/a-file"); + assertThat(resource).isInstanceOf(ClassUtils.resolveClassName( + "org.springframework.boot.web.reactive.context.FilteredReactiveWebContextResource", null)); + } + @Test void getClassLoaderReturnsDelegateClassLoader() { ClassLoader classLoader = new TestClassLoader(this::useTestProtocolResolversFactories);