diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializer.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializer.java index 0d9dbebe6..861ba51ed 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializer.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializer.java @@ -21,10 +21,9 @@ import java.io.InputStream; import java.net.URL; import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; import java.util.function.Function; -import java.util.function.Supplier; import java.util.jar.Manifest; import org.apache.commons.logging.Log; @@ -36,10 +35,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; 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.context.ApplicationContext; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.GenericApplicationContext; import org.springframework.util.ClassUtils; import reactor.core.publisher.Flux; @@ -55,10 +57,6 @@ public class SpringFunctionInitializer implements Closeable { private Function, Publisher> function; - private Consumer> consumer; - - private Supplier> supplier; - private AtomicBoolean initialized = new AtomicBoolean(); @Autowired(required = false) @@ -94,30 +92,22 @@ public class SpringFunctionInitializer implements Closeable { ConfigurableApplicationContext context = builder.run(); context.getAutowireCapableBeanFactory().autowireBean(this); String name = context.getEnvironment().getProperty("function.name"); - boolean defaultName = false; if (name == null) { name = "function"; - defaultName = true; } if (this.catalog == null) { - this.function = context.getBean(name, Function.class); + if (context.containsBean(name)) { + this.function = context.getBean(name, Function.class); + } } else { - this.function = this.catalog.lookup(Function.class, name); - if (this.function == null) { - if (defaultName) { - name = "consumer"; - } + Set functionNames = this.catalog.getNames(Function.class); + if (functionNames.size() == 1) { + this.function = this.catalog.lookup(Function.class, + functionNames.iterator().next()); + } + else { this.function = this.catalog.lookup(Function.class, name); - if (this.function == null) { - this.consumer = this.catalog.lookup(Consumer.class, name); - if (this.consumer == null) { - if (defaultName) { - name = "supplier"; - } - this.supplier = this.catalog.lookup(Supplier.class, name); - } - } } } this.context = context; @@ -125,22 +115,39 @@ public class SpringFunctionInitializer implements Closeable { } private SpringApplication springApplication() { - ApplicationContextInitializer initializer = null; + ApplicationContextInitializer initializer = null; Class sourceClass = configurationClass; if (ApplicationContextInitializer.class.isAssignableFrom(sourceClass)) { - initializer = BeanUtils.instantiateClass(configurationClass, ApplicationContextInitializer.class); + @SuppressWarnings("unchecked") + ApplicationContextInitializer instance = BeanUtils + .instantiateClass(configurationClass, + ApplicationContextInitializer.class); + initializer = instance; + sourceClass = Object.class; + } + else if (Function.class.isAssignableFrom(sourceClass)) { + @SuppressWarnings("unchecked") + final Class> type = (Class>) sourceClass; + initializer = context -> { + context.registerBean(FunctionRegistration.class, + () -> new FunctionRegistration<>( + context.getAutowireCapableBeanFactory().createBean(type)) + .type(FunctionType.of(type))); + }; sourceClass = Object.class; } SpringApplication application; - if (initializer!=null) { + if (initializer != null) { application = new SpringApplication(sourceClass) { @Override protected void load(ApplicationContext context, Object[] sources) { } }; application.addInitializers(initializer); - application.setDefaultProperties(Collections.singletonMap("spring.functional.enabled", "true")); - } else { + application.setDefaultProperties( + Collections.singletonMap("spring.functional.enabled", "true")); + } + else { application = new SpringApplication(sourceClass); } application.setWebApplicationType(WebApplicationType.NONE); @@ -155,21 +162,13 @@ public class SpringFunctionInitializer implements Closeable { } protected Object function() { - return this.function != null ? this.function - : (this.consumer != null ? this.consumer : this.supplier); + return this.function; } protected Publisher apply(Publisher input) { if (this.function != null) { return Flux.from(function.apply(input)); } - if (this.consumer != null) { - this.consumer.accept(input); - return Flux.empty(); - } - if (this.supplier != null) { - return this.supplier.get(); - } throw new IllegalStateException("No function defined"); } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializerTests.java index 53db4c9a3..c3cb96df9 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializerTests.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializerTests.java @@ -18,7 +18,6 @@ package org.springframework.cloud.function.adapter.aws; import java.util.function.Consumer; import java.util.function.Function; -import java.util.function.Supplier; import java.util.stream.Collectors; import org.junit.After; @@ -62,6 +61,14 @@ public class SpringFunctionInitializerTests { assertThat(result.blockFirst()).isInstanceOf(Bar.class); } + @Test + public void functionApp() { + initializer = new SpringFunctionInitializer(FluxFunctionApp.class); + initializer.initialize(); + Flux result = Flux.from(initializer.apply(Flux.just(new Foo()))); + assertThat(result.blockFirst()).isInstanceOf(Bar.class); + } + @Test public void functionCatalog() { initializer = new SpringFunctionInitializer(FunctionConfig.class); @@ -96,14 +103,6 @@ public class SpringFunctionInitializerTests { assertThat(result.toStream().collect(Collectors.toList())).isEmpty(); } - @Test - public void supplierCatalog() { - initializer = new SpringFunctionInitializer(SupplierConfig.class); - initializer.initialize(); - Flux result = Flux.from(initializer.apply(Flux.empty())); - assertThat(result.blockFirst()).isInstanceOf(Bar.class); - } - @Configuration protected static class FluxFunctionConfig { @Bean @@ -112,6 +111,13 @@ public class SpringFunctionInitializerTests { } } + protected static class FluxFunctionApp implements Function, Flux> { + @Override + public Flux apply(Flux flux) { + return flux.map(foo -> new Bar()); + } + } + protected static class FunctionRegistrar implements ApplicationContextInitializer { @@ -147,15 +153,6 @@ public class SpringFunctionInitializerTests { } } - @Configuration - @Import(ContextFunctionCatalogAutoConfiguration.class) - protected static class SupplierConfig { - @Bean - public Supplier supplier() { - return () -> new Bar(); - } - } - @Configuration @Import(ContextFunctionCatalogAutoConfiguration.class) protected static class ConsumerConfig { 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 d56434205..e21871571 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 @@ -64,7 +64,7 @@ public class ContextFunctionCatalogInitializerTests { public void lookUps() { create(SimpleConfiguration.class); assertThat(context.getBean("function")).isInstanceOf(FunctionRegistration.class); - assertThat((Function)catalog.lookup(Function.class, "function")) + assertThat((Function) catalog.lookup(Function.class, "function")) .isInstanceOf(Function.class); // TODO: support for function composition } @@ -73,16 +73,16 @@ public class ContextFunctionCatalogInitializerTests { public void missingType() { create(MissingTypeConfiguration.class); assertThat(context.getBean("function")).isInstanceOf(FunctionRegistration.class); - assertThat((Function)catalog.lookup(Function.class, "function")) + assertThat((Function) catalog.lookup(Function.class, "function")) .isInstanceOf(Function.class); - // TODO: support for type inference from functional bean regsitrations + // TODO: support for type inference from functional bean registrations } @Test public void configurationFunction() { create(FunctionConfiguration.class); assertThat(context.getBean("foos")).isInstanceOf(Function.class); - assertThat((Function)catalog.lookup(Function.class, "foos")) + assertThat((Function) catalog.lookup(Function.class, "foos")) .isInstanceOf(Function.class); assertThat(inspector.getInputType(catalog.lookup(Function.class, "foos"))) .isEqualTo(String.class); @@ -96,7 +96,7 @@ public class ContextFunctionCatalogInitializerTests { public void dependencyInjection() { create(DependencyInjectionConfiguration.class); assertThat(context.getBean("foos")).isInstanceOf(FunctionRegistration.class); - assertThat((Function)catalog.lookup(Function.class, "foos")) + assertThat((Function) catalog.lookup(Function.class, "foos")) .isInstanceOf(Function.class); assertThat(inspector.getInputType(catalog.lookup(Function.class, "foos"))) .isEqualTo(String.class);