From 9434a68bd2b1d73fb3c0af38396e1ffee50a51e9 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Mon, 23 Mar 2020 14:51:56 +0100 Subject: [PATCH] GH-461 Add to register more than one functional class Similar to the way we allow multiple functions to be listed with 'definition' property, this enhancement allows several functional classes to be deployed Resolves #461 --- .../main/asciidoc/spring-cloud-function.adoc | 13 ++++++++++++ .../BeanFactoryAwareFunctionRegistry.java | 2 +- .../function/example/ReverseFunction.java | 13 ++++++++++++ .../deployer/FunctionArchiveDeployer.java | 20 ++++++++++--------- .../deployer/FunctionDeployerTests.java | 14 ++++++++++++- 5 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/ReverseFunction.java diff --git a/docs/src/main/asciidoc/spring-cloud-function.adoc b/docs/src/main/asciidoc/spring-cloud-function.adoc index d1e065adc..3a4578244 100644 --- a/docs/src/main/asciidoc/spring-cloud-function.adoc +++ b/docs/src/main/asciidoc/spring-cloud-function.adoc @@ -397,6 +397,19 @@ All you need to do is specify `location` and `function-class` properties when de --spring.cloud.function.location=target/it/simplestjar/target/simplestjar-1.0.0.RELEASE.jar --spring.cloud.function.function-class=function.example.UpperCaseFunction ``` + +It's conceivable in some cases that you might want to package multiple functions together. For such scenarios you can use +`spring.cloud.function.function-class` property to list several classes delimiting them by `;`. + +For example, + +``` +--spring.cloud.function.function-class=function.example.UpperCaseFunction;function.example.ReverseFunction +``` + +Here we are identifying two functions to deploy, which we can now access in function catalog by name (e.g., `catalog.lookup("reverseFunction");`). + + For more details please reference the complete sample available https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-deployer/src/it/simplestjar[here]. You can also find a corresponding test in https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java#L70[FunctionDeployerTests]. diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java index 893116d09..386835a19 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java @@ -160,7 +160,7 @@ public class BeanFactoryAwareFunctionRegistry if (!routing && this.declaredFunctionDefinitions.size() > 0) { if (StringUtils.hasText(definition)) { - if (this.declaredFunctionDefinitions.size() > 1 &&!this.declaredFunctionDefinitions.contains(definition)) { + if (this.declaredFunctionDefinitions.size() > 1 && !this.declaredFunctionDefinitions.contains(definition)) { logger.warn("Attempted to access un-declared function definition '" + definition + "'. Declared functions are '" + this.declaredFunctionDefinitions + "' specified via `spring.cloud.function.definition` property. If the intention is to access " + "any function available in FunctionCatalog, please remove `spring.cloud.function.definition` property."); diff --git a/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/ReverseFunction.java b/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/ReverseFunction.java new file mode 100644 index 000000000..090a28e2e --- /dev/null +++ b/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/ReverseFunction.java @@ -0,0 +1,13 @@ +package function.example; + +import java.util.function.Function; + +public class ReverseFunction implements Function { + + @Override + public String apply(String value) { + System.out.println("Reversing " + value); + return new StringBuilder(value).reverse().toString(); + } + +} diff --git a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionArchiveDeployer.java b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionArchiveDeployer.java index 8543ce3d2..6322bad32 100644 --- a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionArchiveDeployer.java +++ b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionArchiveDeployer.java @@ -93,11 +93,13 @@ class FunctionArchiveDeployer extends JarLauncher { } } - String functionClassName = discoverFunctionClassName(functionProperties); - if (!StringUtils.isEmpty(functionClassName)) { - FunctionRegistration registration = this.discovereAndLoadFunctionFromClassName(functionClassName); - if (registration != null) { - functionRegistry.register(registration); + String[] functionClassNames = discoverFunctionClassName(functionProperties); + for (String functionClassName : functionClassNames) { + if (!StringUtils.isEmpty(functionClassName)) { + FunctionRegistration registration = this.discovereAndLoadFunctionFromClassName(functionClassName); + if (registration != null) { + functionRegistry.register(registration); + } } } } @@ -169,11 +171,11 @@ class FunctionArchiveDeployer extends JarLauncher { - private String discoverFunctionClassName(FunctionDeployerProperties functionProperties) { + private String[] discoverFunctionClassName(FunctionDeployerProperties functionProperties) { try { return StringUtils.hasText(functionProperties.getFunctionClass()) - ? functionProperties.getFunctionClass() - : this.getArchive().getManifest().getMainAttributes().getValue("Function-Class"); + ? functionProperties.getFunctionClass().split(";") + : new String[] {this.getArchive().getManifest().getMainAttributes().getValue("Function-Class")}; } catch (Exception e) { throw new IllegalStateException("Failed to discover function class name", e); @@ -241,7 +243,7 @@ class FunctionArchiveDeployer extends JarLauncher { Class bootAppClass = Thread.currentThread().getContextClassLoader() .loadClass(SpringApplication.class.getName()); Method runMethod = bootAppClass.getDeclaredMethod("run", Class.class, String[].class); - Object applicationContext = runMethod.invoke(null, mainClass, (Object) args); + Object applicationContext = runMethod.invoke(null, mainClass, args); if (logger.isInfoEnabled()) { logger.info("Application context for archive '" + this.getArchive().getUrl() + "' is created."); } diff --git a/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java b/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java index d73d04840..bf7af7b0b 100644 --- a/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java +++ b/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java @@ -56,7 +56,7 @@ public class FunctionDeployerTests { public void testWithMainAndStartClassNoSpringConfiguration() throws Exception { String[] args = new String[] { "--spring.cloud.function.location=target/it/bootjar/target/bootjar-1.0.0.RELEASE-exec.jar", - "--spring.cloud.function.function-class=function.example.UpperCaseFunction" }; + "--spring.cloud.function.function-class=function.example.UpperCaseFunction;function.example.ReverseFunction" }; ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); @@ -65,11 +65,23 @@ public class FunctionDeployerTests { assertThat(function.apply("bob")).isEqualTo("BOB"); assertThat(function.apply("stacy")).isEqualTo("STACY"); + function = catalog.lookup("reverseFunction"); + + assertThat(function.apply("bob")).isEqualTo("bob"); + assertThat(function.apply("stacy")).isEqualTo("ycats"); + Function, Flux> functionAsFlux = catalog.lookup("upperCaseFunction"); List results = functionAsFlux.apply(Flux.just("bob", "stacy")).collectList().block(); assertThat(results.get(0)).isEqualTo("BOB"); assertThat(results.get(1)).isEqualTo("STACY"); + + functionAsFlux = catalog.lookup("reverseFunction"); + + results = functionAsFlux.apply(Flux.just("bob", "stacy")).collectList().block(); + + assertThat(results.get(0)).isEqualTo("bob"); + assertThat(results.get(1)).isEqualTo("ycats"); } @Test