Skip to content

Commit 2c3ee7d

Browse files
artembilangaryrussell
authored andcommitted
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`
1 parent 60606dd commit 2c3ee7d

File tree

2 files changed

+63
-43
lines changed

2 files changed

+63
-43
lines changed

spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -308,46 +308,10 @@ public void setConversionService(ConversionService conversionService) {
308308
}
309309

310310
public T process(Message<?> message) throws Exception {
311-
Message<?> messageToProcess = possiblyConvert(message);
312-
ParametersWrapper parameters = new ParametersWrapper(messageToProcess);
311+
ParametersWrapper parameters = new ParametersWrapper(message);
313312
return processInternal(parameters);
314313
}
315314

316-
/*
317-
* If there's a single method, the content is JSON, the payload is a
318-
* String or byte[], the parameter doesn't match the payload,
319-
* and there is a Json Object Mapper on the CP,
320-
* convert.
321-
*/
322-
private Message<?> possiblyConvert(Message<?> message) throws Exception {
323-
if (this.handlerMethod != null && this.handlerMethod.getTargetParameterType() != null &&
324-
this.jsonObjectMapper != null) {
325-
Class<?> type = this.handlerMethod.getTargetParameterType();
326-
if ((message.getPayload() instanceof String && !type.equals(String.class)
327-
|| message.getPayload() instanceof byte[] && !type.equals(byte[].class))
328-
&& contentTypeIsJson(message)) {
329-
330-
try {
331-
return getMessageBuilderFactory()
332-
.withPayload(this.jsonObjectMapper.fromJson(message.getPayload(), type))
333-
.copyHeaders(message.getHeaders())
334-
.build();
335-
}
336-
catch (Exception e) {
337-
if (logger.isDebugEnabled()) {
338-
logger.debug("Failed to convert from JSON", e);
339-
}
340-
}
341-
}
342-
}
343-
return message;
344-
}
345-
346-
private boolean contentTypeIsJson(Message<?> message) {
347-
Object contentType = message.getHeaders().get(MessageHeaders.CONTENT_TYPE);
348-
return contentType != null && contentType.toString().contains("json");
349-
}
350-
351315
public T process(Collection<Message<?>> messages, Map<String, Object> headers) throws Exception {
352316
ParametersWrapper parameters = new ParametersWrapper(messages, headers);
353317
return processInternal(parameters);
@@ -645,6 +609,8 @@ else if (e instanceof IllegalStateException) {
645609
@SuppressWarnings("unchecked")
646610
private T invokeExpression(Expression expression, ParametersWrapper parameters) throws Exception {
647611
try {
612+
613+
convertJsonPayloadIfNecessary(parameters);
648614
return (T) evaluateExpression(expression, parameters);
649615
}
650616
catch (Exception e) {
@@ -662,6 +628,52 @@ private T invokeExpression(Expression expression, ParametersWrapper parameters)
662628
}
663629
}
664630

631+
/*
632+
* If there's a single method, it is SpEL only, the content is JSON,
633+
* the payload is a String or byte[], the parameter doesn't match the payload,
634+
* and there is a Json Object Mapper on the CP, convert.
635+
*/
636+
private void convertJsonPayloadIfNecessary(ParametersWrapper parameters) {
637+
if (parameters.message != null &&
638+
this.handlerMethod != null &&
639+
this.handlerMethod.exclusiveMethodParameter != null &&
640+
this.jsonObjectMapper != null) {
641+
642+
Class<?> type = this.handlerMethod.targetParameterType;
643+
if ((parameters.getPayload() instanceof String && !type.equals(String.class)
644+
|| parameters.getPayload() instanceof byte[] && !type.equals(byte[].class))
645+
&& contentTypeIsJson(parameters.message)) {
646+
647+
try {
648+
Object targetPayload = this.jsonObjectMapper.fromJson(parameters.getPayload(), type);
649+
650+
if (this.handlerMethod.targetParameterTypeDescriptor.isAssignableTo(messageTypeDescriptor)) {
651+
parameters.message =
652+
getMessageBuilderFactory()
653+
.withPayload(targetPayload)
654+
.copyHeaders(parameters.getHeaders())
655+
.build();
656+
}
657+
else {
658+
parameters.payload = targetPayload;
659+
}
660+
661+
662+
}
663+
catch (Exception e) {
664+
if (logger.isDebugEnabled()) {
665+
logger.debug("Failed to convert from JSON", e);
666+
}
667+
}
668+
}
669+
}
670+
}
671+
672+
private boolean contentTypeIsJson(Message<?> message) {
673+
Object contentType = message.getHeaders().get(MessageHeaders.CONTENT_TYPE);
674+
return contentType != null && contentType.toString().contains("json");
675+
}
676+
665677
private Map<String, Map<Class<?>, HandlerMethod>> findHandlerMethodsForTarget(final Object targetObject,
666678
final Class<? extends Annotation> annotationType, final String methodName, final boolean requiresReply) {
667679

@@ -1013,6 +1025,8 @@ private static class HandlerMethod {
10131025

10141026
private volatile Class<?> targetParameterType = Void.class;
10151027

1028+
private MethodParameter exclusiveMethodParameter;
1029+
10161030
private volatile boolean messageMethod;
10171031

10181032
private volatile boolean spelOnly;
@@ -1220,6 +1234,7 @@ else if (!StringUtils.hasLength(relativeExpression)) {
12201234

12211235
private void setExclusiveTargetParameterType(TypeDescriptor targetParameterType,
12221236
MethodParameter methodParameter) {
1237+
12231238
if (this.targetParameterTypeDescriptor != null) {
12241239
throw new IneligibleMethodException("Found more than one parameter type candidate: [" +
12251240
this.targetParameterTypeDescriptor + "] and [" + targetParameterType + "]");
@@ -1233,18 +1248,20 @@ private void setExclusiveTargetParameterType(TypeDescriptor targetParameterType,
12331248
else {
12341249
this.targetParameterType = targetParameterType.getObjectType();
12351250
}
1251+
1252+
this.exclusiveMethodParameter = methodParameter;
12361253
}
12371254
}
12381255

12391256
public static class ParametersWrapper {
12401257

1241-
private final Object payload;
1242-
12431258
private final Collection<Message<?>> messages;
12441259

12451260
private final Map<String, Object> headers;
12461261

1247-
private final Message<?> message;
1262+
private Message<?> message;
1263+
1264+
private Object payload;
12481265

12491266
ParametersWrapper(Message<?> message) {
12501267
this.message = message;
@@ -1254,10 +1271,8 @@ public static class ParametersWrapper {
12541271
}
12551272

12561273
ParametersWrapper(Collection<Message<?>> messages, Map<String, Object> headers) {
1257-
this.payload = null;
12581274
this.messages = messages;
12591275
this.headers = headers;
1260-
this.message = null;
12611276
}
12621277

12631278
/**

spring-integration-core/src/test/java/org/springframework/integration/handler/MethodInvokingMessageProcessorTests.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,12 @@ public String myMethod(List<Employee<Person>> msg) {
10171017
ObjectMapper objectMapper = new ObjectMapper();
10181018
byte[] value = objectMapper.writeValueAsBytes(testData);
10191019

1020-
String result = (String) processor.processMessage(new GenericMessage<>(value));
1020+
Message<?> testMessage =
1021+
MessageBuilder.withPayload(value)
1022+
.setHeader(MessageHeaders.CONTENT_TYPE, "application/json")
1023+
.build();
1024+
1025+
String result = (String) processor.processMessage(testMessage);
10211026

10221027
assertEquals("Foo,Bar", result);
10231028
}

0 commit comments

Comments
 (0)