GH-620 Add BeanResolver support for RoutingFunction

Resolves #620
This commit is contained in:
Oleg Zhurakousky
2021-01-22 14:36:34 +01:00
parent 1d625077e8
commit 039d6e94ee
3 changed files with 39 additions and 4 deletions

View File

@@ -1,5 +1,9 @@
/* /*
<<<<<<< HEAD
* Copyright 2016-2019 the original author or authors. * Copyright 2016-2019 the original author or authors.
=======
* Copyright 2016-2021 the original author or authors.
>>>>>>> 3a3fd4a6... GH-620 Add BeanResolver support for RoutingFunction
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -27,6 +31,7 @@ import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson; import com.google.gson.Gson;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -45,6 +50,7 @@ import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.FilterType;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.support.ConfigurableConversionService; import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.messaging.converter.ByteArrayMessageConverter; import org.springframework.messaging.converter.ByteArrayMessageConverter;
@@ -119,8 +125,10 @@ public class ContextFunctionCatalogAutoConfiguration {
} }
@Bean(RoutingFunction.FUNCTION_NAME) @Bean(RoutingFunction.FUNCTION_NAME)
RoutingFunction functionRouter(FunctionCatalog functionCatalog, FunctionInspector functionInspector, FunctionProperties functionProperties) { RoutingFunction functionRouter(FunctionCatalog functionCatalog, FunctionInspector functionInspector,
return new RoutingFunction(functionCatalog, functionInspector, functionProperties); FunctionProperties functionProperties, BeanFactory beanFactory) {
return new RoutingFunction(functionCatalog, functionProperties, functionInspector, new BeanFactoryResolver(beanFactory));
} }
private boolean isConverterEligible(Object messageConverter) { private boolean isConverterEligible(Object messageConverter) {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2019 the original author or authors. * Copyright 2019-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@ import org.springframework.cloud.function.context.FunctionProperties;
import org.springframework.cloud.function.context.catalog.FunctionInspector; import org.springframework.cloud.function.context.catalog.FunctionInspector;
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
import org.springframework.context.expression.MapAccessor; import org.springframework.context.expression.MapAccessor;
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.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext;
@@ -65,11 +66,20 @@ public class RoutingFunction implements Function<Object, Object> {
private final FunctionInspector functionInspector; private final FunctionInspector functionInspector;
public RoutingFunction(FunctionCatalog functionCatalog, FunctionInspector functionInspector, FunctionProperties functionProperties) { public RoutingFunction(FunctionCatalog functionCatalog, FunctionInspector functionInspector,
FunctionProperties functionProperties) {
this(functionCatalog, functionProperties, functionInspector, null);
}
public RoutingFunction(FunctionCatalog functionCatalog, FunctionProperties functionProperties,
FunctionInspector functionInspector, BeanResolver beanResolver) {
this.functionCatalog = functionCatalog; this.functionCatalog = functionCatalog;
this.functionProperties = functionProperties; this.functionProperties = functionProperties;
this.functionInspector = functionInspector; this.functionInspector = functionInspector;
this.evalContext.addPropertyAccessor(new MapAccessor()); this.evalContext.addPropertyAccessor(new MapAccessor());
this.evalContext.setBeanResolver(beanResolver);
evalContext.setBeanResolver(beanResolver);
} }
@Override @Override
@@ -170,6 +180,7 @@ public class RoutingFunction implements Function<Object, Object> {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private Function functionFromExpression(String routingExpression, Object input) { private Function functionFromExpression(String routingExpression, Object input) {
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);
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() + "'");
Function function = functionCatalog.lookup(functionName); Function function = functionCatalog.lookup(functionName);

View File

@@ -127,6 +127,17 @@ public class RoutingFunctionTests {
assertThat(function.apply(message)).isEqualTo("olleh"); assertThat(function.apply(message)).isEqualTo("olleh");
} }
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testInvocationWithRoutingBeanExpression() {
System.setProperty(FunctionProperties.PREFIX + ".routing-expression", "@reverse.apply(#root.getHeaders().get('func'))");
FunctionCatalog functionCatalog = this.configureCatalog();
Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME);
assertThat(function).isNotNull();
Message<String> message = MessageBuilder.withPayload("hello").setHeader("func", "esacreppu").build();
assertThat(function.apply(message)).isEqualTo("HELLO");
}
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
@Test @Test
public void testOtherExpectedFailures() { public void testOtherExpectedFailures() {
@@ -169,6 +180,11 @@ public class RoutingFunctionTests {
@Configuration @Configuration
protected static class RoutingFunctionConfiguration { protected static class RoutingFunctionConfiguration {
@Bean
public String functionName() {
return "reverse";
}
@Bean @Bean
public Function<String, String> reverse() { public Function<String, String> reverse() {
return v -> new StringBuilder(v).reverse().toString(); return v -> new StringBuilder(v).reverse().toString();