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:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user