GH-408 Enhance RoutingFunction with SpEL and application properties

- Added initial support for communicating routing instructions via SpEL thru both message headers and application properties
- Added support for communication function definition via application properties
- Added additional tests and updated documentation

Resolves #408
This commit is contained in:
Oleg Zhurakousky
2019-09-04 18:30:12 +02:00
parent 11ac6cd679
commit 2aed5abff8
11 changed files with 487 additions and 133 deletions

View File

@@ -67,7 +67,7 @@ class FunctionArchiveDeployer extends JarLauncher {
}
@SuppressWarnings({ "unchecked", "rawtypes" })
void deploy(FunctionRegistry functionRegistry, FunctionProperties functionProperties, String[] args) {
void deploy(FunctionRegistry functionRegistry, FunctionDeployerProperties functionProperties, String[] args) {
ClassLoader currentLoader = Thread.currentThread().getContextClassLoader();
try {
@@ -165,7 +165,7 @@ class FunctionArchiveDeployer extends JarLauncher {
|| name.startsWith("reactor.");
}
private String discoverFunctionClassName(FunctionProperties functionProperties) {
private String discoverFunctionClassName(FunctionDeployerProperties functionProperties) {
try {
return StringUtils.hasText(functionProperties.getFunctionClass())
? functionProperties.getFunctionClass()

View File

@@ -33,11 +33,16 @@ import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.loader.archive.Archive;
import org.springframework.boot.loader.archive.ExplodedArchive;
import org.springframework.boot.loader.archive.JarFileArchive;
import org.springframework.cloud.function.context.FunctionProperties;
import org.springframework.cloud.function.context.FunctionRegistry;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.util.StringUtils;
/**
*
@@ -52,13 +57,13 @@ import org.springframework.core.env.ConfigurableEnvironment;
*
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(FunctionProperties.class)
@EnableConfigurationProperties(FunctionDeployerProperties.class)
public class FunctionDeployerConfiguration {
private static Log logger = LogFactory.getLog(FunctionDeployerConfiguration.class);
@Bean
SmartLifecycle functionArchiveDeployer(FunctionProperties functionProperties,
SmartLifecycle functionArchiveDeployer(FunctionDeployerProperties functionProperties,
FunctionRegistry functionRegistry, ApplicationArguments arguments) {
ApplicationArguments updatedArguments = this.updateArguments(arguments);
@@ -123,14 +128,14 @@ public class FunctionDeployerConfiguration {
}
/*
* We need to update actual arguments to ensure that when we may be passing to the deployed archive has the right properties.
* For the current application FunctionProperties already set as a result of EnvironmentPostProcessor
* We need to update the actual arguments with non-legacy properties before passing these arguments to the deployable archive.
* For the current application FunctionProperties already updated and set as a result of EnvironmentPostProcessor
*/
private ApplicationArguments updateArguments(ApplicationArguments arguments) {
List<String> originalArguments = new ArrayList<String>(Arrays.asList(arguments.getSourceArgs()));
if (arguments.containsOption("function.name")) {
originalArguments.add(FunctionProperties.PREFIX + ".function-name=" + arguments.getOptionValues("function.name").get(0));
originalArguments.add(FunctionProperties.PREFIX + ".definition=" + arguments.getOptionValues("function.name").get(0));
}
if (arguments.containsOption("function.location")) {
originalArguments.add(FunctionProperties.PREFIX + ".location=" + arguments.getOptionValues("function.location").get(0));
@@ -147,11 +152,16 @@ public class FunctionDeployerConfiguration {
static class LegacyPropertyEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
if (environment.containsProperty("function.name")) {
System.setProperty(FunctionProperties.PREFIX + ".function-name", environment.getProperty("function.name"));
}
if (environment.containsProperty("function.location")) {
System.setProperty(FunctionProperties.PREFIX + ".location", environment.getProperty("function.location"));
String functionName = environment.containsProperty("function.name") ? environment.getProperty("function.name") : null;
String functionLocation = environment.containsProperty("function.location") ? environment.getProperty("function.location") : null;
if (StringUtils.hasText(functionName) || StringUtils.hasText(functionLocation)) {
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.forEach(ps -> {
if (ps instanceof PropertiesPropertySource) {
((MapPropertySource) ps).getSource().put(FunctionProperties.PREFIX + ".definition", functionName);
((MapPropertySource) ps).getSource().put(FunctionProperties.PREFIX + ".location", functionLocation);
}
});
}
}
}

View File

@@ -19,22 +19,19 @@ package org.springframework.cloud.function.deployer;
import javax.annotation.PostConstruct;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.function.context.FunctionProperties;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Configuration properties for deciding how to locate the functional class to execute.
*
* @author Eric Bottard
* @author Oleg Zhurakousky
*
* @see FunctionProperties
*/
@ConfigurationProperties("spring.cloud.function")
public class FunctionProperties {
/**
* The name prefix for properties defined by this properties class.
*/
public final static String PREFIX = "spring.cloud.function";
@ConfigurationProperties(prefix = FunctionProperties.PREFIX)
public class FunctionDeployerProperties {
/**
* Location of jar archive containing the supplier/function/consumer class or bean to run.
@@ -42,12 +39,7 @@ public class FunctionProperties {
private String location;
/**
* The name of the function to be looked up from the FunctionCatalog (e.g., bean name).
*/
private String functionName;
/**
* The name of the function class tyo be instantiated and loaded into FunctionCatalog. The name of the
* The name of the function class to be instantiated and loaded into FunctionCatalog. The name of the
* function will be decapitalized simple name of this class.
*/
private String functionClass;
@@ -60,14 +52,6 @@ public class FunctionProperties {
return this.functionClass;
}
public void setFunctionName(String functionName) {
this.functionName = StringUtils.hasText(functionName) ? functionName : "";
}
public String getFunctionName() {
return this.functionName;
}
public String getLocation() {
return this.location;
}