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());