Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
Expand Down Expand Up @@ -171,6 +172,10 @@ else if (this.isFunctionPojo(functionCandidate, functionName)) {
Method functionalMethod = FunctionTypeUtils.discoverFunctionalMethod(functionCandidate.getClass());
functionCandidate = this.proxyTarget(functionCandidate, functionalMethod);
functionType = FunctionTypeUtils.fromFunctionMethod(functionalMethod);
// GH-1307: Mark this as a POJO function for special handling
functionRegistration = new FunctionRegistration(functionCandidate, functionName)
.type(functionType)
.properties(Collections.singletonMap("isPojoFunction", "true"));
}
else if (this.isSpecialFunctionRegistration(functionNames, functionName)) {
functionRegistration = this.applicationContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,17 @@ private FunctionInvocationWrapper findFunctionInFunctionRegistrations(String fun
// ignore
}
}
// GH-1307: Mark POJO functions for special Message wrapping behavior
if (functionRegistration != null &&
functionRegistration.getProperties().containsKey("isPojoFunction")) {
try {
String isPojoValue = functionRegistration.getProperties().get("isPojoFunction");
function.setPojoFunction(Boolean.parseBoolean(isPojoValue));
}
catch (Exception e) {
// ignore
}
}
return function;
}

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

private boolean wrappedBiConsumer;

private boolean isPojoFunction;

FunctionInvocationWrapper(String functionDefinition, Object target, Type inputType, Type outputType) {
if (target instanceof PostProcessingFunction) {
this.postProcessor = (PostProcessingFunction) target;
Expand Down Expand Up @@ -489,6 +502,14 @@ public void setWrappedBiConsumer(boolean wrappedBiConsumer) {
this.wrappedBiConsumer = wrappedBiConsumer;
}

public void setPojoFunction(boolean isPojoFunction) {
this.isPojoFunction = isPojoFunction;
}

public boolean isPojoFunction() {
return this.isPojoFunction;
}

public boolean isSkipOutputConversion() {
return skipOutputConversion;
}
Expand Down Expand Up @@ -1245,6 +1266,14 @@ else if (isExtractPayload((Message<?>) convertedOutput, type)) {
}

if (ObjectUtils.isEmpty(contentType)) {
// GH-1307: For POJO functions, wrap output in Message to maintain
// consistency with regular functions
if (this.isPojoFunction && output instanceof Message
&& !(convertedOutput instanceof Message)) {
convertedOutput = MessageBuilder.withPayload(convertedOutput)
.copyHeaders(((Message) output).getHeaders())
.build();
}
return convertedOutput;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,10 @@ public void testWithPojoFunction() {
Function<Integer, String> f2conversion = catalog.lookup("myFunctionLike");
assertThat(f2conversion.apply(123)).isEqualTo("123");

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

Function<Flux<String>, Flux<String>> f3 = catalog.lookup("myFunctionLike");
assertThat(f3.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO");
Expand All @@ -100,6 +102,52 @@ public void testWithPojoFunctionComposition() {
assertThat(f1.apply("foo")).isEqualTo("FOO");
}

/**
* GH-1307: POJO function should return Message consistently with regular functions
* when no contentType is specified.
*/
@Test
public void testPojoFunctionReturnsMessageWithoutContentType() {
FunctionCatalog catalog = this.configureCatalog();

// Test POJO function without contentType
Function<Message<String>, Object> pojoFunction = catalog.lookup("myFunctionLike");
Message<String> input = MessageBuilder.withPayload("test")
.setHeader("correlationId", "123")
.build();

Object result = pojoFunction.apply(input);

// GH-1307: Verify POJO functions return Message for consistency
assertThat(result)
.as("POJO function should return Message, not plain value when input is Message")
.isInstanceOf(Message.class);

Message<?> messageResult = (Message<?>) result;
assertThat(messageResult.getPayload()).isEqualTo("TEST");
assertThat(messageResult.getHeaders().get("correlationId"))
.as("Headers should be preserved")
.isEqualTo("123");
}

/**
* GH-1307: POJO function should NOT wrap output when input is plain String
*/
@Test
public void testPojoFunctionDoesNotWrapPlainStringInput() {
FunctionCatalog catalog = this.configureCatalog();

// GH-1307: POJO function with plain String input should return plain String
Function<String, Object> pojoFunction = catalog.lookup("myFunctionLike");
Object result = pojoFunction.apply("plainInput");

// Should return String, not Message
assertThat(result)
.as("POJO function should return plain String when input is plain String, not wrap in Message")
.isInstanceOf(String.class)
.isEqualTo("PLAININPUT");
}


@EnableAutoConfiguration
@Configuration(proxyBeanMethods = false)
Expand Down