Skip to content

Commit ccec6f2

Browse files
committed
Fix POJO functions to return Message consistently
POJO functions now return Message when input is Message, maintaining consistency with regular Function implementations. Fixes gh-1307
1 parent 9fe4ede commit ccec6f2

File tree

3 files changed

+66
-2
lines changed

3 files changed

+66
-2
lines changed

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.lang.reflect.Type;
2222
import java.util.Arrays;
2323
import java.util.Collection;
24+
import java.util.Collections;
2425
import java.util.Set;
2526
import java.util.function.BiConsumer;
2627
import java.util.function.BiFunction;
@@ -171,6 +172,10 @@ else if (this.isFunctionPojo(functionCandidate, functionName)) {
171172
Method functionalMethod = FunctionTypeUtils.discoverFunctionalMethod(functionCandidate.getClass());
172173
functionCandidate = this.proxyTarget(functionCandidate, functionalMethod);
173174
functionType = FunctionTypeUtils.fromFunctionMethod(functionalMethod);
175+
// GH-1307: Mark this as a POJO function for special handling
176+
functionRegistration = new FunctionRegistration(functionCandidate, functionName)
177+
.type(functionType)
178+
.properties(Collections.singletonMap("isPojoFunction", "true"));
174179
}
175180
else if (this.isSpecialFunctionRegistration(functionNames, functionName)) {
176181
functionRegistration = this.applicationContext

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,17 @@ private FunctionInvocationWrapper findFunctionInFunctionRegistrations(String fun
287287
// ignore
288288
}
289289
}
290+
// GH-1307: Mark POJO functions for special Message wrapping behavior
291+
if (functionRegistration != null &&
292+
functionRegistration.getProperties().containsKey("isPojoFunction")) {
293+
try {
294+
String isPojoValue = functionRegistration.getProperties().get("isPojoFunction");
295+
function.setPojoFunction(Boolean.parseBoolean(isPojoValue));
296+
}
297+
catch (Exception e) {
298+
// ignore
299+
}
300+
}
290301
return function;
291302
}
292303

@@ -439,6 +450,8 @@ public class FunctionInvocationWrapper implements Function<Object, Object>, Cons
439450

440451
private boolean wrappedBiConsumer;
441452

453+
private boolean isPojoFunction;
454+
442455
FunctionInvocationWrapper(String functionDefinition, Object target, Type inputType, Type outputType) {
443456
if (target instanceof PostProcessingFunction) {
444457
this.postProcessor = (PostProcessingFunction) target;
@@ -489,6 +502,14 @@ public void setWrappedBiConsumer(boolean wrappedBiConsumer) {
489502
this.wrappedBiConsumer = wrappedBiConsumer;
490503
}
491504

505+
public void setPojoFunction(boolean isPojoFunction) {
506+
this.isPojoFunction = isPojoFunction;
507+
}
508+
509+
public boolean isPojoFunction() {
510+
return this.isPojoFunction;
511+
}
512+
492513
public boolean isSkipOutputConversion() {
493514
return skipOutputConversion;
494515
}
@@ -1245,6 +1266,14 @@ else if (isExtractPayload((Message<?>) convertedOutput, type)) {
12451266
}
12461267

12471268
if (ObjectUtils.isEmpty(contentType)) {
1269+
// GH-1307: For POJO functions, wrap output in Message to maintain
1270+
// consistency with regular functions
1271+
if (this.isPojoFunction && output instanceof Message
1272+
&& !(convertedOutput instanceof Message)) {
1273+
convertedOutput = MessageBuilder.withPayload(convertedOutput)
1274+
.copyHeaders(((Message) output).getHeaders())
1275+
.build();
1276+
}
12481277
return convertedOutput;
12491278
}
12501279

spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwarePojoFunctionRegistryTests.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,10 @@ public void testWithPojoFunction() {
8383
Function<Integer, String> f2conversion = catalog.lookup("myFunctionLike");
8484
assertThat(f2conversion.apply(123)).isEqualTo("123");
8585

86-
Function<Message<String>, String> f2message = catalog.lookup("myFunctionLike");
87-
assertThat(f2message.apply(MessageBuilder.withPayload("message").build())).isEqualTo("MESSAGE");
86+
// GH-1307: POJO functions now return Message for consistency
87+
Function<Message<String>, Message<?>> f2message = catalog.lookup("myFunctionLike");
88+
Message<?> messageResult = f2message.apply(MessageBuilder.withPayload("message").build());
89+
assertThat(messageResult.getPayload()).isEqualTo("MESSAGE");
8890

8991
Function<Flux<String>, Flux<String>> f3 = catalog.lookup("myFunctionLike");
9092
assertThat(f3.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO");
@@ -100,6 +102,34 @@ public void testWithPojoFunctionComposition() {
100102
assertThat(f1.apply("foo")).isEqualTo("FOO");
101103
}
102104

105+
/**
106+
* GH-1307: POJO function should return Message consistently with regular functions
107+
* when no contentType is specified.
108+
*/
109+
@Test
110+
public void testPojoFunctionReturnsMessageWithoutContentType() {
111+
FunctionCatalog catalog = this.configureCatalog();
112+
113+
// Test POJO function without contentType
114+
Function<Message<String>, Object> pojoFunction = catalog.lookup("myFunctionLike");
115+
Message<String> input = MessageBuilder.withPayload("test")
116+
.setHeader("correlationId", "123")
117+
.build();
118+
119+
Object result = pojoFunction.apply(input);
120+
121+
// GH-1307: Verify POJO functions return Message for consistency
122+
assertThat(result)
123+
.as("POJO function should return Message, not plain value when input is Message")
124+
.isInstanceOf(Message.class);
125+
126+
Message<?> messageResult = (Message<?>) result;
127+
assertThat(messageResult.getPayload()).isEqualTo("TEST");
128+
assertThat(messageResult.getHeaders().get("correlationId"))
129+
.as("Headers should be preserved")
130+
.isEqualTo("123");
131+
}
132+
103133

104134
@EnableAutoConfiguration
105135
@Configuration(proxyBeanMethods = false)

0 commit comments

Comments
 (0)