Skip to content

Commit 7fc3ae1

Browse files
committed
spring-projectsGH-3052: Fix custom converters for lambdas
Fixes spring-projects#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**
1 parent 1e93d51 commit 7fc3ae1

File tree

3 files changed

+109
-27
lines changed

3 files changed

+109
-27
lines changed

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

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@
8686
import org.springframework.messaging.MessageHandlingException;
8787
import org.springframework.messaging.MessageHeaders;
8888
import org.springframework.messaging.converter.MessageConversionException;
89-
import org.springframework.messaging.converter.MessageConverter;
9089
import org.springframework.messaging.handler.annotation.Header;
9190
import org.springframework.messaging.handler.annotation.Headers;
9291
import org.springframework.messaging.handler.annotation.Payload;
@@ -541,7 +540,9 @@ private void initializeHandler(HandlerMethod candidate) {
541540
private void configureLocalMessageHandlerFactory() {
542541
BeanFactory beanFactory = getBeanFactory();
543542

544-
MessageConverter messageConverter = new ConfigurableCompositeMessageConverter();
543+
ConfigurableCompositeMessageConverter messageConverter = new ConfigurableCompositeMessageConverter();
544+
messageConverter.setBeanFactory(beanFactory);
545+
messageConverter.afterPropertiesSet();
545546

546547
List<HandlerMethodArgumentResolver> customArgumentResolvers = new LinkedList<>();
547548
PayloadExpressionArgumentResolver payloadExpressionArgumentResolver = new PayloadExpressionArgumentResolver();
@@ -560,14 +561,11 @@ private void configureLocalMessageHandlerFactory() {
560561

561562
MapArgumentResolver mapArgumentResolver = new MapArgumentResolver();
562563
customArgumentResolvers.add(mapArgumentResolver);
563-
564-
if (beanFactory != null) {
565-
payloadExpressionArgumentResolver.setBeanFactory(beanFactory);
566-
payloadsArgumentResolver.setBeanFactory(beanFactory);
567-
mapArgumentResolver.setBeanFactory(beanFactory);
568-
if (collectionArgumentResolver != null) {
569-
collectionArgumentResolver.setBeanFactory(beanFactory);
570-
}
564+
payloadExpressionArgumentResolver.setBeanFactory(beanFactory);
565+
payloadsArgumentResolver.setBeanFactory(beanFactory);
566+
mapArgumentResolver.setBeanFactory(beanFactory);
567+
if (collectionArgumentResolver != null) {
568+
collectionArgumentResolver.setBeanFactory(beanFactory);
571569
}
572570

573571
DefaultMessageHandlerMethodFactory localHandlerMethodFactory =

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

Lines changed: 31 additions & 3 deletions
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

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,26 @@
1919
import static org.assertj.core.api.Assertions.assertThat;
2020
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2121
import static org.assertj.core.api.Assertions.fail;
22-
import static org.mockito.BDDMockito.given;
23-
import static org.mockito.Mockito.mock;
22+
23+
import java.util.Objects;
24+
import java.util.function.Function;
2425

2526
import org.junit.Test;
27+
import org.junit.runner.RunWith;
2628

2729
import org.springframework.beans.factory.BeanFactory;
28-
import org.springframework.integration.context.IntegrationContextUtils;
30+
import org.springframework.beans.factory.annotation.Autowired;
31+
import org.springframework.context.annotation.Bean;
32+
import org.springframework.context.annotation.Configuration;
33+
import org.springframework.core.convert.converter.Converter;
34+
import org.springframework.integration.config.EnableIntegration;
35+
import org.springframework.integration.config.IntegrationConverter;
2936
import org.springframework.integration.handler.GenericHandler;
3037
import org.springframework.integration.handler.LambdaMessageProcessor;
31-
import org.springframework.integration.support.converter.ConfigurableCompositeMessageConverter;
3238
import org.springframework.integration.transformer.GenericTransformer;
3339
import org.springframework.messaging.Message;
34-
import org.springframework.messaging.converter.MessageConverter;
3540
import org.springframework.messaging.support.GenericMessage;
41+
import org.springframework.test.context.junit4.SpringRunner;
3642

3743

3844
/**
@@ -41,8 +47,12 @@
4147
*
4248
* @since 5.0
4349
*/
50+
@RunWith(SpringRunner.class)
4451
public class LambdaMessageProcessorTests {
4552

53+
@Autowired
54+
private BeanFactory beanFactory;
55+
4656
@Test
4757
@SuppressWarnings("divzero")
4858
public void testException() {
@@ -66,7 +76,7 @@ public Message<?> transform(Message<?> source) {
6676
}
6777

6878
}, null);
69-
lmp.setBeanFactory(mock(BeanFactory.class));
79+
lmp.setBeanFactory(this.beanFactory);
7080
GenericMessage<String> testMessage = new GenericMessage<>("foo");
7181
Object result = lmp.processMessage(testMessage);
7282
assertThat(result).isSameAs(testMessage);
@@ -76,16 +86,24 @@ public Message<?> transform(Message<?> source) {
7686
public void testMessageAsArgumentLambda() {
7787
LambdaMessageProcessor lmp = new LambdaMessageProcessor(
7888
(GenericTransformer<Message<?>, Message<?>>) this::messageTransformer, null);
79-
lmp.setBeanFactory(mock(BeanFactory.class));
89+
lmp.setBeanFactory(this.beanFactory);
8090
GenericMessage<String> testMessage = new GenericMessage<>("foo");
8191
assertThatExceptionOfType(IllegalStateException.class)
8292
.isThrownBy(() -> lmp.processMessage(testMessage))
8393
.withCauseInstanceOf(ClassCastException.class);
8494
}
8595

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+
assertThat(result).isEqualTo(new TestPojo("foo"));
102+
}
103+
86104
private void handle(GenericHandler<?> h) {
87105
LambdaMessageProcessor lmp = new LambdaMessageProcessor(h, String.class);
88-
lmp.setBeanFactory(getBeanFactory());
106+
lmp.setBeanFactory(this.beanFactory);
89107

90108
lmp.processMessage(new GenericMessage<>("foo"));
91109
}
@@ -95,12 +113,50 @@ private Message<?> messageTransformer(Message<?> message) {
95113
}
96114

97115

98-
private BeanFactory getBeanFactory() {
99-
BeanFactory mockBeanFactory = mock(BeanFactory.class);
100-
given(mockBeanFactory.getBean(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME,
101-
MessageConverter.class))
102-
.willReturn(new ConfigurableCompositeMessageConverter());
103-
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+
104160
}
105161

106162
}

0 commit comments

Comments
 (0)