Fix regression for implicit composition

Also, added message check in AWS destination resolver
This commit is contained in:
Oleg Zhurakousky
2020-04-16 21:05:20 +02:00
parent f1c15bf950
commit ab68cb58e9
6 changed files with 48 additions and 10 deletions

View File

@@ -26,10 +26,12 @@ public class LambdaDestinationResolver implements DestinationResolver {
@Override @Override
public String destination(Supplier<?> supplier, String name, Object value) { public String destination(Supplier<?> supplier, String name, Object value) {
Message<?> message = (Message<?>) value; if (value instanceof Message) {
MessageHeaders headers = message.getHeaders(); Message<?> message = (Message<?>) value;
if (headers.containsKey("lambda-runtime-aws-request-id")) { MessageHeaders headers = message.getHeaders();
return (String) headers.get("lambda-runtime-aws-request-id"); if (headers.containsKey("lambda-runtime-aws-request-id")) {
return (String) headers.get("lambda-runtime-aws-request-id");
}
} }
return "unknown"; return "unknown";
} }

View File

@@ -154,7 +154,7 @@ public class BeanFactoryAwareFunctionRegistry extends SimpleFunctionRegistry imp
@Override @Override
String discoverDefaultDefinitionIfNecessary(String definition) { String discoverDefaultDefinitionIfNecessary(String definition) {
if (StringUtils.isEmpty(definition)) { if (StringUtils.isEmpty(definition) || definition.endsWith("|")) {
// the underscores are for Kotlin function registrations (see KotlinLambdaToFunctionAutoConfiguration) // the underscores are for Kotlin function registrations (see KotlinLambdaToFunctionAutoConfiguration)
String[] functionNames = Stream.of(this.applicationContext.getBeanNamesForType(Function.class)) String[] functionNames = Stream.of(this.applicationContext.getBeanNamesForType(Function.class))
.filter(n -> !n.endsWith(FunctionRegistration.REGISTRATION_NAME_SUFFIX) && !n .filter(n -> !n.endsWith(FunctionRegistration.REGISTRATION_NAME_SUFFIX) && !n
@@ -184,6 +184,10 @@ public class BeanFactoryAwareFunctionRegistry extends SimpleFunctionRegistry imp
} }
definition = names.get(0); definition = names.get(0);
} }
else if (definition.endsWith("|")) {
Set<String> fNames = this.getNames(null);
definition = this.determinImpliedDefinition(fNames, definition);
}
else { else {
definition = this.discoverDefaultDefinitionFromRegistration(); definition = this.discoverDefaultDefinitionFromRegistration();
} }

View File

@@ -27,7 +27,10 @@ import org.springframework.util.Assert;
* @author Dave Syer * @author Dave Syer
* @author Mark Fisher * @author Mark Fisher
* @author Oleg Zhurakousky * @author Oleg Zhurakousky
*
* @deprecated since 3.1. End-of-life. Not used by the framework anymore in favor of SimpleFunctionRegistry
*/ */
@Deprecated
public class InMemoryFunctionCatalog extends AbstractComposableFunctionRegistry { public class InMemoryFunctionCatalog extends AbstractComposableFunctionRegistry {
public InMemoryFunctionCatalog() { public InMemoryFunctionCatalog() {

View File

@@ -26,6 +26,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@@ -48,6 +49,7 @@ import reactor.util.function.Tuples;
import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils; import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.function.context.FunctionProperties; import org.springframework.cloud.function.context.FunctionProperties;
import org.springframework.cloud.function.context.FunctionRegistration; import org.springframework.cloud.function.context.FunctionRegistration;
import org.springframework.cloud.function.context.FunctionRegistry; import org.springframework.cloud.function.context.FunctionRegistry;
@@ -72,6 +74,11 @@ import org.springframework.util.StringUtils;
/** /**
*
* Basic implementation of FunctionRegistry which maintains the cache of registered functions while
* decorating them with additional features such as transparent type conversion, composition, routing etc.
*
* Unlike {@link BeanFactoryAwareFunctionRegistry}, this implementation does not depend on {@link BeanFactory}.
* *
* @author Oleg Zhurakousky * @author Oleg Zhurakousky
* *
@@ -217,8 +224,31 @@ public class SimpleFunctionRegistry implements FunctionRegistry, FunctionInspect
else if (!this.registrationsByName.containsKey(definition) && this.registrationsByName.size() == 1) { else if (!this.registrationsByName.containsKey(definition) && this.registrationsByName.size() == 1) {
definition = this.registrationsByName.keySet().iterator().next(); definition = this.registrationsByName.keySet().iterator().next();
} }
else if (definition.endsWith("|")) {
if (this.registrationsByName.size() == 2) {
Set<String> fNames = this.getNames(null);
definition = this.determinImpliedDefinition(fNames, definition);
}
}
return definition; return definition;
}
String determinImpliedDefinition(Set<String> fNames, String originalDefinition) {
if (fNames.size() == 2) {
Iterator<String> iter = fNames.iterator();
String n1 = iter.next();
String n2 = iter.next();
String[] definitionName = StringUtils.delimitedListToStringArray(originalDefinition, "|");
if (definitionName[0].equals(n1)) {
definitionName[1] = n2;
originalDefinition = definitionName[0] + "|" + definitionName[1];
}
else {
definitionName[1] = n1;
originalDefinition = definitionName[0] + "|" + definitionName[1];
}
}
return originalDefinition;
} }
Type discovereFunctionTypeByName(String name) { Type discovereFunctionTypeByName(String name) {

View File

@@ -169,7 +169,6 @@ public class ContextFunctionCatalogInitializer implements ApplicationContextInit
} }
if (this.context.getBeanFactory().getBeanNamesForType(FunctionCatalog.class, false, false).length == 0) { if (this.context.getBeanFactory().getBeanNamesForType(FunctionCatalog.class, false, false).length == 0) {
this.context.registerBean(SimpleFunctionRegistry.class, () -> { this.context.registerBean(SimpleFunctionRegistry.class, () -> {
List<MessageConverter> messageConverters = new ArrayList<>(); List<MessageConverter> messageConverters = new ArrayList<>();
JsonMapper jsonMapper = this.context.getBean(JsonMapper.class); JsonMapper jsonMapper = this.context.getBean(JsonMapper.class);

View File

@@ -28,6 +28,7 @@ import org.junit.Test;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import org.springframework.cloud.function.context.FunctionRegistration; import org.springframework.cloud.function.context.FunctionRegistration;
import org.springframework.cloud.function.context.FunctionRegistry;
import org.springframework.cloud.function.context.FunctionType; import org.springframework.cloud.function.context.FunctionType;
import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper;
import org.springframework.cloud.function.context.config.JsonMessageConverter; import org.springframework.cloud.function.context.config.JsonMessageConverter;
@@ -103,21 +104,20 @@ public class SimpleFunctionRegistryTests {
} }
@Test @Test
@Ignore
public void testFunctionCompositionImplicit() { public void testFunctionCompositionImplicit() {
FunctionRegistration<Words> wordsRegistration = new FunctionRegistration<>( FunctionRegistration<Words> wordsRegistration = new FunctionRegistration<>(
new Words(), "words").type(FunctionType.of(Words.class)); new Words(), "words").type(FunctionType.of(Words.class));
FunctionRegistration<Reverse> reverseRegistration = new FunctionRegistration<>( FunctionRegistration<Reverse> reverseRegistration = new FunctionRegistration<>(
new Reverse(), "reverse").type(FunctionType.of(Reverse.class)); new Reverse(), "reverse").type(FunctionType.of(Reverse.class));
SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter); FunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter);
catalog.register(wordsRegistration); catalog.register(wordsRegistration);
catalog.register(reverseRegistration); catalog.register(reverseRegistration);
// There's only one function, we should be able to leave that blank // There's only one function, we should be able to leave that blank
Supplier<Flux<String>> lookedUpFunction = catalog.lookup("words|"); Supplier<String> lookedUpFunction = catalog.lookup("words|");
assertThat(lookedUpFunction).isNotNull(); assertThat(lookedUpFunction).isNotNull();
assertThat(lookedUpFunction.get().blockFirst()).isEqualTo("olleh"); assertThat(lookedUpFunction.get()).isEqualTo("olleh");
} }
@Test @Test