Skip to content

DATACMNS-822 - Provide customizers for default pageable and sort resolvers. #208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<version>2.0.0.DATACMNS-822-SNAPSHOT</version>

<name>Spring Data Core</name>

Expand Down
11 changes: 10 additions & 1 deletion src/main/asciidoc/repositories.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,16 @@ This method signature will cause Spring MVC try to derive a Pageable instance fr
|`sort`|Properties that should be sorted by in the format `property,property(,ASC\|DESC)`. Default sort direction is ascending. Use multiple `sort` parameters if you want to switch directions, e.g. `?sort=firstname&sort=lastname,asc`.
|===============

To customize this behavior extend either `SpringDataWebConfiguration` or the HATEOAS-enabled equivalent and override the `pageableResolver()` or `sortResolver()` methods and import your customized configuration file instead of using the `@Enable`-annotation.
To customize this behavior register a bean implementing the interface `PageableHandlerMethodArgumentResolverCustomizer` or `SortHandlerMethodArgumentResolverCustomizer` respectively. It's `customize()` method will get called allowing you to change settings. Like in the following example.

[source, java]
----
@Bean SortHandlerMethodArgumentResolverCustomizer sortCustomizer() {
return s -> s.setPropertyDelimiter("<-->");
}
----

If setting the properties of an existing `MethodArgumentResolver` isn't sufficient for your purpose extend either `SpringDataWebConfiguration` or the HATEOAS-enabled equivalent and override the `pageableResolver()` or `sortResolver()` methods and import your customized configuration file instead of using the `@Enable`-annotation.

