MMIH: Convert from JSON only if SpELInvoker

If there is `json`-aware `contentType` header in the message to process
and payload type is `String` or `byte[]` and it isn't equal to the
expected exclusive method argument type (even generic from the `Message<>`),
the conversion should happen only if SpelInvoker is no method.

The regular `InvocableHandlerMethod` takes care about payload conversion
via configured `MessageConverter` in the `PayloadArgumentResolver`

* Fix `MessagingMethodInvokerHelper` to call JSON conversion only when
`this.handlerMethod.spelOnly`

NOTE: We can't do generics conversion right now because it is going
to be a breaking change around
`org.springframework.integration.support.json.JsonObjectMapper` property
in the `MessagingMethodInvokerHelper`.
We can do that only in `5.1` and rely there only on the functionality
from the Jackson Object mapper

* Convert from JSON only in the `invokeExpression()`
* Optimize message recreation only if target param type is `Message`.
Otherwise override just `payload` property of the `ParametersWrapper`
This commit is contained in:
Artem Bilan
2018-02-23 16:33:18 -05:00
committed by Gary Russell
parent 60606dd6fe
commit 2c3ee7d68c
2 changed files with 63 additions and 43 deletions

View File

@@ -308,46 +308,10 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
}
public T process(Message<?> message) throws Exception {
Message<?> messageToProcess = possiblyConvert(message);
ParametersWrapper parameters = new ParametersWrapper(messageToProcess);
ParametersWrapper parameters = new ParametersWrapper(message);
return processInternal(parameters);
}
/*
* If there's a single method, the content is JSON, the payload is a
* String or byte[], the parameter doesn't match the payload,
* and there is a Json Object Mapper on the CP,
* convert.
*/
private Message<?> possiblyConvert(Message<?> message) throws Exception {
if (this.handlerMethod != null && this.handlerMethod.getTargetParameterType() != null &&
this.jsonObjectMapper != null) {
Class<?> type = this.handlerMethod.getTargetParameterType();
if ((message.getPayload() instanceof String && !type.equals(String.class)
|| message.getPayload() instanceof byte[] && !type.equals(byte[].class))
&& contentTypeIsJson(message)) {
try {
return getMessageBuilderFactory()
.withPayload(this.jsonObjectMapper.fromJson(message.getPayload(), type))
.copyHeaders(message.getHeaders())
.build();
}
catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert from JSON", e);
}
}
}
}
return message;
}
private boolean contentTypeIsJson(Message<?> message) {
Object contentType = message.getHeaders().get(MessageHeaders.CONTENT_TYPE);
return contentType != null && contentType.toString().contains("json");
}
public T process(Collection<Message<?>> messages, Map<String, Object> headers) throws Exception {
ParametersWrapper parameters = new ParametersWrapper(messages, headers);
return processInternal(parameters);
@@ -645,6 +609,8 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
@SuppressWarnings("unchecked")
private T invokeExpression(Expression expression, ParametersWrapper parameters) throws Exception {
try {
convertJsonPayloadIfNecessary(parameters);
return (T) evaluateExpression(expression, parameters);
}
catch (Exception e) {
@@ -662,6 +628,52 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
}
}
/*
* If there's a single method, it is SpEL only, the content is JSON,
* the payload is a String or byte[], the parameter doesn't match the payload,
* and there is a Json Object Mapper on the CP, convert.
*/
private void convertJsonPayloadIfNecessary(ParametersWrapper parameters) {
if (parameters.message != null &&
this.handlerMethod != null &&
this.handlerMethod.exclusiveMethodParameter != null &&
this.jsonObjectMapper != null) {
Class<?> type = this.handlerMethod.targetParameterType;
if ((parameters.getPayload() instanceof String && !type.equals(String.class)
|| parameters.getPayload() instanceof byte[] && !type.equals(byte[].class))
&& contentTypeIsJson(parameters.message)) {
try {
Object targetPayload = this.jsonObjectMapper.fromJson(parameters.getPayload(), type);
if (this.handlerMethod.targetParameterTypeDescriptor.isAssignableTo(messageTypeDescriptor)) {
parameters.message =
getMessageBuilderFactory()
.withPayload(targetPayload)
.copyHeaders(parameters.getHeaders())
.build();
}
else {
parameters.payload = targetPayload;
}
}
catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert from JSON", e);
}
}
}
}
}
private boolean contentTypeIsJson(Message<?> message) {
Object contentType = message.getHeaders().get(MessageHeaders.CONTENT_TYPE);
return contentType != null && contentType.toString().contains("json");
}
private Map<String, Map<Class<?>, HandlerMethod>> findHandlerMethodsForTarget(final Object targetObject,
final Class<? extends Annotation> annotationType, final String methodName, final boolean requiresReply) {
@@ -1013,6 +1025,8 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
private volatile Class<?> targetParameterType = Void.class;
private MethodParameter exclusiveMethodParameter;
private volatile boolean messageMethod;
private volatile boolean spelOnly;
@@ -1220,6 +1234,7 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
private void setExclusiveTargetParameterType(TypeDescriptor targetParameterType,
MethodParameter methodParameter) {
if (this.targetParameterTypeDescriptor != null) {
throw new IneligibleMethodException("Found more than one parameter type candidate: [" +
this.targetParameterTypeDescriptor + "] and [" + targetParameterType + "]");
@@ -1233,18 +1248,20 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
else {
this.targetParameterType = targetParameterType.getObjectType();
}
this.exclusiveMethodParameter = methodParameter;
}
}
public static class ParametersWrapper {
private final Object payload;
private final Collection<Message<?>> messages;
private final Map<String, Object> headers;
private final Message<?> message;
private Message<?> message;
private Object payload;
ParametersWrapper(Message<?> message) {
this.message = message;
@@ -1254,10 +1271,8 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
}
ParametersWrapper(Collection<Message<?>> messages, Map<String, Object> headers) {
this.payload = null;
this.messages = messages;
this.headers = headers;
this.message = null;
}
/**

View File

@@ -1017,7 +1017,12 @@ public class MethodInvokingMessageProcessorTests {
ObjectMapper objectMapper = new ObjectMapper();
byte[] value = objectMapper.writeValueAsBytes(testData);
String result = (String) processor.processMessage(new GenericMessage<>(value));
Message<?> testMessage =
MessageBuilder.withPayload(value)
.setHeader(MessageHeaders.CONTENT_TYPE, "application/json")
.build();
String result = (String) processor.processMessage(testMessage);
assertEquals("Foo,Bar", result);
}