Skip to content

Commit a8fb34d

Browse files
omercelikcengolegz
authored andcommitted
Adding Spring Cloud Stream Version To Message Headers For Easier Debugging Of Issues.
Fix checkstyles Resolves #3027
1 parent 17ac635 commit a8fb34d

File tree

7 files changed

+441
-22
lines changed

7 files changed

+441
-22
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ dump.rdb
2525
.apt_generated
2626
artifacts
2727
**/dependency-reduced-pom.xml
28+
core/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/utils/GeneratedBuildProperties.java
2829

2930
node
3031
node_modules

core/spring-cloud-stream-integration-tests/src/test/java/org/springframework/cloud/stream/function/HeaderTests.java

+217-17
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616

1717
package org.springframework.cloud.stream.function;
1818

19+
import java.nio.charset.StandardCharsets;
20+
import java.util.Collections;
1921
import java.util.Locale;
22+
import java.util.Map;
2023
import java.util.function.Function;
2124

2225
import org.junit.jupiter.api.BeforeAll;
@@ -26,9 +29,14 @@
2629
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2730
import org.springframework.boot.builder.SpringApplicationBuilder;
2831
import org.springframework.cloud.function.context.message.MessageUtils;
32+
import org.springframework.cloud.function.json.JsonMapper;
33+
import org.springframework.cloud.stream.binder.BinderHeaders;
34+
import org.springframework.cloud.stream.binder.test.EnableTestBinder;
2935
import org.springframework.cloud.stream.binder.test.InputDestination;
3036
import org.springframework.cloud.stream.binder.test.OutputDestination;
3137
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration;
38+
import org.springframework.cloud.stream.utils.BuildInformationProvider;
39+
import org.springframework.context.ApplicationContext;
3240
import org.springframework.context.ConfigurableApplicationContext;
3341
import org.springframework.context.annotation.Bean;
3442
import org.springframework.context.annotation.Configuration;
@@ -43,7 +51,6 @@
4351
/**
4452
* @author Omer Celik
4553
*/
46-
4754
public class HeaderTests {
4855

4956
@BeforeAll
@@ -63,10 +70,8 @@ void checkWithEmptyPojo() {
6370

6471
OutputDestination outputDestination = context.getBean(OutputDestination.class);
6572
Message<byte[]> messageReceived = outputDestination.receive(1000, "emptyConfigurationDestination");
66-
MessageHeaders headers = messageReceived.getHeaders();
67-
assertThat(headers).isNotNull();
68-
assertThat(headers.get(MessageUtils.TARGET_PROTOCOL)).isEqualTo("kafka");
69-
assertThat(headers.get(MessageHeaders.CONTENT_TYPE)).isEqualTo("application/json");
73+
74+
checkCommonHeaders(messageReceived.getHeaders());
7075
}
7176
}
7277

@@ -75,20 +80,20 @@ void checkIfHeaderProvidedInData() {
7580
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(
7681
TestChannelBinderConfiguration.getCompleteConfiguration(EmptyConfiguration.class))
7782
.web(WebApplicationType.NONE).run("--spring.jmx.enabled=false")) {
83+
7884
StreamBridge streamBridge = context.getBean(StreamBridge.class);
7985
String jsonPayload = "{\"name\":\"Omer\"}";
8086
streamBridge.send("myBinding-out-0",
8187
MessageBuilder.withPayload(jsonPayload.getBytes())
8288
.setHeader("anyHeader", "anyValue")
8389
.build(),
8490
MimeTypeUtils.APPLICATION_JSON);
91+
8592
OutputDestination output = context.getBean(OutputDestination.class);
8693
Message<byte[]> result = output.receive(1000, "myBinding-out-0");
87-
MessageHeaders headers = result.getHeaders();
88-
assertThat(headers).isNotNull();
89-
assertThat(headers.get(MessageUtils.TARGET_PROTOCOL)).isEqualTo("kafka");
90-
assertThat(headers.get(MessageHeaders.CONTENT_TYPE)).isEqualTo("application/json");
91-
assertThat(headers.get("anyHeader")).isEqualTo("anyValue");
94+
95+
checkCommonHeaders(result.getHeaders());
96+
assertThat(result.getHeaders().get("anyHeader")).isEqualTo("anyValue");
9297
}
9398
}
9499

@@ -99,16 +104,35 @@ void checkGenericMessageSent() {
99104
.web(WebApplicationType.NONE)
100105
.run("--spring.jmx.enabled=false",
101106
"--spring.cloud.function.definition=uppercase")) {
107+
102108
String jsonPayload = "{\"surname\":\"Celik\"}";
103109
InputDestination input = context.getBean(InputDestination.class);
104110
input.send(new GenericMessage<>(jsonPayload.getBytes()), "uppercase-in-0");
111+
105112
OutputDestination output = context.getBean(OutputDestination.class);
113+
Message<byte[]> result = output.receive(1000, "uppercase-out-0");
114+
115+
checkCommonHeaders(result.getHeaders());
116+
}
117+
}
106118

