diff --git a/spring-cloud-function-context/pom.xml b/spring-cloud-function-context/pom.xml index fb23fec49..bbffa76d4 100644 --- a/spring-cloud-function-context/pom.xml +++ b/spring-cloud-function-context/pom.xml @@ -43,9 +43,9 @@ true - com.fasterxml.jackson.module - jackson-module-kotlin - true + com.fasterxml.jackson.module + jackson-module-kotlin + true org.springframework.boot @@ -69,7 +69,7 @@ com.vaadin.external.google - android-json + android-json @@ -83,7 +83,7 @@ com.fasterxml.jackson.core jackson-databind - + org.jetbrains.kotlin @@ -109,10 +109,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 cd154b467..dbfe13e49 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 6de22aab7..8e67ed279 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 0274d8c08..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 @@ -24,13 +24,14 @@ import reactor.core.publisher.Flux; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; +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 @@ -38,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());