Skip to content

Commit 0295573

Browse files
committed
Avoid late registration of PathPatternParser on DelegatingHandlerMapping.
Instead of registering the PathPatternParser on DelegatingHandlerMapping via WebMvcConfigurer.configurePathMatch(…) we now consume the bean exposed in context of the fix for spring-projects/spring-framework#26427. We also use the newly introduced RequestMappingInfo.mutate() to add our customizations of the produces clause for Spring Data REST's mappings. Fixes GH-1965.
1 parent 5dc7536 commit 0295573

File tree

5 files changed

+51
-31
lines changed

5 files changed

+51
-31
lines changed

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/BasePathAwareHandlerMapping.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.HashMap;
2323
import java.util.List;
2424
import java.util.Map;
25+
import java.util.Set;
2526
import java.util.function.Predicate;
2627

2728
import javax.servlet.http.HttpServletRequest;
@@ -122,9 +123,11 @@ protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handler
122123
}
123124

124125
ProducesRequestCondition producesCondition = customize(info.getProducesCondition());
126+
Set<MediaType> mediaTypes = producesCondition.getProducibleMediaTypes();
125127

126-
return new RequestMappingInfo(info.getPatternsCondition(), info.getMethodsCondition(), info.getParamsCondition(),
127-
info.getHeadersCondition(), info.getConsumesCondition(), producesCondition, info.getCustomCondition());
128+
return info.mutate()
129+
.produces(mediaTypes.stream().map(MediaType::toString).toArray(String[]::new))
130+
.build();
128131
}
129132

