Skip to content

Commit addf2db

Browse files
committed
GH-3052: Fix custom converters for lambdas
Fixes #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** # Conflicts: # spring-integration-core/src/main/java/org/springframework/integration/handler/support/MessagingMethodInvokerHelper.java # spring-integration-core/src/test/java/org/springframework/integration/dsl/LambdaMessageProcessorTests.java
1 parent d91a45a commit addf2db

File tree

3 files changed

+106
-19
lines changed

3 files changed

+106
-19
lines changed

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -578,18 +578,20 @@ private synchronized void initialize() throws Exception {
578578
* that don't run in an application context.
579579
*/
580580
private void configureLocalMessageHandlerFactory() {
581-
MessageConverter messageConverter = null;
581+
ConfigurableCompositeMessageConverter messageConverter = null;
582582
BeanFactory beanFactory = getBeanFactory();
583583
if (beanFactory != null &&
584584
beanFactory.containsBean(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME)) {
585585
messageConverter = beanFactory
586586
.getBean(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME,
587-
MessageConverter.class);
587+
ConfigurableCompositeMessageConverter.class);
588588
((DefaultMessageHandlerMethodFactory) this.messageHandlerMethodFactory)
589589
.setMessageConverter(messageConverter);
590590
}
591591
else {
592592
messageConverter = new ConfigurableCompositeMessageConverter();
593+
messageConverter.setBeanFactory(beanFactory);
594+
messageConverter.afterPropertiesSet();
593595
}
594596
NullAwarePayloadArgumentResolver nullResolver = new NullAwarePayloadArgumentResolver(messageConverter);
595597
PayloadExpressionArgumentResolver payloadExpressionArgumentResolver = new PayloadExpressionArgumentResolver();

spring-integration-core/src/main/java/org/springframework/integration/support/converter/ConfigurableCompositeMessageConverter.java

+31-3
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,15 @@
2222
import java.util.stream.Collectors;
2323
import java.util.stream.Stream;
2424

25+
import org.springframework.beans.BeansException;
26+
import org.springframework.beans.factory.BeanFactory;
27+
import org.springframework.beans.factory.BeanFactoryAware;
28+
import org.springframework.beans.factory.InitializingBean;
29+
import org.springframework.core.convert.ConversionService;
30+
import org.springframework.core.convert.support.DefaultConversionService;
2531
import org.springframework.integration.support.json.Jackson2JsonObjectMapper;
2632
import org.springframework.integration.support.json.JacksonPresent;
33+
import org.springframework.integration.support.utils.IntegrationUtils;
2734
import org.springframework.messaging.converter.ByteArrayMessageConverter;
2835
import org.springframework.messaging.converter.CompositeMessageConverter;
2936
import org.springframework.messaging.converter.GenericMessageConverter;
@@ -47,13 +54,19 @@
4754
*
4855
* @since 5.0
4956
*/
50-
public class ConfigurableCompositeMessageConverter extends CompositeMessageConverter {
57+
public class ConfigurableCompositeMessageConverter extends CompositeMessageConverter
58+
implements BeanFactoryAware, InitializingBean {
59+
60+
private final boolean registerDefaults;
61+
62+
private BeanFactory beanFactory;
5163

5264
/**
5365
* Create an instance with the default converters.
5466
*/
5567
public ConfigurableCompositeMessageConverter() {
5668
super(initDefaults());
69+
this.registerDefaults = true;
5770
}
5871

5972
/**
@@ -73,6 +86,23 @@ public ConfigurableCompositeMessageConverter(Collection<MessageConverter> conver
7386
super(registerDefaults ?
7487
Stream.concat(converters.stream(), initDefaults().stream()).collect(Collectors.toList())
7588
: converters);
89+
this.registerDefaults = registerDefaults;
90+
}
91+
92+
@Override
93+
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
94+
this.beanFactory = beanFactory;
95+
}
96+
97+
@Override
98+
public void afterPropertiesSet() {
99+
if (this.registerDefaults) {
100+
ConversionService conversionService = IntegrationUtils.getConversionService(this.beanFactory);
101+
if (conversionService == null) {
102+
conversionService = DefaultConversionService.getSharedInstance();
103+
}
104+
getConverters().add(new GenericMessageConverter(conversionService));
105+
}
76106
}
77107

78108
private static Collection<MessageConverter> initDefaults() {
@@ -90,8 +120,6 @@ private static Collection<MessageConverter> initDefaults() {
90120
// TODO do we port it together with MessageConverterUtils ?
91121
// converters.add(new JavaSerializationMessageConverter());
92122

93-
converters.add(new GenericMessageConverter());
94-
95123
return converters;
96124
}
97125

spring-integration-core/src/test/java/org/springframework/integration/dsl/LambdaMessageProcessorTests.java

+71-14
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,30 @@
1818

1919
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2020
import static org.hamcrest.Matchers.instanceOf;
21+
import static org.junit.Assert.assertEquals;
2122
import static org.junit.Assert.assertSame;
2223
import static org.junit.Assert.assertThat;
2324
import static org.junit.Assert.fail;
24-
import static org.mockito.BDDMockito.given;
25-
import static org.mockito.Mockito.mock;
25+
26+
import java.util.Objects;
27+
import java.util.function.Function;
2628

2729
import org.junit.Test;
30+
import org.junit.runner.RunWith;
2831

2932
import org.springframework.beans.factory.BeanFactory;
30-
import org.springframework.integration.context.IntegrationContextUtils;
33+
import org.springframework.beans.factory.annotation.Autowired;
34+
import org.springframework.context.annotation.Bean;
35+
import org.springframework.context.annotation.Configuration;
36+
import org.springframework.core.convert.converter.Converter;
37+
import org.springframework.integration.config.EnableIntegration;
38+
import org.springframework.integration.config.IntegrationConverter;
3139
import org.springframework.integration.handler.GenericHandler;
3240
import org.springframework.integration.handler.LambdaMessageProcessor;
33-
import org.springframework.integration.support.converter.ConfigurableCompositeMessageConverter;
3441
import org.springframework.integration.transformer.GenericTransformer;
3542
import org.springframework.messaging.Message;
36-
import org.springframework.messaging.converter.MessageConverter;
3743
import org.springframework.messaging.support.GenericMessage;
44+
import org.springframework.test.context.junit4.SpringRunner;
3845

3946

4047
/**
@@ -43,8 +50,12 @@
4350
*
4451
* @since 5.0
4552
*/
53+
@RunWith(SpringRunner.class)
4654
public class LambdaMessageProcessorTests {
4755

56+
@Autowired
57+
private BeanFactory beanFactory;
58+
4859
@Test
4960
@SuppressWarnings("divzero")
5061
public void testException() {
@@ -67,7 +78,7 @@ public Message<?> transform(Message<?> source) {
6778
}
6879

6980
}, null);
70-
lmp.setBeanFactory(mock(BeanFactory.class));
81+
lmp.setBeanFactory(this.beanFactory);
7182
GenericMessage<String> testMessage = new GenericMessage<>("foo");
7283
Object result = lmp.processMessage(testMessage);
7384
assertSame(testMessage, result);
@@ -77,14 +88,22 @@ public Message<?> transform(Message<?> source) {
7788
public void testMessageAsArgumentLambda() {
7889
LambdaMessageProcessor lmp = new LambdaMessageProcessor(
7990
(GenericTransformer<Message<?>, Message<?>>) source -> messageTransformer(source), null);
80-
lmp.setBeanFactory(mock(BeanFactory.class));
91+
lmp.setBeanFactory(this.beanFactory);
8192
GenericMessage<String> testMessage = new GenericMessage<>("foo");
8293
assertThatThrownBy(() -> lmp.processMessage(testMessage)).hasCauseExactlyInstanceOf(ClassCastException.class);
8394
}
8495

96+
@Test
97+
public void testCustomConverter() {
98+
LambdaMessageProcessor lmp = new LambdaMessageProcessor(Function.identity(), TestPojo.class);
99+
lmp.setBeanFactory(this.beanFactory);
100+
Object result = lmp.processMessage(new GenericMessage<>("foo"));
101+
assertEquals(new TestPojo("foo"), result);
102+
}
103+
85104
private void handle(GenericHandler<?> h) {
86105
LambdaMessageProcessor lmp = new LambdaMessageProcessor(h, String.class);
87-
lmp.setBeanFactory(getBeanFactory());
106+
lmp.setBeanFactory(this.beanFactory);
88107

89108
lmp.processMessage(new GenericMessage<>("foo"));
90109
}
@@ -94,12 +113,50 @@ private Message<?> messageTransformer(Message<?> message) {
94113
}
95114

96115

97-
private BeanFactory getBeanFactory() {
98-
BeanFactory mockBeanFactory = mock(BeanFactory.class);
99-
given(mockBeanFactory.getBean(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME,
100-
MessageConverter.class))
101-
.willReturn(new ConfigurableCompositeMessageConverter());
102-
return mockBeanFactory;
116+
@Configuration
117+
@EnableIntegration
118+
public static class TestConfiguration {
119+
120+
@Bean
121+
@IntegrationConverter
122+
public Converter<String, TestPojo> testPojoConverter() {
123+
return new Converter<String, TestPojo>() { // Cannot be lambda for explicit generic types
124+
125+
@Override
126+
public TestPojo convert(String source) {
127+
return new TestPojo(source);
128+
}
129+
130+
};
131+
}
132+
133+
}
134+
135+
private static class TestPojo {
136+
137+
private final String value;
138+
139+
TestPojo(String value) {
140+
this.value = value;
141+
}
142+
143+
@Override
144+
public boolean equals(Object o) {
145+
if (this == o) {
146+
return true;
147+
}
148+
if (!(o instanceof TestPojo)) {
149+
return false;
150+
}
151+
TestPojo testPojo = (TestPojo) o;
152+
return Objects.equals(this.value, testPojo.value);
153+
}
154+
155+
@Override
156+
public int hashCode() {
157+
return Objects.hash(this.value);
158+
}
159+
103160
}
104161

105162
}

0 commit comments

Comments
 (0)