Add support for SimpleEvaluationContext to RoutingFunction

This commit is contained in:
Oleg Zhurakousky
2022-03-29 15:14:42 +02:00
parent bf122213e4
commit bcb2a25a28
2 changed files with 34 additions and 20 deletions

View File

@@ -33,6 +33,8 @@ import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.BeanResolver; import org.springframework.expression.BeanResolver;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser; 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.expression.spel.support.StandardEvaluationContext;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@@ -59,6 +61,9 @@ public class RoutingFunction implements Function<Object, Object> {
private final StandardEvaluationContext evalContext = new StandardEvaluationContext(); private final StandardEvaluationContext evalContext = new StandardEvaluationContext();
private final SimpleEvaluationContext headerEvalContext = SimpleEvaluationContext
.forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess()).build();
private final SpelExpressionParser spelParser = new SpelExpressionParser(); private final SpelExpressionParser spelParser = new SpelExpressionParser();
private final FunctionCatalog functionCatalog; private final FunctionCatalog functionCatalog;
@@ -123,13 +128,13 @@ public class RoutingFunction implements Function<Object, Object> {
} }
} }
else if (StringUtils.hasText((String) message.getHeaders().get("spring.cloud.function.routing-expression"))) { 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()) { if (function.isInputTypePublisher()) {
this.assertOriginalInputIsNotPublisher(originalInputIsPublisher); this.assertOriginalInputIsNotPublisher(originalInputIsPublisher);
} }
} }
else if (StringUtils.hasText(functionProperties.getRoutingExpression())) { 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())) { else if (StringUtils.hasText(functionProperties.getDefinition())) {
function = this.functionFromDefinition(functionProperties.getDefinition()); function = this.functionFromDefinition(functionProperties.getDefinition());
@@ -147,7 +152,7 @@ public class RoutingFunction implements Function<Object, Object> {
function = functionFromDefinition(functionProperties.getDefinition()); function = functionFromDefinition(functionProperties.getDefinition());
} }
else if (StringUtils.hasText(functionProperties.getRoutingExpression())) { else if (StringUtils.hasText(functionProperties.getRoutingExpression())) {
function = this.functionFromExpression(functionProperties.getRoutingExpression(), input); function = this.functionFromExpression(functionProperties.getRoutingExpression(), input, false);
} }
else { else {
return input instanceof Mono return input instanceof Mono
@@ -159,7 +164,7 @@ public class RoutingFunction implements Function<Object, Object> {
else { else {
this.assertOriginalInputIsNotPublisher(originalInputIsPublisher); this.assertOriginalInputIsNotPublisher(originalInputIsPublisher);
if (StringUtils.hasText(functionProperties.getRoutingExpression())) { if (StringUtils.hasText(functionProperties.getRoutingExpression())) {
function = this.functionFromExpression(functionProperties.getRoutingExpression(), input); function = this.functionFromExpression(functionProperties.getRoutingExpression(), input, false);
} }
else else
if (StringUtils.hasText(functionProperties.getDefinition())) { if (StringUtils.hasText(functionProperties.getDefinition())) {
@@ -181,20 +186,6 @@ public class RoutingFunction implements Function<Object, Object> {
+ "spring.cloud.function.routing-expression' as application properties."); + "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) { private FunctionInvocationWrapper functionFromDefinition(String definition) {
FunctionInvocationWrapper function = functionCatalog.lookup(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 '" 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<Object, Object> {
return function; return function;
} }
private FunctionInvocationWrapper functionFromExpression(String routingExpression, Object input) { private FunctionInvocationWrapper functionFromExpression(String routingExpression, Object input, boolean isViaHeader) {
Expression expression = spelParser.parseExpression(routingExpression); 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() + "'"); Assert.hasText(functionName, "Failed to resolve function name based on routing expression '" + functionProperties.getRoutingExpression() + "'");
FunctionInvocationWrapper function = functionCatalog.lookup(functionName); FunctionInvocationWrapper function = functionCatalog.lookup(functionName);
Assert.notNull(function, "Failed to lookup function to route to based on the expression '" Assert.notNull(function, "Failed to lookup function to route to based on the expression '"

View File

@@ -35,6 +35,7 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder; import org.springframework.messaging.support.MessageBuilder;
import static org.assertj.core.api.Assertions.assertThat; 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"); 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<String> 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 @EnableAutoConfiguration
@Configuration @Configuration
protected static class RoutingFunctionConfiguration { protected static class RoutingFunctionConfiguration {