diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/LambdaMessageProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/LambdaMessageProcessor.java index 8fa40651fb1..ff74f83dba4 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/handler/LambdaMessageProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/LambdaMessageProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.lang.reflect.Modifier; import java.util.Map; import java.util.Set; +import java.util.function.Function; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -31,8 +32,11 @@ import org.springframework.core.MethodIntrospector; import org.springframework.core.log.LogMessage; import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.core.GenericSelector; +import org.springframework.integration.transformer.GenericTransformer; import org.springframework.lang.Nullable; import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.converter.MessageConverter; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -85,11 +89,20 @@ public LambdaMessageProcessor(Object target, @Nullable Class expectedType) { "classes with single method - functional interface implementations."); this.method = methods.iterator().next(); - ReflectionUtils.makeAccessible(this.method); + if (!isExplicit(target)) { + ReflectionUtils.makeAccessible(this.method); + } this.parameterTypes = this.method.getParameterTypes(); this.expectedType = expectedType; } + private static boolean isExplicit(Object target) { + return target instanceof Function || + target instanceof GenericHandler || + target instanceof GenericSelector || + target instanceof GenericTransformer; + } + @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.messageConverter = @@ -102,20 +115,22 @@ public Object processMessage(Message message) { Object[] args = buildArgs(message); try { - Object result = this.method.invoke(this.target, args); + Object result = invokeMethod(args); if (result != null && org.springframework.integration.util.ClassUtils.isKotlinUnit(result.getClass())) { result = null; } return result; } + catch (RuntimeException ex) { + if (ex instanceof ClassCastException classCastException) { + logClassCastException(classCastException); + } + throw ex; + } catch (InvocationTargetException e) { final Throwable cause = e.getCause(); - if (e.getTargetException() instanceof ClassCastException) { - LOGGER.error("Could not invoke the method '" + this.method + "' due to a class cast exception, " + - "if using a lambda in the DSL, consider using an overloaded EIP method " + - "that takes a Class argument to explicitly specify the type. " + - "An example of when this often occurs is if the lambda is configured to " + - "receive a Message argument.", cause); + if (e.getTargetException() instanceof ClassCastException classCastException) { + logClassCastException(classCastException); } if (cause instanceof RuntimeException) { // NOSONAR throw (RuntimeException) cause; @@ -170,4 +185,41 @@ else if (Map.class.isAssignableFrom(parameterType)) { return args; } + @SuppressWarnings({ "unchecked", "rawtypes" }) + private Object invokeMethod(Object[] args) throws InvocationTargetException, IllegalAccessException { + if (this.target instanceof Function function) { + return function.apply(args[0]); + } + else if (this.target instanceof GenericSelector selector) { + return selector.accept(args[0]); + } + else if (this.target instanceof GenericTransformer transformer) { + return transformer.transform(args[0]); + } + else if (this.target instanceof GenericHandler handler) { + return handler.handle(args[0], (MessageHeaders) args[1]); + } + else { + return this.method.invoke(this.target, args); + } + + /* TODO when preview features are available in the next Java version + return switch (this.target) { + case Function function -> function.apply(args[0]); + case GenericSelector selector -> selector.accept(args[0]); + case GenericTransformer transformer -> transformer.transform(args[0]); + case GenericHandler handler -> handler.handle(args[0], (MessageHeaders) args[1]); + default -> this.method.invoke(this.target, args); + }; +*/ + } + + private void logClassCastException(ClassCastException classCastException) { + LOGGER.error("Could not invoke the method '" + this.method + "' due to a class cast exception, " + + "if using a lambda in the DSL, consider using an overloaded EIP method " + + "that takes a Class argument to explicitly specify the type. " + + "An example of when this often occurs is if the lambda is configured to " + + "receive a Message argument.", classCastException); + } + } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/dsl/LambdaMessageProcessorTests.java b/spring-integration-core/src/test/java/org/springframework/integration/dsl/LambdaMessageProcessorTests.java index 5fb2a6e4606..55d4783255b 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/dsl/LambdaMessageProcessorTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/dsl/LambdaMessageProcessorTests.java @@ -23,7 +23,6 @@ import java.util.Objects; import java.util.function.Function; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanFactory; @@ -107,7 +106,6 @@ public boolean accept(Date payload) { @Test - @Disabled("Until https://github.com/spring-projects/spring-integration/issues/3660") public void testCustomConverter() { LambdaMessageProcessor lmp = new LambdaMessageProcessor(Function.identity(), TestPojo.class); lmp.setBeanFactory(this.beanFactory);