diff --git a/pom.xml b/pom.xml
index 5eefd283b..57946be87 100644
--- a/pom.xml
+++ b/pom.xml
@@ -64,6 +64,7 @@
spring-cloud-function-task
spring-cloud-function-web
spring-cloud-starter-function-web
+ spring-cloud-starter-function-webflux
spring-cloud-function-samples
spring-cloud-function-deployer
spring-cloud-function-adapters
diff --git a/spring-cloud-function-web/pom.xml b/spring-cloud-function-web/pom.xml
index 290000d28..545af070f 100644
--- a/spring-cloud-function-web/pom.xml
+++ b/spring-cloud-function-web/pom.xml
@@ -18,6 +18,12 @@
org.springframework
spring-webmvc
+ true
+
+
+ org.springframework
+ spring-webflux
+ true
org.springframework.cloud
diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/RequestProcessor.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/RequestProcessor.java
new file mode 100644
index 000000000..dff610b90
--- /dev/null
+++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/RequestProcessor.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2018 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;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.reactivestreams.Publisher;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.function.context.catalog.FunctionInspector;
+import org.springframework.cloud.function.context.message.MessageUtils;
+import org.springframework.cloud.function.json.JsonMapper;
+import org.springframework.cloud.function.web.util.HeaderUtils;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.ResponseEntity.BodyBuilder;
+import org.springframework.messaging.Message;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.util.StringUtils;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class RequestProcessor {
+
+ private static Log logger = LogFactory.getLog(RequestProcessor.class);
+
+ private FunctionInspector inspector;
+
+ private StringConverter converter;
+
+ private JsonMapper mapper;
+
+ @Value("${debug:${DEBUG:false}}")
+ private String debug = "false";
+
+ public RequestProcessor(JsonMapper mapper, FunctionInspector inspector,
+ StringConverter converter) {
+ this.mapper = mapper;
+ this.inspector = inspector;
+ this.converter = converter;
+ }
+
+ public static FunctionWrapper wrapper(Function, Publisher>> function,
+ Consumer> consumer, Supplier> supplier) {
+ return new FunctionWrapper(function, consumer, supplier);
+ }
+
+ public static class FunctionWrapper {
+
+ private Function, Publisher>> function;
+
+ private Consumer> consumer;
+
+ private Supplier> supplier;
+
+ private MultiValueMap params = new LinkedMultiValueMap<>();
+
+ private HttpHeaders headers = new HttpHeaders();
+
+ private String argument;
+
+ public FunctionWrapper(Function, Publisher>> function,
+ Consumer> consumer, Supplier> supplier) {
+ this.function = function;
+ this.consumer = consumer;
+ this.supplier = supplier;
+ }
+
+ public Object handler() {
+ return function != null ? function : consumer != null ? consumer : supplier;
+ }
+
+ public Function, Publisher>> function() {
+ return this.function;
+ }
+
+ public Consumer> consumer() {
+ return this.consumer;
+ }
+
+ public Supplier> supplier() {
+ return this.supplier;
+ }
+
+ public MultiValueMap params() {
+ return params;
+ }
+
+ public HttpHeaders headers() {
+ return this.headers;
+ }
+
+ public FunctionWrapper headers(HttpHeaders headers) {
+ this.headers = headers;
+ return this;
+ }
+
+ public FunctionWrapper params(MultiValueMap params) {
+ this.params.addAll(params);
+ return this;
+ }
+
+ public FunctionWrapper argument(String argument) {
+ this.argument = argument;
+ return this;
+ }
+
+ public String argument() {
+ return this.argument;
+ }
+ }
+
+ public Mono> post(FunctionWrapper wrapper, String body,
+ boolean stream) {
+
+ Object function = wrapper.handler();
+ if (!StringUtils.hasText(body)) {
+ return post(wrapper, (List>) null, null, stream);
+ }
+ body = body.trim();
+ Object input;
+ Class> inputType = inspector.getInputType(function);
+ if (body.startsWith("[")) {
+ input = mapper.toList(body, inputType);
+ }
+ else {
+ if (inputType == String.class) {
+ input = body;
+ }
+ else if (body.startsWith("{")) {
+ input = mapper.toSingle(body, inputType);
+ }
+ else if (body.startsWith("\"")) {
+ input = body.substring(1, body.length() - 2);
+ }
+ else {
+ input = converter.convert(function, body);
+ }
+ }
+ if (input instanceof List) {
+ return post(wrapper, (List>) input, null, stream);
+ }
+ return post(wrapper, Collections.singletonList(input), null, stream);
+ }
+
+ private Mono> post(FunctionWrapper wrapper, List> body,
+ MultiValueMap params, boolean stream) {
+
+ Function, Publisher>> function = wrapper.function();
+ Consumer> consumer = wrapper.consumer();
+
+ MultiValueMap form = wrapper.params();
+ if (params != null) {
+ form.putAll(params);
+ }
+
+ Flux> flux = body == null ? Flux.just(form) : Flux.fromIterable(body);
+ if (inspector.isMessage(function)) {
+ flux = messages(wrapper, function == null ? consumer : function, flux);
+ }
+ if (function != null) {
+ Flux> result = Flux.from(function.apply(flux));
+ if (logger.isDebugEnabled()) {
+ logger.debug("Handled POST with function");
+ }
+ if (stream) {
+ return stream(wrapper, result);
+ }
+ return response(function, result, body == null ? null : body.size()<=1, false);
+ }
+
+ if (consumer != null) {
+ consumer.accept(flux);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Handled POST with consumer");
+ }
+ return Mono.just(ResponseEntity.status(HttpStatus.ACCEPTED).build());
+ }
+
+ throw new IllegalArgumentException("no such function");
+ }
+
+ private Flux> messages(FunctionWrapper request, Object function, Flux> flux) {
+ Map headers = HeaderUtils.fromHttp(request.headers());
+ flux = flux.map(payload -> MessageUtils.create(function, payload, headers));
+ return flux;
+ }
+
+ private void addHeaders(BodyBuilder builder, Message> message) {
+ HttpHeaders headers = new HttpHeaders();
+ builder.headers(HeaderUtils.fromMessage(message.getHeaders(), headers));
+ }
+
+ public Mono> stream(FunctionWrapper request) {
+ Publisher> result;
+ if (request.function()!=null) {
+ result = value(request.function(), request.argument());
+ } else {
+ result = supplier(request.supplier());
+ }
+ return stream(request, result);
+ }
+
+
+ private Mono> stream(FunctionWrapper request, Publisher> result) {
+
+ BodyBuilder builder = ResponseEntity.ok();
+ if (inspector.isMessage(request.handler())) {
+ result = Flux.from(result)
+ .doOnNext(value -> addHeaders(builder, (Message>) value))
+ .map(message -> MessageUtils.unpack(request.handler(), message)
+ .getPayload());
+ }
+
+ Publisher> output = result;
+ return Flux.from(output).then(Mono.fromSupplier(() -> builder.body(output)));
+
+ }
+
+ private Mono> response(Object handler, Publisher> result,
+ Boolean single, boolean getter) {
+
+ BodyBuilder builder = ResponseEntity.ok();
+ if (inspector.isMessage(handler)) {
+ result = Flux.from(result)
+ .doOnNext(value -> addHeaders(builder, (Message>) value))
+ .map(message -> MessageUtils.unpack(handler, message).getPayload());
+ }
+
+ if (single != null && single && isOutputSingle(handler)) {
+ result = Mono.from(result);
+ }
+ else if (getter && single == null && isOutputSingle(handler)) {
+ result = Mono.from(result);
+ }
+ else if (isInputMultiple(handler) && isOutputSingle(handler)) {
+ result = Mono.from(result);
+ }
+ Publisher> output = result;
+ if (output instanceof Mono) {
+ return Mono.from(output).flatMap(body -> Mono.just(builder.body(body)));
+ }
+ return Flux.from(output).collectList()
+ .flatMap(body -> Mono.just(builder.body(body)));
+ }
+
+ private boolean isInputMultiple(Object handler) {
+ Class> type = inspector.getInputType(handler);
+ Class> wrapper = inspector.getInputWrapper(handler);
+ return Collection.class.isAssignableFrom(type) || Flux.class.equals(wrapper);
+ }
+
+ private boolean isOutputSingle(Object handler) {
+ Class> type = inspector.getOutputType(handler);
+ Class> wrapper = inspector.getOutputWrapper(handler);
+ if (Stream.class.isAssignableFrom(type)) {
+ return false;
+ }
+ if (wrapper == type) {
+ return true;
+ }
+ return Mono.class.equals(wrapper) || Optional.class.equals(wrapper);
+ }
+
+ private Publisher> supplier(Supplier> supplier) {
+ Publisher> result = supplier.get();
+ return result;
+ }
+
+ private Mono> value(Function, Publisher>> function, String value) {
+ Object input = converter.convert(function, value);
+ Mono> result = Mono.from(function.apply(Flux.just(input)));
+ return result;
+ }
+
+ public Mono> get(FunctionWrapper wrapper) {
+ if (wrapper.function() != null) {
+ return response(wrapper.function(), value(wrapper.function(), wrapper.argument()), true, true);
+ }
+ else {
+ return response(wrapper.supplier(), supplier(wrapper.supplier()), null, true);
+ }
+ }
+
+
+}
diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/RestApplication.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/RestApplication.java
index f59f2afe5..c1e25cdb9 100644
--- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/RestApplication.java
+++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/RestApplication.java
@@ -17,12 +17,14 @@
package org.springframework.cloud.function.web;
import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
/**
* @author Mark Fisher
*/
-@SpringBootApplication
+@SpringBootConfiguration
+@EnableAutoConfiguration
public class RestApplication {
public static void main(String[] args) {
diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/StringConverter.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/StringConverter.java
similarity index 93%
rename from spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/StringConverter.java
rename to spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/StringConverter.java
index c3ad9d96c..092353c3a 100644
--- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/StringConverter.java
+++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/StringConverter.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.cloud.function.web.flux;
+package org.springframework.cloud.function.web;
/**
* @author Dave Syer
diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/constants/WebRequestConstants.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/constants/WebRequestConstants.java
similarity index 82%
rename from spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/constants/WebRequestConstants.java
rename to spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/constants/WebRequestConstants.java
index 4d636133a..ed111dfb5 100644
--- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/constants/WebRequestConstants.java
+++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/constants/WebRequestConstants.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.function.web.flux.constants;
+package org.springframework.cloud.function.web.constants;
/**
* Common storage for web request attribute names (in a separate package to avoid cycles).
@@ -33,9 +33,5 @@ public abstract class WebRequestConstants {
+ ".argument";
public static final String HANDLER = WebRequestConstants.class.getName()
+ ".handler";
- public static final String INPUT_SINGLE = WebRequestConstants.class.getName()
- + ".input_single";
- public static final String OUTPUT_SINGLE = WebRequestConstants.class.getName()
- + ".output_single";
}
diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionController.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionController.java
index 6892f0e55..79d7e6086 100644
--- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionController.java
+++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionController.java
@@ -16,32 +16,24 @@
package org.springframework.cloud.function.web.flux;
-import java.util.Collection;
-import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
-import java.util.stream.Stream;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.reactivestreams.Publisher;
-import org.springframework.cloud.function.context.catalog.FunctionInspector;
-import org.springframework.cloud.function.context.message.MessageUtils;
-import org.springframework.cloud.function.web.flux.constants.WebRequestConstants;
-import org.springframework.cloud.function.web.flux.request.FluxFormRequest;
-import org.springframework.cloud.function.web.flux.request.FluxRequest;
-import org.springframework.http.HttpStatus;
+import org.springframework.cloud.function.web.RequestProcessor;
+import org.springframework.cloud.function.web.RequestProcessor.FunctionWrapper;
+import org.springframework.cloud.function.web.constants.WebRequestConstants;
+import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.context.request.WebRequest;
+import org.springframework.web.server.ServerWebExchange;
-import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
@@ -51,149 +43,65 @@ import reactor.core.publisher.Mono;
@Component
public class FunctionController {
- private static Log logger = LogFactory.getLog(FunctionController.class);
+ private RequestProcessor processor;
- private FunctionInspector inspector;
-
- private boolean debug = false;
-
- private StringConverter converter;
-
- public FunctionController(FunctionInspector inspector, StringConverter converter) {
- this.inspector = inspector;
- this.converter = converter;
+ public FunctionController(RequestProcessor processor) {
+ this.processor = processor;
}
- public void setDebug(boolean debug) {
- this.debug = debug;
+ @PostMapping(path = "/**", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+ @ResponseBody
+ public Mono> form(ServerWebExchange request) {
+ FunctionWrapper wrapper = wrapper(request);
+ return request.getFormData().doOnSuccess(params -> wrapper.params(params))
+ .then(processor.post(wrapper, null, false));
}
@PostMapping(path = "/**")
@ResponseBody
- public ResponseEntity> post(WebRequest request,
- @RequestBody FluxRequest> body) {
-
- @SuppressWarnings("unchecked")
- Function, Publisher>> function = (Function, Publisher>>) request
- .getAttribute(WebRequestConstants.FUNCTION, WebRequest.SCOPE_REQUEST);
- @SuppressWarnings("unchecked")
- Consumer> consumer = (Consumer>) request
- .getAttribute(WebRequestConstants.CONSUMER, WebRequest.SCOPE_REQUEST);
- Boolean single = (Boolean) request.getAttribute(WebRequestConstants.INPUT_SINGLE,
- WebRequest.SCOPE_REQUEST);
-
- FluxFormRequest, ?> form = FluxFormRequest.from(request.getParameterMap());
-
- if (function != null) {
- Flux> flux = body.body() == null ? form.flux() : body.flux();
- if (debug) {
- flux = flux.log();
- }
- Flux> result = Flux.from(function.apply(flux));
- if (inspector.isMessage(function)) {
- result = result.map(message -> MessageUtils.unpack(function, message));
- }
- if (logger.isDebugEnabled()) {
- logger.debug("Handled POST with function");
- }
- return ResponseEntity.ok().body(response(request, function, single, result));
- }
-
- if (consumer != null) {
- Flux> flux = body.body() == null ? form.flux().cache()
- : body.flux().cache(); // send a copy back to the caller
- if (debug) {
- flux = flux.log();
- }
- consumer.accept(flux);
- if (logger.isDebugEnabled()) {
- logger.debug("Handled POST with consumer");
- }
- return ResponseEntity.status(HttpStatus.ACCEPTED).body(flux);
- }
-
- throw new IllegalArgumentException("no such function");
+ public Mono> post(ServerWebExchange request,
+ @RequestBody(required = false) String body) {
+ FunctionWrapper wrapper = wrapper(request);
+ return processor.post(wrapper, body, false);
}
- private Publisher> response(WebRequest request, Object handler, Boolean single,
- Publisher> result) {
-
- if (single != null && single && isOutputSingle(handler)) {
- request.setAttribute(WebRequestConstants.OUTPUT_SINGLE, true,
- WebRequest.SCOPE_REQUEST);
- return Mono.from(result);
- }
-
- if (isInputMultiple(handler) && isOutputSingle(handler)) {
- request.setAttribute(WebRequestConstants.OUTPUT_SINGLE, true,
- WebRequest.SCOPE_REQUEST);
- return Mono.from(result);
- }
-
- request.setAttribute(WebRequestConstants.OUTPUT_SINGLE, false,
- WebRequest.SCOPE_REQUEST);
-
- return result;
- }
-
- private boolean isInputMultiple(Object handler) {
- Class> type = inspector.getInputType(handler);
- Class> wrapper = inspector.getInputWrapper(handler);
- return Collection.class.isAssignableFrom(type) || Flux.class.equals(wrapper);
- }
-
- private boolean isOutputSingle(Object handler) {
- Class> type = inspector.getOutputType(handler);
- Class> wrapper = inspector.getOutputWrapper(handler);
- if (Stream.class.isAssignableFrom(type)) {
- return false;
- }
- if (wrapper == type) {
- return true;
- }
- return Mono.class.equals(wrapper) || Optional.class.equals(wrapper);
+ @PostMapping(path = "/**", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+ @ResponseBody
+ public Mono> postStream(ServerWebExchange request,
+ @RequestBody(required = false) String body) {
+ FunctionWrapper wrapper = wrapper(request);
+ return processor.post(wrapper, body, true);
}
@GetMapping(path = "/**")
@ResponseBody
- public ResponseEntity> get(WebRequest request) {
+ public Mono> get(ServerWebExchange request) {
+ FunctionWrapper wrapper = wrapper(request);
+ return processor.get(wrapper);
+ }
+
+ @GetMapping(path = "/**", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+ @ResponseBody
+ public Mono> getStream(ServerWebExchange request) {
+ FunctionWrapper wrapper = wrapper(request);
+ return processor.stream(wrapper);
+ }
+
+ private FunctionWrapper wrapper(ServerWebExchange request) {
@SuppressWarnings("unchecked")
Function, Publisher>> function = (Function, Publisher>>) request
- .getAttribute(WebRequestConstants.FUNCTION, WebRequest.SCOPE_REQUEST);
+ .getAttribute(WebRequestConstants.FUNCTION);
+ @SuppressWarnings("unchecked")
+ Consumer> consumer = (Consumer>) request
+ .getAttribute(WebRequestConstants.CONSUMER);
@SuppressWarnings("unchecked")
Supplier> supplier = (Supplier>) request
- .getAttribute(WebRequestConstants.SUPPLIER, WebRequest.SCOPE_REQUEST);
- String argument = (String) request.getAttribute(WebRequestConstants.ARGUMENT,
- WebRequest.SCOPE_REQUEST);
-
- Publisher> result;
- if (function != null) {
- result = value(function, argument);
- }
- else {
- result = response(request, supplier, true, supplier(supplier));
- }
- if (inspector.isMessage(function)) {
- result = Flux.from(result)
- .map(message -> MessageUtils.unpack(function, message));
- }
- return ResponseEntity.ok().body(result);
- }
-
- private Publisher> supplier(Supplier> supplier) {
- Publisher> result = supplier.get();
- if (logger.isDebugEnabled()) {
- logger.debug("Handled GET with supplier");
- }
- return debug ? Flux.from(result).log() : result;
- }
-
- private Mono> value(Function, Publisher>> 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");
- }
- return debug ? result.log() : result;
+ .getAttribute(WebRequestConstants.SUPPLIER);
+ FunctionWrapper wrapper = RequestProcessor.wrapper(function, consumer, supplier);
+ wrapper.headers(request.getRequest().getHeaders());
+ wrapper.params(request.getRequest().getQueryParams());
+ String argument = (String) request.getAttribute(WebRequestConstants.ARGUMENT);
+ wrapper.argument(argument);
+ return wrapper;
}
}
diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionHandlerMapping.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionHandlerMapping.java
index d2bf970f7..94033a428 100644
--- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionHandlerMapping.java
+++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionHandlerMapping.java
@@ -20,8 +20,6 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
-import javax.servlet.http.HttpServletRequest;
-
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.InitializingBean;
@@ -29,12 +27,15 @@ 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.web.flux.constants.WebRequestConstants;
+import org.springframework.cloud.function.web.constants.WebRequestConstants;
import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.HandlerMapping;
-import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
+import org.springframework.web.server.ServerWebExchange;
+
+import reactor.core.publisher.Mono;
/**
* @author Dave Syer
@@ -52,9 +53,6 @@ public class FunctionHandlerMapping extends RequestMappingHandlerMapping
@Value("${spring.cloud.function.web.path:}")
private String prefix = "";
- @Value("${debug:${DEBUG:false}}")
- private String debug = "false";
-
@Autowired
public FunctionHandlerMapping(FunctionCatalog catalog,
FunctionController controller) {
@@ -67,7 +65,7 @@ public class FunctionHandlerMapping extends RequestMappingHandlerMapping
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
- this.controller.setDebug(!"false".equals(debug));
+ // this.controller.setDebug(!"false".equals(debug));
detectHandlerMethods(controller);
while (prefix.endsWith("/")) {
prefix = prefix.substring(0, prefix.length() - 1);
@@ -79,68 +77,58 @@ public class FunctionHandlerMapping extends RequestMappingHandlerMapping
}
@Override
- protected HandlerMethod getHandlerInternal(HttpServletRequest request)
- throws Exception {
- HandlerMethod handler = super.getHandlerInternal(request);
- if (handler == null) {
- return null;
- }
- String path = (String) request
- .getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
+ public Mono getHandlerInternal(ServerWebExchange request) {
+ String path = request.getRequest().getPath().pathWithinApplication().value();
if (StringUtils.hasText(prefix) && !path.startsWith(prefix)) {
- return null;
+ return Mono.empty();
+ }
+ Mono handler = super.getHandlerInternal(request);
+ if (path == null) {
+ return handler;
}
if (path.startsWith(prefix)) {
path = path.substring(prefix.length());
}
- if (path == null) {
- return handler;
- }
Object function = findFunctionForGet(request, path);
- if (function != null) {
- if (logger.isDebugEnabled()) {
- logger.debug("Found function for GET: " + path);
- }
- request.setAttribute(WebRequestConstants.HANDLER, function);
- return handler;
+ if (function == null) {
+ function = findFunctionForPost(request, path);
}
- function = findFunctionForPost(request, path);
if (function != null) {
if (logger.isDebugEnabled()) {
logger.debug("Found function for POST: " + path);
}
- request.setAttribute(WebRequestConstants.HANDLER, function);
- return handler;
+ request.getAttributes().put(WebRequestConstants.HANDLER, function);
}
- return null;
+ Object actual = function;
+ return handler.filter(method -> actual != null);
}
- private Object findFunctionForPost(HttpServletRequest request, String path) {
- if (!request.getMethod().equals("POST")) {
+ private Object findFunctionForPost(ServerWebExchange request, String path) {
+ if (!request.getRequest().getMethod().equals(HttpMethod.POST)) {
return null;
}
path = path.startsWith("/") ? path.substring(1) : path;
Consumer> consumer = functions.lookup(Consumer.class, path);
if (consumer != null) {
- request.setAttribute(WebRequestConstants.CONSUMER, consumer);
+ request.getAttributes().put(WebRequestConstants.CONSUMER, consumer);
return consumer;
}
Function