Merge branch '1.5.x'
This commit is contained in:
commit
cf485ce144
|
|
@ -24,10 +24,12 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.BeanDescription;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationConfig;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.introspect.Annotated;
|
||||
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
|
||||
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
|
||||
|
|
@ -38,6 +40,8 @@ import com.fasterxml.jackson.databind.ser.PropertyWriter;
|
|||
import com.fasterxml.jackson.databind.ser.SerializerFactory;
|
||||
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
|
||||
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.boot.actuate.endpoint.Sanitizer;
|
||||
|
|
@ -68,7 +72,7 @@ import org.springframework.util.StringUtils;
|
|||
@Endpoint(id = "configprops")
|
||||
public class ConfigurationPropertiesReportEndpoint implements ApplicationContextAware {
|
||||
|
||||
private static final String CGLIB_FILTER_ID = "cglibFilter";
|
||||
private static final String CONFIGURATION_PROPERTIES_FILTER_ID = "configurationPropertiesFilter";
|
||||
|
||||
private final Sanitizer sanitizer = new Sanitizer();
|
||||
|
||||
|
|
@ -167,7 +171,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
|
|||
protected void configureObjectMapper(ObjectMapper mapper) {
|
||||
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
mapper.setSerializationInclusion(Include.NON_NULL);
|
||||
applyCglibFilters(mapper);
|
||||
applyConfigurationPropertiesFilter(mapper);
|
||||
applySerializationModifier(mapper);
|
||||
}
|
||||
|
||||
|
|
@ -181,15 +185,11 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
|
|||
mapper.setSerializerFactory(factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure PropertyFilter to make sure Jackson doesn't process CGLIB generated bean
|
||||
* properties.
|
||||
* @param mapper the object mapper
|
||||
*/
|
||||
private void applyCglibFilters(ObjectMapper mapper) {
|
||||
mapper.setAnnotationIntrospector(new CglibAnnotationIntrospector());
|
||||
mapper.setFilterProvider(new SimpleFilterProvider().addFilter(CGLIB_FILTER_ID,
|
||||
new CglibBeanPropertyFilter()));
|
||||
private void applyConfigurationPropertiesFilter(ObjectMapper mapper) {
|
||||
mapper.setAnnotationIntrospector(
|
||||
new ConfigurationPropertiesAnnotationIntrospector());
|
||||
mapper.setFilterProvider(new SimpleFilterProvider()
|
||||
.setDefaultFilter(new ConfigurationPropertiesPropertyFilter()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -268,14 +268,14 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
|
|||
* properties.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class CglibAnnotationIntrospector
|
||||
private static class ConfigurationPropertiesAnnotationIntrospector
|
||||
extends JacksonAnnotationIntrospector {
|
||||
|
||||
@Override
|
||||
public Object findFilterId(Annotated a) {
|
||||
Object id = super.findFilterId(a);
|
||||
if (id == null) {
|
||||
id = CGLIB_FILTER_ID;
|
||||
id = CONFIGURATION_PROPERTIES_FILTER_ID;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
|
@ -283,10 +283,20 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
|
|||
}
|
||||
|
||||
/**
|
||||
* {@link SimpleBeanPropertyFilter} to filter out all bean properties whose names
|
||||
* start with '$$'.
|
||||
* {@link SimpleBeanPropertyFilter} for serialization of
|
||||
* {@link ConfigurationProperties} beans. The filter hides:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Properties that have a name starting with '$$'.
|
||||
* <li>Properties that are self-referential.
|
||||
* <li>Properties that throw an exception when retrieving their value.
|
||||
* </ul>
|
||||
*/
|
||||
private static class CglibBeanPropertyFilter extends SimpleBeanPropertyFilter {
|
||||
private static class ConfigurationPropertiesPropertyFilter
|
||||
extends SimpleBeanPropertyFilter {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(ConfigurationPropertiesPropertyFilter.class);
|
||||
|
||||
@Override
|
||||
protected boolean include(BeanPropertyWriter writer) {
|
||||
|
|
@ -302,6 +312,31 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
|
|||
return !name.startsWith("$$");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeAsField(Object pojo, JsonGenerator jgen,
|
||||
SerializerProvider provider, PropertyWriter writer) throws Exception {
|
||||
if (writer instanceof BeanPropertyWriter) {
|
||||
try {
|
||||
if (pojo == ((BeanPropertyWriter) writer).get(pojo)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Skipping '" + writer.getFullName() + "' on '"
|
||||
+ pojo.getClass().getName()
|
||||
+ "' as it is self-referential");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Skipping '" + writer.getFullName() + "' on '"
|
||||
+ pojo.getClass().getName() + "' as an exception "
|
||||
+ "was thrown when retrieving its value", ex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.serializeAsField(pojo, jgen, provider, writer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ConfigurationPropertiesBeanDescriptor;
|
||||
|
|
@ -87,9 +89,10 @@ public class ConfigurationPropertiesReportEndpointSerializationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testCycle() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testSelfReferentialProperty() throws Exception {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withUserConfiguration(CycleConfig.class)
|
||||
.withUserConfiguration(SelfReferentialConfig.class)
|
||||
.withPropertyValues("foo.name:foo");
|
||||
contextRunner.run((context) -> {
|
||||
ConfigurationPropertiesReportEndpoint endpoint = context
|
||||
|
|
@ -97,12 +100,34 @@ public class ConfigurationPropertiesReportEndpointSerializationTests {
|
|||
ConfigurationPropertiesDescriptor properties = endpoint
|
||||
.configurationProperties();
|
||||
ConfigurationPropertiesBeanDescriptor foo = properties.getBeans().get("foo");
|
||||
assertThat(foo).isNotNull();
|
||||
assertThat(foo.getPrefix()).isEqualTo("foo");
|
||||
Map<String, Object> map = foo.getProperties();
|
||||
assertThat(map).isNotNull();
|
||||
assertThat(map).hasSize(1);
|
||||
assertThat(map.get("error")).isEqualTo("Cannot serialize 'foo'");
|
||||
assertThat(map).containsOnlyKeys("bar", "name");
|
||||
assertThat(map).containsEntry("name", "foo");
|
||||
Map<String, Object> bar = (Map<String, Object>) map.get("bar");
|
||||
assertThat(bar).containsOnlyKeys("name");
|
||||
assertThat(bar).containsEntry("name", "123456");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("gh-11037")
|
||||
public void testCycle() {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withUserConfiguration(CycleConfig.class);
|
||||
contextRunner.run((context) -> {
|
||||
ConfigurationPropertiesReportEndpoint endpoint = context
|
||||
.getBean(ConfigurationPropertiesReportEndpoint.class);
|
||||
ConfigurationPropertiesDescriptor properties = endpoint
|
||||
.configurationProperties();
|
||||
ConfigurationPropertiesBeanDescriptor cycle = properties.getBeans()
|
||||
.get("cycle");
|
||||
assertThat(cycle.getPrefix()).isEqualTo("cycle");
|
||||
Map<String, Object> map = cycle.getProperties();
|
||||
assertThat(map).isNotNull();
|
||||
assertThat(map).containsOnlyKeys("error");
|
||||
assertThat(map).containsEntry("error", "Cannot serialize 'cycle'");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +216,6 @@ public class ConfigurationPropertiesReportEndpointSerializationTests {
|
|||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
public void testInitializedMapAndList() throws Exception {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withUserConfiguration(InitializedMapAndListPropertiesConfig.class)
|
||||
|
|
@ -213,6 +237,22 @@ public class ConfigurationPropertiesReportEndpointSerializationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hikariDataSourceConfigurationPropertiesBeanCanBeSerialized() {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withUserConfiguration(HikariDataSourceConfig.class);
|
||||
contextRunner.run((context) -> {
|
||||
ConfigurationPropertiesReportEndpoint endpoint = context
|
||||
.getBean(ConfigurationPropertiesReportEndpoint.class);
|
||||
ConfigurationPropertiesDescriptor properties = endpoint
|
||||
.configurationProperties();
|
||||
ConfigurationPropertiesBeanDescriptor hikariDataSource = properties.getBeans()
|
||||
.get("hikariDataSource");
|
||||
Map<String, Object> nestedProperties = hikariDataSource.getProperties();
|
||||
assertThat(nestedProperties).doesNotContainKey("error");
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
public static class Base {
|
||||
|
|
@ -238,12 +278,12 @@ public class ConfigurationPropertiesReportEndpointSerializationTests {
|
|||
|
||||
@Configuration
|
||||
@Import(Base.class)
|
||||
public static class CycleConfig {
|
||||
public static class SelfReferentialConfig {
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "foo")
|
||||
public Cycle foo() {
|
||||
return new Cycle();
|
||||
public SelfReferential foo() {
|
||||
return new SelfReferential();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -254,8 +294,8 @@ public class ConfigurationPropertiesReportEndpointSerializationTests {
|
|||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "bar")
|
||||
public Cycle foo() {
|
||||
return new Cycle();
|
||||
public SelfReferential foo() {
|
||||
return new SelfReferential();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -363,11 +403,11 @@ public class ConfigurationPropertiesReportEndpointSerializationTests {
|
|||
|
||||
}
|
||||
|
||||
public static class Cycle extends Foo {
|
||||
public static class SelfReferential extends Foo {
|
||||
|
||||
private Foo self;
|
||||
|
||||
public Cycle() {
|
||||
public SelfReferential() {
|
||||
this.self = this;
|
||||
}
|
||||
|
||||
|
|
@ -439,4 +479,58 @@ public class ConfigurationPropertiesReportEndpointSerializationTests {
|
|||
|
||||
}
|
||||
|
||||
static class Cycle {
|
||||
|
||||
private final Alpha alpha = new Alpha(this);
|
||||
|
||||
public Alpha getAlpha() {
|
||||
return this.alpha;
|
||||
}
|
||||
|
||||
static class Alpha {
|
||||
|
||||
private final Cycle cycle;
|
||||
|
||||
Alpha(Cycle cycle) {
|
||||
this.cycle = cycle;
|
||||
}
|
||||
|
||||
public Cycle getCycle() {
|
||||
return this.cycle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(Base.class)
|
||||
static class CycleConfig {
|
||||
|
||||
@Bean
|
||||
// gh-11037
|
||||
// @ConfigurationProperties(prefix = "cycle")
|
||||
public Cycle cycle() {
|
||||
return new Cycle();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
static class HikariDataSourceConfig {
|
||||
|
||||
@Bean
|
||||
public ConfigurationPropertiesReportEndpoint endpoint() {
|
||||
return new ConfigurationPropertiesReportEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "test.datasource")
|
||||
public HikariDataSource hikariDataSource() {
|
||||
return new HikariDataSource();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue