INT-4523: Add DSL convert(Class<> cls) operator (#2556)

* INT-4523: Add DSL `convert(Class<> cls)` operator

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

* Change the `LambdaMessageProcessor` to rely on the `MessageConverter`
populated by the `IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME`
This way all the Lambda-based handlers are going to work the same way
as POJO-based via `MessagingMethodInvokerHelper`
* Add `convert(Class<P> payloadType)` EIP-operator to perform similar
to POJO-based method invocation argument conversion

* * Fix `LambdaMessageProcessorTests` to inject a
`ConfigurableCompositeMessageConverter` from the mocked `BeanFactory`
* Make a `mappingJackson2MessageConverter.setStrictContentTypeMatch(true)`
* Also obtain an `ObjectMapper` from the `Jackson2JsonObjectMapper`
which is configured with the scanned possible Jackson modules.
in the `ConfigurableCompositeMessageConverter` do not try to convert
all the potential content without an appropriate JSON content-type
header
This commit is contained in:
Artem Bilan
2018-09-17 10:40:56 -04:00
committed by Gary Russell
parent 5bfd6971db
commit 54094da76b
5 changed files with 110 additions and 26 deletions

View File

@@ -587,6 +587,21 @@ public abstract class IntegrationFlowDefinition<B extends IntegrationFlowDefinit
.transform(new MethodInvokingTransformer(processor), endpointConfigurer);
}
/**
* Populate the {@link MessageTransformingHandler} instance
* for the provided {@code payloadType} to convert at runtime.
* @param payloadType the {@link Class} for expected payload type.
* @param <P> the payload type - 'convert to'.
* @return the current {@link IntegrationFlowDefinition}.
* @since 5.1
* @see MethodInvokingTransformer
* @see LambdaMessageProcessor
*/
public <P> B convert(Class<P> payloadType) {
return transform(payloadType, p -> p);
}
/**
* Populate the {@link MessageTransformingHandler} instance for the provided {@link GenericTransformer}
* for the specific {@code payloadType} to convert at runtime.
@@ -599,7 +614,7 @@ public abstract class IntegrationFlowDefinition<B extends IntegrationFlowDefinit
* @see LambdaMessageProcessor
*/
public <P, T> B transform(Class<P> payloadType, GenericTransformer<P, T> genericTransformer) {
return this.transform(payloadType, genericTransformer, null);
return transform(payloadType, genericTransformer, null);
}
/**
@@ -619,6 +634,25 @@ public abstract class IntegrationFlowDefinition<B extends IntegrationFlowDefinit
return this.transform(null, genericTransformer, endpointConfigurer);
}
/**
* Populate the {@link MessageTransformingHandler} instance
* for the provided {@code payloadType} to convert at runtime.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* @param payloadType the {@link Class} for expected payload type.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <P> the payload type - 'transform to'.
* @return the current {@link IntegrationFlowDefinition}.
* @since 5.1
* @see MethodInvokingTransformer
* @see LambdaMessageProcessor
* @see GenericEndpointSpec
*/
public <P> B convert(Class<P> payloadType,
Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
return transform(payloadType, p -> p, endpointConfigurer);
}
/**
* Populate the {@link MessageTransformingHandler} instance for the provided {@link GenericTransformer}
* for the specific {@code payloadType} to convert at runtime.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 the original author or authors.
* Copyright 2016-2018 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.
@@ -25,12 +25,10 @@ import java.util.concurrent.atomic.AtomicReference;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.integration.support.utils.IntegrationUtils;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
@@ -48,11 +46,11 @@ public class LambdaMessageProcessor implements MessageProcessor<Object>, BeanFac
private final Method method;
private final TypeDescriptor payloadType;
private final Class<?> payloadType;
private final Class<?>[] parameterTypes;
private ConversionService conversionService;
private MessageConverter messageConverter;
public LambdaMessageProcessor(Object target, Class<?> payloadType) {
Assert.notNull(target, "'target' must not be null");
@@ -79,16 +77,14 @@ public class LambdaMessageProcessor implements MessageProcessor<Object>, BeanFac
this.method = methodValue.get();
this.method.setAccessible(true);
this.parameterTypes = this.method.getParameterTypes();
this.payloadType = payloadType != null ? TypeDescriptor.valueOf(payloadType) : null;
this.payloadType = payloadType;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
ConversionService conversionService = IntegrationUtils.getConversionService(beanFactory);
if (conversionService == null) {
conversionService = DefaultConversionService.getSharedInstance();
}
this.conversionService = conversionService;
this.messageConverter =
beanFactory.getBean(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME,
MessageConverter.class);
}
@Override
@@ -109,14 +105,12 @@ public class LambdaMessageProcessor implements MessageProcessor<Object>, BeanFac
}
else {
if (this.payloadType != null) {
if (Message.class.isAssignableFrom(this.payloadType.getType())) {
if (Message.class.isAssignableFrom(this.payloadType)) {
args[i] = message;
}
else {
args[i] = this.conversionService.convert(message.getPayload(),
TypeDescriptor.forObject(message.getPayload()), this.payloadType);
args[i] = this.messageConverter.fromMessage(message, this.payloadType);
}
}
else {
args[i] = message.getPayload();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-2018 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.
@@ -22,6 +22,7 @@ import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.integration.support.json.Jackson2JsonObjectMapper;
import org.springframework.integration.support.json.JacksonPresent;
import org.springframework.messaging.converter.ByteArrayMessageConverter;
import org.springframework.messaging.converter.CompositeMessageConverter;
@@ -78,7 +79,10 @@ public class ConfigurableCompositeMessageConverter extends CompositeMessageConve
List<MessageConverter> converters = new LinkedList<>();
if (JacksonPresent.isJackson2Present()) {
converters.add(new MappingJackson2MessageConverter());
MappingJackson2MessageConverter mappingJackson2MessageConverter = new MappingJackson2MessageConverter();
mappingJackson2MessageConverter.setStrictContentTypeMatch(true);
mappingJackson2MessageConverter.setObjectMapper(new Jackson2JsonObjectMapper().getObjectMapper());
converters.add(mappingJackson2MessageConverter);
}
converters.add(new ByteArrayMessageConverter());
converters.add(new ObjectStringMessageConverter());