From bcb2a25a28f3d026b35a795abe18d14f9cdb3022 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Tue, 29 Mar 2022 15:14:42 +0200 Subject: [PATCH] Add support for SimpleEvaluationContext to RoutingFunction --- .../context/config/RoutingFunction.java | 32 +++++++------------ .../context/config/RoutingFunctionTests.java | 22 +++++++++++++ 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java index a77c7f3ee..99d4509e1 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java @@ -33,6 +33,8 @@ import org.springframework.context.expression.MapAccessor; import org.springframework.expression.BeanResolver; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.DataBindingPropertyAccessor; +import org.springframework.expression.spel.support.SimpleEvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.messaging.Message; import org.springframework.util.Assert; @@ -59,6 +61,9 @@ public class RoutingFunction implements Function { private final StandardEvaluationContext evalContext = new StandardEvaluationContext(); + private final SimpleEvaluationContext headerEvalContext = SimpleEvaluationContext + .forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess()).build(); + private final SpelExpressionParser spelParser = new SpelExpressionParser(); private final FunctionCatalog functionCatalog; @@ -123,13 +128,13 @@ public class RoutingFunction implements Function { } } else if (StringUtils.hasText((String) message.getHeaders().get("spring.cloud.function.routing-expression"))) { - function = this.functionFromExpression((String) message.getHeaders().get("spring.cloud.function.routing-expression"), message); + function = this.functionFromExpression((String) message.getHeaders().get("spring.cloud.function.routing-expression"), message, true); if (function.isInputTypePublisher()) { this.assertOriginalInputIsNotPublisher(originalInputIsPublisher); } } else if (StringUtils.hasText(functionProperties.getRoutingExpression())) { - function = this.functionFromExpression(functionProperties.getRoutingExpression(), message); + function = this.functionFromExpression(functionProperties.getRoutingExpression(), message, false); } else if (StringUtils.hasText(functionProperties.getDefinition())) { function = this.functionFromDefinition(functionProperties.getDefinition()); @@ -147,7 +152,7 @@ public class RoutingFunction implements Function { function = functionFromDefinition(functionProperties.getDefinition()); } else if (StringUtils.hasText(functionProperties.getRoutingExpression())) { - function = this.functionFromExpression(functionProperties.getRoutingExpression(), input); + function = this.functionFromExpression(functionProperties.getRoutingExpression(), input, false); } else { return input instanceof Mono @@ -159,7 +164,7 @@ public class RoutingFunction implements Function { else { this.assertOriginalInputIsNotPublisher(originalInputIsPublisher); if (StringUtils.hasText(functionProperties.getRoutingExpression())) { - function = this.functionFromExpression(functionProperties.getRoutingExpression(), input); + function = this.functionFromExpression(functionProperties.getRoutingExpression(), input, false); } else if (StringUtils.hasText(functionProperties.getDefinition())) { @@ -181,20 +186,6 @@ public class RoutingFunction implements Function { + "spring.cloud.function.routing-expression' as application properties."); } -// private FunctionInvocationWrapper functionFromCallback(Object input) { -// if (input instanceof Message) { -// Object routingResult = this.routingCallback.functionDefinition((Message) input); -// if (routingResult != null && routingResult instanceof String) { -// -// } -// if (StringUtils.hasText(functionDefinition)) { -// return this.functionFromDefinition(functionDefinition); -// } -// } -// logger.info("Unable to determine route-to function from the provided MessageRoutingCallback"); -// return null; -// } - private FunctionInvocationWrapper functionFromDefinition(String definition) { FunctionInvocationWrapper function = functionCatalog.lookup(definition); Assert.notNull(function, "Failed to lookup function to route based on the value of 'spring.cloud.function.definition' property '" @@ -205,9 +196,10 @@ public class RoutingFunction implements Function { return function; } - private FunctionInvocationWrapper functionFromExpression(String routingExpression, Object input) { + private FunctionInvocationWrapper functionFromExpression(String routingExpression, Object input, boolean isViaHeader) { Expression expression = spelParser.parseExpression(routingExpression); - String functionName = expression.getValue(this.evalContext, input, String.class); +// String functionName = expression.getValue(this.evalContext, input, String.class); + String functionName = isViaHeader ? expression.getValue(this.headerEvalContext, input, String.class) : expression.getValue(this.evalContext, input, String.class); Assert.hasText(functionName, "Failed to resolve function name based on routing expression '" + functionProperties.getRoutingExpression() + "'"); FunctionInvocationWrapper function = functionCatalog.lookup(functionName); Assert.notNull(function, "Failed to lookup function to route to based on the expression '" diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/RoutingFunctionTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/RoutingFunctionTests.java index cb9267b91..b03edc3ec 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/RoutingFunctionTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/RoutingFunctionTests.java @@ -35,6 +35,7 @@ import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; /** * @@ -183,6 +184,27 @@ public class RoutingFunctionTests { assertThat(function.apply(message)).isEqualTo("OLLEH"); } + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Test + public void failWithHeaderProvidedExpressionAccessingRuntime() { + FunctionCatalog functionCatalog = this.configureCatalog(); + Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); + assertThat(function).isNotNull(); + Message message = MessageBuilder.withPayload("hello") + .setHeader(FunctionProperties.PREFIX + ".routing-expression", + "T(java.lang.Runtime).getRuntime().exec(\"open -a calculator.app\")") + .build(); + try { + function.apply(message); + fail("Function shoudl not succeed"); + } + catch (Exception e) { + assertThat(e.getMessage()).isEqualTo("EL1005E: Type cannot be found 'java.lang.Runtime'"); + } + + } + + @EnableAutoConfiguration @Configuration protected static class RoutingFunctionConfiguration {