diff --git a/spring-cloud-function-deployer-new/pom.xml b/spring-cloud-function-deployer-new/pom.xml index 295d2c5d1..ad178f090 100644 --- a/spring-cloud-function-deployer-new/pom.xml +++ b/spring-cloud-function-deployer-new/pom.xml @@ -30,6 +30,11 @@ org.springframework.cloud spring-cloud-function-context + + org.springframework.boot + spring-boot-starter-test + test + org.springframework.boot spring-boot-configuration-processor @@ -37,6 +42,47 @@ + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-invoker-plugin + 3.0.1 + + ${project.build.directory}/local-repo + + + + + prepare-test + test-compile + + run + + + ${project.build.directory}/it + + src/it/settings.xml + true + true + + + + + + + spring-milestones diff --git a/spring-cloud-function-deployer-new/src/it/bootapp/pom.xml b/spring-cloud-function-deployer-new/src/it/bootapp/pom.xml new file mode 100644 index 000000000..e319a8cf8 --- /dev/null +++ b/spring-cloud-function-deployer-new/src/it/bootapp/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + function.example + bootapp + 0.0.1.BUILD-SNAPSHOT + jar + + + org.springframework.boot + spring-boot-starter-parent + 2.2.0.BUILD-SNAPSHOT + + + + + 1.8 + 3.0.0.BUILD-SNAPSHOT + 1.0.17.RELEASE + + + + + org.springframework.boot + spring-boot-starter + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack + package + + unpack + + + + + ${project.groupId} + ${project.artifactId} + ${project.version} + exec + + + + + + + + + diff --git a/spring-cloud-function-deployer-new/src/it/bootapp/src/main/java/function/example/SimpleFunctionAppApplication.java b/spring-cloud-function-deployer-new/src/it/bootapp/src/main/java/function/example/SimpleFunctionAppApplication.java new file mode 100644 index 000000000..ed8f694ef --- /dev/null +++ b/spring-cloud-function-deployer-new/src/it/bootapp/src/main/java/function/example/SimpleFunctionAppApplication.java @@ -0,0 +1,57 @@ +package function.example; + +import java.util.function.Function; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +public class SimpleFunctionAppApplication { + + public static void main(String[] args) { + SpringApplication.run(SimpleFunctionAppApplication.class, args); + } + + @Bean + public Function uppercase() { + System.out.println("==> CREATING 'uppercase' FUNCTION bean"); + return new UpperCaseFunction(); + } + + @Bean + public Function uppercasePerson() { + System.out.println("==> CREATING 'uppercasePerson' FUNCTION bean"); + return person -> { + Person p = new Person(); + p.setId(person.getId()); + p.setName(person.getName().toUpperCase()); + return p; + }; + } + + + + public static class Person { + private String name; + + private int id; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + } +} diff --git a/spring-cloud-function-deployer-new/src/it/bootapp/src/main/java/function/example/UpperCaseFunction.java b/spring-cloud-function-deployer-new/src/it/bootapp/src/main/java/function/example/UpperCaseFunction.java new file mode 100644 index 000000000..859a54a58 --- /dev/null +++ b/spring-cloud-function-deployer-new/src/it/bootapp/src/main/java/function/example/UpperCaseFunction.java @@ -0,0 +1,13 @@ +package function.example; + +import java.util.function.Function; + +public class UpperCaseFunction implements Function { + + @Override + public String apply(String value) { + System.out.println("Uppercasing " + value); + return value.toUpperCase(); + } + +} diff --git a/spring-cloud-function-deployer-new/src/it/bootjar/pom.xml b/spring-cloud-function-deployer-new/src/it/bootjar/pom.xml new file mode 100644 index 000000000..e6b3d767e --- /dev/null +++ b/spring-cloud-function-deployer-new/src/it/bootjar/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + function.example + bootjar + 0.0.1.BUILD-SNAPSHOT + jar + + + org.springframework.boot + spring-boot-starter-parent + 2.2.0.BUILD-SNAPSHOT + + + + + 1.8 + 3.0.0.BUILD-SNAPSHOT + 1.0.17.RELEASE + + + + + org.springframework.boot + spring-boot-starter + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack + package + + unpack + + + + + ${project.groupId} + ${project.artifactId} + ${project.version} + exec + + + + + + + + + diff --git a/spring-cloud-function-deployer-new/src/it/bootjar/src/main/java/function/example/SimpleFunctionAppApplication.java b/spring-cloud-function-deployer-new/src/it/bootjar/src/main/java/function/example/SimpleFunctionAppApplication.java new file mode 100644 index 000000000..4327b2cbe --- /dev/null +++ b/spring-cloud-function-deployer-new/src/it/bootjar/src/main/java/function/example/SimpleFunctionAppApplication.java @@ -0,0 +1,38 @@ +package function.example; + +import java.util.function.Function; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +public class SimpleFunctionAppApplication { + + public static void main(String[] args) { + SpringApplication.run(SimpleFunctionAppApplication.class, args); + } + + public static class Person { + private String name; + + private int id; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + } +} diff --git a/spring-cloud-function-deployer-new/src/it/bootjar/src/main/java/function/example/UpperCaseFunction.java b/spring-cloud-function-deployer-new/src/it/bootjar/src/main/java/function/example/UpperCaseFunction.java new file mode 100644 index 000000000..859a54a58 --- /dev/null +++ b/spring-cloud-function-deployer-new/src/it/bootjar/src/main/java/function/example/UpperCaseFunction.java @@ -0,0 +1,13 @@ +package function.example; + +import java.util.function.Function; + +public class UpperCaseFunction implements Function { + + @Override + public String apply(String value) { + System.out.println("Uppercasing " + value); + return value.toUpperCase(); + } + +} diff --git a/spring-cloud-function-deployer-new/src/it/settings.xml b/spring-cloud-function-deployer-new/src/it/settings.xml new file mode 100644 index 000000000..e1e0ace34 --- /dev/null +++ b/spring-cloud-function-deployer-new/src/it/settings.xml @@ -0,0 +1,35 @@ + + + + + it-repo + + true + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + diff --git a/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/ApplicationContainer.java b/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/ApplicationContainer.java index 5bf6cecf6..887234311 100644 --- a/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/ApplicationContainer.java +++ b/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/ApplicationContainer.java @@ -32,14 +32,11 @@ public abstract class ApplicationContainer { private final FunctionProperties functionProperties; - private final Object function; - public ApplicationContainer(FunctionCatalog functionCatalog, FunctionInspector functionInspector, FunctionProperties functionProperties) { this.functionCatalog = functionCatalog; this.functionInspector = functionInspector; this.functionProperties = functionProperties; - this.function = this.functionCatalog.lookup(this.functionProperties.getFunctionName()); } protected FunctionCatalog getFunctionCatalog() { @@ -56,6 +53,11 @@ public abstract class ApplicationContainer { @SuppressWarnings("unchecked") public T getFunction() { - return (T) this.function; + return (T) this.functionCatalog.lookup(this.functionProperties.getFunctionName()); + } + + @SuppressWarnings("unchecked") + public T getFunction(String... acceptedOutputMimeTypes) { + return (T) this.functionCatalog.lookup(this.functionProperties.getFunctionName(), acceptedOutputMimeTypes); } } diff --git a/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/FunctionProperties.java b/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/FunctionProperties.java index d399a6563..c6c612dba 100644 --- a/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/FunctionProperties.java +++ b/spring-cloud-function-deployer-new/src/main/java/org/springframework/cloud/function/deployer/FunctionProperties.java @@ -31,12 +31,19 @@ import org.springframework.util.StringUtils; public class FunctionProperties { /** - * Location(s) of jar archives containing the supplier/function/consumer class to run. + * Location of jar archive containing the supplier/function/consumer class or bean to run. */ 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 + * function will be decapitalized simple name of this class. + */ private String functionClass; public void setFunctionClass(String functionClass) { diff --git a/spring-cloud-function-deployer-new/src/test/java/org/springframework/cloud/function/deployer/ApplicationContainerTests.java b/spring-cloud-function-deployer-new/src/test/java/org/springframework/cloud/function/deployer/ApplicationContainerTests.java new file mode 100644 index 000000000..bff4eb402 --- /dev/null +++ b/spring-cloud-function-deployer-new/src/test/java/org/springframework/cloud/function/deployer/ApplicationContainerTests.java @@ -0,0 +1,91 @@ +/* + * Copyright 2017-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.function.deployer; + +import java.nio.charset.StandardCharsets; +import java.util.function.Function; + +import org.junit.Test; + +import org.springframework.cloud.function.context.FunctionCatalog; +import org.springframework.cloud.function.context.catalog.FunctionInspector; +import org.springframework.messaging.Message; +import org.springframework.messaging.support.MessageBuilder; + +import static org.assertj.core.api.Assertions.assertThat; +/** + * + * @author Oleg Zhurakousky + * @since 3.0 + */ +public class ApplicationContainerTests { + + @Test + public void testCustomApplicationContainerWithBootJar() throws Exception { + String[] args = new String[] {"--spring.cloud.function.location=target/it/bootjar/target/bootjar-0.0.1.BUILD-SNAPSHOT-exec.jar", + "--spring.cloud.function.function-class=function.example.UpperCaseFunction"}; + + JavaInvoker invokerByClass = + FunctionDeployerBootstrap.instance(args).run(JavaInvoker.class, args); + + assertThat(invokerByClass.uppercaseSimple("bob")).isEqualTo("BOB"); + assertThat(invokerByClass.uppercaseSimple("stacy")).isEqualTo("STACY"); + } + + @Test + public void testCustomApplicationContainerWithBootAppSimpleTypes() throws Exception { + + String[] args = new String[] {"--spring.cloud.function.location=target/it/bootapp/target/bootapp-0.0.1.BUILD-SNAPSHOT-exec.jar", + "--spring.cloud.function.function-name=uppercase"}; + JavaInvoker invokerByBean = + FunctionDeployerBootstrap.instance(args).run(JavaInvoker.class, args); + + Message result = invokerByBean.uppercase(MessageBuilder.withPayload("\"bob\"".getBytes(StandardCharsets.UTF_8)).build()); + assertThat(new String(result.getPayload(), StandardCharsets.UTF_8)).isEqualTo("\"BOB\""); + } + + @Test + public void testCustomApplicationContainerWithBootAppWithTypeConversion() throws Exception { + + String[] args = new String[] {"--spring.cloud.function.location=target/it/bootapp/target/bootapp-0.0.1.BUILD-SNAPSHOT-exec.jar", + "--spring.cloud.function.function-name=uppercasePerson"}; + + JavaInvoker invokerByBean = + FunctionDeployerBootstrap.instance(args).run(JavaInvoker.class, args); + + Message result = invokerByBean.uppercase(MessageBuilder.withPayload("{\"name\":\"bob\",\"id\":1}".getBytes(StandardCharsets.UTF_8)).build()); + assertThat(new String(result.getPayload(), StandardCharsets.UTF_8)).isEqualTo("{\"name\":\"BOB\",\"id\":1}"); + } + + private static class JavaInvoker extends ApplicationContainer { + + JavaInvoker(FunctionCatalog functionCatalog, FunctionInspector functionInspector, + FunctionProperties functionProperties) { + super(functionCatalog, functionInspector, functionProperties); + } + + public Message uppercase(Message input) { + Function, Message> functon = this.getFunction("application/json"); + return functon.apply(input); + } + + public String uppercaseSimple(String input) { + Function functon = this.getFunction(); + return functon.apply(input); + } + } +} diff --git a/spring-cloud-function-deployer-new/src/test/java/org/springframework/cloud/function/deployer/sample/SampleInvoker.java b/spring-cloud-function-deployer-new/src/test/java/org/springframework/cloud/function/deployer/sample/SampleInvoker.java deleted file mode 100644 index 4aa562b7b..000000000 --- a/spring-cloud-function-deployer-new/src/test/java/org/springframework/cloud/function/deployer/sample/SampleInvoker.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.deployer.sample; - -import java.util.function.Function; - -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.catalog.FunctionInspector; -import org.springframework.cloud.function.deployer.ApplicationContainer; -import org.springframework.cloud.function.deployer.FunctionDeployerBootstrap; -import org.springframework.cloud.function.deployer.FunctionProperties; - -/** - * - * @author Oleg Zhurakousky - * @since 3.0 - */ -public class SampleInvoker extends ApplicationContainer { - - public static void main(String[] args) throws Exception { - SampleInvoker invokerByClass = FunctionDeployerBootstrap.instance( - "--spring.cloud.function.location=/Users/olegz/Downloads/simple-function-app/jars/simple-function-jar-0.0.1-SNAPSHOT.jar", - "--spring.cloud.function.function-class=oz.function.simplefunctionapp.UpperCaseFunction") - .run(SampleInvoker.class, args); - - System.out.println(invokerByClass.uppercase("eric")); - System.out.println(invokerByClass.uppercase("oleg")); - - SampleInvoker invokerByBean = FunctionDeployerBootstrap.instance( - "--spring.cloud.function.location=/Users/olegz/Downloads/simple-function-app/jars/simple-function-app-0.0.1-SNAPSHOT.jar", - "--spring.cloud.function.function-name=uppercase") - .run(SampleInvoker.class, args); - - System.out.println(invokerByBean.uppercase("eric")); - System.out.println(invokerByBean.uppercase("oleg")); - } - - public SampleInvoker(FunctionCatalog functionCatalog, FunctionInspector functionInspector, - FunctionProperties functionProperties) { - super(functionCatalog, functionInspector, functionProperties); - } - - public String uppercase(String value) { - Function functon = this.getFunction(); - return functon.apply(value); - } - -}