diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/support/MessagingMethodInvokerHelper.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/support/MessagingMethodInvokerHelper.java index df40a24f964..94616b3b783 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/handler/support/MessagingMethodInvokerHelper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/support/MessagingMethodInvokerHelper.java @@ -576,6 +576,7 @@ private void configureLocalMessageHandlerFactory() { localHandlerMethodFactory.afterPropertiesSet(); } + @Nullable private Object invokeHandlerMethod(HandlerMethod handlerMethod, ParametersWrapper parameters) { try { return handlerMethod.invoke(parameters); @@ -1093,13 +1094,20 @@ void setInvocableHandlerMethod(InvocableHandlerMethod newInvocableHandlerMethod) this.invocableHandlerMethod = newInvocableHandlerMethod; } + @Nullable public Object invoke(ParametersWrapper parameters) { Message message = parameters.getMessage(); if (this.canProcessMessageList) { message = new MutableMessage<>(parameters.getMessages(), parameters.getHeaders()); } try { - return this.invocableHandlerMethod.invoke(message); + Object result = this.invocableHandlerMethod.invoke(message); + if (result != null + && org.springframework.integration.util.ClassUtils.isKotlinUnit(result.getClass())) { + + result = null; + } + return result; } catch (RuntimeException ex) { // NOSONAR no way to handle conditional catch according Sonar rules throw ex; diff --git a/spring-integration-core/src/main/java/org/springframework/integration/util/ClassUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/util/ClassUtils.java index 8ac0609ab12..85dda3b0671 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/util/ClassUtils.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/util/ClassUtils.java @@ -83,6 +83,11 @@ public abstract class ClassUtils { */ public static final Class KOTLIN_FUNCTION_1_CLASS; + /** + * The {@code kotlin.Unit} class object. + */ + public static final Class KOTLIN_UNIT_CLASS; + static { PRIMITIVE_WRAPPER_TYPE_MAP.put(Boolean.class, boolean.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Byte.class, byte.class); @@ -161,6 +166,18 @@ public abstract class ClassUtils { finally { KOTLIN_FUNCTION_1_CLASS = kotlinClass; } + + kotlinClass = null; + try { + kotlinClass = org.springframework.util.ClassUtils.forName("kotlin.Unit", + org.springframework.util.ClassUtils.getDefaultClassLoader()); + } + catch (ClassNotFoundException e) { + //Ignore: assume no Kotlin in classpath + } + finally { + KOTLIN_UNIT_CLASS = kotlinClass; + } } public static Class findClosestMatch(Class type, Set> candidates, boolean failOnTie) { @@ -247,4 +264,14 @@ public static boolean isKotlinFaction1(Class aClass) { return KOTLIN_FUNCTION_1_CLASS != null && KOTLIN_FUNCTION_1_CLASS.isAssignableFrom(aClass); } + /** + * Check if class is {@code kotlin.Unit}. + * @param aClass the {@link Class} to check. + * @return true if class is a {@code kotlin.Unit} implementation. + * @since 5.3.2 + */ + public static boolean isKotlinUnit(Class aClass) { + return KOTLIN_UNIT_CLASS != null && KOTLIN_UNIT_CLASS.isAssignableFrom(aClass); + } + } diff --git a/spring-integration-core/src/test/kotlin/org/springframework/integration/dsl/KotlinDslTests.kt b/spring-integration-core/src/test/kotlin/org/springframework/integration/dsl/KotlinDslTests.kt index 91b1ed10c67..3bf29a29e2f 100644 --- a/spring-integration-core/src/test/kotlin/org/springframework/integration/dsl/KotlinDslTests.kt +++ b/spring-integration-core/src/test/kotlin/org/springframework/integration/dsl/KotlinDslTests.kt @@ -17,12 +17,9 @@ package org.springframework.integration.dsl import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.isGreaterThanOrEqualTo -import assertk.assertions.isInstanceOf -import assertk.assertions.isNotNull -import assertk.assertions.isTrue -import assertk.assertions.size +import assertk.assertions.* +import org.apache.commons.logging.Log +import org.apache.commons.logging.LogFactory import org.junit.jupiter.api.Test import org.springframework.beans.factory.BeanFactory import org.springframework.beans.factory.annotation.Autowired @@ -51,6 +48,7 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig import reactor.core.publisher.Flux import reactor.test.StepVerifier import java.util.* +import java.util.concurrent.atomic.AtomicReference import java.util.function.Function /** @@ -210,6 +208,23 @@ class KotlinDslTests { assertThat(payload).isInstanceOf(List::class.java).size().isGreaterThanOrEqualTo(1) } + @Test + fun `no reply from handle`() { + val payloadReference = AtomicReference() + val integrationFlow = + integrationFlow("handlerInputChanenl") { + handle { payload, _ -> payloadReference.set(payload) } + } + + val registration = this.integrationFlowContext.registration(integrationFlow).register() + + registration.inputChannel.send(GenericMessage("test")) + + assertThat(payloadReference.get()).isEqualTo("test") + + registration.destroy() + } + @Configuration @EnableIntegration class Config {