119+
@Test
120+
void checkGenericMessageSentUsingStreamBridge() {
121+
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(
122+
TestChannelBinderConfiguration.getCompleteConfiguration(FunctionUpperCaseConfiguration.class))
123+
.web(WebApplicationType.NONE)
124+
.run("--spring.jmx.enabled=false",
125+
"--spring.cloud.function.definition=uppercase")) {
126+
127+
String jsonPayload = "{\"anyFieldName\":\"anyValue\"}";
128+
final StreamBridge streamBridge = context.getBean(StreamBridge.class);
129+
GenericMessage<String> message = new GenericMessage<>(jsonPayload);
130+
streamBridge.send("uppercase-in-0", message);
131+
132+
OutputDestination output = context.getBean(OutputDestination.class);
107133
Message<byte[]> result = output.receive(1000, "uppercase-out-0");
108-
MessageHeaders headers = result.getHeaders();
109-
assertThat(headers).isNotNull();
110-
assertThat(headers.get(MessageUtils.TARGET_PROTOCOL)).isEqualTo("kafka");
111-
assertThat(headers.get(MessageHeaders.CONTENT_TYPE)).isEqualTo("application/json");
134+
135+
checkCommonHeaders(result.getHeaders());
112136
}
113137
}
114138

@@ -127,11 +151,96 @@ void checkMessageWrappedFunctionalConsumer() {
127151

128152
OutputDestination target = context.getBean(OutputDestination.class);
129153
Message<byte[]> message = target.receive(5, "uppercase-out-0");
130-
MessageHeaders headers = message.getHeaders();
131-
assertThat(headers).isNotNull();
154+
155+
checkCommonHeaders(message.getHeaders());
156+
}
157+
158+
@Test
159+
void checkStringToMapMessageStreamListener() {
160+
ApplicationContext context = new SpringApplicationBuilder(
161+
StringToMapMessageConfiguration.class).web(WebApplicationType.NONE)
162+
.run("--spring.jmx.enabled=false");
163+
InputDestination source = context.getBean(InputDestination.class);
164+
String jsonPayload = "{\"name\":\"Omer\"}";
165+
source.send(new GenericMessage<>(jsonPayload.getBytes()));
166+
OutputDestination target = context.getBean(OutputDestination.class);
167+
Message<byte[]> outputMessage = target.receive();
168+
checkCommonHeaders(outputMessage.getHeaders());
169+
}
170+
171+
@Test
172+
void checkPojoToPojo() {
173+
ApplicationContext context = new SpringApplicationBuilder(
174+
PojoToPojoConfiguration.class).web(WebApplicationType.NONE)
175+
.run("--spring.jmx.enabled=false");
176+
InputDestination source = context.getBean(InputDestination.class);
177+
String jsonPayload = "{\"name\":\"Omer\"}";
178+
source.send(new GenericMessage<>(jsonPayload.getBytes()));
179+
OutputDestination target = context.getBean(OutputDestination.class);
180+
Message<byte[]> outputMessage = target.receive();
181+
checkCommonHeaders(outputMessage.getHeaders());
182+
}
183+
184+
@Test
185+
void checkPojoToString() {
186+
ApplicationContext context = new SpringApplicationBuilder(
187+
PojoToStringConfiguration.class).web(WebApplicationType.NONE)
188+
.run("--spring.jmx.enabled=false");
189+
InputDestination source = context.getBean(InputDestination.class);
190+
OutputDestination target = context.getBean(OutputDestination.class);
191+
String jsonPayload = "{\"name\":\"Neso\"}";
192+
source.send(new GenericMessage<>(jsonPayload.getBytes()));
193+
Message<byte[]> outputMessage = target.receive();
194+
checkCommonHeaders(outputMessage.getHeaders());
195+
}
196+
197+
@Test
198+
void checkPojoToByteArray() {
199+
ApplicationContext context = new SpringApplicationBuilder(
200+
PojoToByteArrayConfiguration.class).web(WebApplicationType.NONE)
201+
.run("--spring.jmx.enabled=false");
202+
InputDestination source = context.getBean(InputDestination.class);
203+
OutputDestination target = context.getBean(OutputDestination.class);
204+
String jsonPayload = "{\"name\":\"Neptune\"}";
205+
source.send(new GenericMessage<>(jsonPayload.getBytes()));
206+
Message<byte[]> outputMessage = target.receive();
207+
checkCommonHeaders(outputMessage.getHeaders());
208+
}
209+
210+
@Test
211+
void checkStringToPojoInboundContentTypeHeader() {
212+
ApplicationContext context = new SpringApplicationBuilder(
213+
StringToPojoConfiguration.class).web(WebApplicationType.NONE)
214+
.run("--spring.jmx.enabled=false");
215+
InputDestination source = context.getBean(InputDestination.class);
216+
OutputDestination target = context.getBean(OutputDestination.class);
217+
String jsonPayload = "{\"name\":\"Mercury\"}";
218+
source.send(new GenericMessage<>(jsonPayload.getBytes(),
219+
new MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE,
220+
MimeTypeUtils.APPLICATION_JSON_VALUE))));
221+
Message<byte[]> outputMessage = target.receive();
222+
checkCommonHeaders(outputMessage.getHeaders());
223+
}
224+
225+
@Test
226+
void checkPojoMessageToStringMessage() {
227+
ApplicationContext context = new SpringApplicationBuilder(
228+
PojoMessageToStringMessageConfiguration.class)
229+
.web(WebApplicationType.NONE).run("--spring.jmx.enabled=false");
230+
InputDestination source = context.getBean(InputDestination.class);
231+
OutputDestination target = context.getBean(OutputDestination.class);
232+
String jsonPayload = "{\"name\":\"Earth\"}";
233+
source.send(new GenericMessage<>(jsonPayload.getBytes()));
234+
Message<byte[]> outputMessage = target.receive();
235+
MessageHeaders headers = outputMessage.getHeaders();
236+
assertThat(BuildInformationProvider.isVersionValid((String) headers.get(BinderHeaders.SCST_VERSION))).isTrue();
237+
}
238+
239+
private void checkCommonHeaders(MessageHeaders headers) {
132240
assertThat(headers).isNotNull();
133-
assertThat(headers.get(MessageHeaders.CONTENT_TYPE)).isEqualTo("application/json");
134241
assertThat(headers.get(MessageUtils.TARGET_PROTOCOL)).isEqualTo("kafka");
242+
assertThat(headers.get(MessageHeaders.CONTENT_TYPE)).isEqualTo("application/json");
243+
assertThat(BuildInformationProvider.isVersionValid((String) headers.get(BinderHeaders.SCST_VERSION))).isTrue();
135244
}
136245

