Polish "Make Actuator dedicated ConversionService configurable"

See gh-16449
pull/17611/head
Stephane Nicoll 5 years ago
parent 2aea437536
commit 94c35ae1de

@ -16,21 +16,18 @@
package org.springframework.boot.actuate.autoconfigure.endpoint;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.EndpointConverter;
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.convert.ApplicationConversionService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
@ -53,59 +50,29 @@ public class EndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public ParameterValueMapper endpointOperationParameterMapper(
ApplicationContext applicationContext) {
return new ConversionServiceParameterValueMapper(
new Factory(applicationContext.getAutowireCapableBeanFactory()).create());
@EndpointConverter ObjectProvider<Converter<?, ?>> converters,
@EndpointConverter ObjectProvider<GenericConverter> genericConverters) {
ConversionService conversionService = createConversionService(
converters.orderedStream().collect(Collectors.toList()),
genericConverters.orderedStream().collect(Collectors.toList()));
return new ConversionServiceParameterValueMapper(conversionService);
}
@Bean
@ConditionalOnMissingBean
public CachingOperationInvokerAdvisor endpointCachingOperationInvokerAdvisor(Environment environment) {
return new CachingOperationInvokerAdvisor(new EndpointIdTimeToLivePropertyFunction(environment));
}
private static class Factory {
@SuppressWarnings("rawtypes")
private final List<Converter> converters;
private final List<GenericConverter> genericConverters;
Factory(BeanFactory beanFactory) {
this.converters = beans(beanFactory, Converter.class,
EndpointConverter.VALUE);
this.genericConverters = beans(beanFactory, GenericConverter.class,
EndpointConverter.VALUE);
}
private <T> List<T> beans(BeanFactory beanFactory, Class<T> type,
String qualifier) {
if (beanFactory instanceof ListableBeanFactory) {
return beans(type, qualifier, (ListableBeanFactory) beanFactory);
}
return Collections.emptyList();
}
private <T> List<T> beans(Class<T> type, String qualifier,
ListableBeanFactory beanFactory) {
return new ArrayList<>(BeanFactoryAnnotationUtils
.qualifiedBeansOfType(beanFactory, type, qualifier).values());
}
public ConversionService create() {
if (this.converters.isEmpty() && this.genericConverters.isEmpty()) {
private ConversionService createConversionService(List<Converter<?, ?>> converters,
List<GenericConverter> genericConverters) {
if (genericConverters.isEmpty() && converters.isEmpty()) {
return ApplicationConversionService.getSharedInstance();
}
ApplicationConversionService conversionService = new ApplicationConversionService();
for (Converter<?, ?> converter : this.converters) {
conversionService.addConverter(converter);
}
for (GenericConverter genericConverter : this.genericConverters) {
conversionService.addConverter(genericConverter);
}
converters.forEach(conversionService::addConverter);
genericConverters.forEach(conversionService::addConverter);
return conversionService;
}
@Bean
@ConditionalOnMissingBean
public CachingOperationInvokerAdvisor endpointCachingOperationInvokerAdvisor(Environment environment) {
return new CachingOperationInvokerAdvisor(new EndpointIdTimeToLivePropertyFunction(environment));
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2018 the original author or authors.
* Copyright 2012-2019 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.
@ -19,13 +19,14 @@ package org.springframework.boot.actuate.autoconfigure.endpoint;
import java.util.Collections;
import java.util.Set;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.endpoint.annotation.EndpointConverter;
import org.springframework.boot.actuate.endpoint.invoke.OperationParameter;
import org.springframework.boot.actuate.endpoint.invoke.ParameterMappingException;
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConverterNotFoundException;
@ -42,22 +43,17 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
*
* @author Chao Chang
*/
public class EndpointAutoConfigurationTests {
class EndpointAutoConfigurationTests {
private static final AutoConfigurations CONFIGURATIONS = AutoConfigurations
.of(EndpointAutoConfiguration.class);
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(CONFIGURATIONS);
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(EndpointAutoConfiguration.class));
@Test
public void mapShouldUseConfigurationConverter() {
this.contextRunner.withUserConfiguration(ConverterConfiguration.class)
.run((context) -> {
ParameterValueMapper parameterValueMapper = context
.getBean(ParameterValueMapper.class);
Object paramValue = parameterValueMapper.mapParameterValue(
new TestOperationParameter(Person.class), "John Smith");
void mapShouldUseConfigurationConverter() {
this.contextRunner.withUserConfiguration(ConverterConfiguration.class).run((context) -> {
ParameterValueMapper parameterValueMapper = context.getBean(ParameterValueMapper.class);
Object paramValue = parameterValueMapper.mapParameterValue(new TestOperationParameter(Person.class),
"John Smith");
assertThat(paramValue).isInstanceOf(Person.class);
Person person = (Person) paramValue;
assertThat(person.firstName).isEqualTo("John");
@ -66,29 +62,22 @@ public class EndpointAutoConfigurationTests {
}
@Test
public void mapWhenConfigurationConverterIsNotQualifiedShouldNotConvert() {
void mapWhenConfigurationConverterIsNotQualifiedShouldNotConvert() {
assertThatExceptionOfType(ParameterMappingException.class).isThrownBy(() -> {
this.contextRunner
.withUserConfiguration(NonQualifiedConverterConfiguration.class)
.run((context) -> {
ParameterValueMapper parameterValueMapper = context
.getBean(ParameterValueMapper.class);
parameterValueMapper.mapParameterValue(
new TestOperationParameter(Person.class), "John Smith");
this.contextRunner.withUserConfiguration(NonQualifiedConverterConfiguration.class).run((context) -> {
ParameterValueMapper parameterValueMapper = context.getBean(ParameterValueMapper.class);
parameterValueMapper.mapParameterValue(new TestOperationParameter(Person.class), "John Smith");
});
}).withCauseInstanceOf(ConverterNotFoundException.class);
}
@Test
public void mapShouldUseGenericConfigurationConverter() {
this.contextRunner.withUserConfiguration(GenericConverterConfiguration.class)
.run((context) -> {
ParameterValueMapper parameterValueMapper = context
.getBean(ParameterValueMapper.class);
Object paramValue = parameterValueMapper.mapParameterValue(
new TestOperationParameter(Person.class), "John Smith");
void mapShouldUseGenericConfigurationConverter() {
this.contextRunner.withUserConfiguration(GenericConverterConfiguration.class).run((context) -> {
ParameterValueMapper parameterValueMapper = context.getBean(ParameterValueMapper.class);
Object paramValue = parameterValueMapper.mapParameterValue(new TestOperationParameter(Person.class),
"John Smith");
assertThat(paramValue).isInstanceOf(Person.class);
Person person = (Person) paramValue;
assertThat(person.firstName).isEqualTo("John");
@ -97,16 +86,11 @@ public class EndpointAutoConfigurationTests {
}
@Test
public void mapWhenGenericConfigurationConverterIsNotQualifiedShouldNotConvert() {
void mapWhenGenericConfigurationConverterIsNotQualifiedShouldNotConvert() {
assertThatExceptionOfType(ParameterMappingException.class).isThrownBy(() -> {
this.contextRunner
.withUserConfiguration(
NonQualifiedGenericConverterConfiguration.class)
.run((context) -> {
ParameterValueMapper parameterValueMapper = context
.getBean(ParameterValueMapper.class);
parameterValueMapper.mapParameterValue(
new TestOperationParameter(Person.class), "John Smith");
this.contextRunner.withUserConfiguration(NonQualifiedGenericConverterConfiguration.class).run((context) -> {
ParameterValueMapper parameterValueMapper = context.getBean(ParameterValueMapper.class);
parameterValueMapper.mapParameterValue(new TestOperationParameter(Person.class), "John Smith");
});
}).withCauseInstanceOf(ConverterNotFoundException.class);
@ -131,8 +115,7 @@ public class EndpointAutoConfigurationTests {
}
@Override
public Object convert(Object source, TypeDescriptor sourceType,
TypeDescriptor targetType) {
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
String[] content = StringUtils.split((String) source, " ");
return new Person(content[0], content[1]);
}
@ -144,7 +127,7 @@ public class EndpointAutoConfigurationTests {
@Bean
@EndpointConverter
public Converter<String, Person> personConverter() {
Converter<String, Person> personConverter() {
return new PersonConverter();
}
@ -154,7 +137,7 @@ public class EndpointAutoConfigurationTests {
static class NonQualifiedConverterConfiguration {
@Bean
public Converter<String, Person> personConverter() {
Converter<String, Person> personConverter() {
return new PersonConverter();
}
@ -165,7 +148,7 @@ public class EndpointAutoConfigurationTests {
@Bean
@EndpointConverter
public GenericConverter genericPersonConverter() {
GenericConverter genericPersonConverter() {
return new GenericPersonConverter();
}
@ -175,7 +158,7 @@ public class EndpointAutoConfigurationTests {
static class NonQualifiedGenericConverterConfiguration {
@Bean
public GenericConverter genericPersonConverter() {
GenericConverter genericPersonConverter() {
return new GenericPersonConverter();
}
@ -192,14 +175,6 @@ public class EndpointAutoConfigurationTests {
this.lastName = lastName;
}
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
}
private static class TestOperationParameter implements OperationParameter {

@ -1,5 +1,5 @@
/*
* Copyright 2012-2018 the original author or authors.
* Copyright 2012-2019 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.
@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.endpoint;
package org.springframework.boot.actuate.endpoint.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
@ -23,22 +23,17 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
/**
* Qualifier for beans that are needed to be converters for {@link Endpoint}.
* Qualifier for beans that are needed to convert {@link Endpoint} input parameters.
*
* @author Chao Chang
* @since 2.2.0
*/
@Qualifier(EndpointConverter.VALUE)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier
public @interface EndpointConverter {
/**
* Concrete value for the {@link Qualifier @Qualifier}.
*/
String VALUE = "org.springframework.boot.actuate.autoconfigure.endpoint.EndpointConverter";
}

@ -558,8 +558,8 @@ automatically if you are using Spring Boot's Gradle plugin or if you are using M
The parameters passed to endpoint operation methods are, if necessary, automatically
converted to the required type. Before calling an operation method, the input received via
JMX or an HTTP request is converted to the required types using an instance of
`ApplicationConversionService`.
`ApplicationConversionService` as well as any `Converter` or `GenericConverter` beans
qualified with `@EndpointConverter`.
[[production-ready-endpoints-custom-web]]

Loading…
Cancel
Save