Change FunctionCatalog to key off Class<?>
Makes it possible to support other "function" types in the future. The user is always taking a risk with the lookup that the object returned has the generic type desired (but that hasn't changed with this commit). FunctionCatalog is a lot simpler as a result and also a lot more flexible.
This commit is contained in:
@@ -99,17 +99,17 @@ public class SpringFunctionInitializer implements Closeable {
|
||||
this.function = context.getBean(name, Function.class);
|
||||
}
|
||||
else {
|
||||
this.function = this.catalog.lookupFunction(name);
|
||||
this.function = this.catalog.lookup(Function.class, name);
|
||||
if (this.function == null) {
|
||||
if (defaultName) {
|
||||
name = "consumer";
|
||||
}
|
||||
this.consumer = this.catalog.lookupConsumer(name);
|
||||
this.consumer = this.catalog.lookup(Consumer.class, name);
|
||||
if (this.consumer == null) {
|
||||
if (defaultName) {
|
||||
name = "supplier";
|
||||
}
|
||||
this.supplier = this.catalog.lookupSupplier(name);
|
||||
this.supplier = this.catalog.lookup(Supplier.class, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,8 +105,8 @@ public class AzureSpringFunctionInitializer implements Closeable {
|
||||
this.function = context.getBean(name, Function.class);
|
||||
}
|
||||
else {
|
||||
Set<String> functionNames = this.catalog.getFunctionNames();
|
||||
this.function = this.catalog.lookupFunction(functionNames.iterator().next());
|
||||
Set<String> functionNames = this.catalog.getNames(Function.class);
|
||||
this.function = this.catalog.lookup(Function.class, functionNames.iterator().next());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,11 +64,11 @@ public class OpenWhiskFunctionInitializer {
|
||||
String name = this.properties.getName();
|
||||
String type = this.properties.getType();
|
||||
if ("function".equals(type)) {
|
||||
this.function = this.catalog.lookupFunction(name);
|
||||
this.function = this.catalog.lookup(Function.class, name);
|
||||
} else if ("consumer".equals(type)) {
|
||||
this.consumer = this.catalog.lookupConsumer(name);
|
||||
this.consumer = this.catalog.lookup(Consumer.class, name);
|
||||
} else if ("supplier".equals(type)) {
|
||||
this.supplier = this.catalog.lookupSupplier(name);
|
||||
this.supplier = this.catalog.lookup(Supplier.class, name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,24 +17,14 @@
|
||||
package org.springframework.cloud.function.context;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public interface FunctionCatalog {
|
||||
|
||||
<T> Supplier<T> lookupSupplier(String name);
|
||||
<T> T lookup(Class<?> type, String name);
|
||||
|
||||
<T, R> Function<T, R> lookupFunction(String name);
|
||||
Set<String> getNames(Class<?> type);
|
||||
|
||||
<T> Consumer<T> lookupConsumer(String name);
|
||||
|
||||
Set<String> getSupplierNames();
|
||||
|
||||
Set<String> getFunctionNames();
|
||||
|
||||
Set<String> getConsumerNames();
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ public class FunctionType {
|
||||
}
|
||||
|
||||
public FunctionType wrap(Class<?> wrapper) {
|
||||
if (wrapper.isAssignableFrom(getInputWrapper())) {
|
||||
if (wrapper.isAssignableFrom(getInputWrapper()) || !isWrapper(wrapper)) {
|
||||
return this;
|
||||
}
|
||||
return new FunctionType(ResolvableType.forClassWithGenerics(Function.class,
|
||||
|
||||
@@ -16,24 +16,48 @@
|
||||
|
||||
package org.springframework.cloud.function.context.catalog;
|
||||
|
||||
import org.springframework.cloud.function.context.FunctionRegistration;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface FunctionInspector {
|
||||
|
||||
boolean isMessage(Object function);
|
||||
FunctionRegistration<?> getRegistration(Object function);
|
||||
|
||||
Class<?> getInputType(Object function);
|
||||
default boolean isMessage(Object function) {
|
||||
FunctionRegistration<?> registration = getRegistration(function);
|
||||
return registration == null ? false : registration.getType().isMessage();
|
||||
}
|
||||
|
||||
Class<?> getOutputType(Object function);
|
||||
default Class<?> getInputType(Object function) {
|
||||
FunctionRegistration<?> registration = getRegistration(function);
|
||||
return registration == null ? Object.class
|
||||
: registration.getType().getInputType();
|
||||
}
|
||||
|
||||
Class<?> getInputWrapper(Object function);
|
||||
default Class<?> getOutputType(Object function) {
|
||||
FunctionRegistration<?> registration = getRegistration(function);
|
||||
return registration == null ? Object.class
|
||||
: registration.getType().getOutputType();
|
||||
}
|
||||
|
||||
Class<?> getOutputWrapper(Object function);
|
||||
default Class<?> getInputWrapper(Object function) {
|
||||
FunctionRegistration<?> registration = getRegistration(function);
|
||||
return registration == null ? Object.class
|
||||
: registration.getType().getInputWrapper();
|
||||
}
|
||||
|
||||
Object convert(Object function, String value);
|
||||
default Class<?> getOutputWrapper(Object function) {
|
||||
FunctionRegistration<?> registration = getRegistration(function);
|
||||
return registration == null ? Object.class
|
||||
: registration.getType().getOutputWrapper();
|
||||
}
|
||||
|
||||
String getName(Object function);
|
||||
default String getName(Object function) {
|
||||
FunctionRegistration<?> registration = getRegistration(function);
|
||||
return registration == null ? null : registration.getNames().iterator().next();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,11 +42,7 @@ import org.springframework.util.Assert;
|
||||
public class InMemoryFunctionCatalog
|
||||
implements FunctionRegistry, ApplicationEventPublisherAware {
|
||||
|
||||
private final Map<String, Function<?, ?>> functions;
|
||||
|
||||
private final Map<String, Consumer<?>> consumers;
|
||||
|
||||
private final Map<String, Supplier<?>> suppliers;
|
||||
private final Map<Class<?>, Map<String, Object>> functions;
|
||||
|
||||
@Autowired(required = false)
|
||||
private ApplicationEventPublisher publisher;
|
||||
@@ -57,49 +53,42 @@ public class InMemoryFunctionCatalog
|
||||
|
||||
public InMemoryFunctionCatalog(Set<FunctionRegistration<?>> registrations) {
|
||||
Assert.notNull(registrations, "'registrations' must not be null");
|
||||
this.suppliers = new HashMap<>();
|
||||
this.functions = new HashMap<>();
|
||||
this.consumers = new HashMap<>();
|
||||
registrations.stream().forEach(reg -> reg.getNames().stream().forEach(name -> {
|
||||
if (reg.getTarget() instanceof Consumer) {
|
||||
consumers.put(name, (Consumer<?>) reg.getTarget());
|
||||
}
|
||||
else if (reg.getTarget() instanceof Function) {
|
||||
functions.put(name, (Function<?, ?>) reg.getTarget());
|
||||
}
|
||||
else if (reg.getTarget() instanceof Supplier) {
|
||||
suppliers.put(name, (Supplier<?>) reg.getTarget());
|
||||
}
|
||||
}));
|
||||
registrations.stream().forEach(reg -> register(reg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void register(FunctionRegistration<T> registration) {
|
||||
Map<String, ?> values = null;
|
||||
FunctionRegistrationEvent event;
|
||||
Class<?> type;
|
||||
if (registration.getTarget() instanceof Function) {
|
||||
values = this.functions;
|
||||
type = Function.class;
|
||||
event = new FunctionRegistrationEvent(this, Function.class,
|
||||
registration.getNames());
|
||||
}
|
||||
else if (registration.getTarget() instanceof Supplier) {
|
||||
values = this.suppliers;
|
||||
type = Supplier.class;
|
||||
event = new FunctionRegistrationEvent(this, Supplier.class,
|
||||
registration.getNames());
|
||||
}
|
||||
else {
|
||||
values = this.consumers;
|
||||
else if (registration.getTarget() instanceof Consumer) {
|
||||
type = Consumer.class;
|
||||
event = new FunctionRegistrationEvent(this, Consumer.class,
|
||||
registration.getNames());
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) values;
|
||||
else {
|
||||
type = Object.class;
|
||||
event = new FunctionRegistrationEvent(this, Object.class,
|
||||
registration.getNames());
|
||||
}
|
||||
Map<String, Object> map = functions.computeIfAbsent(type, key -> new HashMap<>());
|
||||
for (String name : registration.getNames()) {
|
||||
map.put(name, registration.getTarget());
|
||||
}
|
||||
publisher.publishEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
|
||||
this.publisher = publisher;
|
||||
}
|
||||
@@ -108,16 +97,10 @@ public class InMemoryFunctionCatalog
|
||||
public void init() {
|
||||
if (publisher != null) {
|
||||
if (!functions.isEmpty()) {
|
||||
publisher.publishEvent(new FunctionRegistrationEvent(this, Function.class,
|
||||
functions.keySet()));
|
||||
}
|
||||
if (!consumers.isEmpty()) {
|
||||
publisher.publishEvent(new FunctionRegistrationEvent(this, Consumer.class,
|
||||
consumers.keySet()));
|
||||
}
|
||||
if (!suppliers.isEmpty()) {
|
||||
publisher.publishEvent(new FunctionRegistrationEvent(this, Supplier.class,
|
||||
suppliers.keySet()));
|
||||
for (Class<?> type : functions.keySet()) {
|
||||
publisher.publishEvent(new FunctionRegistrationEvent(this, type,
|
||||
functions.get(type).keySet()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,50 +109,48 @@ public class InMemoryFunctionCatalog
|
||||
public void close() {
|
||||
if (publisher != null) {
|
||||
if (!functions.isEmpty()) {
|
||||
publisher.publishEvent(new FunctionUnregistrationEvent(this,
|
||||
Function.class, functions.keySet()));
|
||||
}
|
||||
if (!consumers.isEmpty()) {
|
||||
publisher.publishEvent(new FunctionUnregistrationEvent(this,
|
||||
Consumer.class, consumers.keySet()));
|
||||
}
|
||||
if (!suppliers.isEmpty()) {
|
||||
publisher.publishEvent(new FunctionUnregistrationEvent(this,
|
||||
Supplier.class, suppliers.keySet()));
|
||||
for (Class<?> type : functions.keySet()) {
|
||||
publisher.publishEvent(new FunctionUnregistrationEvent(this, type,
|
||||
functions.get(type).keySet()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Supplier<T> lookupSupplier(String name) {
|
||||
return (Supplier<T>) suppliers.get(name);
|
||||
public <T> T lookup(Class<?> type, String name) {
|
||||
Map<String, Object> map = null;
|
||||
for (Class<?> key : functions.keySet()) {
|
||||
if (key != Object.class) {
|
||||
if (key.isAssignableFrom(type)) {
|
||||
map = functions.get(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (map == null) {
|
||||
map = functions.get(Object.class);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) map.get(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T, R> Function<T, R> lookupFunction(String name) {
|
||||
return (Function<T, R>) functions.get(name);
|
||||
public Set<String> getNames(Class<?> type) {
|
||||
Map<String, Object> map = null;
|
||||
for (Class<?> key : functions.keySet()) {
|
||||
if (key != Object.class) {
|
||||
if (key.isAssignableFrom(type)) {
|
||||
map = functions.get(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (map == null) {
|
||||
map = functions.get(Object.class);
|
||||
}
|
||||
return map == null ? Collections.emptySet() : map.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Consumer<T> lookupConsumer(String name) {
|
||||
return (Consumer<T>) consumers.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSupplierNames() {
|
||||
return suppliers.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getFunctionNames() {
|
||||
return functions.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getConsumerNames() {
|
||||
return consumers.keySet();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,8 +64,6 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.type.StandardMethodMetadata;
|
||||
import org.springframework.core.type.classreading.MethodMetadataReadingVisitor;
|
||||
@@ -119,38 +117,31 @@ public class ContextFunctionCatalogAutoConfiguration {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Supplier<T> lookupSupplier(String name) {
|
||||
Supplier<T> result = (Supplier<T>) processor.lookupSupplier(name);
|
||||
return result;
|
||||
public <T> T lookup(Class<?> type, String name) {
|
||||
if (Supplier.class.isAssignableFrom(type)) {
|
||||
return (T) processor.lookupSupplier(name);
|
||||
}
|
||||
if (Consumer.class.isAssignableFrom(type)) {
|
||||
return (T) processor.lookupConsumer(name);
|
||||
}
|
||||
if (Function.class.isAssignableFrom(type)) {
|
||||
return (T) processor.lookupFunction(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T, R> Function<T, R> lookupFunction(String name) {
|
||||
Function<T, R> result = (Function<T, R>) processor.lookupFunction(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Consumer<T> lookupConsumer(String name) {
|
||||
Consumer<T> result = (Consumer<T>) processor.lookupConsumer(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSupplierNames() {
|
||||
return this.processor.getSuppliers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getFunctionNames() {
|
||||
return this.processor.getFunctions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getConsumerNames() {
|
||||
return this.processor.getConsumers();
|
||||
public Set<String> getNames(Class<?> type) {
|
||||
if (Supplier.class.isAssignableFrom(type)) {
|
||||
return this.processor.getSuppliers();
|
||||
}
|
||||
if (Consumer.class.isAssignableFrom(type)) {
|
||||
return this.processor.getConsumers();
|
||||
}
|
||||
if (Function.class.isAssignableFrom(type)) {
|
||||
return this.processor.getFunctions();
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public BeanFactoryFunctionCatalog(ContextFunctionRegistry processor) {
|
||||
@@ -168,38 +159,8 @@ public class ContextFunctionCatalogAutoConfiguration {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMessage(Object function) {
|
||||
return processor.isMessage(function);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getInputWrapper(Object function) {
|
||||
return processor.findType(function).getInputWrapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getOutputWrapper(Object function) {
|
||||
return processor.findType(function).getOutputWrapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getInputType(Object function) {
|
||||
return processor.findType(function).getInputType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getOutputType(Object function) {
|
||||
return processor.findType(function).getOutputType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convert(Object function, String value) {
|
||||
return processor.convert(function, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName(Object function) {
|
||||
return processor.registrations.get(function);
|
||||
public FunctionRegistration<?> getRegistration(Object function) {
|
||||
return processor.getRegistration(function);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -219,9 +180,7 @@ public class ContextFunctionCatalogAutoConfiguration {
|
||||
@Autowired
|
||||
private ConfigurableListableBeanFactory registry;
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
private Map<Object, String> registrations = new HashMap<>();
|
||||
private Map<Object, String> names = new HashMap<>();
|
||||
|
||||
private Map<String, FunctionType> types = new HashMap<>();
|
||||
|
||||
@@ -229,6 +188,14 @@ public class ContextFunctionCatalogAutoConfiguration {
|
||||
return this.suppliers.keySet();
|
||||
}
|
||||
|
||||
public FunctionRegistration<?> getRegistration(Object function) {
|
||||
if (!names.containsKey(function)) {
|
||||
return null;
|
||||
}
|
||||
return new FunctionRegistration<>(function).name(names.get(function))
|
||||
.type(findType(function).getType());
|
||||
}
|
||||
|
||||
public Set<String> getConsumers() {
|
||||
return this.consumers.keySet();
|
||||
}
|
||||
@@ -302,7 +269,7 @@ public class ContextFunctionCatalogAutoConfiguration {
|
||||
types.put(name, FunctionType.compose(input, output));
|
||||
}
|
||||
}
|
||||
registrations.put(function, name);
|
||||
names.put(function, name);
|
||||
return function;
|
||||
}
|
||||
|
||||
@@ -413,19 +380,6 @@ public class ContextFunctionCatalogAutoConfiguration {
|
||||
return registrations;
|
||||
}
|
||||
|
||||
private Object convert(Object function, String value) {
|
||||
if (conversionService == null && registry != null) {
|
||||
ConversionService conversionService = this.registry
|
||||
.getConversionService();
|
||||
this.conversionService = conversionService != null ? conversionService
|
||||
: new DefaultConversionService();
|
||||
}
|
||||
Class<?> type = findType(function).getInputType();
|
||||
return conversionService.canConvert(String.class, type)
|
||||
? conversionService.convert(value, type)
|
||||
: value;
|
||||
}
|
||||
|
||||
private Collection<String> getAliases(String key) {
|
||||
Collection<String> names = new LinkedHashSet<>();
|
||||
String value = getQualifier(key);
|
||||
@@ -438,7 +392,7 @@ public class ContextFunctionCatalogAutoConfiguration {
|
||||
|
||||
private void wrap(FunctionRegistration<Object> registration, String key) {
|
||||
Object target = registration.getTarget();
|
||||
this.registrations.put(target, key);
|
||||
this.names.put(target, key);
|
||||
if (registration.getType() != null) {
|
||||
this.types.put(key, registration.getType());
|
||||
}
|
||||
@@ -470,8 +424,8 @@ public class ContextFunctionCatalogAutoConfiguration {
|
||||
else {
|
||||
return;
|
||||
}
|
||||
this.registrations.remove(target);
|
||||
this.registrations.put(registration.getTarget(), key);
|
||||
this.names.remove(target);
|
||||
this.names.put(registration.getTarget(), key);
|
||||
if (publisher != null) {
|
||||
publisher.publishEvent(new FunctionRegistrationEvent(
|
||||
registration.getTarget(), type, registration.getNames()));
|
||||
@@ -618,12 +572,8 @@ public class ContextFunctionCatalogAutoConfiguration {
|
||||
return ReflectionUtils.getField(field, target);
|
||||
}
|
||||
|
||||
private boolean isMessage(Object function) {
|
||||
return findType(function).isMessage();
|
||||
}
|
||||
|
||||
private FunctionType findType(Object function) {
|
||||
String name = registrations.get(function);
|
||||
String name = names.get(function);
|
||||
if (types.containsKey(name)) {
|
||||
return types.get(name);
|
||||
}
|
||||
|
||||
@@ -179,6 +179,12 @@ public class FunctionTypeTests {
|
||||
assertThat(function).isSameAs(function.wrap(Flux.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonWrapper() {
|
||||
FunctionType function = FunctionType.from(Foo.class).to(Bar.class);
|
||||
assertThat(function).isSameAs(function.wrap(Object.class));
|
||||
}
|
||||
|
||||
private static class IntegerToString implements Function<Integer, String> {
|
||||
@Override
|
||||
public String apply(Integer t) {
|
||||
|
||||
@@ -45,14 +45,15 @@ public class BeanFactoryFunctionCatalogTests {
|
||||
@Test
|
||||
public void basicRegistrationFeatures() {
|
||||
processor.register(new FunctionRegistration<>(new Foos()).names("foos"));
|
||||
Function<Flux<Integer>, Flux<String>> foos = processor.lookupFunction("foos");
|
||||
Function<Flux<Integer>, Flux<String>> foos = processor.lookup(Function.class,
|
||||
"foos");
|
||||
assertThat(foos.apply(Flux.just(2)).blockFirst()).isEqualTo("4");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lookupFunctionWithEmptyName() {
|
||||
processor.register(new FunctionRegistration<>(new Foos()).names("foos"));
|
||||
Function<Flux<Integer>, Flux<String>> foos = processor.lookupFunction("");
|
||||
Function<Flux<Integer>, Flux<String>> foos = processor.lookup(Function.class, "");
|
||||
assertThat(foos.apply(Flux.just(2)).blockFirst()).isEqualTo("4");
|
||||
}
|
||||
|
||||
@@ -61,7 +62,7 @@ public class BeanFactoryFunctionCatalogTests {
|
||||
processor.register(new FunctionRegistration<Function<Integer, String>>(
|
||||
(Integer i) -> "i=" + i).names("foos").type(
|
||||
FunctionType.from(Integer.class).to(String.class).getType()));
|
||||
Function<Flux<Integer>, Flux<String>> foos = processor.lookupFunction("");
|
||||
Function<Flux<Integer>, Flux<String>> foos = processor.lookup(Function.class, "");
|
||||
assertThat(foos.apply(Flux.just(2)).blockFirst()).isEqualTo("i=2");
|
||||
}
|
||||
|
||||
@@ -72,14 +73,14 @@ public class BeanFactoryFunctionCatalogTests {
|
||||
ints -> ints.map(i -> "i=" + i)).names("foos")
|
||||
.type(FunctionType.from(Integer.class).to(String.class)
|
||||
.wrap(Flux.class).getType()));
|
||||
Function<Flux<Integer>, Flux<String>> foos = processor.lookupFunction("");
|
||||
Function<Flux<Integer>, Flux<String>> foos = processor.lookup(Function.class, "");
|
||||
assertThat(foos.apply(Flux.just(2)).blockFirst()).isEqualTo("i=2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lookupNonExistentConsumerWithEmptyName() {
|
||||
processor.register(new FunctionRegistration<>(new Foos()).names("foos"));
|
||||
Consumer<Flux<String>> foos = processor.lookupConsumer("");
|
||||
Consumer<Flux<String>> foos = processor.lookup(Consumer.class, "");
|
||||
assertThat(foos).isNull();
|
||||
}
|
||||
|
||||
@@ -87,8 +88,7 @@ public class BeanFactoryFunctionCatalogTests {
|
||||
public void composeFunction() {
|
||||
processor.register(new FunctionRegistration<>(new Foos()).names("foos"));
|
||||
processor.register(new FunctionRegistration<>(new Bars()).names("bars"));
|
||||
Function<Flux<Integer>, Flux<String>> foos = processor
|
||||
.lookupFunction("foos,bars");
|
||||
Function<Flux<Integer>, Flux<String>> foos = processor.lookup(Function.class, "foos,bars");
|
||||
assertThat(foos.apply(Flux.just(2)).blockFirst()).isEqualTo("Hello 4");
|
||||
}
|
||||
|
||||
@@ -96,14 +96,14 @@ public class BeanFactoryFunctionCatalogTests {
|
||||
public void composeSupplier() {
|
||||
processor.register(new FunctionRegistration<>(new Source()).names("numbers"));
|
||||
processor.register(new FunctionRegistration<>(new Foos()).names("foos"));
|
||||
Supplier<Flux<String>> foos = processor.lookupSupplier("numbers,foos");
|
||||
Supplier<Flux<String>> foos = processor.lookup(Supplier.class, "numbers,foos");
|
||||
assertThat(foos.get().blockFirst()).isEqualTo("6");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void composeUniqueSupplier() {
|
||||
processor.register(new FunctionRegistration<>(new Source()).names("numbers"));
|
||||
Supplier<Flux<Integer>> foos = processor.lookupSupplier("");
|
||||
Supplier<Flux<Integer>> foos = processor.lookup(Supplier.class, "");
|
||||
assertThat(foos.get().blockFirst()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ public class BeanFactoryFunctionCatalogTests {
|
||||
processor.register(new FunctionRegistration<>(new Foos()).names("foos"));
|
||||
Sink sink = new Sink();
|
||||
processor.register(new FunctionRegistration<>(sink).names("sink"));
|
||||
Consumer<Flux<Integer>> foos = processor.lookupConsumer("foos,sink");
|
||||
Consumer<Flux<Integer>> foos = processor.lookup(Consumer.class, "foos,sink");
|
||||
foos.accept(Flux.just(2));
|
||||
assertThat(sink.values).contains("4");
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public class BeanFactoryFunctionCatalogTests {
|
||||
public void composeUniqueConsumer() {
|
||||
Sink sink = new Sink();
|
||||
processor.register(new FunctionRegistration<>(sink).names("sink"));
|
||||
Consumer<Flux<String>> foos = processor.lookupConsumer("");
|
||||
Consumer<Flux<String>> foos = processor.lookup(Consumer.class, "");
|
||||
foos.accept(Flux.just("2"));
|
||||
assertThat(sink.values).contains("2");
|
||||
}
|
||||
|
||||
@@ -90,30 +90,35 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void lookUps() {
|
||||
create(SimpleConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||
assertThat(context.getBean("function2")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function,function2"))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function"))
|
||||
.isInstanceOf(Function.class);
|
||||
Function<Flux<String>, Flux<String>> f = catalog
|
||||
.lookupFunction("function,function2,function3");
|
||||
assertThat(context.getBean("function2")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function,function2"))
|
||||
.isInstanceOf(Function.class);
|
||||
Function<Flux<String>, Flux<String>> f = catalog.lookup(Function.class,
|
||||
"function,function2,function3");
|
||||
assertThat(f).isInstanceOf(Function.class);
|
||||
assertThat(f.apply(Flux.just("hello")).blockFirst())
|
||||
.isEqualTo("HELLOfunction2function3");
|
||||
assertThat(context.getBean("supplierFoo")).isInstanceOf(Supplier.class);
|
||||
assertThat(catalog.lookupSupplier("supplierFoo")).isInstanceOf(Supplier.class);
|
||||
assertThat(catalog.<Supplier<?>>lookup(Supplier.class, "supplierFoo"))
|
||||
.isInstanceOf(Supplier.class);
|
||||
assertThat(context.getBean("supplier_Foo")).isInstanceOf(Supplier.class);
|
||||
assertThat(catalog.lookupSupplier("supplier_Foo")).isInstanceOf(Supplier.class);
|
||||
assertThat(catalog.<Supplier<?>>lookup(Supplier.class, "supplier_Foo"))
|
||||
.isInstanceOf(Supplier.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ambiguousFunction() {
|
||||
create(AmbiguousConfiguration.class);
|
||||
assertThat(context.getBean("foos")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("foos")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupConsumer("foos")).isInstanceOf(Consumer.class);
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("foos")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "foos"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(catalog.<Consumer<?>>lookup(Consumer.class, "foos"))
|
||||
.isInstanceOf(Consumer.class);
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "foos")))
|
||||
.isEqualTo(String.class);
|
||||
assertThat(inspector.getInputType(catalog.lookupConsumer("foos")))
|
||||
assertThat(inspector.getInputType(catalog.lookup(Consumer.class, "foos")))
|
||||
.isEqualTo(Foo.class);
|
||||
|
||||
}
|
||||
@@ -121,35 +126,38 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
@Test
|
||||
public void composedFunction() {
|
||||
create(MultipleConfiguration.class);
|
||||
assertThat(catalog.lookupFunction("foos,bars")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("names,foos")).isNull();
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("foos,bars")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "foos,bars"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "names,foos")).isNull();
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "foos,bars")))
|
||||
.isAssignableFrom(String.class);
|
||||
assertThat(inspector.getOutputType(catalog.lookupFunction("foos,bars")))
|
||||
assertThat(inspector.getOutputType(catalog.lookup(Function.class, "foos,bars")))
|
||||
.isAssignableFrom(Bar.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void composedSupplier() {
|
||||
create(MultipleConfiguration.class);
|
||||
assertThat(catalog.lookupSupplier("names,foos")).isInstanceOf(Supplier.class);
|
||||
assertThat(catalog.lookupFunction("names,foos")).isNull();
|
||||
assertThat(inspector.getOutputType(catalog.lookupSupplier("names,foos")))
|
||||
assertThat(catalog.<Supplier<?>>lookup(Supplier.class, "names,foos"))
|
||||
.isInstanceOf(Supplier.class);
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "names,foos")).isNull();
|
||||
assertThat(inspector.getOutputType(catalog.lookup(Supplier.class, "names,foos")))
|
||||
.isAssignableFrom(Foo.class);
|
||||
// The input type is the same as the input type of the first element in the chain
|
||||
assertThat(inspector.getInputType(catalog.lookupSupplier("names,foos")))
|
||||
assertThat(inspector.getInputType(catalog.lookup(Supplier.class, "names,foos")))
|
||||
.isAssignableFrom(Void.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void composedConsumer() {
|
||||
create(MultipleConfiguration.class);
|
||||
assertThat(catalog.lookupConsumer("foos,print")).isInstanceOf(Consumer.class);
|
||||
assertThat(catalog.lookupFunction("foos,print")).isNull();
|
||||
assertThat(inspector.getInputType(catalog.lookupConsumer("foos,print")))
|
||||
assertThat(catalog.<Consumer<?>>lookup(Consumer.class, "foos,print"))
|
||||
.isInstanceOf(Consumer.class);
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "foos,print")).isNull();
|
||||
assertThat(inspector.getInputType(catalog.lookup(Consumer.class, "foos,print")))
|
||||
.isAssignableFrom(String.class);
|
||||
// The output type is the same as the output type of the last element in the chain
|
||||
assertThat(inspector.getOutputType(catalog.lookupConsumer("foos,print")))
|
||||
assertThat(inspector.getOutputType(catalog.lookup(Consumer.class, "foos,print")))
|
||||
.isAssignableFrom(Void.class);
|
||||
}
|
||||
|
||||
@@ -157,10 +165,11 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void genericFunction() {
|
||||
create(GenericConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("function")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Map.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupFunction("function")))
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Map.class);
|
||||
}
|
||||
|
||||
@@ -168,11 +177,13 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void fluxMessageFunction() {
|
||||
create(FluxMessageConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.isMessage(catalog.lookupFunction("function"))).isTrue();
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("function")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.isMessage(catalog.lookup(Function.class, "function")))
|
||||
.isTrue();
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(String.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupFunction("function")))
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Flux.class);
|
||||
}
|
||||
|
||||
@@ -180,11 +191,13 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void publisherMessageFunction() {
|
||||
create(PublisherMessageConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.isMessage(catalog.lookupFunction("function"))).isTrue();
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("function")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.isMessage(catalog.lookup(Function.class, "function")))
|
||||
.isTrue();
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(String.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupFunction("function")))
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Publisher.class);
|
||||
}
|
||||
|
||||
@@ -192,11 +205,13 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void messageFunction() {
|
||||
create(MessageConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.isMessage(catalog.lookupFunction("function"))).isTrue();
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("function")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.isMessage(catalog.lookup(Function.class, "function")))
|
||||
.isTrue();
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(String.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupFunction("function")))
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(String.class);
|
||||
}
|
||||
|
||||
@@ -204,10 +219,11 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void genericFluxFunction() {
|
||||
create(GenericFluxConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("function")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Map.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupFunction("function")))
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Flux.class);
|
||||
}
|
||||
|
||||
@@ -215,10 +231,11 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void externalFunction() {
|
||||
create(ExternalConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("function")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Map.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupFunction("function")))
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Map.class);
|
||||
}
|
||||
|
||||
@@ -226,10 +243,11 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void singletonFunction() {
|
||||
create(SingletonConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("function")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Integer.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupFunction("function")))
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Integer.class);
|
||||
}
|
||||
|
||||
@@ -237,22 +255,25 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void singletonMessageFunction() {
|
||||
create(SingletonMessageConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("function")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Integer.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupFunction("function")))
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Integer.class);
|
||||
assertThat(inspector.isMessage(catalog.lookupFunction("function"))).isTrue();
|
||||
assertThat(inspector.isMessage(catalog.lookup(Function.class, "function")))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonParametericTypeFunction() {
|
||||
create(NonParametricTypeSingletonConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("function")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Integer.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupFunction("function")))
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Integer.class);
|
||||
}
|
||||
|
||||
@@ -260,10 +281,11 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void componentScanBeanFunction() {
|
||||
create(ComponentScanBeanConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("function")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Map.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupFunction("function")))
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Map.class);
|
||||
}
|
||||
|
||||
@@ -271,10 +293,11 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void componentScanFunction() {
|
||||
create(ComponentScanConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("function")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Map.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupFunction("function")))
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Function.class, "function")))
|
||||
.isAssignableFrom(Map.class);
|
||||
}
|
||||
|
||||
@@ -283,11 +306,13 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
try {
|
||||
create("greeter.jar", ComponentScanJarConfiguration.class);
|
||||
assertThat(context.getBean("greeter")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("greeter")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("greeter")))
|
||||
.isAssignableFrom(String.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupFunction("greeter")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "greeter"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "greeter")))
|
||||
.isAssignableFrom(String.class);
|
||||
assertThat(
|
||||
inspector.getInputWrapper(catalog.lookup(Function.class, "greeter")))
|
||||
.isAssignableFrom(String.class);
|
||||
}
|
||||
finally {
|
||||
ClassUtils.overrideThreadContextClassLoader(getClass().getClassLoader());
|
||||
@@ -310,7 +335,7 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void simpleSupplier() {
|
||||
create(SimpleConfiguration.class);
|
||||
assertThat(context.getBean("supplier")).isInstanceOf(Supplier.class);
|
||||
Supplier<Flux<String>> supplier = catalog.lookupSupplier("supplier");
|
||||
Supplier<Flux<String>> supplier = catalog.lookup(Supplier.class, "supplier");
|
||||
assertThat(supplier.get().blockFirst()).isEqualTo("hello");
|
||||
}
|
||||
|
||||
@@ -318,7 +343,7 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void simpleConsumer() {
|
||||
create(SimpleConfiguration.class);
|
||||
assertThat(context.getBean("consumer")).isInstanceOf(Consumer.class);
|
||||
Consumer<Flux<String>> consumer = catalog.lookupConsumer("consumer");
|
||||
Consumer<Flux<String>> consumer = catalog.lookup(Consumer.class, "consumer");
|
||||
consumer.accept(Flux.just("foo", "bar"));
|
||||
assertThat(context.getBean(SimpleConfiguration.class).list).hasSize(2);
|
||||
}
|
||||
@@ -327,9 +352,10 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void qualifiedBean() {
|
||||
create(QualifiedConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isNull();
|
||||
assertThat(catalog.lookupFunction("other")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookupFunction("other")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function")).isNull();
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "other"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputType(catalog.lookup(Function.class, "other")))
|
||||
.isEqualTo(String.class);
|
||||
}
|
||||
|
||||
@@ -337,17 +363,21 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void aliasBean() {
|
||||
create(AliasConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isNotNull();
|
||||
assertThat(catalog.lookupFunction("other")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function"))
|
||||
.isNotNull();
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "other"))
|
||||
.isInstanceOf(Function.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registrationBean() {
|
||||
create(RegistrationConfiguration.class);
|
||||
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("function")).isNull();
|
||||
assertThat(catalog.lookupFunction("registration")).isNull();
|
||||
assertThat(catalog.lookupFunction("other")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "function")).isNull();
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "registration"))
|
||||
.isNull();
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "other"))
|
||||
.isInstanceOf(Function.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -357,8 +387,9 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
"spring.cloud.function.compile.foos.inputType=String",
|
||||
"spring.cloud.function.compile.foos.outputType=String");
|
||||
assertThat(context.getBean("foos")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("foos")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupFunction("foos")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "foos"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Function.class, "foos")))
|
||||
.isEqualTo(String.class);
|
||||
}
|
||||
|
||||
@@ -372,8 +403,9 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
create(EmptyConfiguration.class,
|
||||
"spring.cloud.function.import.foos.location=file:./target/foos.fun");
|
||||
assertThat(context.getBean("foos")).isInstanceOf(Function.class);
|
||||
assertThat(catalog.lookupFunction("foos")).isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupFunction("foos")))
|
||||
assertThat(catalog.<Function<?, ?>>lookup(Function.class, "foos"))
|
||||
.isInstanceOf(Function.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Function.class, "foos")))
|
||||
.isEqualTo(String.class);
|
||||
}
|
||||
|
||||
@@ -384,8 +416,9 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
+ "::set",
|
||||
"spring.cloud.function.compile.foos.type=consumer",
|
||||
"spring.cloud.function.compile.foos.inputType=String");
|
||||
assertThat(catalog.lookupConsumer("foos")).isInstanceOf(Consumer.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupConsumer("foos")))
|
||||
assertThat(catalog.<Consumer<?>>lookup(Consumer.class, "foos"))
|
||||
.isInstanceOf(Consumer.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Consumer.class, "foos")))
|
||||
.isEqualTo(String.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Consumer<String> consumer = (Consumer<String>) context.getBean("foos");
|
||||
@@ -399,8 +432,9 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
"spring.cloud.function.compile.foos.lambda=f -> f.subscribe("
|
||||
+ getClass().getName() + "::set)",
|
||||
"spring.cloud.function.compile.foos.type=consumer");
|
||||
assertThat(catalog.lookupConsumer("foos")).isInstanceOf(Consumer.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookupConsumer("foos")))
|
||||
assertThat(catalog.<Consumer<?>>lookup(Consumer.class, "foos"))
|
||||
.isInstanceOf(Consumer.class);
|
||||
assertThat(inspector.getInputWrapper(catalog.lookup(Consumer.class, "foos")))
|
||||
.isEqualTo(Flux.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Consumer<Flux<String>> consumer = (Consumer<Flux<String>>) context
|
||||
@@ -413,8 +447,10 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
||||
public void factoryBeanFunction() {
|
||||
create(FactoryBeanConfiguration.class);
|
||||
assertThat(this.context.getBean("function")).isInstanceOf(Function.class);
|
||||
assertThat(this.catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||
Function<Flux<String>, Flux<String>> f = this.catalog.lookupFunction("function");
|
||||
assertThat(this.catalog.<Function<?, ?>>lookup(Function.class, "function"))
|
||||
.isInstanceOf(Function.class);
|
||||
Function<Flux<String>, Flux<String>> f = this.catalog.lookup(Function.class,
|
||||
"function");
|
||||
assertThat(f.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO-bar");
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -38,6 +36,8 @@ import org.springframework.cloud.deployer.spi.core.AppDefinition;
|
||||
import org.springframework.cloud.deployer.spi.core.AppDeploymentRequest;
|
||||
import org.springframework.cloud.deployer.thin.ThinJarAppDeployer;
|
||||
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.stream.config.SupplierInvokingMessageProducer;
|
||||
import org.springframework.cloud.stream.binder.servlet.RouteRegistrar;
|
||||
@@ -89,75 +89,66 @@ public class FunctionExtractingFunctionCatalog
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> Consumer<T> lookupConsumer(String name) {
|
||||
return (Consumer<T>) lookup(name, "lookupConsumer");
|
||||
public FunctionRegistration<?> getRegistration(Object function) {
|
||||
String name = getName(function);
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
return new FunctionRegistration<>(function).name(name)
|
||||
.type(findType(function).getType());
|
||||
}
|
||||
|
||||
private FunctionType findType(Object function) {
|
||||
FunctionType type = FunctionType.from(getInputType(function))
|
||||
.to(getOutputType(function)).wrap(getInputWrapper(function));
|
||||
if (isMessage(function)) {
|
||||
type = type.message();
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T, R> Function<T, R> lookupFunction(String name) {
|
||||
return (Function<T, R>) lookup(name, "lookupFunction");
|
||||
public <T> T lookup(Class<?> type, String name) {
|
||||
return (T) lookup(type, name, "lookup");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> Supplier<T> lookupSupplier(String name) {
|
||||
return (Supplier<T>) lookup(name, "lookupSupplier");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Set<String> getSupplierNames() {
|
||||
return (Set<String>) catalog("getSupplierNames");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Set<String> getFunctionNames() {
|
||||
return (Set<String>) catalog("getFunctionNames");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Set<String> getConsumerNames() {
|
||||
return (Set<String>) catalog("getConsumerNames");
|
||||
public Set<String> getNames(Class<?> type) {
|
||||
return (Set<String>) getNames("getNames", type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMessage(Object function) {
|
||||
return (Boolean) inspect(function, "isMessage");
|
||||
return (Boolean) type(function, "isMessage");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getInputType(Object function) {
|
||||
return (Class<?>) inspect(function, "getInputType");
|
||||
return (Class<?>) type(function, "getInputType");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getOutputType(Object function) {
|
||||
return (Class<?>) inspect(function, "getOutputType");
|
||||
return (Class<?>) type(function, "getOutputType");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getInputWrapper(Object function) {
|
||||
return (Class<?>) inspect(function, "getInputWrapper");
|
||||
return (Class<?>) type(function, "getInputWrapper");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getOutputWrapper(Object function) {
|
||||
return (Class<?>) inspect(function, "getOutputWrapper");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convert(Object function, String value) {
|
||||
return inspect(function, "convert");
|
||||
return (Class<?>) type(function, "getOutputWrapper");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public String getName(Object function) {
|
||||
return (String) inspect(function, "getName");
|
||||
return ((Set<String>) inspect(function, "getNames")).iterator().next();
|
||||
}
|
||||
|
||||
public String deploy(String name, String path, String... args) {
|
||||
@@ -208,7 +199,8 @@ public class FunctionExtractingFunctionCatalog
|
||||
@SuppressWarnings("unchecked")
|
||||
private Set<String> getSupplierNames(String name) {
|
||||
String id = this.names.get(name);
|
||||
return (Set<String>) invoke(id, FunctionCatalog.class, "getSupplierNames");
|
||||
return (Set<String>) invoke(id, FunctionCatalog.class, "getNames",
|
||||
Supplier.class);
|
||||
}
|
||||
|
||||
private void unregister(String name) {
|
||||
@@ -228,24 +220,67 @@ public class FunctionExtractingFunctionCatalog
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Inspecting " + method);
|
||||
}
|
||||
return invoke(FunctionInspector.class, method, arg);
|
||||
return invoke(FunctionInspector.class, "getRegistration", (id, result) -> {
|
||||
return prefix(id, invoke(result, method));
|
||||
}, arg);
|
||||
}
|
||||
|
||||
private Object lookup(String name, String method) {
|
||||
private Object type(Object arg, String method) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking up " + name + " with " + method);
|
||||
logger.debug("Inspecting " + method);
|
||||
}
|
||||
return invoke(FunctionCatalog.class, method, name);
|
||||
return invoke(invoke(invoke(FunctionInspector.class, "getRegistration", arg),
|
||||
"getType"), method);
|
||||
}
|
||||
|
||||
private Object catalog(String method) {
|
||||
private Object prefix(String id, Object result) {
|
||||
String name = this.ids.get(id);
|
||||
String prefix = name + "/";
|
||||
if (result != null) {
|
||||
if (result instanceof Collection) {
|
||||
Set<String> results = new LinkedHashSet<>();
|
||||
for (Object value : (Collection<?>) result) {
|
||||
results.add(prefix + value);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
else if (result instanceof String) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Prefixed (from \" + name + \"): " + result);
|
||||
}
|
||||
return prefix + result;
|
||||
}
|
||||
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Result (from " + name + "): " + result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object lookup(Class<?> type, String name, String method) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking up " + type + " named " + name + " with " + method);
|
||||
}
|
||||
return invoke(FunctionCatalog.class, method, type, name);
|
||||
}
|
||||
|
||||
private Object getNames(String method, Class<?> type) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Calling " + method);
|
||||
}
|
||||
return invoke(FunctionCatalog.class, method);
|
||||
return invoke(FunctionCatalog.class, method, type);
|
||||
}
|
||||
|
||||
private Object invoke(Class<?> type, String method, Object... arg) {
|
||||
return invoke(type, method, null, arg);
|
||||
}
|
||||
|
||||
private Object invoke(Class<?> type, String method, Callback callback,
|
||||
Object... arg) {
|
||||
Set<Object> results = new LinkedHashSet<>();
|
||||
Object fallback = null;
|
||||
for (String id : this.deployed.keySet()) {
|
||||
@@ -265,6 +300,9 @@ public class FunctionExtractingFunctionCatalog
|
||||
fallback = false;
|
||||
continue;
|
||||
}
|
||||
if (callback != null) {
|
||||
return callback.call(id, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -274,7 +312,7 @@ public class FunctionExtractingFunctionCatalog
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Results: " + results);
|
||||
}
|
||||
return arg.length > 0 ? null : results;
|
||||
return "lookup".equals(method) ? null : results;
|
||||
}
|
||||
|
||||
private Object invoke(String id, Class<?> type, String method, Object... arg) {
|
||||
@@ -284,11 +322,11 @@ public class FunctionExtractingFunctionCatalog
|
||||
}
|
||||
String name = this.ids.get(id);
|
||||
String prefix = name + "/";
|
||||
if (arg.length == 1) {
|
||||
if (arg[0] instanceof String) {
|
||||
String specific = arg[0].toString();
|
||||
if (arg.length == 2 && arg[0] instanceof Class) {
|
||||
if (arg[1] instanceof String) {
|
||||
String specific = arg[1].toString();
|
||||
if (specific.startsWith(prefix)) {
|
||||
arg[0] = specific.substring(prefix.length());
|
||||
arg[1] = specific.substring(prefix.length());
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
@@ -296,39 +334,26 @@ public class FunctionExtractingFunctionCatalog
|
||||
}
|
||||
}
|
||||
try {
|
||||
MethodInvoker invoker = new MethodInvoker();
|
||||
invoker.setTargetObject(catalog);
|
||||
invoker.setTargetMethod(method);
|
||||
invoker.setArguments(arg);
|
||||
invoker.prepare();
|
||||
Object result = invoker.invoke();
|
||||
if (result != null) {
|
||||
if (result instanceof Collection) {
|
||||
Set<String> results = new LinkedHashSet<>();
|
||||
for (Object value : (Collection<?>) result) {
|
||||
results.add(prefix + value);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
else if (result instanceof String) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Prefixed (from \" + name + \"): " + result);
|
||||
}
|
||||
return prefix + result;
|
||||
}
|
||||
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Result (from " + name + "): " + result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Object result = invoke(catalog, method, arg);
|
||||
return prefix(id, result);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Cannot extract catalog", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object invoke(Object target, String method, Object... arg) {
|
||||
MethodInvoker invoker = new MethodInvoker();
|
||||
invoker.setTargetObject(target);
|
||||
invoker.setTargetMethod(method);
|
||||
invoker.setArguments(arg);
|
||||
try {
|
||||
invoker.prepare();
|
||||
return invoker.invoke();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Cannot invoke method", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> deployed() {
|
||||
@@ -340,6 +365,10 @@ public class FunctionExtractingFunctionCatalog
|
||||
return result;
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
Object call(String id, Object result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DeployedArtifact {
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
package org.springframework.cloud.function.deployer;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@@ -47,7 +51,8 @@ public class FunctionExtractingFunctionCatalogTests {
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
if (id == null) {
|
||||
deploy("sample", "maven://io.spring.sample:function-sample:1.0.0.BUILD-SNAPSHOT");
|
||||
deploy("sample",
|
||||
"maven://io.spring.sample:function-sample:1.0.0.BUILD-SNAPSHOT");
|
||||
// "--debug");
|
||||
id = deploy("pojos",
|
||||
"maven://io.spring.sample:function-sample-pojo:1.0.0.BUILD-SNAPSHOT");
|
||||
@@ -64,13 +69,13 @@ public class FunctionExtractingFunctionCatalogTests {
|
||||
|
||||
@Test
|
||||
public void listFunctions() throws Exception {
|
||||
assertThat(deployer.getFunctionNames()).contains("sample/uppercase",
|
||||
assertThat(deployer.getNames(Function.class)).contains("sample/uppercase",
|
||||
"pojos/uppercase");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nameFunction() throws Exception {
|
||||
assertThat(deployer.getName(deployer.lookupFunction("sample/uppercase")))
|
||||
assertThat(deployer.getName(deployer.lookup(Function.class, "sample/uppercase")))
|
||||
.isEqualTo("sample/uppercase");
|
||||
}
|
||||
|
||||
@@ -79,37 +84,40 @@ public class FunctionExtractingFunctionCatalogTests {
|
||||
// This one can only work if you change the boot classpath to contain reactor-core
|
||||
// and reactive-streams
|
||||
expected.expect(ClassCastException.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Flux<String> result = (Flux<String>) deployer.lookupFunction("pojos/uppercase")
|
||||
.apply(Flux.just("foo"));
|
||||
Function<Flux<String>, Flux<String>> function = deployer.lookup(Function.class,
|
||||
"pojos/uppercase");
|
||||
Flux<String> result = function.apply(Flux.just("foo"));
|
||||
assertThat(result.blockFirst()).isEqualTo("FOO");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listConsumers() throws Exception {
|
||||
assertThat(deployer.getConsumerNames()).isEmpty();
|
||||
assertThat(deployer.getNames(Consumer.class)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deployAndExtractConsumers() throws Exception {
|
||||
assertThat(deployer.lookupConsumer("pojos/sink")).isNull();
|
||||
assertThat(deployer.<Consumer<?>>lookup(Consumer.class, "pojos/sink")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listSuppliers() throws Exception {
|
||||
assertThat(deployer.getSupplierNames()).contains("sample/words", "pojos/words");
|
||||
assertThat(deployer.getNames(Supplier.class)).contains("sample/words",
|
||||
"pojos/words");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nameSupplier() throws Exception {
|
||||
assertThat(deployer.getName(deployer.lookupSupplier("sample/words")))
|
||||
assertThat(deployer.getName(deployer.lookup(Supplier.class, "sample/words")))
|
||||
.isEqualTo("sample/words");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deployAndExtractSuppliers() throws Exception {
|
||||
assertThat(deployer.lookupSupplier("sample/words")).isNotNull();
|
||||
assertThat(deployer.lookupSupplier("pojos/words")).isNotNull();
|
||||
assertThat(deployer.<Supplier<?>>lookup(Supplier.class, "sample/words"))
|
||||
.isNotNull();
|
||||
assertThat(deployer.<Supplier<?>>lookup(Supplier.class, "pojos/words"))
|
||||
.isNotNull();
|
||||
}
|
||||
|
||||
private static String deploy(String name, String path, String... args)
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.cloud.function.stream.config;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@@ -36,7 +38,7 @@ public class RouteRegistryAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public RouteRegistry supplierRoutes(FunctionCatalog registry) {
|
||||
return () -> registry.getSupplierNames();
|
||||
return () -> registry.getNames(Supplier.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ public class StreamListeningFunctionInvoker implements SmartInitializingSingleto
|
||||
}
|
||||
|
||||
private Flux<Message<?>> function(String name, Flux<Message<?>> flux) {
|
||||
Function<Object, Flux<?>> function = functionCatalog.lookupFunction(name);
|
||||
Function<Object, Flux<?>> function = functionCatalog.lookup(Function.class, name);
|
||||
return flux.publish(values -> {
|
||||
Flux<?> result = function
|
||||
.apply(values.map(message -> convertInput(function).apply(message)));
|
||||
@@ -114,7 +114,7 @@ public class StreamListeningFunctionInvoker implements SmartInitializingSingleto
|
||||
}
|
||||
|
||||
private Flux<Message<?>> consumer(String name, Flux<Message<?>> flux) {
|
||||
Consumer<Object> consumer = functionCatalog.lookupConsumer(name);
|
||||
Consumer<Object> consumer = functionCatalog.lookup(Consumer.class, name);
|
||||
consumer.accept(flux.map(message -> convertInput(consumer).apply(message))
|
||||
.filter(transformed -> transformed != UNCONVERTED));
|
||||
return Flux.empty();
|
||||
@@ -125,7 +125,7 @@ public class StreamListeningFunctionInvoker implements SmartInitializingSingleto
|
||||
return Flux.empty();
|
||||
}
|
||||
String name = choose(names);
|
||||
if (functionCatalog.lookupConsumer(name) != null) {
|
||||
if (functionCatalog.lookup(Consumer.class, name) != null) {
|
||||
return consumer(name, flux);
|
||||
}
|
||||
return function(name, flux);
|
||||
@@ -149,8 +149,8 @@ public class StreamListeningFunctionInvoker implements SmartInitializingSingleto
|
||||
name = stash(defaultRoute);
|
||||
}
|
||||
if (name == null) {
|
||||
Set<String> names = new LinkedHashSet<>(functionCatalog.getFunctionNames());
|
||||
names.addAll(functionCatalog.getConsumerNames());
|
||||
Set<String> names = new LinkedHashSet<>(functionCatalog.getNames(Function.class));
|
||||
names.addAll(functionCatalog.getNames(Consumer.class));
|
||||
List<String> matches = new ArrayList<>();
|
||||
if (names.size() == 1) {
|
||||
String key = names.iterator().next();
|
||||
@@ -158,9 +158,9 @@ public class StreamListeningFunctionInvoker implements SmartInitializingSingleto
|
||||
}
|
||||
else {
|
||||
for (String candidate : names) {
|
||||
Object function = functionCatalog.lookupFunction(candidate);
|
||||
Object function = functionCatalog.lookup(Function.class, candidate);
|
||||
if (function == null) {
|
||||
function = functionCatalog.lookupConsumer(candidate);
|
||||
function = functionCatalog.lookup(Consumer.class, candidate);
|
||||
}
|
||||
if (function == null) {
|
||||
continue;
|
||||
@@ -187,13 +187,13 @@ public class StreamListeningFunctionInvoker implements SmartInitializingSingleto
|
||||
}
|
||||
|
||||
private String stash(String key) {
|
||||
if (functionCatalog.lookupFunction(key) != null) {
|
||||
if (functionCatalog.lookup(Function.class, key) != null) {
|
||||
if (!processors.containsKey(key)) {
|
||||
processors.put(key, flux -> function(key, flux));
|
||||
}
|
||||
return key;
|
||||
}
|
||||
else if (functionCatalog.lookupConsumer(key) != null) {
|
||||
else if (functionCatalog.lookup(Consumer.class, key) != null) {
|
||||
if (!processors.containsKey(key)) {
|
||||
processors.put(key, flux -> consumer(key, flux));
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public class SupplierInvokingMessageProducer<T> extends MessageProducerSupport {
|
||||
|
||||
@Override
|
||||
protected void doStart() {
|
||||
for (String name : functionCatalog.getSupplierNames()) {
|
||||
for (String name : functionCatalog.getNames(Supplier.class)) {
|
||||
start(name);
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,7 @@ public class SupplierInvokingMessageProducer<T> extends MessageProducerSupport {
|
||||
if (!disposables.containsKey(name)) {
|
||||
synchronized (disposables) {
|
||||
if (!disposables.containsKey(name)) {
|
||||
Supplier<Flux<?>> supplier = functionCatalog.lookupSupplier(name);
|
||||
Supplier<Flux<?>> supplier = functionCatalog.lookup(Supplier.class, name);
|
||||
if (supplier != null) {
|
||||
suppliers.add(name);
|
||||
disposables.put(name,
|
||||
@@ -96,7 +96,7 @@ public class SupplierInvokingMessageProducer<T> extends MessageProducerSupport {
|
||||
}
|
||||
|
||||
private void send(String name, Object payload) {
|
||||
Supplier<Flux<?>> supplier = functionCatalog.lookupSupplier(name);
|
||||
Supplier<Flux<?>> supplier = functionCatalog.lookup(Supplier.class, name);
|
||||
Message<?> message = MessageUtils.unpack(supplier, payload);
|
||||
message = MessageBuilder.fromMessage(message)
|
||||
.setHeaderIfAbsent(StreamConfigurationProperties.ROUTE_KEY, name).build();
|
||||
|
||||
@@ -45,12 +45,9 @@ public class TaskConfiguration {
|
||||
|
||||
@Bean
|
||||
public CommandLineRunner commandLineRunner(FunctionCatalog registry) {
|
||||
final Supplier<Flux<Object>> supplier = registry
|
||||
.lookupSupplier(properties.getSupplier());
|
||||
final Function<Flux<Object>, Flux<Object>> function = registry
|
||||
.lookupFunction(properties.getFunction());
|
||||
final Consumer<Flux<Object>> consumer = registry
|
||||
.lookupConsumer(properties.getConsumer());
|
||||
final Supplier<Flux<Object>> supplier = registry.lookup(Supplier.class, properties.getSupplier());
|
||||
final Function<Flux<Object>, Flux<Object>> function = registry.lookup(Function.class, properties.getFunction());
|
||||
final Consumer<Flux<Object>> consumer = registry.lookup(Consumer.class, properties.getConsumer());
|
||||
CommandLineRunner runner = new CommandLineRunner() {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -52,8 +51,11 @@ public class FunctionController {
|
||||
|
||||
private boolean debug = false;
|
||||
|
||||
public FunctionController(FunctionInspector inspector) {
|
||||
private StringConverter converter;
|
||||
|
||||
public FunctionController(FunctionInspector inspector, StringConverter converter) {
|
||||
this.inspector = inspector;
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
public void setDebug(boolean debug) {
|
||||
@@ -115,9 +117,8 @@ public class FunctionController {
|
||||
return debug ? result.log() : result;
|
||||
}
|
||||
|
||||
private Mono<?> value(Function<Flux<?>, Flux<?>> function,
|
||||
@PathVariable String value) {
|
||||
Object input = inspector.convert(function, value);
|
||||
private Mono<?> value(Function<Flux<?>, Flux<?>> function, String value) {
|
||||
Object input = converter.convert(function, value);
|
||||
Mono<?> result = Mono.from(function.apply(Flux.just(input)));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Handled GET with function");
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
||||
import org.springframework.cloud.function.context.catalog.FunctionInspector;
|
||||
import org.springframework.cloud.function.web.flux.constants.WebRequestConstants;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -55,11 +54,12 @@ public class FunctionHandlerMapping extends RequestMappingHandlerMapping
|
||||
private String debug = "false";
|
||||
|
||||
@Autowired
|
||||
public FunctionHandlerMapping(FunctionCatalog catalog, FunctionInspector inspector) {
|
||||
public FunctionHandlerMapping(FunctionCatalog catalog,
|
||||
FunctionController controller) {
|
||||
this.functions = catalog;
|
||||
logger.info("FunctionCatalog: " + catalog + ", FunctionInspector: " + inspector);
|
||||
logger.info("FunctionCatalog: " + catalog);
|
||||
setOrder(super.getOrder() - 5);
|
||||
this.controller = new FunctionController(inspector);
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -118,12 +118,12 @@ public class FunctionHandlerMapping extends RequestMappingHandlerMapping
|
||||
return null;
|
||||
}
|
||||
path = path.startsWith("/") ? path.substring(1) : path;
|
||||
Consumer<Object> consumer = functions.lookupConsumer(path);
|
||||
Consumer<Object> consumer = functions.lookup(Consumer.class, path);
|
||||
if (consumer != null) {
|
||||
request.setAttribute(WebRequestConstants.CONSUMER, consumer);
|
||||
return consumer;
|
||||
}
|
||||
Function<Object, Object> function = functions.lookupFunction(path);
|
||||
Function<Object, Object> function = functions.lookup(Function.class, path);
|
||||
if (function != null) {
|
||||
request.setAttribute(WebRequestConstants.FUNCTION, function);
|
||||
return function;
|
||||
@@ -136,7 +136,7 @@ public class FunctionHandlerMapping extends RequestMappingHandlerMapping
|
||||
return null;
|
||||
}
|
||||
path = path.startsWith("/") ? path.substring(1) : path;
|
||||
Supplier<Object> supplier = functions.lookupSupplier(path);
|
||||
Supplier<Object> supplier = functions.lookup(Supplier.class, path);
|
||||
if (supplier != null) {
|
||||
request.setAttribute(WebRequestConstants.SUPPLIER, supplier);
|
||||
return supplier;
|
||||
@@ -152,7 +152,7 @@ public class FunctionHandlerMapping extends RequestMappingHandlerMapping
|
||||
name = builder.toString();
|
||||
value = path.length() > name.length() ? path.substring(name.length() + 1)
|
||||
: null;
|
||||
Function<Object, Object> function = functions.lookupFunction(name);
|
||||
Function<Object, Object> function = functions.lookup(Function.class, name);
|
||||
if (function != null) {
|
||||
request.setAttribute(WebRequestConstants.FUNCTION, function);
|
||||
request.setAttribute(WebRequestConstants.ARGUMENT, value);
|
||||
|
||||
@@ -23,8 +23,10 @@ import com.google.gson.Gson;
|
||||
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
|
||||
@@ -36,6 +38,9 @@ import org.springframework.cloud.function.web.flux.response.FluxReturnValueHandl
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.GsonHttpMessageConverter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -54,6 +59,7 @@ import reactor.core.publisher.Flux;
|
||||
@ConditionalOnWebApplication
|
||||
@ConditionalOnClass({ Flux.class, AsyncHandlerMethodReturnValueHandler.class })
|
||||
@AutoConfigureBefore(HttpMessageConvertersAutoConfiguration.class)
|
||||
@Import(FunctionController.class)
|
||||
public class ReactorAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
@@ -61,8 +67,15 @@ public class ReactorAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public FunctionHandlerMapping functionHandlerMapping(FunctionCatalog catalog,
|
||||
FunctionInspector inspector) {
|
||||
return new FunctionHandlerMapping(catalog, inspector);
|
||||
FunctionController controller) {
|
||||
return new FunctionHandlerMapping(catalog, controller);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public StringConverter functionStringConverter(FunctionInspector inspector,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
return new BasicStringConverter(inspector, beanFactory);
|
||||
}
|
||||
|
||||
// TODO: remove this when https://jira.spring.io/browse/SPR-16529 is resolved
|
||||
@@ -125,4 +138,32 @@ public class ReactorAutoConfiguration {
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private static class BasicStringConverter implements StringConverter {
|
||||
|
||||
private ConversionService conversionService;
|
||||
private ConfigurableListableBeanFactory registry;
|
||||
private FunctionInspector inspector;
|
||||
|
||||
public BasicStringConverter(FunctionInspector inspector,
|
||||
ConfigurableListableBeanFactory registry) {
|
||||
this.inspector = inspector;
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convert(Object function, String value) {
|
||||
if (conversionService == null && registry != null) {
|
||||
ConversionService conversionService = this.registry
|
||||
.getConversionService();
|
||||
this.conversionService = conversionService != null ? conversionService
|
||||
: new DefaultConversionService();
|
||||
}
|
||||
Class<?> type = inspector.getInputType(function);
|
||||
return conversionService.canConvert(String.class, type)
|
||||
? conversionService.convert(value, type)
|
||||
: value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2016-2017 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.web.flux;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface StringConverter {
|
||||
|
||||
Object convert(Object function, String value);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user