Fix JsonToObjectTransformer for ClassNotFoundEx (#3228)

* Fix JsonToObjectTransformer for ClassNotFoundEx

Related to https://github.com/spring-projects/spring-integration/issues/3223

The `JsonToObjectTransformer` consults `JsonHeaders` first and tries to build
a `ResolvableType` from other type headers which may be just a string identifications.
In this case a `ClassNotFoundException` could be thrown if a `ResolvableType` cannot
be build against non-class identificators

* Add `valueTypeExpression` option to the `JsonToObjectTransformer` to let to
build a `ResolvableType` using any possible custom logic, e.g. resolving
target classes from some registry using their ids from the mentioned headers

**Cherry-pick to 5.2.x**

* * Fix English language mistakes
This commit is contained in:
Artem Bilan
2020-03-27 15:42:24 -04:00
committed by GitHub
parent f1a0a066af
commit 9c46b3420e
7 changed files with 129 additions and 28 deletions

View File

@@ -8,7 +8,8 @@
https://www.springframework.org/schema/integration/spring-integration.xsd">
<json-to-object-transformer id="defaultJacksonMapperTransformer" input-channel="defaultObjectMapperInput"
type="org.springframework.integration.json.TestPerson"/>
type="org.springframework.integration.json.TestPerson"
value-type-expression="T (Class).forName('non.existing.type')"/>
<json-to-object-transformer id="customJsonMapperTransformer" input-channel="customJsonObjectMapperInput"
type="org.springframework.integration.json.TestPerson"

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -17,10 +17,15 @@
package org.springframework.integration.json;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.commons.logging.Log;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.ResolvableType;
@@ -32,8 +37,7 @@ import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
/**
* @author Mark Fisher
@@ -41,8 +45,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
*
* @since 2.0
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@SpringJUnitConfig
public class JsonToObjectTransformerParserTests {
@Autowired
@@ -63,12 +66,16 @@ public class JsonToObjectTransformerParserTests {
private JsonObjectMapper<?, ?> jsonObjectMapper;
@Test
public void defaultObjectMapper() {
public void testDefaultObjectMapper() {
Object jsonToObjectTransformer =
TestUtils.getPropertyValue(this.defaultJacksonMapperTransformer, "transformer");
assertThat(TestUtils.getPropertyValue(jsonToObjectTransformer, "jsonObjectMapper").getClass())
.isEqualTo(Jackson2JsonObjectMapper.class);
DirectFieldAccessor dfa = new DirectFieldAccessor(jsonToObjectTransformer);
Log logger = (Log) spy(dfa.getPropertyValue("logger"));
dfa.setPropertyValue("logger", logger);
String jsonString =
"{\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":42," +
"\"address\":{\"number\":123,\"street\":\"Main Street\"}}";
@@ -84,6 +91,12 @@ public class JsonToObjectTransformerParserTests {
assertThat(person.getLastName()).isEqualTo("Doe");
assertThat(person.getAge()).isEqualTo(42);
assertThat(person.getAddress().toString()).isEqualTo("123 Main Street");
ArgumentCaptor<String> stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
verify(logger).info(stringArgumentCaptor.capture(), any(Exception.class));
String logMessage = stringArgumentCaptor.getValue();
assertThat(logMessage).startsWith("Cannot build a ResolvableType from the request message");
}
@Test

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -20,10 +20,11 @@ import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.integration.expression.ValueExpression;
import org.springframework.integration.support.json.Jackson2JsonObjectMapper;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
@@ -72,8 +73,8 @@ public class JsonToObjectTransformerTests {
ObjectMapper customMapper = new ObjectMapper();
customMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, Boolean.TRUE);
customMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, Boolean.TRUE);
JsonToObjectTransformer transformer =
new JsonToObjectTransformer(TestPerson.class, new Jackson2JsonObjectMapper(customMapper));
JsonToObjectTransformer transformer = new JsonToObjectTransformer(new Jackson2JsonObjectMapper(customMapper));
transformer.setValueTypeExpression(new ValueExpression<>(ResolvableType.forClass(TestPerson.class)));
String jsonString = "{firstName:'John', lastName:'Doe', age:42, address:{number:123, street:'Main Street'}}";
Message<?> message = transformer.transform(new GenericMessage<>(jsonString));
TestPerson person = (TestPerson) message.getPayload();