Skip to content

Commit 9c1bdcd

Browse files
garyrussellartembilan
authored andcommitted
GH-1093: @RabbitListener fix abstract return type
Fixes #1093 AMQP-807 added support for generic return types; however this broke abstract return types since the `__TypeId__` header was set to the abstract type. If the return type is not a container type and represents an abstract class or interterface use the concrete return type to construct the `JavaType`. **cherry-pick to 2.1.x**
1 parent af7c32f commit 9c1bdcd

File tree

2 files changed

+187
-4
lines changed

2 files changed

+187
-4
lines changed

spring-amqp/src/main/java/org/springframework/amqp/support/converter/AbstractJackson2MessageConverter.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.amqp.support.converter;
1818

1919
import java.io.IOException;
20+
import java.lang.reflect.Modifier;
2021
import java.lang.reflect.Type;
2122
import java.nio.charset.Charset;
2223
import java.nio.charset.StandardCharsets;
@@ -324,8 +325,7 @@ protected Message createMessage(Object objectToConvert, MessageProperties messag
324325

325326
@Override
326327
protected Message createMessage(Object objectToConvert, MessageProperties messageProperties,
327-
@Nullable Type genericType)
328-
throws MessageConversionException {
328+
@Nullable Type genericType) throws MessageConversionException {
329329

330330
byte[] bytes;
331331
try {
@@ -346,8 +346,13 @@ protected Message createMessage(Object objectToConvert, MessageProperties messag
346346
messageProperties.setContentLength(bytes.length);
347347

348348
if (getClassMapper() == null) {
349-
getJavaTypeMapper().fromJavaType(this.objectMapper.constructType(
350-
genericType == null ? objectToConvert.getClass() : genericType), messageProperties);
349+
JavaType type = this.objectMapper.constructType(
350+
genericType == null ? objectToConvert.getClass() : genericType);
351+
if (genericType != null && !type.isContainerType()
352+
&& (type.getRawClass().isInterface() || Modifier.isAbstract(type.getRawClass().getModifiers()))) {
353+
type = this.objectMapper.constructType(objectToConvert.getClass());
354+
}
355+
getJavaTypeMapper().fromJavaType(type, messageProperties);
351356
}
352357
else {
353358
getClassMapper().fromClass(objectToConvert.getClass(), messageProperties); // NOSONAR never null
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* Copyright 2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.amqp.rabbit.annotation;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
24+
import org.junit.jupiter.api.Test;
25+
26+
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
27+
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
28+
import org.springframework.amqp.rabbit.core.RabbitTemplate;
29+
import org.springframework.amqp.rabbit.junit.RabbitAvailable;
30+
import org.springframework.amqp.rabbit.junit.RabbitAvailableCondition;
31+
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
32+
import org.springframework.beans.factory.annotation.Autowired;
33+
import org.springframework.context.annotation.Bean;
34+
import org.springframework.context.annotation.Configuration;
35+
import org.springframework.test.annotation.DirtiesContext;
36+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
37+
38+
/**
39+
* @author Gary Russell
40+
* @since 2.2
41+
*
42+
*/
43+
@SpringJUnitConfig
44+
@DirtiesContext
45+
@RabbitAvailable(queues = { "EnableRabbitReturnTypesTests.1", "EnableRabbitReturnTypesTests.2",
46+
"EnableRabbitReturnTypesTests.3", "EnableRabbitReturnTypesTests.4" })
47+
public class EnableRabbitReturnTypesTests {
48+
49+
@Test
50+
void testInterfaceReturn(@Autowired RabbitTemplate template) {
51+
Object reply = template.convertSendAndReceive("EnableRabbitReturnTypesTests.1", "3");
52+
assertThat(reply).isInstanceOf(Three.class);
53+
reply = template.convertSendAndReceive("EnableRabbitReturnTypesTests.1", "4");
54+
assertThat(reply).isInstanceOf(Four.class);
55+
}
56+
57+
@Test
58+
void testAbstractReturn(@Autowired RabbitTemplate template) {
59+
Object reply = template.convertSendAndReceive("EnableRabbitReturnTypesTests.2", "3");
60+
assertThat(reply).isInstanceOf(Three.class);
61+
reply = template.convertSendAndReceive("EnableRabbitReturnTypesTests.2", "4");
62+
assertThat(reply).isInstanceOf(Four.class);
63+
}
64+
65+
@Test
66+
void testListOfThree(@Autowired RabbitTemplate template) {
67+
Object reply = template.convertSendAndReceive("EnableRabbitReturnTypesTests.3", "3");
68+
assertThat(reply).isInstanceOf(List.class);
69+
assertThat(((List<?>) reply).get(0)).isInstanceOf(Three.class);
70+
}
71+
72+
@Test
73+
void testGenericInterfaceReturn(@Autowired RabbitTemplate template) {
74+
Object reply = template.convertSendAndReceive("EnableRabbitReturnTypesTests.4", "3");
75+
assertThat(reply).isInstanceOf(Three.class);
76+
reply = template.convertSendAndReceive("EnableRabbitReturnTypesTests.4", "4");
77+
assertThat(reply).isInstanceOf(Four.class);
78+
}
79+
80+
@Configuration(proxyBeanMethods = false)
81+
@EnableRabbit
82+
public static class Config<O extends One> {
83+
84+
@Bean
85+
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(CachingConnectionFactory cf,
86+
Jackson2JsonMessageConverter converter) {
87+
88+
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
89+
factory.setConnectionFactory(cf);
90+
factory.setMessageConverter(converter);
91+
factory.setDefaultRequeueRejected(false);
92+
return factory;
93+
}
94+
95+
@Bean
96+
public RabbitTemplate template(CachingConnectionFactory cf, Jackson2JsonMessageConverter converter) {
97+
RabbitTemplate template = new RabbitTemplate(cf);
98+
template.setMessageConverter(converter);
99+
return template;
100+
}
101+
102+
@Bean
103+
public CachingConnectionFactory cf() {
104+
return new CachingConnectionFactory(RabbitAvailableCondition.getBrokerRunning().getConnectionFactory());
105+
}
106+
107+
@Bean
108+
public Jackson2JsonMessageConverter converter() {
109+
return new Jackson2JsonMessageConverter();
110+
}
111+
112+
@RabbitListener(queues = "EnableRabbitReturnTypesTests.1")
113+
public One listen1(String in) {
114+
if ("3".equals(in)) {
115+
return new Three();
116+
}
117+
else {
118+
return new Four();
119+
}
120+
}
121+
122+
@RabbitListener(queues = "EnableRabbitReturnTypesTests.2")
123+
public Two listen2(String in) {
124+
if ("3".equals(in)) {
125+
return new Three();
126+
}
127+
else {
128+
return new Four();
129+
}
130+
}
131+
132+
@RabbitListener(queues = "EnableRabbitReturnTypesTests.3")
133+
public List<Three> listen3(@SuppressWarnings("unused") String in) {
134+
List<Three> list = new ArrayList<>();
135+
list.add(new Three());
136+
return list;
137+
}
138+
139+
@SuppressWarnings("unchecked")
140+
@RabbitListener(queues = "EnableRabbitReturnTypesTests.4")
141+
public O listen4(String in) {
142+
if ("3".equals(in)) {
143+
return (O) new Three();
144+
}
145+
else {
146+
return (O) new Four();
147+
}
148+
}
149+
150+
}
151+
152+
public interface One {
153+
154+
}
155+
156+
public static abstract class Two implements One {
157+
158+
private String field;
159+
160+
public String getField() {
161+
return this.field;
162+
}
163+
164+
public void setField(String field) {
165+
this.field = field;
166+
}
167+
168+
}
169+
170+
public static class Three extends Two {
171+
172+
}
173+
174+
public static class Four extends Two {
175+
176+
}
177+
178+
}

0 commit comments

Comments
 (0)