In case you need multiple `Pageable` or `Sort` instances to be resolved from the request (for multiple tables, for example) you can use Spring's `@Qualifier` annotation to distinguish one from another. The request parameters then have to be prefixed with `${qualifier}_`. So for a method signature like this:

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2017 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.
Expand Down Expand Up @@ -36,6 +36,7 @@
* @author Oliver Gierke
* @author Nick Williams
* @author Ben Hale
* @author Vedran Pavic
*/
@Configuration
public class HateoasAwareSpringDataWebConfiguration extends SpringDataWebConfiguration {
Expand All @@ -56,7 +57,11 @@ public HateoasAwareSpringDataWebConfiguration(ApplicationContext context,
@Bean
@Override
public HateoasPageableHandlerMethodArgumentResolver pageableResolver() {
return new HateoasPageableHandlerMethodArgumentResolver(sortResolver());

HateoasPageableHandlerMethodArgumentResolver pageableResolver = //
new HateoasPageableHandlerMethodArgumentResolver(sortResolver());
customizePageableResolver(pageableResolver);
return pageableResolver;
}

/*
Expand All @@ -66,7 +71,10 @@ public HateoasPageableHandlerMethodArgumentResolver pageableResolver() {
@Bean
@Override
public HateoasSortHandlerMethodArgumentResolver sortResolver() {
return new HateoasSortHandlerMethodArgumentResolver();

HateoasSortHandlerMethodArgumentResolver sortResolver = new HateoasSortHandlerMethodArgumentResolver();
customizeSortResolver(sortResolver);
return sortResolver;
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2017 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.web.config;

import org.springframework.data.web.PageableHandlerMethodArgumentResolver;

/**
* Callback interface that can be implemented by beans wishing to customize the
* {@link PageableHandlerMethodArgumentResolver} configuration.
*
* @author Vedran Pavic
*/
public interface PageableHandlerMethodArgumentResolverCustomizer {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this is going to be a feature for the Kay release we could add @FunctionalInterface here. Not sure if extending Consumer<PageableHandlerMethodArgumentResolver> and removing customize makes sense here.
Also I'd prefer a naming like PageableHandlerMethodArgumentResolverConfigurationAdapter over ...*Customizer.


/**
* Customize the pageable resolver
*
* @param pageableResolver the {@link PageableHandlerMethodArgumentResolver} to customize
*/
void customize(PageableHandlerMethodArgumentResolver pageableResolver);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2017 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.web.config;

import org.springframework.data.web.SortHandlerMethodArgumentResolver;

/**
* Callback interface that can be implemented by beans wishing to customize the
* {@link SortHandlerMethodArgumentResolver} configuration.
*
* @author Vedran Pavic
*/
public interface SortHandlerMethodArgumentResolverCustomizer {

/**
* Customize the sort resolver
*
* @param sortResolver the {@link SortHandlerMethodArgumentResolver} to customize
*/
void customize(SortHandlerMethodArgumentResolver sortResolver);

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
package org.springframework.data.web.config;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
Expand Down Expand Up @@ -46,6 +48,7 @@
*
* @since 1.6
* @author Oliver Gierke
* @author Vedran Pavic
* @author Jens Schauder
*/
@Configuration
Expand All @@ -54,8 +57,13 @@ public class SpringDataWebConfiguration extends WebMvcConfigurerAdapter {
private final ApplicationContext context;
private final ObjectFactory<ConversionService> conversionService;

public SpringDataWebConfiguration(ApplicationContext context,
@Qualifier("mvcConversionService") ObjectFactory<ConversionService> conversionService) {
@Autowired private Optional<PageableHandlerMethodArgumentResolverCustomizer> pageableResolverCustomizer;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we do without the field injection here?

@Autowired private Optional<SortHandlerMethodArgumentResolverCustomizer> sortResolverCustomizer;

public SpringDataWebConfiguration( //
ApplicationContext context, //
@Qualifier("mvcConversionService") ObjectFactory<ConversionService> conversionService //
) {

this.context = context;
this.conversionService = conversionService;
Expand All @@ -67,7 +75,11 @@ public SpringDataWebConfiguration(ApplicationContext context,
*/
@Bean
public PageableHandlerMethodArgumentResolver pageableResolver() {
return new PageableHandlerMethodArgumentResolver(sortResolver());

PageableHandlerMethodArgumentResolver pageableResolver = //
new PageableHandlerMethodArgumentResolver(sortResolver());
customizePageableResolver(pageableResolver);
return pageableResolver;
}

/*
Expand All @@ -76,7 +88,10 @@ public PageableHandlerMethodArgumentResolver pageableResolver() {
*/
@Bean
public SortHandlerMethodArgumentResolver sortResolver() {
return new SortHandlerMethodArgumentResolver();

SortHandlerMethodArgumentResolver sortResolver = new SortHandlerMethodArgumentResolver();
customizeSortResolver(sortResolver);
return sortResolver;
}

/*
Expand All @@ -95,8 +110,7 @@ public void addFormatters(FormatterRegistry registry) {

FormattingConversionService conversionService = (FormattingConversionService) registry;

DomainClassConverter<FormattingConversionService> converter = new DomainClassConverter<>(
conversionService);
DomainClassConverter<FormattingConversionService> converter = new DomainClassConverter<>(conversionService);
converter.setApplicationContext(context);
}

Expand Down Expand Up @@ -139,4 +153,13 @@ public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0, new XmlBeamHttpMessageConverter());
}
}

protected void customizePageableResolver(PageableHandlerMethodArgumentResolver pageableResolver) {
pageableResolverCustomizer.ifPresent(c -> c.customize(pageableResolver));
}

protected void customizeSortResolver(SortHandlerMethodArgumentResolver sortResolver) {
sortResolverCustomizer.ifPresent(c -> c.customize(sortResolver));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.springframework.data.web.SortHandlerMethodArgumentResolver;
import org.springframework.data.web.WebTestUtils;
import org.springframework.data.web.config.EnableSpringDataWebSupport.SpringDataWebConfigurationImportSelector;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.ReflectionUtils;
Expand All @@ -49,6 +50,8 @@
* Integration tests for {@link EnableSpringDataWebSupport}.
*
* @author Oliver Gierke
* @author Vedran Pavic
* @author Jens Schauder
*/
public class EnableSpringDataWebSupportIntegrationTests {

Expand All @@ -65,8 +68,31 @@ static class SampleConfig {
}
}

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
static class PageableResolverCustomizerConfig extends SampleConfig {

@Bean
public PageableHandlerMethodArgumentResolverCustomizer testPageableResolverCustomizer() {
return pageableResolver -> pageableResolver.setMaxPageSize(100);
}
}

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
static class SortResolverCustomizerConfig extends SampleConfig {

@Bean
public SortHandlerMethodArgumentResolverCustomizer testSortResolverCustomizer() {
return sortResolver -> sortResolver.setSortParameter("foo");
}
}

@After
public void tearDown() {

reEnable(HATEOAS);
reEnable(JACKSON);
}
Expand Down Expand Up @@ -165,6 +191,28 @@ public void picksUpWebConfigurationMixins() {
assertThat(names).contains("sampleBean");
}

@Test // DATACMNS-822
public void picksUpPageableResolverCustomizer() {

ApplicationContext context = WebTestUtils.createApplicationContext(PageableResolverCustomizerConfig.class);
List<String> names = Arrays.asList(context.getBeanDefinitionNames());
PageableHandlerMethodArgumentResolver resolver = context.getBean(PageableHandlerMethodArgumentResolver.class);

assertThat(names).contains("testPageableResolverCustomizer");
assertThat((Integer) ReflectionTestUtils.getField(resolver, "maxPageSize")).isEqualTo(100);
}

@Test // DATACMNS-822
public void picksUpSortResolverCustomizer() {

ApplicationContext context = WebTestUtils.createApplicationContext(SortResolverCustomizerConfig.class);
List<String> names = Arrays.asList(context.getBeanDefinitionNames());
SortHandlerMethodArgumentResolver resolver = context.getBean(SortHandlerMethodArgumentResolver.class);

assertThat(names).contains("testSortResolverCustomizer");
assertThat((String) ReflectionTestUtils.getField(resolver, "sortParameter")).isEqualTo("foo");
}

private static void assertResolversRegistered(ApplicationContext context, Class<?>... resolverTypes) {

RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class);
Expand Down