Skip to content

Commit 531ad91

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 bc3f3c1 commit 531ad91

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
@@ -119,11 +119,11 @@
119119
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
120120
import org.springframework.web.servlet.HandlerExceptionResolver;
121121
import org.springframework.web.servlet.HandlerMapping;
122-
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
123122
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
124123
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
125124
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
126125
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
126+
import org.springframework.web.util.pattern.PathPatternParser;
127127

128128
import com.fasterxml.jackson.databind.DeserializationFeature;
129129
import com.fasterxml.jackson.databind.Module;
@@ -170,9 +170,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
170170
ObjectProvider<RepresentationModelProcessorInvoker> invoker;
171171
ObjectProvider<MessageResolver> resolver;
172172
ObjectProvider<GeoModule> geoModule;
173+
173174
ConversionService defaultConversionService;
174175

175176
private final Lazy<ObjectMapper> mapper;
177+
private final ObjectProvider<PathPatternParser> parser;
176178

177179
private ClassLoader beanClassLoader;
178180

@@ -183,7 +185,6 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
183185
private Lazy<BaseUri> baseUri;
184186
private Lazy<RepositoryResourceMappings> resourceMappings;
185187
private Lazy<Repositories> repositories;
186-
private Lazy<DelegatingHandlerMapping> restHandlerMapping;
187188
private Lazy<ResourceMetadataHandlerMethodArgumentResolver> resourceMetadataHandlerMethodArgumentResolver;
188189
private Lazy<ExcerptProjector> excerptProjector;
189190
private Lazy<PersistentEntities> persistentEntities;
@@ -206,7 +207,8 @@ public RepositoryRestMvcConfiguration( //
206207
ObjectProvider<ObjectMapper> objectMapper, //
207208
ObjectProvider<RepresentationModelProcessorInvoker> invoker, //
208209
ObjectProvider<MessageResolver> resolver, //
209-
ObjectProvider<GeoModule> geoModule) {
210+
ObjectProvider<GeoModule> geoModule, //
211+
ObjectProvider<PathPatternParser> parser) {
210212

211213
super(context, conversionService);
212214

@@ -217,6 +219,7 @@ public RepositoryRestMvcConfiguration( //
217219
this.invoker = invoker;
218220
this.resolver = resolver;
219221
this.geoModule = geoModule;
222+
this.parser = parser;
220223

221224
this.mapper = Lazy.of(() -> {
222225

@@ -241,7 +244,6 @@ public RepositoryRestMvcConfiguration( //
241244
this.baseUri = Lazy.of(() -> context.getBean(BaseUri.class));
242245
this.resourceMappings = Lazy.of(() -> context.getBean(RepositoryResourceMappings.class));
243246
this.repositories = Lazy.of(() -> context.getBean(Repositories.class));
244-
this.restHandlerMapping = Lazy.of(() -> context.getBean("restHandlerMapping", DelegatingHandlerMapping.class));
245247
this.resourceMetadataHandlerMethodArgumentResolver = Lazy
246248
.of(() -> context.getBean(ResourceMetadataHandlerMethodArgumentResolver.class));
247249
this.excerptProjector = Lazy.of(() -> context.getBean(ExcerptProjector.class));
@@ -347,6 +349,7 @@ public JpaHelper jpaHelper() {
347349
* Main configuration for the REST exporter.
348350
*/
349351
@Bean
352+
@SuppressWarnings("unchecked")
350353
public <T extends RepositoryRestConfiguration & CorsConfigurationAware> T repositoryRestConfiguration() {
351354

352355
ProjectionDefinitionConfiguration configuration = new ProjectionDefinitionConfiguration();
@@ -643,33 +646,27 @@ public DelegatingHandlerMapping restHandlerMapping(Repositories repositories,
643646
RepositoryRestConfiguration repositoryRestConfiguration, CorsConfigurationAware corsRestConfiguration) {
644647

645648
Map<String, CorsConfiguration> corsConfigurations = corsRestConfiguration.getCorsConfigurations();
649+
PathPatternParser parser = this.parser.getIfAvailable();
646650

647651
RepositoryRestHandlerMapping repositoryMapping = new RepositoryRestHandlerMapping(resourceMappings,
648652
repositoryRestConfiguration, repositories);
649653
repositoryMapping.setJpaHelper(jpaHelper.orElse(null));
650654
repositoryMapping.setApplicationContext(applicationContext);
651655
repositoryMapping.setCorsConfigurations(corsConfigurations);
656+
repositoryMapping.setPatternParser(parser);
652657
repositoryMapping.afterPropertiesSet();
653658

654659
BasePathAwareHandlerMapping basePathMapping = new BasePathAwareHandlerMapping(repositoryRestConfiguration);
655660
basePathMapping.setApplicationContext(applicationContext);
656661
basePathMapping.setCorsConfigurations(corsConfigurations);
662+
basePathMapping.setPatternParser(parser);
657663
basePathMapping.afterPropertiesSet();
658664

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

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

675672
@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)