130133
/**

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.springframework.web.bind.annotation.RequestMethod;
4545
import org.springframework.web.cors.CorsConfiguration;
4646
import org.springframework.web.method.HandlerMethod;
47+
import org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition;
4748
import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
4849
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
4950
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@@ -266,13 +267,29 @@ private void exposeEffectiveLookupPathKey(HandlerMethod method, HttpServletReque
266267
return;
267268
}
268269

269-
String pattern = mappingInfo.getPatternsCondition() //
270-
.getMatchingCondition(request)//
271-
.getPatterns() //
272-
.iterator().next();
270+
String pattern = getPattern(mappingInfo, request);
271+
272+
PathPatternParser parser = getPatternParser();
273+
parser = parser != null ? parser : PARSER;
273274

274275
request.setAttribute(EFFECTIVE_LOOKUP_PATH_ATTRIBUTE,
275-
PARSER.parse(pattern.replace("/{repository}", repositoryBasePath)));
276+
parser.parse(pattern.replace("/{repository}", repositoryBasePath)));
277+
}
278+
279+
private static String getPattern(RequestMappingInfo info, HttpServletRequest request) {
280+
281+
PathPatternsRequestCondition pathPatternsCondition = info.getPathPatternsCondition();
282+
283+
if (pathPatternsCondition != null) {
284+
return pathPatternsCondition
285+
.getMatchingCondition(request)
286+
.getFirstPattern().getPatternString();
287+
}
288+
289+
return info.getPatternsCondition() //
290+
.getMatchingCondition(request)//
291+
.getPatterns()
292+
.iterator().next();
276293
}
277294

278295
/**

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMapping.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@
2222
import javax.servlet.http.HttpServletRequest;
2323

2424
import org.springframework.core.Ordered;
25+
import org.springframework.lang.Nullable;
2526
import org.springframework.util.Assert;
2627
import org.springframework.web.HttpMediaTypeNotAcceptableException;
2728
import org.springframework.web.HttpMediaTypeNotSupportedException;
2829
import org.springframework.web.HttpRequestMethodNotSupportedException;
2930
import org.springframework.web.bind.UnsatisfiedServletRequestParameterException;
3031
import org.springframework.web.servlet.HandlerExecutionChain;
3132
import org.springframework.web.servlet.HandlerMapping;
32-
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
3333
import org.springframework.web.servlet.handler.MatchableHandlerMapping;
3434
import org.springframework.web.servlet.handler.RequestMatchResult;
3535
import org.springframework.web.util.pattern.PathPatternParser;
@@ -45,25 +45,28 @@ class DelegatingHandlerMapping
4545
implements org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping, Ordered {
4646

4747
private final List<HandlerMapping> delegates;
48+
private final @Nullable PathPatternParser parser;
4849

4950
/**
5051
* Creates a new {@link DelegatingHandlerMapping} for the given delegates.
5152
*
5253
* @param delegates must not be {@literal null}.
5354
*/
54-
public DelegatingHandlerMapping(List<HandlerMapping> delegates) {
55+
public DelegatingHandlerMapping(List<HandlerMapping> delegates, @Nullable PathPatternParser parser) {
5556

5657
Assert.notNull(delegates, "Delegates must not be null!");
5758

5859
this.delegates = delegates;
60+
this.parser = parser;
5961
}
6062

61-
void setPatternParser(PathPatternParser parser) {
62-
63-
delegates.stream() //
64-
.filter(AbstractHandlerMapping.class::isInstance) //
65-
.map(AbstractHandlerMapping.class::cast) //
66-
.forEach(it -> it.setPatternParser(parser));
63+
/*
64+
* (non-Javadoc)
65+
* @see org.springframework.web.servlet.HandlerMapping#usesPathPatterns()
66+
*/
67+
@Override
68+
public boolean usesPathPatterns() {
69+
return parser != null;
6770
}
6871

6972
@SuppressWarnings("all")

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,10 @@
118118
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
119119
import org.springframework.web.servlet.HandlerExceptionResolver;
120120
import org.springframework.web.servlet.HandlerMapping;
121-
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
122121
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
123122
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
124123
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
124+
import org.springframework.web.util.pattern.PathPatternParser;
125125

126126
import com.fasterxml.jackson.databind.DeserializationFeature;
127127
import com.fasterxml.jackson.databind.Module;
@@ -168,9 +168,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
168168
ObjectProvider<RepresentationModelProcessorInvoker> invoker;
169169
ObjectProvider<MessageResolver> resolver;
170170
ObjectProvider<GeoModule> geoModule;
171+
171172
ConversionService defaultConversionService;
172173

173174
private final Lazy<ObjectMapper> mapper;
175+
private final ObjectProvider<PathPatternParser> parser;
174176

175177
private ClassLoader beanClassLoader;
176178

@@ -181,7 +183,6 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
181183
private Lazy<BaseUri> baseUri;
182184
private Lazy<RepositoryResourceMappings> resourceMappings;
183185
private Lazy<Repositories> repositories;
184-
private Lazy<DelegatingHandlerMapping> restHandlerMapping;
185186
private Lazy<ResourceMetadataHandlerMethodArgumentResolver> resourceMetadataHandlerMethodArgumentResolver;
186187
private Lazy<ExcerptProjector> excerptProjector;
187188
private Lazy<PersistentEntities> persistentEntities;
@@ -204,7 +205,8 @@ public RepositoryRestMvcConfiguration( //
204205
ObjectProvider<ObjectMapper> objectMapper, //
205206
ObjectProvider<RepresentationModelProcessorInvoker> invoker, //
206207
ObjectProvider<MessageResolver> resolver, //
207-
ObjectProvider<GeoModule> geoModule) {
208+
ObjectProvider<GeoModule> geoModule, //
209+
ObjectProvider<PathPatternParser> parser) {
208210

209211
super(context, conversionService);
210212

@@ -215,6 +217,7 @@ public RepositoryRestMvcConfiguration( //
215217
this.invoker = invoker;
216218
this.resolver = resolver;
217219
this.geoModule = geoModule;
220+
this.parser = parser;
218221

219222
this.mapper = Lazy.of(() -> {
220223

@@ -239,7 +242,6 @@ public RepositoryRestMvcConfiguration( //
239242
this.baseUri = Lazy.of(() -> context.getBean(BaseUri.class));
240243
this.resourceMappings = Lazy.of(() -> context.getBean(RepositoryResourceMappings.class));
241244
this.repositories = Lazy.of(() -> context.getBean(Repositories.class));
242-
this.restHandlerMapping = Lazy.of(() -> context.getBean("restHandlerMapping", DelegatingHandlerMapping.class));
243245
this.resourceMetadataHandlerMethodArgumentResolver = Lazy
244246
.of(() -> context.getBean(ResourceMetadataHandlerMethodArgumentResolver.class));
245247
this.excerptProjector = Lazy.of(() -> context.getBean(ExcerptProjector.class));
@@ -345,6 +347,7 @@ public JpaHelper jpaHelper() {
345347
* Main configuration for the REST exporter.
346348
*/
347349
@Bean
350+
@SuppressWarnings("unchecked")
348351
public <T extends RepositoryRestConfiguration & CorsConfigurationAware> T repositoryRestConfiguration() {
349352

350353
ProjectionDefinitionConfiguration configuration = new ProjectionDefinitionConfiguration();
@@ -641,33 +644,27 @@ public DelegatingHandlerMapping restHandlerMapping(Repositories repositories,
641644
RepositoryRestConfiguration repositoryRestConfiguration, CorsConfigurationAware corsRestConfiguration) {
642645

643646
Map<String, CorsConfiguration> corsConfigurations = corsRestConfiguration.getCorsConfigurations();
647+
PathPatternParser parser = this.parser.getIfAvailable();
644648

645649
RepositoryRestHandlerMapping repositoryMapping = new RepositoryRestHandlerMapping(resourceMappings,
646650
repositoryRestConfiguration, repositories);
647651
repositoryMapping.setJpaHelper(jpaHelper.orElse(null));
648652
repositoryMapping.setApplicationContext(applicationContext);
649653
repositoryMapping.setCorsConfigurations(corsConfigurations);
654+
repositoryMapping.setPatternParser(parser);
650655
repositoryMapping.afterPropertiesSet();
651656

652657
BasePathAwareHandlerMapping basePathMapping = new BasePathAwareHandlerMapping(repositoryRestConfiguration);
653658
basePathMapping.setApplicationContext(applicationContext);
654659
basePathMapping.setCorsConfigurations(corsConfigurations);
660+
basePathMapping.setPatternParser(parser);
655661
basePathMapping.afterPropertiesSet();
656662

657663
List<HandlerMapping> mappings = new ArrayList<>();
658664
mappings.add(basePathMapping);
659665
mappings.add(repositoryMapping);
660666

661-
return new DelegatingHandlerMapping(mappings);
662-
}
663-
664-
/*
665-
* (non-Javadoc)
666-
* @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer#configurePathMatch(org.springframework.web.servlet.config.annotation.PathMatchConfigurer)
667-
*/
668-
@Override
669-
public void configurePathMatch(PathMatchConfigurer configurer) {
670-
restHandlerMapping.get().setPatternParser(configurer.getPatternParser());
667+
return new DelegatingHandlerMapping(mappings, parser);
671668
}
672669

673670
@Bean

spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMappingUnitTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public class DelegatingHandlerMappingUnitTests {
5252
@Test // DATAREST-490, DATAREST-522, DATAREST-1387
5353
public void consultsAllHandlerMappingsAndThrowsExceptionEventually() throws Exception {
5454

55-
DelegatingHandlerMapping mapping = new DelegatingHandlerMapping(Arrays.asList(first, second));
55+
DelegatingHandlerMapping mapping = new DelegatingHandlerMapping(Arrays.asList(first, second), null);
5656

5757
assertHandlerTriedButExceptionThrown(mapping, HttpMediaTypeNotAcceptableException.class);
5858
assertHandlerTriedButExceptionThrown(mapping, HttpRequestMethodNotSupportedException.class);
@@ -73,7 +73,7 @@ public void exposesMatchabilityOfSelectedMapping() {
7373
MatchableHandlerMapping fourth = mock(MatchableHandlerMapping.class, Answers.RETURNS_MOCKS);
7474
doReturn(result).when(fourth).match(any(), any(String.class));
7575

76-
DelegatingHandlerMapping mapping = new DelegatingHandlerMapping(Arrays.asList(first, second, third, fourth));
76+
DelegatingHandlerMapping mapping = new DelegatingHandlerMapping(Arrays.asList(first, second, third, fourth), null);
7777

7878
assertThat(mapping.match(request, "somePattern")).isEqualTo(result);
7979
}

0 commit comments

Comments
 (0)