INT-4318: Register Jackson modules if available

JIRA: https://jira.spring.io/browse/INT-4318

Do not `ObjectMApper.findAndRegisterModules()` unconditionally.
If there is no the target module library in classpath, we end up
with the `NoClassDefFoundError`

* Copy-paste the well-known modules registration logic from the
`org.springframework.http.converter.json.Jackson2ObjectMapperBuilder`
since we can't add `spring-web`  dependency to the `SI-core`
This commit is contained in:
Artem Bilan
2017-08-07 17:26:14 -04:00
committed by Gary Russell
parent 32fdd2119f
commit a3160f80f6
3 changed files with 82 additions and 14 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 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.
@@ -36,6 +36,7 @@ import org.springframework.util.ClassUtils;
* @param <J> - The expected type of Java Type representation.
*
* @author Artem Bilan
*
* @since 3.0
*/
public abstract class AbstractJacksonJsonObjectMapper<N, P, J> extends JsonObjectMapperAdapter<N, P>
@@ -51,6 +52,10 @@ public abstract class AbstractJacksonJsonObjectMapper<N, P, J> extends JsonObjec
this.classLoader = classLoader;
}
protected ClassLoader getClassLoader() {
return this.classLoader;
}
@Override
public <T> T fromJson(Object json, Class<T> valueType) throws Exception {
return this.fromJson(json, this.constructType(valueType));

View File

@@ -25,18 +25,22 @@ import java.net.URL;
import java.util.Collection;
import java.util.Map;
import org.springframework.beans.BeanUtils;
import org.springframework.integration.mapping.support.JsonHeaders;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Jackson 2 JSON-processor (@link https://github.com/FasterXML) {@linkplain JsonObjectMapper} implementation.
* Jackson 2 JSON-processor (@link https://github.com/FasterXML)
* {@linkplain JsonObjectMapper} implementation.
* Delegates <code>toJson</code> and <code>fromJson</code>
* to the {@linkplain com.fasterxml.jackson.databind.ObjectMapper}
* <p>
@@ -44,13 +48,17 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* <ul>
* <li>{@link MapperFeature#DEFAULT_VIEW_INCLUSION} is disabled</li>
* <li>{@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} is disabled</li>
* <li>{@link ObjectMapper#findAndRegisterModules()} is performed</li>
* <li>The well-known modules are registered through the classpath scan</li>
* </ul>
*
* See {@code org.springframework.http.converter.json.Jackson2ObjectMapperBuilder}
* in the spring-web for more information.
*
* @author Artem Bilan
* @author Vikas Prasad
*
* @since 3.0
*
*/
public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper<JsonNode, JsonParser, JavaType> {
@@ -60,7 +68,7 @@ public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper<Js
this.objectMapper = new ObjectMapper();
this.objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
this.objectMapper.findAndRegisterModules();
registerWellKnownModulesIfAvailable();
}
public Jackson2JsonObjectMapper(ObjectMapper objectMapper) {
@@ -68,6 +76,10 @@ public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper<Js
this.objectMapper = objectMapper;
}
public ObjectMapper getObjectMapper() {
return this.objectMapper;
}
@Override
public String toJson(Object value) throws Exception {
return this.objectMapper.writeValueAsString(value);
@@ -138,4 +150,58 @@ public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper<Js
return this.objectMapper.constructType(type);
}
@SuppressWarnings("unchecked")
private void registerWellKnownModulesIfAvailable() {
try {
Class<? extends Module> jdk7Module = (Class<? extends Module>)
ClassUtils.forName("com.fasterxml.jackson.datatype.jdk7.Jdk7Module", getClassLoader());
this.objectMapper.registerModule(BeanUtils.instantiateClass(jdk7Module));
}
catch (ClassNotFoundException ex) {
// jackson-datatype-jdk7 not available
}
try {
Class<? extends Module> jdk8Module = (Class<? extends Module>)
ClassUtils.forName("com.fasterxml.jackson.datatype.jdk8.Jdk8Module", getClassLoader());
this.objectMapper.registerModule(BeanUtils.instantiateClass(jdk8Module));
}
catch (ClassNotFoundException ex) {
// jackson-datatype-jdk8 not available
}
try {
Class<? extends Module> javaTimeModule = (Class<? extends Module>)
ClassUtils.forName("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule", getClassLoader());
this.objectMapper.registerModule(BeanUtils.instantiateClass(javaTimeModule));
}
catch (ClassNotFoundException ex) {
// jackson-datatype-jsr310 not available
}
// Joda-Time present?
if (ClassUtils.isPresent("org.joda.time.LocalDate", getClassLoader())) {
try {
Class<? extends Module> jodaModule = (Class<? extends Module>)
ClassUtils.forName("com.fasterxml.jackson.datatype.joda.JodaModule", getClassLoader());
this.objectMapper.registerModule(BeanUtils.instantiateClass(jodaModule));
}
catch (ClassNotFoundException ex) {
// jackson-datatype-joda not available
}
}
// Kotlin present?
if (ClassUtils.isPresent("kotlin.Unit", getClassLoader())) {
try {
Class<? extends Module> kotlinModule = (Class<? extends Module>)
ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", getClassLoader());
this.objectMapper.registerModule(BeanUtils.instantiateClass(kotlinModule));
}
catch (ClassNotFoundException ex) {
//jackson-module-kotlin not available
}
}
}
}

View File

@@ -31,9 +31,7 @@ import org.springframework.messaging.support.GenericMessage;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.jsontype.NamedType;
@@ -56,7 +54,7 @@ public final class JacksonJsonUtils {
}
/**
* Return an {@link com.fasterxml.jackson.databind.ObjectMapper} if available,
* Return an {@link ObjectMapper} if available,
* supplied with Message specific serializers and deserializers.
* Also configured to store typo info in the {@code @class} property.
* @param trustedPackages the trusted Java packages for deserialization.
@@ -64,11 +62,9 @@ public final class JacksonJsonUtils {
* @throws IllegalStateException if an implementation is not available.
* @since 4.3.10
*/
public static com.fasterxml.jackson.databind.ObjectMapper messagingAwareMapper(String... trustedPackages) {
public static ObjectMapper messagingAwareMapper(String... trustedPackages) {
if (JacksonPresent.isJackson2Present()) {
ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
ObjectMapper mapper = new Jackson2JsonObjectMapper().getObjectMapper();
mapper.setDefaultTyping(new WhitelistTypeResolverBuilder(trustedPackages));
@@ -84,13 +80,14 @@ public final class JacksonJsonUtils {
MutableMessageJacksonDeserializer mutableMessageDeserializer = new MutableMessageJacksonDeserializer();
mutableMessageDeserializer.setMapper(mapper);
mapper.registerModule(new SimpleModule()
SimpleModule simpleModule = new SimpleModule()
.addSerializer(new MessageHeadersJacksonSerializer())
.addDeserializer(GenericMessage.class, genericMessageDeserializer)
.addDeserializer(ErrorMessage.class, errorMessageDeserializer)
.addDeserializer(AdviceMessage.class, adviceMessageDeserializer)
.addDeserializer(MutableMessage.class, mutableMessageDeserializer)
);
.addDeserializer(MutableMessage.class, mutableMessageDeserializer);
mapper.registerModule(simpleModule);
return mapper;
}
else {