Skip to content

Commit a71bd7c

Browse files
committed
Immutable Resource[Resolver|Transformer]Chains
Backport of #f121aa5e31, applied to spring-webflux only. Issue: SPR-16862
1 parent 1fefe2a commit a71bd7c

File tree

5 files changed

+129
-140
lines changed

5 files changed

+129
-140
lines changed
Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,7 +17,9 @@
1717
package org.springframework.web.reactive.resource;
1818

1919
import java.util.ArrayList;
20+
import java.util.Collections;
2021
import java.util.List;
22+
import java.util.ListIterator;
2123

2224
import reactor.core.publisher.Mono;
2325

@@ -27,68 +29,63 @@
2729
import org.springframework.web.server.ServerWebExchange;
2830

2931
/**
30-
* A default implementation of {@link ResourceResolverChain} for invoking a list
31-
* of {@link ResourceResolver}s.
32+
* Default immutable implementation of {@link ResourceResolverChain}.
3233
*
3334
* @author Rossen Stoyanchev
3435
* @since 5.0
3536
*/
3637
class DefaultResourceResolverChain implements ResourceResolverChain {
3738

38-
private final List<ResourceResolver> resolvers = new ArrayList<>();
39+
@Nullable
40+
private final ResourceResolver resolver;
3941

40-
private int index = -1;
42+
@Nullable
43+
private final ResourceResolverChain nextChain;
4144

4245

4346
public DefaultResourceResolverChain(@Nullable List<? extends ResourceResolver> resolvers) {
44-
if (resolvers != null) {
45-
this.resolvers.addAll(resolvers);
47+
resolvers = resolvers != null ? resolvers : Collections.emptyList();
48+
DefaultResourceResolverChain chain = initChain(new ArrayList<>(resolvers));
49+
this.resolver = chain.resolver;
50+
this.nextChain = chain.nextChain;
51+
}
52+
53+
private static DefaultResourceResolverChain initChain(ArrayList<? extends ResourceResolver> resolvers) {
54+
DefaultResourceResolverChain chain = new DefaultResourceResolverChain(null, null);
55+
ListIterator<? extends ResourceResolver> itr = resolvers.listIterator(resolvers.size());
56+
while (itr.hasPrevious()) {
57+
chain = new DefaultResourceResolverChain(itr.previous(), chain);
4658
}
59+
return chain;
60+
}
61+
62+
private DefaultResourceResolverChain(@Nullable ResourceResolver resolver,
63+
@Nullable ResourceResolverChain chain) {
64+
65+
Assert.isTrue((resolver == null && chain == null) || (resolver != null && chain != null),
66+
"Both resolver and resolver chain must be null, or neither is");
67+
68+
this.resolver = resolver;
69+
this.nextChain = chain;
4770
}
4871

4972

5073
@Override
74+
@SuppressWarnings("ConstantConditions")
5175
public Mono<Resource> resolveResource(@Nullable ServerWebExchange exchange, String requestPath,
5276
List<? extends Resource> locations) {
5377

54-
ResourceResolver resolver = getNext();
55-
if (resolver == null) {
56-
return Mono.empty();
57-
}
58-
59-
try {
60-
return resolver.resolveResource(exchange, requestPath, locations, this);
61-
}
62-
finally {
63-
this.index--;
64-
}
78+
return this.resolver != null ?
79+
this.resolver.resolveResource(exchange, requestPath, locations, this.nextChain) :
80+
Mono.empty();
6581
}
6682

6783
@Override
84+
@SuppressWarnings("ConstantConditions")
6885
public Mono<String> resolveUrlPath(String resourcePath, List<? extends Resource> locations) {
69-
ResourceResolver resolver = getNext();
70-
if (resolver == null) {
71-
return Mono.empty();
72-
}
73-
74-
try {
75-
return resolver.resolveUrlPath(resourcePath, locations, this);
76-
}
77-
finally {
78-
this.index--;
79-
}
80-
}
81-
82-
@Nullable
83-
private ResourceResolver getNext() {
84-
Assert.state(this.index <= this.resolvers.size(),
85-
"Current index exceeds the number of configured ResourceResolvers");
86-
87-
if (this.index == (this.resolvers.size() - 1)) {
88-
return null;
89-
}
90-
this.index++;
91-
return this.resolvers.get(this.index);
86+
return this.resolver != null ?
87+
this.resolver.resolveUrlPath(resourcePath, locations, this.nextChain) :
88+
Mono.empty();
9289
}
9390

9491
}
Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-201/ the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,7 +17,9 @@
1717
package org.springframework.web.reactive.resource;
1818

1919
import java.util.ArrayList;
20+
import java.util.Collections;
2021
import java.util.List;
22+
import java.util.ListIterator;
2123

2224
import reactor.core.publisher.Mono;
2325

@@ -27,8 +29,7 @@
2729
import org.springframework.web.server.ServerWebExchange;
2830

2931
/**
30-
* A default implementation of {@link ResourceTransformerChain} for invoking
31-
* a list of {@link ResourceTransformer}s.
32+
* Default immutable implementation of {@link ResourceTransformerChain}.
3233
*
3334
* @author Rossen Stoyanchev
3435
* @since 5.0
@@ -37,52 +38,58 @@ class DefaultResourceTransformerChain implements ResourceTransformerChain {
3738

3839
private final ResourceResolverChain resolverChain;
3940

40-
private final List<ResourceTransformer> transformers = new ArrayList<>();
41+
@Nullable
42+
private final ResourceTransformer transformer;
4143

42-
private int index = -1;
44+
@Nullable
45+
private final ResourceTransformerChain nextChain;
4346

4447

4548
public DefaultResourceTransformerChain(ResourceResolverChain resolverChain,
4649
@Nullable List<ResourceTransformer> transformers) {
4750

4851
Assert.notNull(resolverChain, "ResourceResolverChain is required");
4952
this.resolverChain = resolverChain;
50-
if (transformers != null) {
51-
this.transformers.addAll(transformers);
53+
54+
transformers = transformers != null ? transformers : Collections.emptyList();
55+
DefaultResourceTransformerChain chain = initTransformerChain(resolverChain, new ArrayList<>(transformers));
56+
this.transformer = chain.transformer;
57+
this.nextChain = chain.nextChain;
58+
}
59+
60+
private DefaultResourceTransformerChain initTransformerChain(ResourceResolverChain resolverChain,
61+
ArrayList<ResourceTransformer> transformers) {
62+
63+
DefaultResourceTransformerChain chain = new DefaultResourceTransformerChain(resolverChain, null, null);
64+
ListIterator<? extends ResourceTransformer> itr = transformers.listIterator(transformers.size());
65+
while (itr.hasPrevious()) {
66+
chain = new DefaultResourceTransformerChain(resolverChain, itr.previous(), chain);
5267
}
68+
return chain;
5369
}
5470

71+
public DefaultResourceTransformerChain(ResourceResolverChain resolverChain,
72+
@Nullable ResourceTransformer transformer, @Nullable ResourceTransformerChain chain) {
73+
74+
Assert.isTrue((transformer == null && chain == null) || (transformer != null && chain != null),
75+
"Both transformer and transformer chain must be null, or neither is");
76+
77+
this.resolverChain = resolverChain;
78+
this.transformer = transformer;
79+
this.nextChain = chain;
80+
}
5581

5682
public ResourceResolverChain getResolverChain() {
5783
return this.resolverChain;
5884
}
5985

6086

6187
@Override
88+
@SuppressWarnings("ConstantConditions")
6289
public Mono<Resource> transform(ServerWebExchange exchange, Resource resource) {
63-
ResourceTransformer transformer = getNext();
64-
if (transformer == null) {
65-
return Mono.just(resource);
66-
}
67-
try {
68-
return transformer.transform(exchange, resource, this);
69-
}
70-
finally {
71-
this.index--;
72-
}
73-
}
74-
75-
@Nullable
76-
private ResourceTransformer getNext() {
77-
Assert.state(this.index <= this.transformers.size(),
78-
"Current index exceeds the number of configured ResourceTransformer's");
79-
80-
if (this.index == (this.transformers.size() - 1)) {
81-
return null;
82-
}
83-
84-
this.index++;
85-
return this.transformers.get(this.index);
90+
return this.transformer != null ?
91+
this.transformer.transform(exchange, resource, this.nextChain) :
92+
Mono.just(resource);
8693
}
8794

8895
}

spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
9797

9898
private final List<ResourceTransformer> resourceTransformers = new ArrayList<>(4);
9999

100+
@Nullable
101+
private ResourceResolverChain resolverChain;
102+
103+
@Nullable
104+
private ResourceTransformerChain transformerChain;
105+
100106
@Nullable
101107
private CacheControl cacheControl;
102108

@@ -199,10 +205,17 @@ public void afterPropertiesSet() throws Exception {
199205
if (this.resourceResolvers.isEmpty()) {
200206
this.resourceResolvers.add(new PathResourceResolver());
201207
}
208+
202209
initAllowedLocations();
210+
203211
if (getResourceHttpMessageWriter() == null) {
204212
this.resourceHttpMessageWriter = new ResourceHttpMessageWriter();
205213
}
214+
215+
216+
// Initialize immutable resolver and transformer chains
217+
this.resolverChain = new DefaultResourceResolverChain(this.resourceResolvers);
218+
this.transformerChain = new DefaultResourceTransformerChain(this.resolverChain, this.resourceTransformers);
206219
}
207220

208221
/**
@@ -330,12 +343,11 @@ protected Mono<Resource> getResource(ServerWebExchange exchange) {
330343
return Mono.empty();
331344
}
332345

333-
ResourceResolverChain resolveChain = createResolverChain();
334-
return resolveChain.resolveResource(exchange, path, getLocations())
335-
.flatMap(resource -> {
336-
ResourceTransformerChain transformerChain = createTransformerChain(resolveChain);
337-
return transformerChain.transform(exchange, resource);
338-
});
346+
Assert.notNull(this.resolverChain, "ResourceResolverChain not initialized.");
347+
Assert.notNull(this.transformerChain, "ResourceTransformerChain not initialized.");
348+
349+
return this.resolverChain.resolveResource(exchange, path, getLocations())
350+
.flatMap(resource -> this.transformerChain.transform(exchange, resource));
339351
}
340352

341353
/**
@@ -470,14 +482,6 @@ protected boolean isInvalidPath(String path) {
470482
return false;
471483
}
472484

473-
private ResourceResolverChain createResolverChain() {
474-
return new DefaultResourceResolverChain(getResourceResolvers());
475-
}
476-
477-
private ResourceTransformerChain createTransformerChain(ResourceResolverChain resolverChain) {
478-
return new DefaultResourceTransformerChain(resolverChain, getResourceTransformers());
479-
}
480-
481485
/**
482486
* Set headers on the response. Called for both GET and HEAD requests.
483487
* @param exchange current exchange

0 commit comments

Comments
 (0)