diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringBootStreamHandler.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringBootStreamHandler.java index a04cb7a98..89b344efa 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringBootStreamHandler.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringBootStreamHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2017-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. @@ -19,9 +19,7 @@ package org.springframework.cloud.function.adapter.aws; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; @@ -30,12 +28,14 @@ import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.function.context.AbstractSpringFunctionAdapterInitializer; + /** * @author Dave Syer * @author Oleg Zhurakousky */ -public class SpringBootStreamHandler extends SpringFunctionInitializer +public class SpringBootStreamHandler extends AbstractSpringFunctionAdapterInitializer implements RequestStreamHandler { @Autowired(required = false) @@ -50,8 +50,8 @@ public class SpringBootStreamHandler extends SpringFunctionInitializer } @Override - protected void initialize() { - super.initialize(); + protected void initialize(Context context) { + super.initialize(context); if (this.mapper == null) { this.mapper = new ObjectMapper(); } @@ -60,27 +60,12 @@ public class SpringBootStreamHandler extends SpringFunctionInitializer @Override public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - initialize(); + initialize(context); Object value = convertStream(input); Publisher flux = apply(extract(value)); this.mapper.writeValue(output, result(value, flux)); } - private Object result(Object input, Publisher flux) { - List result = new ArrayList<>(); - for (Object value : Flux.from(flux).toIterable()) { - result.add(value); - } - if (isSingleValue(input) && result.size() == 1) { - return result.get(0); - } - return result; - } - - private boolean isSingleValue(Object input) { - return !(input instanceof Collection); - } - private Flux extract(Object input) { if (input instanceof Collection) { return Flux.fromIterable((Iterable) input); 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 ba1cfa489..27fd3771f 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 @@ -36,6 +36,7 @@ import reactor.core.publisher.Flux; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; +import org.springframework.cloud.function.context.AbstractSpringFunctionAdapterInitializer; import org.springframework.cloud.function.context.FunctionCatalog; import org.springframework.cloud.function.context.catalog.FunctionInspector; import org.springframework.context.ConfigurableApplicationContext; @@ -44,7 +45,10 @@ import org.springframework.util.ClassUtils; /** * @author Dave Syer * @author Semyon Fishman + * @deprecated as of 2.1 in favor of {@link AbstractSpringFunctionAdapterInitializer}. + * It is no longer used by the framework and only exists for avoiding potential regressions. */ +@Deprecated public class SpringFunctionInitializer implements Closeable { private static Log logger = LogFactory.getLog(SpringFunctionInitializer.class); diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/SpringBootStreamHandlerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/SpringBootStreamHandlerTests.java index ccce8b355..cc89e3c22 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/SpringBootStreamHandlerTests.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/SpringBootStreamHandlerTests.java @@ -41,7 +41,7 @@ public class SpringBootStreamHandlerTests { @Test public void functionBeanWithJacksonConfig() throws Exception { this.handler = new SpringBootStreamHandler(FunctionConfigWithJackson.class); - this.handler.initialize(); + this.handler.initialize(null); ByteArrayOutputStream output = new ByteArrayOutputStream(); this.handler.handleRequest( new ByteArrayInputStream("{\"value\":\"foo\"}".getBytes()), output, null); @@ -51,7 +51,7 @@ public class SpringBootStreamHandlerTests { @Test public void functionBeanWithoutJacksonConfig() throws Exception { this.handler = new SpringBootStreamHandler(FunctionConfigWithoutJackson.class); - this.handler.initialize(); + this.handler.initialize(null); ByteArrayOutputStream output = new ByteArrayOutputStream(); this.handler.handleRequest( new ByteArrayInputStream("{\"value\":\"foo\"}".getBytes()), output, null); diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureSpringBootRequestHandler.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureSpringBootRequestHandler.java index c01da04a5..9a7a2231d 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureSpringBootRequestHandler.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureSpringBootRequestHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2017-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. @@ -17,9 +17,7 @@ package org.springframework.cloud.function.adapter.azure; import java.lang.reflect.UndeclaredThrowableException; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; import java.util.function.Function; import com.microsoft.azure.functions.ExecutionContext; @@ -27,12 +25,15 @@ import com.microsoft.azure.functions.OutputBinding; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; +import org.springframework.cloud.function.context.AbstractSpringFunctionAdapterInitializer; + /** * @param input type * @param result type * @author Soby Chacko + * @author Oleg Zhurakousky */ -public class AzureSpringBootRequestHandler extends AzureSpringFunctionInitializer { +public class AzureSpringBootRequestHandler extends AbstractSpringFunctionAdapterInitializer { public AzureSpringBootRequestHandler(Class configurationClass) { super(configurationClass); @@ -51,10 +52,9 @@ public class AzureSpringBootRequestHandler extends AzureSpringFunctionInit } initialize(context); - Function, Publisher> function = lookup(name); - Publisher events = extract(function, convertEvent(input)); - Publisher output = function.apply(events); - return result(function, input, output); + Publisher events = extract(convertEvent(input)); + Publisher output = apply(events); + return result(input, output); } catch (Throwable ex) { if (context != null) { @@ -75,6 +75,11 @@ public class AzureSpringBootRequestHandler extends AzureSpringFunctionInit } } + @Override + protected String doResolveName(Object targetContext) { + return ((ExecutionContext) targetContext).getFunctionName(); + } + public void handleOutput(I input, OutputBinding binding, ExecutionContext context) { O result = handleRequest(input, context); @@ -85,8 +90,8 @@ public class AzureSpringBootRequestHandler extends AzureSpringFunctionInit return input; } - private Flux extract(Function function, Object input) { - if (!isSingleInput(function, input)) { + protected Flux extract(Object input) { + if (!isSingleInput(this.getFunction(), input)) { if (input instanceof Collection) { return Flux.fromIterable((Iterable) input); } @@ -94,29 +99,31 @@ public class AzureSpringBootRequestHandler extends AzureSpringFunctionInit return Flux.just(input); } - private O result(Function function, Object input, Publisher output) { - List result = new ArrayList<>(); - for (Object value : Flux.from(output).toIterable()) { - result.add(convertOutput(value)); - } - if (isSingleInput(function, input) && result.size() == 1) { - @SuppressWarnings("unchecked") - O value = (O) result.get(0); - return value; - } - if (isSingleOutput(function, input) && result.size() == 1) { - @SuppressWarnings("unchecked") - O value = (O) result.get(0); - return value; - } - @SuppressWarnings("unchecked") - O value = (O) result; - return value; - } - @SuppressWarnings("unchecked") protected O convertOutput(Object output) { return (O) output; } + protected boolean isSingleInput(Function function, Object input) { + if (!(input instanceof Collection)) { + return true; + } + if (getInspector() != null) { + return Collection.class + .isAssignableFrom(getInspector().getInputType(function)); + } + return ((Collection) input).size() <= 1; + } + + protected boolean isSingleOutput(Function function, Object output) { + if (!(output instanceof Collection)) { + return true; + } + if (getInspector() != null) { + return Collection.class + .isAssignableFrom(getInspector().getOutputType(function)); + } + return ((Collection) output).size() <= 1; + } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureSpringFunctionInitializer.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureSpringFunctionInitializer.java index 68f3ee808..44e90b151 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureSpringFunctionInitializer.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureSpringFunctionInitializer.java @@ -35,6 +35,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.function.context.AbstractSpringFunctionAdapterInitializer; import org.springframework.cloud.function.context.FunctionCatalog; import org.springframework.cloud.function.context.catalog.FunctionInspector; import org.springframework.context.ConfigurableApplicationContext; @@ -42,7 +43,10 @@ import org.springframework.util.ClassUtils; /** * @author Soby Chacko + * @deprecated as of 2.1 in favor of {@link AbstractSpringFunctionAdapterInitializer}. + * It is no longer used by the framework and only exists for avoiding potential regressions. */ +@Deprecated public class AzureSpringFunctionInitializer implements Closeable { private volatile static ConfigurableApplicationContext context; diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/AzureSpringFunctionInitializerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/AzureSpringFunctionInitializerTests.java deleted file mode 100644 index 3303490ed..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/AzureSpringFunctionInitializerTests.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2012-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 - * - * http://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.adapter.azure; - -import java.io.IOException; -import java.util.function.Function; - -import org.junit.After; -import org.junit.Test; -import reactor.core.publisher.Flux; - -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionType; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.annotation.Bean; -import org.springframework.context.support.GenericApplicationContext; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -public class AzureSpringFunctionInitializerTests { - - private AzureSpringFunctionInitializer handler = null; - - AzureSpringFunctionInitializer handler(Class config) { - AzureSpringFunctionInitializer handler = new AzureSpringFunctionInitializer( - config); - this.handler = handler; - return handler; - } - - @After - public void close() throws IOException { - if (this.handler != null) { - this.handler.close(); - } - } - - @Test - public void bareConfig() { - AzureSpringFunctionInitializer handler = handler(BareConfig.class); - handler.initialize(new TestExecutionContext("uppercase")); - Bar bar = (Bar) Flux.from(handler.getFunction().apply(Flux.just(new Foo("bar")))) - .blockFirst(); - assertThat(bar.getValue()).isEqualTo("BAR"); - } - - @Test - public void initializer() { - AzureSpringFunctionInitializer handler = handler(InitializerConfig.class); - handler.initialize(new TestExecutionContext("uppercase")); - Bar bar = (Bar) Flux.from(handler.getFunction().apply(Flux.just(new Foo("bar")))) - .blockFirst(); - assertThat(bar.getValue()).isEqualTo("BAR"); - } - - @Test - public void function() { - AzureSpringFunctionInitializer handler = handler(FunctionConfig.class); - handler.initialize(new TestExecutionContext("uppercase")); - Bar bar = (Bar) Flux.from(handler.getFunction().apply(Flux.just(new Foo("bar")))) - .blockFirst(); - assertThat(bar.getValue()).isEqualTo("BAR"); - } - - @SpringBootConfiguration - @EnableAutoConfiguration - protected static class BareConfig { - - @Bean - public Function function() { - return foo -> new Bar(foo.getValue().toUpperCase()); - } - - } - - @SpringBootConfiguration - protected static class FunctionConfig implements Function { - - @Override - public Bar apply(Foo foo) { - return new Bar(foo.getValue().toUpperCase()); - } - - } - - @SpringBootConfiguration - protected static class InitializerConfig - implements ApplicationContextInitializer { - - public Function function() { - return foo -> new Bar(foo.getValue().toUpperCase()); - } - - @Override - public void initialize(GenericApplicationContext context) { - context.registerBean(FunctionRegistration.class, - () -> new FunctionRegistration>(function(), - "uppercase") - .type(FunctionType.from(Foo.class).to(Bar.class))); - } - - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java new file mode 100644 index 000000000..1707ee5ae --- /dev/null +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java @@ -0,0 +1,370 @@ +/* + * 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 + * + * http://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; + +import java.io.Closeable; +import java.io.InputStream; +import java.lang.reflect.Type; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +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; +import org.apache.commons.logging.LogFactory; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.WebApplicationType; +import org.springframework.cloud.function.context.catalog.FunctionInspector; +import org.springframework.cloud.function.context.config.FunctionContextUtils; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.util.ClassUtils; + +/** + * Base implementation for adapter initializers and request handlers. + * + * @param the type of the target specific (native) context object. + * + * @author Oleg Zhurakousky + * @since 2.1 + */ +public abstract class AbstractSpringFunctionAdapterInitializer implements Closeable { + + private static Log logger = LogFactory.getLog(AbstractSpringFunctionAdapterInitializer.class); + + private final Class configurationClass; + + private Function, Publisher> function; + + private Consumer> consumer; + + private Supplier> supplier; + + private AtomicBoolean initialized = new AtomicBoolean(); + + @Autowired(required = false) + private FunctionInspector inspector; + + @Autowired(required = false) + private FunctionCatalog catalog; + + private ConfigurableApplicationContext context; + + public ConfigurableApplicationContext getContext() { + return context; + } + + public AbstractSpringFunctionAdapterInitializer(Class configurationClass) { + this.configurationClass = configurationClass; + } + + public AbstractSpringFunctionAdapterInitializer() { + this(getStartClass()); + } + + @Override + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + protected void initialize(C targetContext) { + if (!this.initialized.compareAndSet(false, true)) { + return; + } + logger.info("Initializing: " + this.configurationClass); + SpringApplication builder = springApplication(); + ConfigurableApplicationContext context = builder.run(); + context.getAutowireCapableBeanFactory().autowireBean(this); + this.context = context; + if (this.catalog == null) { + initFunctionConsumerOrSupplierFromContext(targetContext); + } + else { + initFunctionConsumerOrSupplierFromCatalog(targetContext); + } + } + + protected FunctionInspector getInspector() { + return inspector; + } + + protected Class getInputType() { + if (this.inspector != null) { + return this.inspector.getInputType(function()); + } + return Object.class; + } + + protected Function, Publisher> getFunction() { + return function; + } + + protected Object function() { + if (this.function != null) { + return this.function; + } + else if (this.consumer != null) { + return this.consumer; + } + else if (this.supplier != null) { + return this.supplier; + } + return null; + } + + protected Publisher apply(Publisher input) { + if (this.function != null) { + return Flux.from(this.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"); + } + + /** + * Allows you to resolve function name for cases where it + * could not be located under default name. + * + * Default implementation returns empty string. + * + * @param targetContext the target context instance + * @return the name of the function + */ + protected String doResolveName(Object targetContext) { + return ""; + } + + protected O result(Object input, Publisher output) { + List result = new ArrayList<>(); + for (Object value : Flux.from(output).toIterable()) { + result.add(value); + } + if (isSingleInput(getFunction(), input) && result.size() == 1) { + @SuppressWarnings("unchecked") + O value = (O) result.get(0); + return value; + } + if (isSingleOutput(getFunction(), input) && result.size() == 1) { + @SuppressWarnings("unchecked") + O value = (O) result.get(0); + return value; + } + @SuppressWarnings("unchecked") + O value = (O) result; + return value; + } + + private boolean isSingleInput(Function function, Object input) { + if (!(input instanceof Collection)) { + return true; + } + if (getInspector() != null) { + return Collection.class + .isAssignableFrom(getInspector().getInputType(function)); + } + return ((Collection) input).size() <= 1; + } + + private boolean isSingleOutput(Function function, Object output) { + if (!(output instanceof Collection)) { + return true; + } + if (getInspector() != null) { + return Collection.class + .isAssignableFrom(getInspector().getOutputType(function)); + } + return ((Collection) output).size() <= 1; + } + + private String resolveName(Class type, Object targetContext) { + String functionName = context.getEnvironment().getProperty("function.name"); + if (functionName != null) { + return functionName; + } + else if (type.isAssignableFrom(Function.class)) { + return "function"; + } + else if (type.isAssignableFrom(Consumer.class)) { + return "consumer"; + } + else if (type.isAssignableFrom(Supplier.class)) { + return "supplier"; + } + throw new IllegalStateException("Unknown type " + type); + } + + private static Class getStartClass() { + ClassLoader classLoader = AbstractSpringFunctionAdapterInitializer.class.getClassLoader(); + if (System.getenv("MAIN_CLASS") != null) { + return ClassUtils.resolveClassName(System.getenv("MAIN_CLASS"), classLoader); + } + try { + Class result = getStartClass( + Collections.list(classLoader.getResources("META-INF/MANIFEST.MF"))); + if (result == null) { + result = getStartClass(Collections + .list(classLoader.getResources("meta-inf/manifest.mf"))); + } + logger.info("Main class: " + result); + return result; + } + catch (Exception ex) { + logger.error("Failed to find main class", ex); + return null; + } + } + + private static Class getStartClass(List list) { + logger.info("Searching manifests: " + list); + for (URL url : list) { + try { + logger.info("Searching manifest: " + url); + InputStream inputStream = url.openStream(); + try { + Manifest manifest = new Manifest(inputStream); + String startClass = manifest.getMainAttributes() + .getValue("Start-Class"); + if (startClass != null) { + return ClassUtils.forName(startClass, + AbstractSpringFunctionAdapterInitializer.class.getClassLoader()); + } + } + finally { + inputStream.close(); + } + } + catch (Exception ex) { + } + } + return null; + } + + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private T getAndInstrumentFromContext(String name) { + FunctionRegistration functionRegistration = + new FunctionRegistration(context.getBean(name), name); + + Type type = FunctionContextUtils. + findType(name, (ConfigurableListableBeanFactory) this.context.getBeanFactory()); + FunctionType functionType = new FunctionType(type); + return (T) functionRegistration.type(functionType).wrap().getTarget(); + } + + private void initFunctionConsumerOrSupplierFromContext(Object targetContext) { + String name = resolveName(Function.class, targetContext); + if (context.containsBean(name) && context.getBean(name) instanceof Function) { + this.function = getAndInstrumentFromContext(name); + return; + } + + name = resolveName(Consumer.class, targetContext); + if (context.containsBean(name) && context.getBean(name) instanceof Consumer) { + this.consumer = getAndInstrumentFromContext(name); + return; + } + + name = resolveName(Supplier.class, targetContext); + if (context.containsBean(name) && context.getBean(name) instanceof Supplier) { + this.supplier = getAndInstrumentFromContext(name); + return; + } + } + + private void initFunctionConsumerOrSupplierFromCatalog(Object targetContext) { + String name = resolveName(Function.class, targetContext); + this.function = this.catalog.lookup(Function.class, name); + if (this.function != null) { + return; + } + + name = resolveName(Consumer.class, targetContext); + this.consumer = this.catalog.lookup(Consumer.class, name); + if (this.consumer != null) { + return; + } + + name = resolveName(Supplier.class, targetContext); + this.supplier = this.catalog.lookup(Supplier.class, name); + if (this.supplier != null) { + return; + } + + if (this.catalog.size() == 1) { + Iterator names = this.catalog.getNames(Function.class).iterator(); + if (names.hasNext()) { + this.function = this.catalog.lookup(Function.class, names.next()); + return; + } + + names = this.catalog.getNames(Consumer.class).iterator(); + if (names.hasNext()) { + this.consumer = this.catalog.lookup(Consumer.class, names.next()); + return; + } + + names = this.catalog.getNames(Supplier.class).iterator(); + if (names.hasNext()) { + this.supplier = this.catalog.lookup(Supplier.class, names.next()); + return; + } + } + else { + name = this.doResolveName(targetContext); + this.function = this.catalog.lookup(Function.class, name); + if (this.function != null) { + return; + } + + this.consumer = this.catalog.lookup(Consumer.class, name); + if (this.consumer != null) { + return; + } + this.supplier = this.catalog.lookup(Supplier.class, name); + if (this.supplier != null) { + return; + } + } + } + + + private SpringApplication springApplication() { + Class sourceClass = this.configurationClass; + SpringApplication application = new org.springframework.cloud.function.context.FunctionalSpringApplication( + sourceClass); + application.setWebApplicationType(WebApplicationType.NONE); + return application; + } +} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionContextUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionContextUtils.java index aad4d043b..cfbae2e0c 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionContextUtils.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionContextUtils.java @@ -40,7 +40,7 @@ import org.springframework.util.ReflectionUtils; * @author Oleg Zhurakousky * @since 2.0 */ -abstract class FunctionContextUtils { +public abstract class FunctionContextUtils { public static Type findType(String name, ConfigurableListableBeanFactory registry) { AbstractBeanDefinition definition = (AbstractBeanDefinition) registry 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-context/src/test/java/org/springframework/cloud/function/context/SpringFunctionAdapterInitializerTests.java similarity index 78% rename from spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializerTests.java rename to spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/SpringFunctionAdapterInitializerTests.java index b0829f297..e9765c9c9 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-context/src/test/java/org/springframework/cloud/function/context/SpringFunctionAdapterInitializerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.function.adapter.aws; +package org.springframework.cloud.function.context; import java.util.function.Consumer; import java.util.function.Function; @@ -26,8 +26,6 @@ import org.junit.Ignore; import org.junit.Test; import reactor.core.publisher.Flux; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionType; import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.annotation.Bean; @@ -35,15 +33,18 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.support.GenericApplicationContext; + + import static org.assertj.core.api.Assertions.assertThat; /** - * @author Dave Syer + * + * @author Oleg Zhurakousky * */ -public class SpringFunctionInitializerTests { +public class SpringFunctionAdapterInitializerTests { - private SpringFunctionInitializer initializer; + private AbstractSpringFunctionAdapterInitializer initializer; @After public void after() { @@ -55,24 +56,30 @@ public class SpringFunctionInitializerTests { @Test public void functionBean() { - this.initializer = new SpringFunctionInitializer(FluxFunctionConfig.class); - this.initializer.initialize(); + this.initializer = new AbstractSpringFunctionAdapterInitializer(FluxFunctionConfig.class) { + + }; + this.initializer.initialize(null); Flux result = Flux.from(this.initializer.apply(Flux.just(new Foo()))); assertThat(result.blockFirst()).isInstanceOf(Bar.class); } @Test public void functionApp() { - this.initializer = new SpringFunctionInitializer(FluxFunctionApp.class); - this.initializer.initialize(); + this.initializer = new AbstractSpringFunctionAdapterInitializer(FluxFunctionApp.class) { + + }; + this.initializer.initialize(null); Flux result = Flux.from(this.initializer.apply(Flux.just(new Foo()))); assertThat(result.blockFirst()).isInstanceOf(Bar.class); } @Test public void functionCatalog() { - this.initializer = new SpringFunctionInitializer(FunctionConfig.class); - this.initializer.initialize(); + this.initializer = new AbstractSpringFunctionAdapterInitializer(FunctionConfig.class) { + + }; + this.initializer.initialize(null); Flux result = Flux.from(this.initializer.apply(Flux.just(new Foo()))); assertThat(result.blockFirst()).isInstanceOf(Bar.class); } @@ -80,33 +87,41 @@ public class SpringFunctionInitializerTests { @Test @Ignore // related to boot 2.1 no bean override change public void functionRegistrar() { - this.initializer = new SpringFunctionInitializer(FunctionRegistrar.class); - this.initializer.initialize(); + this.initializer = new AbstractSpringFunctionAdapterInitializer(FunctionRegistrar.class) { + + }; + this.initializer.initialize(null); Flux result = Flux.from(this.initializer.apply(Flux.just(new Foo()))); assertThat(result.blockFirst()).isInstanceOf(Bar.class); } @Test public void namedFunctionCatalog() { - this.initializer = new SpringFunctionInitializer(NamedFunctionConfig.class); + this.initializer = new AbstractSpringFunctionAdapterInitializer(NamedFunctionConfig.class) { + + }; System.setProperty("function.name", "other"); - this.initializer.initialize(); + this.initializer.initialize(null); Flux result = Flux.from(this.initializer.apply(Flux.just(new Foo()))); assertThat(result.blockFirst()).isInstanceOf(Bar.class); } @Test public void consumerCatalog() { - this.initializer = new SpringFunctionInitializer(ConsumerConfig.class); - this.initializer.initialize(); + this.initializer = new AbstractSpringFunctionAdapterInitializer(ConsumerConfig.class) { + + }; + this.initializer.initialize(null); Flux result = Flux.from(this.initializer.apply(Flux.just(new Foo()))); assertThat(result.toStream().collect(Collectors.toList())).isEmpty(); } @Test public void supplierCatalog() { - initializer = new SpringFunctionInitializer(SupplierConfig.class); - initializer.initialize(); + initializer = new AbstractSpringFunctionAdapterInitializer(SupplierConfig.class) { + + }; + initializer.initialize(null); Flux result = Flux.from(initializer.apply(Flux.empty())); assertThat(result.blockFirst()).isInstanceOf(Bar.class); } @@ -198,5 +213,4 @@ public class SpringFunctionInitializerTests { protected static class Bar { } - } diff --git a/spring-cloud-function-samples/function-sample-azure/pom.xml b/spring-cloud-function-samples/function-sample-azure/pom.xml index a21cbffa1..d0a2a76dd 100644 --- a/spring-cloud-function-samples/function-sample-azure/pom.xml +++ b/spring-cloud-function-samples/function-sample-azure/pom.xml @@ -55,7 +55,7 @@ org.springframework.cloud spring-cloud-function-dependencies - 2.0.0.BUILD-SNAPSHOT + 2.1.0.BUILD-SNAPSHOT pom import