Polish "Make Actuator dedicated ConversionService configurable"
See gh-16449
This commit is contained in:
@@ -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,9 +50,23 @@ 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);
|
||||
}
|
||||
|
||||
private ConversionService createConversionService(List<Converter<?, ?>> converters,
|
||||
List<GenericConverter> genericConverters) {
|
||||
if (genericConverters.isEmpty() && converters.isEmpty()) {
|
||||
return ApplicationConversionService.getSharedInstance();
|
||||
}
|
||||
ApplicationConversionService conversionService = new ApplicationConversionService();
|
||||
converters.forEach(conversionService::addConverter);
|
||||
genericConverters.forEach(conversionService::addConverter);
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -64,48 +75,4 @@ public class EndpointAutoConfiguration {
|
||||
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()) {
|
||||
return ApplicationConversionService.getSharedInstance();
|
||||
}
|
||||
ApplicationConversionService conversionService = new ApplicationConversionService();
|
||||
for (Converter<?, ?> converter : this.converters) {
|
||||
conversionService.addConverter(converter);
|
||||
}
|
||||
for (GenericConverter genericConverter : this.genericConverters) {
|
||||
conversionService.addConverter(genericConverter);
|
||||
}
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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,72 +43,55 @@ 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");
|
||||
assertThat(paramValue).isInstanceOf(Person.class);
|
||||
Person person = (Person) paramValue;
|
||||
assertThat(person.firstName).isEqualTo("John");
|
||||
assertThat(person.lastName).isEqualTo("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");
|
||||
assertThat(person.lastName).isEqualTo("Smith");
|
||||
});
|
||||
}
|
||||
|
||||
@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");
|
||||
assertThat(paramValue).isInstanceOf(Person.class);
|
||||
Person person = (Person) paramValue;
|
||||
assertThat(person.firstName).isEqualTo("John");
|
||||
assertThat(person.lastName).isEqualTo("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");
|
||||
assertThat(person.lastName).isEqualTo("Smith");
|
||||
});
|
||||
}
|
||||
|
||||
@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]]
|
||||
|
||||
Reference in New Issue
Block a user