GH-409 Fix default discovery of functions

Fixed discovery of functions to ensure that even in cases where default function is found but it's type can not be determined such function is discarded. This effectively ensures that if the actual instance does not match the declared type such function is not treated as function.

Resolves #409
This commit is contained in:
Oleg Zhurakousky
2019-09-22 17:15:19 -04:00
parent f1939e1541
commit 11a6e923ee
3 changed files with 72 additions and 16 deletions

View File

@@ -164,10 +164,6 @@ public class BeanFactoryAwareFunctionRegistry
return this.registrationsByFunction.get(function);
}
public FunctionType getFunctionType(String name) {
return FunctionType.of(FunctionTypeUtils.getFunctionType(this.lookup(name), this));
}
private Object locateFunction(String name) {
Object function = null;
if (this.applicationContext.containsBean(name)) {
@@ -214,6 +210,7 @@ public class BeanFactoryAwareFunctionRegistry
.filter(n -> !n.endsWith(FunctionRegistration.REGISTRATION_NAME_SUFFIX) && !n.equals(RoutingFunction.FUNCTION_NAME)).toArray(String[]::new);
String[] supplierNames = Stream.of(this.applicationContext.getBeanNamesForType(Supplier.class))
.filter(n -> !n.endsWith(FunctionRegistration.REGISTRATION_NAME_SUFFIX) && !n.equals(RoutingFunction.FUNCTION_NAME)).toArray(String[]::new);
/*
* we may need to add BiFunction and BiConsumer at some point
*/
@@ -221,7 +218,8 @@ public class BeanFactoryAwareFunctionRegistry
.concat(Stream.of(functionNames), Stream.concat(Stream.of(consumerNames), Stream.of(supplierNames))).collect(Collectors.toList());
if (!ObjectUtils.isEmpty(names)) {
Assert.isTrue(names.size() == 1, "Found more then one function in BeanFactory: " + names);
Assert.isTrue(names.size() == 1, "Found more then one function in BeanFactory: " + names
+ ". Consider providing 'spring.cloud.function.definition' property.");
definition = names.get(0);
}
else {
@@ -230,6 +228,15 @@ public class BeanFactoryAwareFunctionRegistry
definition = this.registrationsByName.keySet().iterator().next();
}
}
if (StringUtils.hasText(definition)) {
Type functionType = discoverFunctionType(this.applicationContext.getBean(definition), definition);
if (!FunctionTypeUtils.isSupplier(functionType) && !FunctionTypeUtils.isFunction(functionType) && !FunctionTypeUtils.isConsumer(functionType)) {
logger.info("Discovered functional instance of bean '" + definition + "' as a default function, however its "
+ "function argument types can not be determined. Discarding.");
definition = null;
}
}
}
return definition;
}
@@ -576,8 +583,11 @@ public class BeanFactoryAwareFunctionRegistry
List<MimeType> acceptedContentTypes = MimeTypeUtils.parseMimeTypes(acceptedOutputMimeTypes[0].toString());
convertedValue = acceptedContentTypes.stream()
.map(acceptedContentType -> messageConverter
.toMessage(value, new MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, acceptedContentType))))
.map(acceptedContentType -> {
Object v = value instanceof Message ? ((Message<?>) value).getPayload() : value;
return messageConverter
.toMessage(v, new MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, acceptedContentType)));
})
.filter(v -> v != null)
.findFirst().orElse(null);
}

View File

@@ -128,14 +128,6 @@ public final class FunctionTypeUtils {
}
}
public static Type getFunctionType(Object function, FunctionInspector inspector) {
FunctionRegistration<?> registration = inspector.getRegistration(function);
if (registration != null) {
return registration.getType().getType();
}
return null;
}
public static Type unwrapActualTypeByIndex(Type type, int index) {
if (isMessage(type) || isPublisher(type)) {
if (isPublisher(type)) {

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.function.context.catalog;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
@@ -53,7 +54,11 @@ import static org.assertj.core.api.Assertions.assertThat;
public class BeanFactoryAwareFunctionRegistryTests {
private FunctionCatalog configureCatalog() {
ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class)
return this.configureCatalog(SampleFunctionConfiguration.class);
}
private FunctionCatalog configureCatalog(Class<?>... configClass) {
ApplicationContext context = new SpringApplicationBuilder(configClass)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.main.lazy-initialization=true");
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
@@ -268,6 +273,15 @@ public class BeanFactoryAwareFunctionRegistryTests {
result.getT3().subscribe(v -> System.out.println("=> 3: " + v));
}
@Test
public void SCF_GH_409ConfigurationTests() {
FunctionCatalog catalog = this.configureCatalog(SCF_GH_409ConfigurationAsSupplier.class);
assertThat((Object) catalog.lookup("")).isNull();
catalog = this.configureCatalog(SCF_GH_409ConfigurationAsFunction.class);
assertThat((Object) catalog.lookup("")).isNull();
}
@EnableAutoConfiguration
@Configuration
@@ -431,6 +445,46 @@ public class BeanFactoryAwareFunctionRegistryTests {
}
}
@EnableAutoConfiguration
public static class SCF_GH_409ConfigurationAsSupplier {
@Bean
public Serializable blah() {
return new Foo();
}
private static class Foo implements Supplier<Object>, Serializable {
@Override
public Object get() {
// TODO Auto-generated method stub
return null;
}
}
}
@EnableAutoConfiguration
public static class SCF_GH_409ConfigurationAsFunction {
@Bean
public Serializable blah() {
return new Foo();
}
private static class Foo implements Function<Object, Object>, Serializable {
@Override
public Object apply(Object t) {
// TODO Auto-generated method stub
return null;
}
}
}
public static class Person {
private String name;
private int id;