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:
Dave Syer
2018-02-27 10:22:44 +00:00
parent c11a4454ff
commit 33b33adb4b
21 changed files with 498 additions and 407 deletions

View File

@@ -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");

View File

@@ -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);

View File

@@ -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;
}
}
}

View File

@@ -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);
}