From 57689755f2fc92a977e93953afcbc99e0070094e Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Thu, 6 Jun 2019 16:40:17 +0100 Subject: [PATCH] Add support for spring.cloud.function.scan.packages in functional Fixes gh-372 --- .../ContextFunctionCatalogInitializer.java | 28 ++++++++++++++++ ...ontextFunctionCatalogInitializerTests.java | 20 ++++++++++-- .../function/context/scan/TestFunction.java | 32 +++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/scan/TestFunction.java diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializer.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializer.java index 0ef806ed4..9e08e37ae 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializer.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializer.java @@ -16,12 +16,17 @@ package org.springframework.cloud.function.context.config; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; @@ -36,9 +41,12 @@ import org.springframework.cloud.function.context.catalog.InMemoryFunctionCatalo import org.springframework.cloud.function.json.JsonMapper; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.annotation.AnnotationConfigUtils; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.context.support.GenericApplicationContext; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.core.io.ClassPathResource; import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.AssignableTypeFilter; import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -155,6 +163,26 @@ public class ContextFunctionCatalogInitializer } + String basePackage = this.context.getEnvironment() + .getProperty("spring.cloud.function.scan.packages", "functions"); + if (new ClassPathResource(basePackage.replace(".", "/")).exists()) { + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner( + this.context, false, this.context.getEnvironment(), this.context); + scanner.addIncludeFilter(new AssignableTypeFilter(Function.class)); + scanner.addIncludeFilter(new AssignableTypeFilter(Supplier.class)); + scanner.addIncludeFilter(new AssignableTypeFilter(Consumer.class)); + for (BeanDefinition bean : scanner.findCandidateComponents(basePackage)) { + String name = bean.getBeanClassName(); + Class type = ClassUtils.resolveClassName(name, + this.context.getClassLoader()); + this.context.registerBeanDefinition(name, bean); + this.context.registerBean("registration_" + name, + FunctionRegistration.class, + () -> new FunctionRegistration<>(this.context.getBean(name), + name).type(type)); + } + } + if (this.context.getBeanFactory().getBeanNamesForType(FunctionCatalog.class, false, false).length == 0) { this.context.registerBean(InMemoryFunctionCatalog.class, diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java index 843de58c8..483d1c7f6 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java @@ -40,6 +40,7 @@ import org.springframework.cloud.function.context.FunctionCatalog; import org.springframework.cloud.function.context.FunctionRegistration; import org.springframework.cloud.function.context.FunctionType; import org.springframework.cloud.function.context.catalog.FunctionInspector; +import org.springframework.cloud.function.context.scan.TestFunction; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.annotation.Bean; import org.springframework.context.support.GenericApplicationContext; @@ -162,8 +163,23 @@ public class ContextFunctionCatalogInitializerTests { assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); assertThat(bean).isNotSameAs(function); assertThat(this.inspector.getRegistration(function)).isNotNull(); - assertThat(this.inspector.getRegistration(function).getType()).isEqualTo( - FunctionType.from(String.class).to(String.class)); + assertThat(this.inspector.getRegistration(function).getType()) + .isEqualTo(FunctionType.from(String.class).to(String.class)); + } + + @Test + public void scanFunction() { + create(EmptyConfiguration.class, + "spring.cloud.function.scan.packages=org.springframework.cloud.function.context.scan"); + Object bean = this.context.getBean(TestFunction.class.getName()); + assertThat(bean).isInstanceOf(Function.class); + Function, Flux> function = this.catalog + .lookup(Function.class, TestFunction.class.getName()); + assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); + assertThat(bean).isNotSameAs(function); + assertThat(this.inspector.getRegistration(function)).isNotNull(); + assertThat(this.inspector.getRegistration(function).getType()) + .isEqualTo(FunctionType.from(String.class).to(String.class)); } @Test diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/scan/TestFunction.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/scan/TestFunction.java new file mode 100644 index 000000000..ad12ee4b9 --- /dev/null +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/scan/TestFunction.java @@ -0,0 +1,32 @@ +/* + * Copyright 2019-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.context.scan; + +import java.util.function.Function; + +/** + * @author Dave Syer + * + */ +public class TestFunction implements Function { + + @Override + public String apply(String t) { + return t.toUpperCase(); + } + +}