diff --git a/spring-cloud-function-context/pom.xml b/spring-cloud-function-context/pom.xml index 21dbb0858..4d6096b3a 100644 --- a/spring-cloud-function-context/pom.xml +++ b/spring-cloud-function-context/pom.xml @@ -33,6 +33,16 @@ org.springframework spring-messaging + + org.springframework + spring-web + true + + + com.fasterxml.jackson.module + jackson-module-kotlin + true + org.springframework.boot spring-boot-configuration-processor @@ -55,7 +65,7 @@ com.vaadin.external.google - android-json + android-json @@ -69,7 +79,7 @@ com.fasterxml.jackson.core jackson-databind - + org.jetbrains.kotlin @@ -89,10 +99,17 @@ true - io.cloudevents - cloudevents-spring - 2.2.0 - true + io.cloudevents + cloudevents-spring + 2.2.0 + true + + + + + org.springframework.boot + spring-boot-starter-actuator + true diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java index 4f988cbcc..1e2dd1dfe 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java @@ -440,7 +440,7 @@ public class SimpleFunctionRegistry implements FunctionRegistry, FunctionInspect public boolean isPrototype() { - return this.isPrototype(); + return !this.isSingleton; } public void setSkipInputConversion(boolean skipInputConversion) { diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionsEndpointAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionsEndpointAutoConfiguration.java new file mode 100644 index 000000000..912f5f2b9 --- /dev/null +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionsEndpointAutoConfiguration.java @@ -0,0 +1,46 @@ +/* + * Copyright 2021-2021 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.context.config; + +import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.cloud.function.context.FunctionCatalog; +import org.springframework.cloud.function.endpoint.FunctionsEndpoint; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Oleg Zhurakousky + * @since 3.2 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(name = { + "org.springframework.boot.actuate.endpoint.annotation.Endpoint" }) +@ConditionalOnBean(FunctionCatalog.class) +@AutoConfigureAfter(EndpointAutoConfiguration.class) +public class FunctionsEndpointAutoConfiguration { + + @Bean + @ConditionalOnAvailableEndpoint + public FunctionsEndpoint bindingsEndpoint(FunctionCatalog functionCatalog) { + return new FunctionsEndpoint(functionCatalog); + } + +} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/endpoint/FunctionsEndpoint.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/endpoint/FunctionsEndpoint.java new file mode 100644 index 000000000..c27820e6e --- /dev/null +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/endpoint/FunctionsEndpoint.java @@ -0,0 +1,72 @@ +/* + * Copyright 2021-2021 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.endpoint; + +import java.util.LinkedHashMap; + +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.cloud.function.context.FunctionCatalog; +import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; +import org.springframework.cloud.function.json.JsonMapper; + +/** + * + * Actuator endpoint to access {@link FunctionCatalog} + * + * @author Oleg Zhurakousky + * @since 3.2 + */ +@Endpoint(id = "functions") +public class FunctionsEndpoint { + + private final FunctionCatalog functionCatalog; + + public FunctionsEndpoint(FunctionCatalog functionCatalog) { + this.functionCatalog = functionCatalog; + } + + @ReadOperation + public Map> listAll() { + Map> allFunctions = new TreeMap<>(); + Set names = functionCatalog.getNames(null); + for (String name : names) { + FunctionInvocationWrapper function = functionCatalog.lookup(name); + Map functionMap = new LinkedHashMap<>(); + if (function.isFunction()) { + functionMap.put("type", "FUNCTION"); + functionMap.put("input-type", function.getInputType().toString()); + functionMap.put("output-type", function.getOutputType().toString()); + } + else if (function.isConsumer()) { + functionMap.put("type", "CONSUMER"); + functionMap.put("input-type", function.getInputType().toString()); + } + else { + functionMap.put("type", "SUPPLIER"); + functionMap.put("output-type", function.getOutputType().toString()); + } + allFunctions.put(name, functionMap); + } + return allFunctions; + } + +} diff --git a/spring-cloud-function-context/src/main/resources/META-INF/spring.factories b/spring-cloud-function-context/src/main/resources/META-INF/spring.factories index 845272816..ca1caf4a9 100644 --- a/spring-cloud-function-context/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-function-context/src/main/resources/META-INF/spring.factories @@ -1,7 +1,8 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration,\ org.springframework.cloud.function.cloudevent.CloudEventsFunctionExtensionConfiguration,\ -org.springframework.cloud.function.context.config.KotlinLambdaToFunctionAutoConfiguration +org.springframework.cloud.function.context.config.KotlinLambdaToFunctionAutoConfiguration,\ +org.springframework.cloud.function.context.config.FunctionsEndpointAutoConfiguration org.springframework.cloud.function.context.WrapperDetector=\ org.springframework.cloud.function.context.config.FluxWrapperDetector org.springframework.context.ApplicationContextInitializer=\ diff --git a/spring-cloud-function-samples/function-sample/pom.xml b/spring-cloud-function-samples/function-sample/pom.xml index 5b72fe8f8..4843464d0 100644 --- a/spring-cloud-function-samples/function-sample/pom.xml +++ b/spring-cloud-function-samples/function-sample/pom.xml @@ -26,6 +26,10 @@ + org.springframework.boot + spring-boot-starter-actuator + + org.springframework.cloud spring-cloud-starter-function-webflux diff --git a/spring-cloud-function-samples/function-sample/src/main/java/com/example/SampleApplication.java b/spring-cloud-function-samples/function-sample/src/main/java/com/example/SampleApplication.java index 8f3097b0f..54f2e9dc6 100644 --- a/spring-cloud-function-samples/function-sample/src/main/java/com/example/SampleApplication.java +++ b/spring-cloud-function-samples/function-sample/src/main/java/com/example/SampleApplication.java @@ -23,17 +23,15 @@ import reactor.core.publisher.Flux; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.function.compiler.FunctionCompiler; -import org.springframework.cloud.function.compiler.proxy.LambdaCompilingFunction; import org.springframework.context.annotation.Bean; -import org.springframework.core.io.ByteArrayResource; +import org.springframework.messaging.Message; // @checkstyle:off @SpringBootApplication public class SampleApplication { public static void main(String[] args) throws Exception { - SpringApplication.run(SampleApplication.class, args); + SpringApplication.run(SampleApplication.class, "--management.endpoints.web.exposure.include=functions"); } @Bean @@ -41,6 +39,11 @@ public class SampleApplication { return value -> value.toUpperCase(); } + @Bean + public Function, String> uppercaseMessage() { + return value -> value.getPayload().toUpperCase(); + } + @Bean public Function, Flux> lowercase() { return flux -> flux.map(value -> value.toLowerCase()); @@ -56,28 +59,5 @@ public class SampleApplication { return () -> Flux.fromArray(new String[] {"foo", "bar"}); } - @Bean - public Function compiledUppercase( - FunctionCompiler compiler) { - String lambda = "s -> s.toUpperCase()"; - LambdaCompilingFunction function = new LambdaCompilingFunction<>( - new ByteArrayResource(lambda.getBytes()), compiler); - function.setTypeParameterizations("String", "String"); - return function; - } - - @Bean - public Function, Flux> compiledLowercase( - FunctionCompiler, Flux> compiler) { - String lambda = "f->f.map(o->o.toString().toLowerCase())"; - return new LambdaCompilingFunction<>(new ByteArrayResource(lambda.getBytes()), - compiler); - } - - @Bean - public FunctionCompiler compiler() { - return new FunctionCompiler<>(); - } - } // @checkstyle:on