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