Skip to content

Commit cbdab9e

Browse files
committed
Add support for custom ProtocolResolver with Devtools
Prior to this commit, custom `ProtocolResolvers` set on the `ApplicationContext` were lost when Devtools is used as the customized `ResourceLoader` did not copy any customization made to the default resource loader. This commit makes sure to copy any `ProtocolResolver` set on the context. Closes gh-9331
1 parent aa6dbdb commit cbdab9e

File tree

3 files changed

+73
-3
lines changed

3 files changed

+73
-3
lines changed

spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/ClassLoaderFilesResourcePatternResolver.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.net.MalformedURLException;
2323
import java.net.URL;
2424
import java.util.ArrayList;
25+
import java.util.Collection;
2526
import java.util.List;
2627
import java.util.Map.Entry;
2728

@@ -33,6 +34,7 @@
3334
import org.springframework.context.ApplicationContext;
3435
import org.springframework.core.io.AbstractResource;
3536
import org.springframework.core.io.DefaultResourceLoader;
37+
import org.springframework.core.io.ProtocolResolver;
3638
import org.springframework.core.io.Resource;
3739
import org.springframework.core.io.ResourceLoader;
3840
import org.springframework.core.io.UrlResource;
@@ -52,6 +54,7 @@
5254
*
5355
* @author Andy Wilkinson
5456
* @author Phillip Webb
57+
* @author Stephane Nicoll
5558
*/
5659
final class ClassLoaderFilesResourcePatternResolver implements ResourcePatternResolver {
5760

@@ -206,7 +209,20 @@ private static class ResourcePatternResolverFactory {
206209
public ResourcePatternResolver getResourcePatternResolver(
207210
ApplicationContext applicationContext, ResourceLoader resourceLoader) {
208211
return new PathMatchingResourcePatternResolver(resourceLoader == null
209-
? new DefaultResourceLoader() : resourceLoader);
212+
? createResourceLoader(applicationContext) : resourceLoader);
213+
}
214+
215+
private ResourceLoader createResourceLoader(
216+
ApplicationContext applicationContext) {
217+
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
218+
if (applicationContext instanceof DefaultResourceLoader) {
219+
Collection<ProtocolResolver> protocolResolvers =
220+
((DefaultResourceLoader) applicationContext).getProtocolResolvers();
221+
for (ProtocolResolver protocolResolver : protocolResolvers) {
222+
resourceLoader.addProtocolResolver(protocolResolver);
223+
}
224+
}
225+
return resourceLoader;
210226
}
211227

212228
}
@@ -223,13 +239,26 @@ public ResourcePatternResolver getResourcePatternResolver(
223239
ApplicationContext applicationContext, ResourceLoader resourceLoader) {
224240
if (applicationContext instanceof WebApplicationContext) {
225241
return new ServletContextResourcePatternResolver(resourceLoader == null
226-
? new WebApplicationContextResourceLoader(
227-
(WebApplicationContext) applicationContext)
242+
? createResourceLoader((WebApplicationContext) applicationContext)
228243
: resourceLoader);
229244
}
230245
return super.getResourcePatternResolver(applicationContext, resourceLoader);
231246
}
232247

248+
private ResourceLoader createResourceLoader(
249+
WebApplicationContext applicationContext) {
250+
WebApplicationContextResourceLoader resourceLoader =
251+
new WebApplicationContextResourceLoader(applicationContext);
252+
if (applicationContext instanceof DefaultResourceLoader) {
253+
Collection<ProtocolResolver> protocolResolvers =
254+
((DefaultResourceLoader) applicationContext).getProtocolResolvers();
255+
for (ProtocolResolver protocolResolver : protocolResolvers) {
256+
resourceLoader.addProtocolResolver(protocolResolver);
257+
}
258+
}
259+
return resourceLoader;
260+
}
261+
233262
}
234263