137246
@EnableAutoConfiguration
@@ -156,6 +265,97 @@ public Function<String, String> uppercase() {
156265
}
157266
}
158267

268+
@EnableTestBinder
269+
@EnableAutoConfiguration
270+
public static class StringToMapMessageConfiguration {
271+
@Bean
272+
public Function<Message<Map<?, ?>>, String> echo() {
273+
return value -> {
274+
assertThat(value.getPayload() instanceof Map).isTrue();
275+
return (String) value.getPayload().get("name");
276+
};
277+
}
278+
}
279+
280+
@EnableTestBinder
281+
@EnableAutoConfiguration
282+
public static class PojoToPojoConfiguration {
283+
284+
@Bean
285+
public Function<Planet, Planet> echo() {
286+
return value -> value;
287+
}
288+
}
289+
290+
@EnableTestBinder
291+
@EnableAutoConfiguration
292+
public static class PojoToStringConfiguration {
293+
294+
@Bean
295+
public Function<Planet, String> echo() {
296+
return Planet::toString;
297+
}
298+
}
299+
300+
@EnableTestBinder
301+
@EnableAutoConfiguration
302+
public static class PojoToByteArrayConfiguration {
303+
304+
@Bean
305+
public Function<Planet, byte[]> echo() {
306+
return value -> value.toString().getBytes(StandardCharsets.UTF_8);
307+
}
308+
}
309+
310+
@EnableTestBinder
311+
@EnableAutoConfiguration
312+
public static class StringToPojoConfiguration {
313+
314+
@Bean
315+
public Function<String, Planet> echo(JsonMapper mapper) {
316+
return value -> mapper.fromJson(value, Planet.class);
317+
}
318+
}
319+
320+
@EnableTestBinder
321+
@EnableAutoConfiguration
322+
public static class PojoMessageToStringMessageConfiguration {
323+
324+
@Bean
325+
public Function<Message<Planet>, Message<String>> echo() {
326+
return value -> MessageBuilder.withPayload(value.getPayload().toString())
327+
.setHeader("expected-content-type", MimeTypeUtils.TEXT_PLAIN_VALUE)
328+
.build();
329+
}
330+
}
331+
332+
public static class Planet {
333+
334+
private String name;
335+
336+
Planet() {
337+
this(null);
338+
}
339+
340+
Planet(String name) {
341+
this.name = name;
342+
}
343+
344+
public String getName() {
345+
return this.name;
346+
}
347+
348+
public void setName(String name) {
349+
this.name = name;
350+
}
351+
352+
@Override
353+
public String toString() {
354+
return this.name;
355+
}
356+
357+
}
358+
159359
public static class EmptyPojo {
160360

161361
}

0 commit comments

Comments
 (0)