From 55fee27fb67389e3849417671d04857583905c01 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 20 Mar 2020 09:40:39 +0100 Subject: [PATCH] DATAMONGO-625 - Polishing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce default interface methods where possible. Rename save(…) to store(…) to align with method naming. Reduce constructor visibility to avoid invalid API usage. Replace mutable object in builder with fields to avoid mutation of already built objects when reusing the builder. Remove Options.chunked(…) factory method to avoid confusion with chunkSize method. Reformat code, strip trailing whitespaces. Original pull request: #842. --- .../data/mongodb/gridfs/GridFsObject.java | 29 ++---- .../data/mongodb/gridfs/GridFsOperations.java | 18 ++-- .../data/mongodb/gridfs/GridFsTemplate.java | 27 +---- .../data/mongodb/gridfs/GridFsUpload.java | 99 ++++++++++++------- .../gridfs/ReactiveGridFsOperations.java | 4 +- .../gridfs/ReactiveGridFsTemplate.java | 2 +- .../mongodb/gridfs/ReactiveGridFsUpload.java | 66 ++++++++----- .../data/mongodb/util/BsonUtils.java | 12 +-- .../GridFsTemplateIntegrationTests.java | 2 +- .../gridfs/ReactiveGridFsTemplateTests.java | 2 +- 10 files changed, 140 insertions(+), 121 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsObject.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsObject.java index 592bab940..fd158b82b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsObject.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsObject.java @@ -21,7 +21,7 @@ import org.springframework.lang.Nullable; import com.mongodb.client.gridfs.model.GridFSFile; /** - * A common interface when dealing with GridFs items using Spring Data.o + * A common interface when dealing with GridFs items using Spring Data. * * @author Christoph Strobl * @since 3.0 @@ -31,7 +31,7 @@ public interface GridFsObject { /** * The {@link GridFSFile#getId()} value converted into its simple java type.
* A {@link org.bson.BsonString} will be converted to plain {@link String}. - * + * * @return can be {@literal null} depending on the implementation. */ @Nullable @@ -39,34 +39,35 @@ public interface GridFsObject { /** * The filename. - * + * * @return */ String getFilename(); /** * The actual file content. - * + * * @return + * @throws IllegalStateException if the content cannot be obtained. */ CONTENT getContent(); /** * Additional information like file metadata (eg. contentType). - * + * * @return never {@literal null}. */ Options getOptions(); /** * Additional, context relevant information. - * + * * @author Christoph Strobl */ class Options { - private Document metadata = new Document(); - private int chunkSize = -1; + private final Document metadata; + private final int chunkSize; private Options(Document metadata, int chunkSize) { @@ -83,16 +84,6 @@ public interface GridFsObject { return new Options(new Document(), -1); } - /** - * Static factory method to create {@link Options} with given chunk size. - * - * @param chunkSize - * @return new instance of {@link Options}. - */ - public static Options chunked(int chunkSize) { - return new Options(new Document(), chunkSize); - } - /** * Static factory method to create {@link Options} with given content type. * @@ -115,7 +106,7 @@ public interface GridFsObject { /** * Set the associated content type. - * + * * @param contentType must not be {@literal null}. * @return new instance of {@link Options}. */ diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperations.java index 5f7ee4ce9..036666857 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperations.java @@ -59,7 +59,9 @@ public interface GridFsOperations extends ResourcePatternResolver { * @param metadata can be {@literal null}. * @return the {@link ObjectId} of the {@link com.mongodb.client.gridfs.model.GridFSFile} just created. */ - ObjectId store(InputStream content, @Nullable Object metadata); + default ObjectId store(InputStream content, @Nullable Object metadata) { + return store(content, null, metadata); + } /** * Stores the given content into a file with the given name. @@ -80,7 +82,9 @@ public interface GridFsOperations extends ResourcePatternResolver { * @param contentType can be {@literal null}. * @return the {@link ObjectId} of the {@link com.mongodb.client.gridfs.model.GridFSFile} just created. */ - ObjectId store(InputStream content, @Nullable String filename, @Nullable String contentType); + default ObjectId store(InputStream content, @Nullable String filename, @Nullable String contentType) { + return store(content, filename, contentType, null); + } /** * Stores the given content into a file with the given name using the given metadata. The metadata object will be @@ -91,7 +95,9 @@ public interface GridFsOperations extends ResourcePatternResolver { * @param metadata can be {@literal null}. * @return the {@link ObjectId} of the {@link com.mongodb.client.gridfs.model.GridFSFile} just created. */ - ObjectId store(InputStream content, @Nullable String filename, @Nullable Object metadata); + default ObjectId store(InputStream content, @Nullable String filename, @Nullable Object metadata) { + return store(content, filename, null, metadata); + } /** * Stores the given content into a file with the given name and content type using the given metadata. The metadata @@ -141,21 +147,21 @@ public interface GridFsOperations extends ResourcePatternResolver { uploadBuilder.metadata(metadata); } - return save(uploadBuilder.build()); + return store(uploadBuilder.build()); } /** * Stores the given {@link GridFsObject}, likely a {@link GridFsUpload}, into into a file with given * {@link GridFsObject#getFilename() name}. If the {@link GridFsObject#getFileId()} is set, the file will be stored * with that id, otherwise the server auto creates a new id.
- * + * * @param upload the {@link GridFsObject} (most likely a {@link GridFsUpload}) to be stored. * @param id type of the underlying {@link com.mongodb.client.gridfs.model.GridFSFile} * @return the id of the stored file. Either an auto created value or {@link GridFsObject#getFileId()}, but never * {@literal null}. * @since 3.0 */ - T save(GridFsObject upload); + T store(GridFsObject upload); /** * Returns all files matching the given query. Note, that currently {@link Sort} criterias defined at the diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsTemplate.java index cf8b2211c..45702ef24 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsTemplate.java @@ -87,31 +87,6 @@ public class GridFsTemplate extends GridFsOperationsSupport implements GridFsOpe this.bucket = bucket; } - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.gridfs.GridFsOperations#store(java.io.InputStream, java.lang.Object) - */ - @Override - public ObjectId store(InputStream content, @Nullable Object metadata) { - return store(content, null, metadata); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.gridfs.GridFsOperations#store(java.io.InputStream, java.lang.String, java.lang.String) - */ - public ObjectId store(InputStream content, @Nullable String filename, @Nullable String contentType) { - return store(content, filename, contentType, (Object) null); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.gridfs.GridFsOperations#store(java.io.InputStream, java.lang.String, java.lang.Object) - */ - public ObjectId store(InputStream content, @Nullable String filename, @Nullable Object metadata) { - return store(content, filename, null, metadata); - } - /* * (non-Javadoc) * @see org.springframework.data.mongodb.gridfs.GridFsOperations#store(java.io.InputStream, java.lang.String, java.lang.String, java.lang.Object) @@ -125,7 +100,7 @@ public class GridFsTemplate extends GridFsOperationsSupport implements GridFsOpe * (non-Javadoc) * @see org.springframework.data.mongodb.gridfs.GridFsOperations#save(org.springframework.data.mongodb.gridfs.GridFsObject) */ - public T save(GridFsObject upload) { + public T store(GridFsObject upload) { GridFSUploadOptions uploadOptions = computeUploadOptionsFor(upload.getOptions().getContentType(), upload.getOptions().getMetadata()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsUpload.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsUpload.java index ee80c7936..c86926db7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsUpload.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsUpload.java @@ -15,42 +15,54 @@ */ package org.springframework.data.mongodb.gridfs; -import java.io.IOException; import java.io.InputStream; +import java.util.function.Supplier; import org.bson.Document; import org.bson.types.ObjectId; + import org.springframework.data.util.Lazy; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.StreamUtils; import com.mongodb.client.gridfs.model.GridFSFile; /** + * Upload descriptor for a GridFS file upload. + * * @author Christoph Strobl + * @author Mark Paluch * @since 3.0 */ public class GridFsUpload implements GridFsObject { - private static final InputStream EMPTY_STREAM = new InputStream() { - @Override - public int read() throws IOException { - return -1; - } - }; + private final @Nullable ID id; + private final Lazy dataStream; + private final String filename; + private final Options options; - private ID id; - private Lazy dataStream; - private String filename; - private Options options; + private GridFsUpload(@Nullable ID id, Lazy dataStream, String filename, Options options) { + + Assert.notNull(dataStream, "Data Stream must not be null"); + Assert.notNull(filename, "Filename must not be null"); + Assert.notNull(options, "Options must not be null"); + + this.id = id; + this.dataStream = dataStream; + this.filename = filename; + this.options = options; + } /** * The {@link GridFSFile#getId()} value converted into its simple java type.
* A {@link org.bson.BsonString} will be converted to plain {@link String}. - * + * * @return can be {@literal null}. * @see org.springframework.data.mongodb.gridfs.GridFsObject#getFileId() */ @Override + @Nullable public ID getFileId() { return id; } @@ -70,7 +82,7 @@ public class GridFsUpload implements GridFsObject { */ @Override public InputStream getContent() { - return dataStream.orElse(EMPTY_STREAM); + return dataStream.orElse(StreamUtils.emptyInput()); } /* @@ -89,22 +101,22 @@ public class GridFsUpload implements GridFsObject { * @return new instance of {@link GridFsUpload}. */ public static GridFsUploadBuilder fromStream(InputStream stream) { - return new GridFsUploadBuilder().content(stream); + return new GridFsUploadBuilder().content(stream); } /** * Builder to create {@link GridFsUpload} in a fluent way. - * + * * @param the target id type. */ public static class GridFsUploadBuilder { - private GridFsUpload upload; + private Object id; + private Lazy dataStream; + private String filename; + private Options options = Options.none(); - public GridFsUploadBuilder() { - this.upload = new GridFsUpload(); - this.upload.options = Options.none(); - } + private GridFsUploadBuilder() {} /** * Define the content of the file to upload. @@ -114,7 +126,22 @@ public class GridFsUpload implements GridFsObject { */ public GridFsUploadBuilder content(InputStream stream) { - upload.dataStream = Lazy.of(() -> stream); + Assert.notNull(stream, "InputStream must not be null"); + + return content(() -> stream); + } + + /** + * Define the content of the file to upload. + * + * @param stream the upload content. + * @return this. + */ + public GridFsUploadBuilder content(Supplier stream) { + + Assert.notNull(stream, "InputStream Supplier must not be null"); + + this.dataStream = Lazy.of(stream); return this; } @@ -127,7 +154,7 @@ public class GridFsUpload implements GridFsObject { */ public GridFsUploadBuilder id(T1 id) { - upload.id = id; + this.id = id; return (GridFsUploadBuilder) this; } @@ -139,7 +166,7 @@ public class GridFsUpload implements GridFsObject { */ public GridFsUploadBuilder filename(String filename) { - upload.filename = filename; + this.filename = filename; return this; } @@ -151,7 +178,9 @@ public class GridFsUpload implements GridFsObject { */ public GridFsUploadBuilder options(Options options) { - upload.options = options; + Assert.notNull(options, "Options must not be null"); + + this.options = options; return this; } @@ -163,7 +192,7 @@ public class GridFsUpload implements GridFsObject { */ public GridFsUploadBuilder metadata(Document metadata) { - upload.options = upload.options.metadata(metadata); + this.options = this.options.metadata(metadata); return this; } @@ -175,40 +204,42 @@ public class GridFsUpload implements GridFsObject { */ public GridFsUploadBuilder chunkSize(int chunkSize) { - upload.options = upload.options.chunkSize(chunkSize); + this.options = this.options.chunkSize(chunkSize); return this; } /** * Set id, filename, metadata and chunk size from given file. - * + * * @param gridFSFile must not be {@literal null}. * @return this. */ public GridFsUploadBuilder gridFsFile(GridFSFile gridFSFile) { - upload.id = gridFSFile.getId(); - upload.filename = gridFSFile.getFilename(); - upload.options = upload.options.metadata(gridFSFile.getMetadata()); - upload.options = upload.options.chunkSize(gridFSFile.getChunkSize()); + Assert.notNull(gridFSFile, "GridFSFile must not be null"); + + this.id = gridFSFile.getId(); + this.filename = gridFSFile.getFilename(); + this.options = this.options.metadata(gridFSFile.getMetadata()); + this.options = this.options.chunkSize(gridFSFile.getChunkSize()); return this; } /** * Set the content type. - * + * * @param contentType must not be {@literal null}. * @return this. */ public GridFsUploadBuilder contentType(String contentType) { - upload.options = upload.options.contentType(contentType); + this.options = this.options.contentType(contentType); return this; } public GridFsUpload build() { - return (GridFsUpload) upload; + return new GridFsUpload(id, dataStream, filename, options); } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsOperations.java index fa562582f..fe7f5bd14 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsOperations.java @@ -155,7 +155,7 @@ public interface ReactiveGridFsOperations { uploadBuilder.metadata(metadata); } - return save(uploadBuilder.build()); + return store(uploadBuilder.build()); } /** @@ -169,7 +169,7 @@ public interface ReactiveGridFsOperations { * {@link GridFsObject#getFileId()}. * @since 3.0 */ - Mono save(GridFsObject> upload); + Mono store(GridFsObject> upload); /** * Returns a {@link Flux} emitting all files matching the given query.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplate.java index 49cd83fba..524de7b9b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplate.java @@ -121,7 +121,7 @@ public class ReactiveGridFsTemplate extends GridFsOperationsSupport implements R * (non-Javadoc) * @see org.springframework.data.mongodb.gridfs.ReactiveGridFsOperations#save(org.springframework.data.mongodb.gridfs.GridFsObject) */ - public Mono save(GridFsObject> upload) { + public Mono store(GridFsObject> upload) { GridFSUploadOptions uploadOptions = computeUploadOptionsFor(upload.getOptions().getContentType(), upload.getOptions().getMetadata()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsUpload.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsUpload.java index 30a33f3ae..9decedc78 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsUpload.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsUpload.java @@ -20,19 +20,35 @@ import org.bson.types.ObjectId; import org.reactivestreams.Publisher; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import com.mongodb.client.gridfs.model.GridFSFile; /** + * Upload descriptor for a GridFS file upload. + * * @author Christoph Strobl + * @author Mark Paluch * @since 3.0 */ public class ReactiveGridFsUpload implements GridFsObject> { - private ID id; - private Publisher dataStream; - private String filename; - private Options options; + private final @Nullable ID id; + private final Publisher dataStream; + private final String filename; + private final Options options; + + private ReactiveGridFsUpload(@Nullable ID id, Publisher dataStream, String filename, Options options) { + + Assert.notNull(dataStream, "Data Stream must not be null"); + Assert.notNull(filename, "Filename must not be null"); + Assert.notNull(options, "Options must not be null"); + + this.id = id; + this.dataStream = dataStream; + this.filename = filename; + this.options = options; + } /** * The {@link GridFSFile#getId()} value converted into its simple java type.
@@ -42,6 +58,7 @@ public class ReactiveGridFsUpload implements GridFsObject implements GridFsObject fromPublisher(Publisher source) { - return new ReactiveGridFsUploadBuilder().content(source); + return new ReactiveGridFsUploadBuilder().content(source); } /** @@ -90,13 +107,12 @@ public class ReactiveGridFsUpload implements GridFsObject { - ReactiveGridFsUpload upload; + private @Nullable Object id; + private Publisher dataStream; + private String filename; + private Options options = Options.none(); - public ReactiveGridFsUploadBuilder() { - - this.upload = new ReactiveGridFsUpload(); - this.upload.options = Options.none(); - } + private ReactiveGridFsUploadBuilder() {} /** * Define the content of the file to upload. @@ -105,7 +121,7 @@ public class ReactiveGridFsUpload implements GridFsObject content(Publisher source) { - upload.dataStream = source; + this.dataStream = source; return this; } @@ -118,7 +134,7 @@ public class ReactiveGridFsUpload implements GridFsObject ReactiveGridFsUploadBuilder id(T1 id) { - upload.id = id; + this.id = id; return (ReactiveGridFsUploadBuilder) this; } @@ -130,7 +146,7 @@ public class ReactiveGridFsUpload implements GridFsObject filename(String filename) { - upload.filename = filename; + this.filename = filename; return this; } @@ -142,7 +158,9 @@ public class ReactiveGridFsUpload implements GridFsObject options(Options options) { - upload.options = options; + Assert.notNull(options, "Options must not be null"); + + this.options = options; return this; } @@ -154,7 +172,7 @@ public class ReactiveGridFsUpload implements GridFsObject metadata(Document metadata) { - upload.options = upload.options.metadata(metadata); + this.options = this.options.metadata(metadata); return this; } @@ -166,7 +184,7 @@ public class ReactiveGridFsUpload implements GridFsObject chunkSize(int chunkSize) { - upload.options = upload.options.chunkSize(chunkSize); + this.options = this.options.chunkSize(chunkSize); return this; } @@ -178,10 +196,12 @@ public class ReactiveGridFsUpload implements GridFsObject gridFsFile(GridFSFile gridFSFile) { - upload.id = gridFSFile.getId(); - upload.filename = gridFSFile.getFilename(); - upload.options = upload.options.metadata(gridFSFile.getMetadata()); - upload.options = upload.options.chunkSize(gridFSFile.getChunkSize()); + Assert.notNull(gridFSFile, "GridFSFile must not be null"); + + this.id = gridFSFile.getId(); + this.filename = gridFSFile.getFilename(); + this.options = this.options.metadata(gridFSFile.getMetadata()); + this.options = this.options.chunkSize(gridFSFile.getChunkSize()); return this; } @@ -194,12 +214,12 @@ public class ReactiveGridFsUpload implements GridFsObject contentType(String contentType) { - upload.options = upload.options.contentType(contentType); + this.options = this.options.contentType(contentType); return this; } public ReactiveGridFsUpload build() { - return (ReactiveGridFsUpload) upload; + return new ReactiveGridFsUpload(id, dataStream, filename, options); } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java index e1e868da6..c2dcec68b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java @@ -36,11 +36,11 @@ import org.bson.codecs.DocumentCodec; import org.bson.conversions.Bson; import org.bson.json.JsonParseException; import org.bson.types.ObjectId; + import org.springframework.core.convert.converter.Converter; import org.springframework.data.mongodb.CodecRegistryProvider; import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.NumberUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -171,16 +171,12 @@ public class BsonUtils { return new BsonBoolean((Boolean) source); } - if(source instanceof Float) { + if (source instanceof Float) { return new BsonDouble((Float) source); } - if (source instanceof Double) { - return new BsonDouble((Double) source); - } - - throw new IllegalArgumentException( - String.format("Unable to convert % (%s) to BsonValue.", source, source != null ? source.getClass() : "null")); + throw new IllegalArgumentException(String.format("Unable to convert %s (%s) to BsonValue.", source, + source != null ? source.getClass().getName() : "null")); } /** diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/GridFsTemplateIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/GridFsTemplateIntegrationTests.java index 737f397f3..ac012f426 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/GridFsTemplateIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/GridFsTemplateIntegrationTests.java @@ -301,7 +301,7 @@ public class GridFsTemplateIntegrationTests { .contentType("xml") // .build(); - assertThat(operations.save(upload)).isEqualTo(id); + assertThat(operations.store(upload)).isEqualTo(id); GridFsResource fsFile = operations.getResource(operations.findOne(query(where("_id").is(id)))); byte[] content = StreamUtils.copyToByteArray(fsFile.getInputStream()); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplateTests.java index 236f3b78d..7ec42852c 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplateTests.java @@ -278,7 +278,7 @@ public class ReactiveGridFsTemplateTests { .contentType("xml") // .build(); - operations.save(upload).as(StepVerifier::create).expectNext(id).verifyComplete(); + operations.store(upload).as(StepVerifier::create).expectNext(id).verifyComplete(); operations.findOne(query(where("_id").is(id))).flatMap(operations::getResource) .flatMapMany(ReactiveGridFsResource::getDownloadStream) //