From ef5087c5ee9f65e44ba800f3e34a05f312b0a3e9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 1 Mar 2016 16:37:51 +0000 Subject: [PATCH] Upgrade to Jackson 2.7.2 Closes gh-5081 --- ...ConfigurationPropertiesReportEndpoint.java | 4 +- .../actuate/endpoint/jmx/EndpointMBean.java | 13 +++++- .../jmx/EndpointMBeanExporterTests.java | 46 ++++++++++++++++--- .../jackson/JacksonAutoConfiguration.java | 4 +- .../jackson/JacksonProperties.java | 21 +++++++-- .../JacksonAutoConfigurationTests.java | 18 ++++---- .../DataSourceJsonSerializationTests.java | 4 +- spring-boot-dependencies/pom.xml | 2 +- 8 files changed, 83 insertions(+), 29 deletions(-) diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java index ec32d41f3b5..5160d4a00dc 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java @@ -310,7 +310,7 @@ public class ConfigurationPropertiesReportEndpoint private boolean isReadable(BeanDescription beanDesc, BeanPropertyWriter writer) { String parentType = beanDesc.getType().getRawClass().getName(); - String type = writer.getPropertyType().getName(); + String type = writer.getType().getTypeName(); AnnotatedMethod setter = findSetter(beanDesc, writer); // If there's a setter, we assume it's OK to report on the value, // similarly, if there's no setter but the package names match, we assume @@ -324,7 +324,7 @@ public class ConfigurationPropertiesReportEndpoint private AnnotatedMethod findSetter(BeanDescription beanDesc, BeanPropertyWriter writer) { String name = "set" + StringUtils.capitalize(writer.getName()); - Class type = writer.getPropertyType(); + Class type = writer.getType().getRawClass(); AnnotatedMethod setter = beanDesc.findMethod(name, new Class[] { type }); // The enabled property of endpoints returns a boolean primitive but is set // using a Boolean class diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java index 2a6cec2f3a6..e9ffdfcd39a 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java @@ -19,6 +19,7 @@ package org.springframework.boot.actuate.endpoint.jmx; import java.util.List; import java.util.Map; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.boot.actuate.endpoint.Endpoint; @@ -40,6 +41,10 @@ public class EndpointMBean { private final ObjectMapper mapper; + private final JavaType listObject; + + private final JavaType mapStringObject; + /** * Create a new {@link EndpointMBean} instance. * @param beanName the bean name @@ -53,6 +58,10 @@ public class EndpointMBean { Assert.notNull(objectMapper, "ObjectMapper must not be null"); this.endpoint = endpoint; this.mapper = objectMapper; + this.listObject = objectMapper.getTypeFactory() + .constructParametricType(List.class, Object.class); + this.mapStringObject = objectMapper.getTypeFactory() + .constructParametricType(Map.class, String.class, Object.class); } @ManagedAttribute(description = "Returns the class of the underlying endpoint") @@ -77,9 +86,9 @@ public class EndpointMBean { return result; } if (result.getClass().isArray() || result instanceof List) { - return this.mapper.convertValue(result, List.class); + return this.mapper.convertValue(result, this.listObject); } - return this.mapper.convertValue(result, Map.class); + return this.mapper.convertValue(result, this.mapStringObject); } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporterTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporterTests.java index d6402904462..c50fff0165c 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporterTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporterTests.java @@ -17,10 +17,12 @@ package org.springframework.boot.actuate.endpoint.jmx; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Properties; @@ -199,12 +201,12 @@ public class EndpointMBeanExporterTests { } @Test - public void jsonConversionWithDefaultObjectMapper() throws Exception { + public void jsonMapConversionWithDefaultObjectMapper() throws Exception { this.context = new GenericApplicationContext(); this.context.registerBeanDefinition("endpointMbeanExporter", new RootBeanDefinition(EndpointMBeanExporter.class)); this.context.registerBeanDefinition("endpoint1", - new RootBeanDefinition(JsonConversionEndpoint.class)); + new RootBeanDefinition(JsonMapConversionEndpoint.class)); this.context.refresh(); MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class); Object response = mbeanExporter.getServer().invoke( @@ -215,7 +217,7 @@ public class EndpointMBeanExporterTests { } @Test - public void jsonConversionWithCustomObjectMapper() throws Exception { + public void jsonMapConversionWithCustomObjectMapper() throws Exception { this.context = new GenericApplicationContext(); ConstructorArgumentValues constructorArgs = new ConstructorArgumentValues(); ObjectMapper objectMapper = new ObjectMapper(); @@ -225,7 +227,7 @@ public class EndpointMBeanExporterTests { new RootBeanDefinition(EndpointMBeanExporter.class, constructorArgs, null)); this.context.registerBeanDefinition("endpoint1", - new RootBeanDefinition(JsonConversionEndpoint.class)); + new RootBeanDefinition(JsonMapConversionEndpoint.class)); this.context.refresh(); MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class); Object response = mbeanExporter.getServer().invoke( @@ -235,6 +237,22 @@ public class EndpointMBeanExporterTests { assertThat(((Map) response).get("date")).isInstanceOf(String.class); } + @Test + public void jsonListConversion() throws Exception { + this.context = new GenericApplicationContext(); + this.context.registerBeanDefinition("endpointMbeanExporter", + new RootBeanDefinition(EndpointMBeanExporter.class)); + this.context.registerBeanDefinition("endpoint1", + new RootBeanDefinition(JsonListConversionEndpoint.class)); + this.context.refresh(); + MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class); + Object response = mbeanExporter.getServer().invoke( + getObjectName("endpoint1", this.context), "getData", new Object[0], + new String[0]); + assertThat(response).isInstanceOf(List.class); + assertThat(((List) response).get(0)).isInstanceOf(Long.class); + } + private ObjectName getObjectName(String beanKey, GenericApplicationContext context) throws MalformedObjectNameException { return getObjectName("org.springframework.boot", beanKey, false, context); @@ -265,11 +283,11 @@ public class EndpointMBeanExporterTests { } - public static class JsonConversionEndpoint + public static class JsonMapConversionEndpoint extends AbstractEndpoint> { - public JsonConversionEndpoint() { - super("json-conversion"); + public JsonMapConversionEndpoint() { + super("json-map-conversion"); } @Override @@ -281,4 +299,18 @@ public class EndpointMBeanExporterTests { } + public static class JsonListConversionEndpoint + extends AbstractEndpoint> { + + public JsonListConversionEndpoint() { + super("json-list-conversion"); + } + + @Override + public List invoke() { + return Arrays.asList(new Date()); + } + + } + } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java index 8318c3afb9c..95c8fe5141e 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java @@ -166,9 +166,9 @@ public class JacksonAutoConfiguration { public Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder() { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); builder.applicationContext(this.applicationContext); - if (this.jacksonProperties.getSerializationInclusion() != null) { + if (this.jacksonProperties.getDefaultPropertyInclusion() != null) { builder.serializationInclusion( - this.jacksonProperties.getSerializationInclusion()); + this.jacksonProperties.getDefaultPropertyInclusion()); } if (this.jacksonProperties.getTimeZone() != null) { builder.timeZone(this.jacksonProperties.getTimeZone()); diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java index 42d86439b13..760834ccc69 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -29,6 +29,7 @@ import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.SerializationFeature; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; /** * Configuration properties to configure Jackson. @@ -89,7 +90,7 @@ public class JacksonProperties { * Controls the inclusion of properties during serialization. Configured with one of * the values in Jackson's JsonInclude.Include enumeration. */ - private JsonInclude.Include serializationInclusion; + private JsonInclude.Include defaultPropertyInclusion; /** * Time zone used when formatting dates. Configured using any recognized time zone @@ -146,12 +147,24 @@ public class JacksonProperties { return this.generator; } + @Deprecated + @DeprecatedConfigurationProperty(reason = "ObjectMapper.setSerializationInclusion was deprecated in Jackson 2.7", replacement = "spring.jackson.default-property-inclusion") public JsonInclude.Include getSerializationInclusion() { - return this.serializationInclusion; + return getDefaultPropertyInclusion(); } + @Deprecated public void setSerializationInclusion(JsonInclude.Include serializationInclusion) { - this.serializationInclusion = serializationInclusion; + setDefaultPropertyInclusion(serializationInclusion); + } + + public JsonInclude.Include getDefaultPropertyInclusion() { + return this.defaultPropertyInclusion; + } + + public void setDefaultPropertyInclusion( + JsonInclude.Include defaultPropertyInclusion) { + this.defaultPropertyInclusion = defaultPropertyInclusion; } public TimeZone getTimeZone() { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java index 9d6a71470ca..1a07a7388ea 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java @@ -35,7 +35,7 @@ import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy; +import com.fasterxml.jackson.databind.PropertyNamingStrategy.SnakeCaseStrategy; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.introspect.Annotated; @@ -167,22 +167,22 @@ public class JacksonAutoConfigurationTests { public void customPropertyNamingStrategyField() throws Exception { this.context.register(JacksonAutoConfiguration.class); EnvironmentTestUtils.addEnvironment(this.context, - "spring.jackson.property-naming-strategy:CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES"); + "spring.jackson.property-naming-strategy:SNAKE_CASE"); this.context.refresh(); ObjectMapper mapper = this.context.getBean(ObjectMapper.class); assertThat(mapper.getPropertyNamingStrategy()) - .isInstanceOf(LowerCaseWithUnderscoresStrategy.class); + .isInstanceOf(SnakeCaseStrategy.class); } @Test public void customPropertyNamingStrategyClass() throws Exception { this.context.register(JacksonAutoConfiguration.class); EnvironmentTestUtils.addEnvironment(this.context, - "spring.jackson.property-naming-strategy:com.fasterxml.jackson.databind.PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy"); + "spring.jackson.property-naming-strategy:com.fasterxml.jackson.databind.PropertyNamingStrategy.SnakeCaseStrategy"); this.context.refresh(); ObjectMapper mapper = this.context.getBean(ObjectMapper.class); assertThat(mapper.getPropertyNamingStrategy()) - .isInstanceOf(LowerCaseWithUnderscoresStrategy.class); + .isInstanceOf(SnakeCaseStrategy.class); } @Test @@ -355,8 +355,8 @@ public class JacksonAutoConfigurationTests { this.context.refresh(); ObjectMapper objectMapper = this.context .getBean(Jackson2ObjectMapperBuilder.class).build(); - assertThat(objectMapper.getSerializationConfig().getSerializationInclusion()) - .isEqualTo(JsonInclude.Include.ALWAYS); + assertThat(objectMapper.getSerializationConfig().getDefaultPropertyInclusion() + .getValueInclusion()).isEqualTo(JsonInclude.Include.USE_DEFAULTS); } @Test @@ -367,8 +367,8 @@ public class JacksonAutoConfigurationTests { this.context.refresh(); ObjectMapper objectMapper = this.context .getBean(Jackson2ObjectMapperBuilder.class).build(); - assertThat(objectMapper.getSerializationConfig().getSerializationInclusion()) - .isEqualTo(JsonInclude.Include.NON_NULL); + assertThat(objectMapper.getSerializationConfig().getDefaultPropertyInclusion() + .getValueInclusion()).isEqualTo(JsonInclude.Include.NON_NULL); } @Test diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceJsonSerializationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceJsonSerializationTests.java index ba41470ca74..3c382e2b8dd 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceJsonSerializationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceJsonSerializationTests.java @@ -112,9 +112,9 @@ public class DataSourceJsonSerializationTests { for (BeanPropertyWriter writer : beanProperties) { AnnotatedMethod setter = beanDesc.findMethod( "set" + StringUtils.capitalize(writer.getName()), - new Class[] { writer.getPropertyType() }); + new Class[] { writer.getType().getRawClass() }); if (setter != null && this.conversionService.canConvert(String.class, - writer.getPropertyType())) { + writer.getType().getRawClass())) { result.add(writer); } } diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index cca22d650d9..cf2dcecdf73 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -86,7 +86,7 @@ 4.5.1 4.4.4 8.1.2.Final - 2.6.5 + 2.7.2 2.7.8 3.18.1-GA 1.0.0