33
33
import java .util .HashSet ;
34
34
import java .util .Set ;
35
35
import java .util .concurrent .Callable ;
36
+ import java .util .concurrent .Executor ;
36
37
import java .util .concurrent .atomic .AtomicBoolean ;
37
38
import java .util .concurrent .atomic .AtomicLong ;
38
39
import java .util .concurrent .atomic .AtomicReference ;
41
42
import org .apache .commons .logging .Log ;
42
43
import org .apache .commons .logging .LogFactory ;
43
44
import org .reactivestreams .Publisher ;
45
+ import org .reactivestreams .Subscriber ;
44
46
import org .reactivestreams .Subscription ;
45
47
import reactor .core .publisher .BaseSubscriber ;
46
48
import reactor .core .publisher .Flux ;
@@ -66,6 +68,9 @@ public abstract class DataBufferUtils {
66
68
67
69
private static final Consumer <DataBuffer > RELEASE_CONSUMER = DataBufferUtils ::release ;
68
70
71
+ private static final int DEFAULT_CHUNK_SIZE = 1024 ;
72
+
73
+
69
74
70
75
//---------------------------------------------------------------------
71
76
// Reading
@@ -405,6 +410,83 @@ static void closeChannel(@Nullable Channel channel) {
405
410
}
406
411
407
412
413
+ /**
414
+ * Create a new {@code Publisher<DataBuffer>} based on bytes written to a
415
+ * {@code OutputStream}.
416
+ * <ul>
417
+ * <li>The parameter {@code outputStreamConsumer} is invoked once per
418
+ * subscription of the returned {@code Publisher}, when the first
419
+ * item is
420
+ * {@linkplain Subscription#request(long) requested}.</li>
421
+ * <li>{@link OutputStream#write(byte[], int, int) OutputStream.write()}
422
+ * invocations made by {@code outputStreamConsumer} are buffered until they
423
+ * exceed the default chunk size of 1024, or when the stream is
424
+ * {@linkplain OutputStream#flush() flushed} and then result in a
425
+ * {@linkplain Subscriber#onNext(Object) published} item
426
+ * if there is {@linkplain Subscription#request(long) demand}.</li>
427
+ * <li>If there is <em>no demand</em>, {@code OutputStream.write()} will block
428
+ * until there is.</li>
429
+ * <li>If the subscription is {@linkplain Subscription#cancel() cancelled},
430
+ * {@code OutputStream.write()} will throw a {@code IOException}.</li>
431
+ * <li>The subscription is
432
+ * {@linkplain Subscriber#onComplete() completed} when
433
+ * {@code outputStreamHandler} completes.</li>
434
+ * <li>Any exceptions thrown from {@code outputStreamHandler} will
435
+ * be dispatched to the {@linkplain Subscriber#onError(Throwable) Subscriber}.
436
+ * </ul>
437
+ * @param outputStreamConsumer invoked when the first buffer is requested
438
+ * @param executor used to invoke the {@code outputStreamHandler}
439
+ * @return a {@code Publisher<DataBuffer>} based on bytes written by
440
+ * {@code outputStreamHandler}
441
+ */
442
+ public static Publisher <DataBuffer > outputStreamPublisher (Consumer <OutputStream > outputStreamConsumer ,
443
+ DataBufferFactory bufferFactory , Executor executor ) {
444
+
445
+ return outputStreamPublisher (outputStreamConsumer , bufferFactory , executor , DEFAULT_CHUNK_SIZE );
446
+ }
447
+
448
+ /**
449
+ * Creates a new {@code Publisher<DataBuffer>} based on bytes written to a
450
+ * {@code OutputStream}.
451
+ * <ul>
452
+ * <li>The parameter {@code outputStreamConsumer} is invoked once per
453
+ * subscription of the returned {@code Publisher}, when the first
454
+ * item is
455
+ * {@linkplain Subscription#request(long) requested}.</li>
456
+ * <li>{@link OutputStream#write(byte[], int, int) OutputStream.write()}
457
+ * invocations made by {@code outputStreamHandler} are buffered until they
458
+ * reach or exceed {@code chunkSize}, or when the stream is
459
+ * {@linkplain OutputStream#flush() flushed} and then result in a
460
+ * {@linkplain Subscriber#onNext(Object) published} item
461
+ * if there is {@linkplain Subscription#request(long) demand}.</li>
462
+ * <li>If there is <em>no demand</em>, {@code OutputStream.write()} will block
463
+ * until there is.</li>
464
+ * <li>If the subscription is {@linkplain Subscription#cancel() cancelled},
465
+ * {@code OutputStream.write()} will throw a {@code IOException}.</li>
466
+ * <li>The subscription is
467
+ * {@linkplain Subscriber#onComplete() completed} when
468
+ * {@code outputStreamHandler} completes.</li>
469
+ * <li>Any exceptions thrown from {@code outputStreamHandler} will
470
+ * be dispatched to the {@linkplain Subscriber#onError(Throwable) Subscriber}.
471
+ * </ul>
472
+ * @param outputStreamConsumer invoked when the first buffer is requested
473
+ * @param executor used to invoke the {@code outputStreamHandler}
474
+ * @param chunkSize minimum size of the buffer produced by the publisher
475
+ * @return a {@code Publisher<DataBuffer>} based on bytes written by
476
+ * {@code outputStreamHandler}
477
+ */
478
+ public static Publisher <DataBuffer > outputStreamPublisher (Consumer <OutputStream > outputStreamConsumer ,
479
+ DataBufferFactory bufferFactory , Executor executor , int chunkSize ) {
480
+
481
+ Assert .notNull (outputStreamConsumer , "OutputStreamConsumer must not be null" );
482
+ Assert .notNull (bufferFactory , "BufferFactory must not be null" );
483
+ Assert .notNull (executor , "Executor must not be null" );
484
+ Assert .isTrue (chunkSize > 0 , "Chunk size must be > 0" );
485
+
486
+ return new OutputStreamPublisher (outputStreamConsumer , bufferFactory , executor , chunkSize );
487
+ }
488
+
489
+
408
490
//---------------------------------------------------------------------
409
491
// Various
410
492
//---------------------------------------------------------------------
0 commit comments