From a3160f80f6ff2dbbe76cfa38b14f16efbacd3dfa Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Mon, 7 Aug 2017 17:26:14 -0400 Subject: [PATCH] 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` --- .../json/AbstractJacksonJsonObjectMapper.java | 7 +- .../json/Jackson2JsonObjectMapper.java | 72 ++++++++++++++++++- .../support/json/JacksonJsonUtils.java | 17 ++--- 3 files changed, 82 insertions(+), 14 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/json/AbstractJacksonJsonObjectMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/support/json/AbstractJacksonJsonObjectMapper.java index aa5b1a8888..81c19a31d6 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/json/AbstractJacksonJsonObjectMapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/json/AbstractJacksonJsonObjectMapper.java @@ -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 - The expected type of Java Type representation. * * @author Artem Bilan + * * @since 3.0 */ public abstract class AbstractJacksonJsonObjectMapper extends JsonObjectMapperAdapter @@ -51,6 +52,10 @@ public abstract class AbstractJacksonJsonObjectMapper extends JsonObjec this.classLoader = classLoader; } + protected ClassLoader getClassLoader() { + return this.classLoader; + } + @Override public T fromJson(Object json, Class valueType) throws Exception { return this.fromJson(json, this.constructType(valueType)); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/json/Jackson2JsonObjectMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/support/json/Jackson2JsonObjectMapper.java index 6aa6038d17..be5e186bca 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/json/Jackson2JsonObjectMapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/json/Jackson2JsonObjectMapper.java @@ -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 toJson and fromJson * to the {@linkplain com.fasterxml.jackson.databind.ObjectMapper} *

@@ -44,13 +48,17 @@ import com.fasterxml.jackson.databind.ObjectMapper; *

    *
  • {@link MapperFeature#DEFAULT_VIEW_INCLUSION} is disabled
  • *
  • {@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} is disabled
  • - *
  • {@link ObjectMapper#findAndRegisterModules()} is performed
  • + *
  • The well-known modules are registered through the classpath scan
  • *
* + * 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 { @@ -60,7 +68,7 @@ public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper jdk7Module = (Class) + 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 jdk8Module = (Class) + 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 javaTimeModule = (Class) + 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 jodaModule = (Class) + 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 kotlinModule = (Class) + ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", getClassLoader()); + this.objectMapper.registerModule(BeanUtils.instantiateClass(kotlinModule)); + } + catch (ClassNotFoundException ex) { + //jackson-module-kotlin not available + } + } + } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonUtils.java index 58930635e2..c3398b3912 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonUtils.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonUtils.java @@ -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 {