235264
/**

spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/ClassLoaderFilesResourcePatternResolverTests.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFiles;
3131
import org.springframework.context.support.GenericApplicationContext;
3232
import org.springframework.core.io.ClassPathResource;
33+
import org.springframework.core.io.ProtocolResolver;
3334
import org.springframework.core.io.Resource;
3435
import org.springframework.core.io.ResourceLoader;
3536
import org.springframework.mock.web.MockServletContext;
@@ -38,6 +39,9 @@
3839
import org.springframework.web.context.support.ServletContextResource;
3940

4041
import static org.assertj.core.api.Assertions.assertThat;
42+
import static org.mockito.BDDMockito.given;
43+
import static org.mockito.Matchers.any;
44+
import static org.mockito.Matchers.eq;
4145
import static org.mockito.Mockito.mock;
4246
import static org.mockito.Mockito.verify;
4347

@@ -46,6 +50,7 @@
4650
*
4751
* @author Phillip Webb
4852
* @author Andy Wilkinson
53+
* @author Stephane Nicoll
4954
*/
5055
public class ClassLoaderFilesResourcePatternResolverTests {
5156

@@ -125,6 +130,18 @@ public void customResourceLoaderIsUsedInNonWebApplication() throws Exception {
125130
verify(resourceLoader).getResource("foo.txt");
126131
}
127132

133+
@Test
134+
public void customProtocolResolverIsUsedInNonWebApplication() throws Exception {
135+
GenericApplicationContext context = new GenericApplicationContext();
136+
Resource resource = mock(Resource.class);
137+
ProtocolResolver resolver = mockProtocolResolver("foo:some-file.txt", resource);
138+
context.addProtocolResolver(resolver);
139+
this.resolver = new ClassLoaderFilesResourcePatternResolver(context, this.files);
140+
Resource actual = this.resolver.getResource("foo:some-file.txt");
141+
assertThat(actual).isSameAs(resource);
142+
verify(resolver).resolve(eq("foo:some-file.txt"), any(ResourceLoader.class));
143+
}
144+
128145
@Test
129146
public void customResourceLoaderIsUsedInWebApplication() throws Exception {
130147
GenericWebApplicationContext context = new GenericWebApplicationContext(
@@ -136,6 +153,26 @@ public void customResourceLoaderIsUsedInWebApplication() throws Exception {
136153
verify(resourceLoader).getResource("foo.txt");
137154
}
138155

156+
@Test
157+
public void customProtocolResolverIsUsedInWebApplication() throws Exception {
158+
GenericWebApplicationContext context = new GenericWebApplicationContext(
159+
new MockServletContext());
160+
Resource resource = mock(Resource.class);
161+
ProtocolResolver resolver = mockProtocolResolver("foo:some-file.txt", resource);
162+
context.addProtocolResolver(resolver);
163+
this.resolver = new ClassLoaderFilesResourcePatternResolver(context, this.files);
164+
Resource actual = this.resolver.getResource("foo:some-file.txt");
165+
assertThat(actual).isSameAs(resource);
166+
verify(resolver).resolve(eq("foo:some-file.txt"), any(ResourceLoader.class));
167+
}
168+
169+
private ProtocolResolver mockProtocolResolver(String path, Resource resource) {
170+
ProtocolResolver resolver = mock(ProtocolResolver.class);
171+
given(resolver.resolve(eq(path), any(ResourceLoader.class)))
172+
.willReturn(resource);
173+
return resolver;
174+
}
175+
139176
private File createFile(File folder, String name) throws IOException {
140177
File file = new File(folder, name);
141178
FileCopyUtils.copy("test".getBytes(), file);

spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,10 @@ NOTE: When deciding if an entry on the classpath should trigger a restart when i
853853
DevTools automatically ignores projects named `spring-boot`, `spring-boot-devtools`,
854854
`spring-boot-autoconfigure`, `spring-boot-actuator`, and `spring-boot-starter`.
855855

856+
NOTE: DevTools needs to customize the `ResourceLoader` used by the `ApplicationContext`:
857+
if your application provides one already, it is going to be wrapped. Direct override of
858+
the `getResource` method on the `ApplicationContext` is not supported.
859+
856860
[[using-spring-boot-restart-vs-reload]]
857861
.Restart vs Reload
858862
****

0 commit comments

Comments
 (0)