diff --git a/spring-cloud-function-context/pom.xml b/spring-cloud-function-context/pom.xml
index 757fe4d27..863e28b3c 100644
--- a/spring-cloud-function-context/pom.xml
+++ b/spring-cloud-function-context/pom.xml
@@ -37,18 +37,6 @@
-
-
-
- org.springframework.boot
- spring-boot-dependencies
- 2.0.0.BUILD-SNAPSHOT
- pom
- import
-
-
-
-
diff --git a/spring-cloud-function-deployer/pom.xml b/spring-cloud-function-deployer/pom.xml
index dccb0937b..f8daf6a0e 100644
--- a/spring-cloud-function-deployer/pom.xml
+++ b/spring-cloud-function-deployer/pom.xml
@@ -54,20 +54,6 @@
-
- org.springframework.boot
- spring-boot-dependencies
- 2.0.0.BUILD-SNAPSHOT
- pom
- import
-
-
- org.springframework.boot.experimental
- spring-boot-dependencies-web-reactive
- 0.1.0.BUILD-SNAPSHOT
- pom
- import
- org.springframework.cloudspring-cloud-function-parent
diff --git a/spring-cloud-function-samples/spring-cloud-function-sample-pojo/pom.xml b/spring-cloud-function-samples/spring-cloud-function-sample-pojo/pom.xml
index fb2b1973a..620f889ba 100644
--- a/spring-cloud-function-samples/spring-cloud-function-sample-pojo/pom.xml
+++ b/spring-cloud-function-samples/spring-cloud-function-sample-pojo/pom.xml
@@ -7,13 +7,13 @@
function-sample-pojo1.0.0.BUILD-SNAPSHOTjar
- spring-cloud-function-sample
+ spring-cloud-function-sample-pojoSpring Cloud Function Web Supportorg.springframework.bootspring-boot-starter-parent
- 2.0.0.BUILD-SNAPSHOT
+ 1.5.0.BUILD-SNAPSHOT
@@ -41,18 +41,6 @@
-
-
-
- org.springframework.boot
- spring-boot-dependencies
- 2.0.0.BUILD-SNAPSHOT
- pom
- import
-
-
-
-
diff --git a/spring-cloud-function-samples/spring-cloud-function-sample/pom.xml b/spring-cloud-function-samples/spring-cloud-function-sample/pom.xml
index c2d277cc1..5940479cd 100644
--- a/spring-cloud-function-samples/spring-cloud-function-sample/pom.xml
+++ b/spring-cloud-function-samples/spring-cloud-function-sample/pom.xml
@@ -13,7 +13,7 @@
org.springframework.bootspring-boot-starter-parent
- 2.0.0.BUILD-SNAPSHOT
+ 1.5.0.BUILD-SNAPSHOT
@@ -46,18 +46,6 @@
-
-
-
- org.springframework.boot
- spring-boot-dependencies
- 2.0.0.BUILD-SNAPSHOT
- pom
- import
-
-
-
-
diff --git a/spring-cloud-function-web/pom.xml b/spring-cloud-function-web/pom.xml
index b5a3b0bba..6f6d16ba7 100644
--- a/spring-cloud-function-web/pom.xml
+++ b/spring-cloud-function-web/pom.xml
@@ -20,8 +20,8 @@
- org.springframework.boot.experimental
- spring-boot-starter-web-reactive
+ org.springframework.boot
+ spring-boot-starter-weborg.springframework.cloud
@@ -34,25 +34,6 @@
-
-
-
- org.springframework.boot
- spring-boot-dependencies
- 2.0.0.BUILD-SNAPSHOT
- pom
- import
-
-
- org.springframework.boot.experimental
- spring-boot-dependencies-web-reactive
- 0.1.0.BUILD-SNAPSHOT
- pom
- import
-
-
-
-
diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/FunctionController.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/FunctionController.java
index 42646d51c..117043345 100644
--- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/FunctionController.java
+++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/FunctionController.java
@@ -20,7 +20,6 @@ import java.util.function.Function;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.context.embedded.ReactiveServerProperties;
import org.springframework.cloud.function.registry.FunctionCatalog;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -35,7 +34,7 @@ import reactor.core.publisher.Flux;
*
*/
@RestController
-@ConditionalOnClass({ RestController.class, ReactiveServerProperties.class })
+@ConditionalOnClass(RestController.class)
public class FunctionController {
@Value("${debug:${DEBUG:false}}")
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 262bf7d26..aea2fd5a3 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 the original author or authors.
+ * 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.
@@ -16,311 +16,16 @@
package org.springframework.cloud.function.web;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-
-import org.apache.commons.io.Charsets;
-import org.reactivestreams.Publisher;
-
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.core.ResolvableType;
-import org.springframework.core.codec.AbstractDecoder;
-import org.springframework.core.io.buffer.DataBuffer;
-import org.springframework.core.io.buffer.DataBufferFactory;
-import org.springframework.core.io.buffer.DataBufferUtils;
-import org.springframework.http.codec.DecoderHttpMessageReader;
-import org.springframework.http.codec.HttpMessageReader;
-import org.springframework.http.codec.ServerSentEventHttpMessageReader;
-import org.springframework.util.MimeType;
-import org.springframework.util.StreamUtils;
-import org.springframework.web.reactive.config.WebReactiveConfigurer;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufUtil;
-import io.netty.buffer.Unpooled;
-import reactor.core.publisher.Flux;
/**
* @author Mark Fisher
*/
@SpringBootApplication
-public class RestApplication implements WebReactiveConfigurer {
-
- @Override
- public void extendMessageReaders(List> readers) {
- readers.add(0, new ServerSentEventHttpMessageReader());
- // Instead of the default JSON decoder we want to keep the Strings unparsed
- readers.add(1, new DecoderHttpMessageReader<>(new JsonObjectDecoder()));
- }
+public class RestApplication {
public static void main(String[] args) {
SpringApplication.run(RestApplication.class, args);
}
}
-
-/**
- * Decode an arbitrary split byte stream representing JSON objects to a byte stream where
- * each chunk is a well-formed JSON object.
- *
- *
- * This class does not do any real parsing or validation. A sequence of bytes is
- * considered a JSON object/array if it contains a matching number of opening and closing
- * braces/brackets.
- *
- *
- * Based on Netty
- * JsonObjectDecoder
- *
- * @author Sebastien Deleuze
- *
- */
-// Copied from spring-web
-class JsonObjectDecoder extends AbstractDecoder {
-
- private static final int ST_CORRUPTED = -1;
-
- private static final int ST_INIT = 0;
-
- private static final int ST_DECODING_NORMAL = 1;
-
- private static final int ST_DECODING_ARRAY_STREAM = 2;
-
- private final int maxObjectLength;
-
- private final boolean streamArrayElements;
-
- public JsonObjectDecoder() {
- // 1 MB
- this(1024 * 1024);
- }
-
- public JsonObjectDecoder(int maxObjectLength) {
- this(maxObjectLength, true);
- }
-
- public JsonObjectDecoder(boolean streamArrayElements) {
- this(1024 * 1024, streamArrayElements);
- }
-
- /**
- * @param maxObjectLength maximum number of bytes a JSON object/array may use
- * (including braces and all). Objects exceeding this length are dropped and an
- * {@link IllegalStateException} is thrown.
- * @param streamArrayElements if set to true and the "top level" JSON object is an
- * array, each of its entries is passed through the pipeline individually and
- * immediately after it was fully received, allowing for arrays with
- */
- public JsonObjectDecoder(int maxObjectLength, boolean streamArrayElements) {
- super(new MimeType("application", "json", StandardCharsets.UTF_8),
- new MimeType("application", "*+json", StandardCharsets.UTF_8));
- if (maxObjectLength < 1) {
- throw new IllegalArgumentException("maxObjectLength must be a positive int");
- }
- this.maxObjectLength = maxObjectLength;
- this.streamArrayElements = streamArrayElements;
- }
-
- @Override
- public Flux decode(Publisher inputStream,
- ResolvableType elementType, MimeType mimeType, Map hints) {
-
- return Flux.from(inputStream)
- .flatMap(new Function>() {
-
- int openBraces;
- int index;
- int state;
- boolean insideString;
- ByteBuf input;
- Integer writerIndex;
-
- @Override
- public Publisher apply(DataBuffer buffer) {
- List chunks = new ArrayList<>();
- if (this.input == null) {
- this.input = Unpooled.copiedBuffer(buffer.asByteBuffer());
- DataBufferUtils.release(buffer);
- this.writerIndex = this.input.writerIndex();
- }
- else {
- this.index = this.index - this.input.readerIndex();
- this.input = Unpooled.copiedBuffer(this.input,
- Unpooled.copiedBuffer(buffer.asByteBuffer()));
- DataBufferUtils.release(buffer);
- this.writerIndex = this.input.writerIndex();
- }
- if (this.state == ST_CORRUPTED) {
- this.input.skipBytes(this.input.readableBytes());
- return Flux
- .error(new IllegalStateException("Corrupted stream"));
- }
- if (this.writerIndex > maxObjectLength) {
- // buffer size exceeded maxObjectLength; discarding the
- // complete buffer.
- this.input.skipBytes(this.input.readableBytes());
- reset();
- return Flux.error(new IllegalStateException(
- "object length exceeds " + maxObjectLength + ": "
- + this.writerIndex + " bytes discarded"));
- }
- DataBufferFactory dataBufferFactory = buffer.factory();
- for (/* use current index */; this.index < this.writerIndex; this.index++) {
- byte c = this.input.getByte(this.index);
- if (this.state == ST_DECODING_NORMAL) {
- decodeByte(c, this.input, this.index);
-
- // All opening braces/brackets have been closed. That's
- // enough to conclude
- // that the JSON object/array is complete.
- if (this.openBraces == 0) {
- ByteBuf json = extractObject(this.input,
- this.input.readerIndex(),
- this.index + 1 - this.input.readerIndex());
- if (json != null) {
- chunks.add(
- dataBufferFactory.wrap(json.nioBuffer()));
- }
-
- // The JSON object/array was extracted => discard the
- // bytes from
- // the input buffer.
- this.input.readerIndex(this.index + 1);
- // Reset the object state to get ready for the next
- // JSON object/text
- // coming along the byte stream.
- reset();
- }
- }
- else if (this.state == ST_DECODING_ARRAY_STREAM) {
- decodeByte(c, this.input, this.index);
-
- if (!this.insideString
- && (this.openBraces == 1 && c == ','
- || this.openBraces == 0 && c == ']')) {
- // skip leading spaces. No range check is needed and
- // the loop will terminate
- // because the byte at position index is not a
- // whitespace.
- for (int i = this.input.readerIndex(); Character
- .isWhitespace(this.input.getByte(i)); i++) {
- this.input.skipBytes(1);
- }
-
- // skip trailing spaces.
- int idxNoSpaces = this.index - 1;
- while (idxNoSpaces >= this.input.readerIndex()
- && Character.isWhitespace(
- this.input.getByte(idxNoSpaces))) {
-
- idxNoSpaces--;
- }
-
- ByteBuf json = extractObject(this.input,
- this.input.readerIndex(),
- idxNoSpaces + 1 - this.input.readerIndex());
-
- if (json != null) {
- chunks.add(
- dataBufferFactory.wrap(json.nioBuffer()));
- }
-
- this.input.readerIndex(this.index + 1);
-
- if (c == ']') {
- reset();
- }
- }
- // JSON object/array detected. Accumulate bytes until all
- // braces/brackets are closed.
- }
- else if (c == '{' || c == '[') {
- initDecoding(c, streamArrayElements);
-
- if (this.state == ST_DECODING_ARRAY_STREAM) {
- // Discard the array bracket
- this.input.skipBytes(1);
- }
- // Discard leading spaces in front of a JSON object/array.
- }
- else if (Character.isWhitespace(c)) {
- this.input.skipBytes(1);
- }
- else {
- this.state = ST_CORRUPTED;
- return Flux.error(new IllegalStateException(
- "invalid JSON received at byte position "
- + this.index + ": "
- + ByteBufUtil.hexDump(this.input)));
- }
- }
-
- return Flux.fromIterable(chunks).map(buf -> {
- try {
- return StreamUtils.copyToString(buf.asInputStream(),
- Charsets.UTF_8);
- }
- catch (IOException e) {
- throw new IllegalStateException(
- "Cannot convert buffer to String" + buf, e);
- }
- });
- }
-
- /**
- * Override this method if you want to filter the json objects/arrays
- * that get passed through the pipeline.
- */
- protected ByteBuf extractObject(ByteBuf buffer, int index,
- int length) {
- return buffer.slice(index, length).retain();
- }
-
- private void decodeByte(byte c, ByteBuf input, int index) {
- if ((c == '{' || c == '[') && !this.insideString) {
- this.openBraces++;
- }
- else if ((c == '}' || c == ']') && !this.insideString) {
- this.openBraces--;
- }
- else if (c == '"') {
- // start of a new JSON string. It's necessary to detect
- // strings as they may
- // also contain braces/brackets and that could lead to
- // incorrect results.
- if (!this.insideString) {
- this.insideString = true;
- // If the double quote wasn't escaped then this is the end
- // of a string.
- }
- else if (input.getByte(index - 1) != '\\') {
- this.insideString = false;
- }
- }
- }
-
- private void initDecoding(byte openingBrace,
- boolean streamArrayElements) {
- this.openBraces = 1;
- if (openingBrace == '[' && streamArrayElements) {
- this.state = ST_DECODING_ARRAY_STREAM;
- }
- else {
- this.state = ST_DECODING_NORMAL;
- }
- }
-
- private void reset() {
- this.insideString = false;
- this.state = ST_INIT;
- this.openBraces = 0;
- }
- });
- }
-
-}
\ No newline at end of file
diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FluxHttpMessageConverter.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FluxHttpMessageConverter.java
new file mode 100644
index 000000000..7db689bf5
--- /dev/null
+++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FluxHttpMessageConverter.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2012-2015 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;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+
+import reactor.core.publisher.Flux;
+
+/**
+ * Converter for request bodies of type Flux.
+ *
+ * @author Dave Syer
+ *
+ */
+public class FluxHttpMessageConverter implements HttpMessageConverter> {
+
+ private static final MediaType EVENT_STREAM = MediaType.valueOf("text/event-stream");
+
+ @Override
+ public boolean canRead(Class> clazz, MediaType mediaType) {
+ return Flux.class.isAssignableFrom(clazz);
+ }
+
+ @Override
+ public boolean canWrite(Class> clazz, MediaType mediaType) {
+ return false;
+ }
+
+ @Override
+ public List getSupportedMediaTypes() {
+ return Arrays.asList(MediaType.ALL);
+ }
+
+ @Override
+ public Flux