GH-3052: Fix custom converters for lambdas
Fixes https://github.com/spring-projects/spring-integration/issues/3052 Starting with version `5.1`, a `LambdaMessageProcessor` is based on the `MessageConverter` instead of plain `ConversionService`. (A `ConfigurableCompositeMessageConverter` is used from the application context.) However a type conversion based on the `@IntegrationConverter` is lost in the `GenericMessageConverter` because it doesn't use an `integrationConversionService` from the application context * Change `ConfigurableCompositeMessageConverter` to configure a `GenericMessageConverter` with an `integrationConversionService` if any * Fix `MessagingMethodInvokerHelper` to populate a `BeanFactory` into its internal `ConfigurableCompositeMessageConverter` * Ensure in the `LambdaMessageProcessorTests` that `@IntegrationConverter` is applied for ``LambdaMessageProcessor` as well **Cherry-pick to 5.1.x**
This commit is contained in:
committed by
Gary Russell
parent
1e93d51697
commit
9272782a48
@@ -86,7 +86,6 @@ import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandlingException;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.converter.MessageConversionException;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
import org.springframework.messaging.handler.annotation.Header;
|
||||
import org.springframework.messaging.handler.annotation.Headers;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
@@ -541,7 +540,9 @@ public class MessagingMethodInvokerHelper extends AbstractExpressionEvaluator im
|
||||
private void configureLocalMessageHandlerFactory() {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
|
||||
MessageConverter messageConverter = new ConfigurableCompositeMessageConverter();
|
||||
ConfigurableCompositeMessageConverter messageConverter = new ConfigurableCompositeMessageConverter();
|
||||
messageConverter.setBeanFactory(beanFactory);
|
||||
messageConverter.afterPropertiesSet();
|
||||
|
||||
List<HandlerMethodArgumentResolver> customArgumentResolvers = new LinkedList<>();
|
||||
PayloadExpressionArgumentResolver payloadExpressionArgumentResolver = new PayloadExpressionArgumentResolver();
|
||||
@@ -560,14 +561,11 @@ public class MessagingMethodInvokerHelper extends AbstractExpressionEvaluator im
|
||||
|
||||
MapArgumentResolver mapArgumentResolver = new MapArgumentResolver();
|
||||
customArgumentResolvers.add(mapArgumentResolver);
|
||||
|
||||
if (beanFactory != null) {
|
||||
payloadExpressionArgumentResolver.setBeanFactory(beanFactory);
|
||||
payloadsArgumentResolver.setBeanFactory(beanFactory);
|
||||
mapArgumentResolver.setBeanFactory(beanFactory);
|
||||
if (collectionArgumentResolver != null) {
|
||||
collectionArgumentResolver.setBeanFactory(beanFactory);
|
||||
}
|
||||
payloadExpressionArgumentResolver.setBeanFactory(beanFactory);
|
||||
payloadsArgumentResolver.setBeanFactory(beanFactory);
|
||||
mapArgumentResolver.setBeanFactory(beanFactory);
|
||||
if (collectionArgumentResolver != null) {
|
||||
collectionArgumentResolver.setBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
DefaultMessageHandlerMethodFactory localHandlerMethodFactory =
|
||||
|
||||
@@ -22,8 +22,15 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.integration.support.json.Jackson2JsonObjectMapper;
|
||||
import org.springframework.integration.support.json.JacksonPresent;
|
||||
import org.springframework.integration.support.utils.IntegrationUtils;
|
||||
import org.springframework.messaging.converter.ByteArrayMessageConverter;
|
||||
import org.springframework.messaging.converter.CompositeMessageConverter;
|
||||
import org.springframework.messaging.converter.GenericMessageConverter;
|
||||
@@ -47,13 +54,19 @@ import org.springframework.messaging.converter.MessageConverter;
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ConfigurableCompositeMessageConverter extends CompositeMessageConverter {
|
||||
public class ConfigurableCompositeMessageConverter extends CompositeMessageConverter
|
||||
implements BeanFactoryAware, InitializingBean {
|
||||
|
||||
private final boolean registerDefaults;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
/**
|
||||
* Create an instance with the default converters.
|
||||
*/
|
||||
public ConfigurableCompositeMessageConverter() {
|
||||
super(initDefaults());
|
||||
this.registerDefaults = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,6 +86,23 @@ public class ConfigurableCompositeMessageConverter extends CompositeMessageConve
|
||||
super(registerDefaults ?
|
||||
Stream.concat(converters.stream(), initDefaults().stream()).collect(Collectors.toList())
|
||||
: converters);
|
||||
this.registerDefaults = registerDefaults;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
if (this.registerDefaults) {
|
||||
ConversionService conversionService = IntegrationUtils.getConversionService(this.beanFactory);
|
||||
if (conversionService == null) {
|
||||
conversionService = DefaultConversionService.getSharedInstance();
|
||||
}
|
||||
getConverters().add(new GenericMessageConverter(conversionService));
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection<MessageConverter> initDefaults() {
|
||||
@@ -90,8 +120,6 @@ public class ConfigurableCompositeMessageConverter extends CompositeMessageConve
|
||||
// TODO do we port it together with MessageConverterUtils ?
|
||||
// converters.add(new JavaSerializationMessageConverter());
|
||||
|
||||
converters.add(new GenericMessageConverter());
|
||||
|
||||
return converters;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,20 +19,26 @@ package org.springframework.integration.dsl;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.config.IntegrationConverter;
|
||||
import org.springframework.integration.handler.GenericHandler;
|
||||
import org.springframework.integration.handler.LambdaMessageProcessor;
|
||||
import org.springframework.integration.support.converter.ConfigurableCompositeMessageConverter;
|
||||
import org.springframework.integration.transformer.GenericTransformer;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
|
||||
/**
|
||||
@@ -41,8 +47,12 @@ import org.springframework.messaging.support.GenericMessage;
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
public class LambdaMessageProcessorTests {
|
||||
|
||||
@Autowired
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("divzero")
|
||||
public void testException() {
|
||||
@@ -66,7 +76,7 @@ public class LambdaMessageProcessorTests {
|
||||
}
|
||||
|
||||
}, null);
|
||||
lmp.setBeanFactory(mock(BeanFactory.class));
|
||||
lmp.setBeanFactory(this.beanFactory);
|
||||
GenericMessage<String> testMessage = new GenericMessage<>("foo");
|
||||
Object result = lmp.processMessage(testMessage);
|
||||
assertThat(result).isSameAs(testMessage);
|
||||
@@ -76,16 +86,24 @@ public class LambdaMessageProcessorTests {
|
||||
public void testMessageAsArgumentLambda() {
|
||||
LambdaMessageProcessor lmp = new LambdaMessageProcessor(
|
||||
(GenericTransformer<Message<?>, Message<?>>) this::messageTransformer, null);
|
||||
lmp.setBeanFactory(mock(BeanFactory.class));
|
||||
lmp.setBeanFactory(this.beanFactory);
|
||||
GenericMessage<String> testMessage = new GenericMessage<>("foo");
|
||||
assertThatExceptionOfType(IllegalStateException.class)
|
||||
.isThrownBy(() -> lmp.processMessage(testMessage))
|
||||
.withCauseInstanceOf(ClassCastException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomConverter() {
|
||||
LambdaMessageProcessor lmp = new LambdaMessageProcessor(Function.identity(), TestPojo.class);
|
||||
lmp.setBeanFactory(this.beanFactory);
|
||||
Object result = lmp.processMessage(new GenericMessage<>("foo"));
|
||||
assertThat(result).isEqualTo(new TestPojo("foo"));
|
||||
}
|
||||
|
||||
private void handle(GenericHandler<?> h) {
|
||||
LambdaMessageProcessor lmp = new LambdaMessageProcessor(h, String.class);
|
||||
lmp.setBeanFactory(getBeanFactory());
|
||||
lmp.setBeanFactory(this.beanFactory);
|
||||
|
||||
lmp.processMessage(new GenericMessage<>("foo"));
|
||||
}
|
||||
@@ -95,12 +113,50 @@ public class LambdaMessageProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
private BeanFactory getBeanFactory() {
|
||||
BeanFactory mockBeanFactory = mock(BeanFactory.class);
|
||||
given(mockBeanFactory.getBean(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME,
|
||||
MessageConverter.class))
|
||||
.willReturn(new ConfigurableCompositeMessageConverter());
|
||||
return mockBeanFactory;
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
public static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
@IntegrationConverter
|
||||
public Converter<String, TestPojo> testPojoConverter() {
|
||||
return new Converter<String, TestPojo>() { // Cannot be lambda for explicit generic types
|
||||
|
||||
@Override
|
||||
public TestPojo convert(String source) {
|
||||
return new TestPojo(source);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestPojo {
|
||||
|
||||
private final String value;
|
||||
|
||||
TestPojo(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof TestPojo)) {
|
||||
return false;
|
||||
}
|
||||
TestPojo testPojo = (TestPojo) o;
|
||||
return Objects.equals(this.value, testPojo.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user