Interface StreamMessage<T>

Type Parameters:
T - the type of element signaled
All Superinterfaces:
org.reactivestreams.Publisher<T>
All Known Subinterfaces:
ByteStreamMessage, HttpMessage, HttpRequest, HttpRequestWriter, HttpResponse, HttpResponseWriter
All Known Implementing Classes:
DefaultStreamMessage, DeferredStreamMessage, FilteredHttpRequest, FilteredHttpResponse, FilteredStreamMessage, PublisherBasedStreamMessage, StreamMessageWrapper

public interface StreamMessage<T> extends org.reactivestreams.Publisher<T>
A variant of Reactive Streams Publisher, which allows only one Subscriber. Unlike a usual Publisher, a StreamMessage can stream itself only once. It has the following additional operations on top of what the Reactive Streams API provides:

When is a StreamMessage fully consumed?

A StreamMessage is complete (or 'fully consumed') when:

  • the Subscriber consumes all elements and Subscriber.onComplete() is invoked,
  • an error occurred and Subscriber.onError(Throwable) is invoked,
  • the Subscription has been cancelled or
  • abort() has been requested.

When fully consumed, the CompletableFuture returned by whenComplete() will complete, which you may find useful because Subscriber does not notify you when a stream is cancelled.

Publication and Consumption of pooled HttpData objects

StreamMessage will discard the publication request of a pooled HttpData silently and release it automatically when the publication is attempted after the stream is closed.

For pooled HttpData, StreamMessage will convert them into its unpooled version that never leak, so that the Subscriber does not need to worry about leaks.

If a Subscriber does not want a StreamMessage to make a copy of a pooled HttpData, specify SubscriptionOption.WITH_POOLED_OBJECTS when you subscribe. Note that the Subscriber is responsible for releasing the objects given with Subscriber.onNext(Object).

Subscriber.onError(Throwable) is invoked when any exception is raised except the CancelledSubscriptionException which is caused by Subscription.cancel(). If you want your Subscriber get notified by Subscriber.onError(Throwable) when Subscription.cancel() is called, specify SubscriptionOption.NOTIFY_CANCELLATION when you subscribe.

  • Method Details

    • of

      static <T> StreamMessage<T> of()
      Creates a new StreamMessage that will publish no objects, just a close event.
    • of

      static <T> StreamMessage<T> of(T obj)
      Creates a new StreamMessage that will publish the single obj.
    • of

      static <T> StreamMessage<T> of(T obj1, T obj2)
      Creates a new StreamMessage that will publish the two obj1 and obj2.
    • of

      static <T> StreamMessage<T> of(T obj1, T obj2, T obj3)
      Creates a new StreamMessage that will publish the three obj1, obj2 and obj3.
    • of

      @SafeVarargs static <T> StreamMessage<T> of(T... objs)
      Creates a new StreamMessage that will publish the given objs.
    • of

      static <T> StreamMessage<T> of(org.reactivestreams.Publisher<? extends T> publisher)
      Creates a new StreamMessage from the specified Publisher.
    • of

      static ByteStreamMessage of(File file)
      Creates a new StreamMessage that streams the specified File. The default buffer size(8192) is used to create a buffer used to read data from the File. Therefore, the returned StreamMessage will emit HttpDatas chunked to size less than or equal to 8192.
    • of

      static ByteStreamMessage of(Path path)
      Creates a new StreamMessage that streams the specified Path. The default buffer size(8192) is used to create a buffer used to read data from the Path. Therefore, the returned StreamMessage will emit HttpDatas chunked to size less than or equal to 8192.
    • builder

      Returns a new PathStreamMessageBuilder with the specified Path.
    • of

      @Deprecated static ByteStreamMessage of(Path path, int bufferSize)
      Creates a new StreamMessage that streams the specified Path. The specified bufferSize is used to create a buffer used to read data from the Path. Therefore, the returned StreamMessage will emit HttpDatas chunked to size less than or equal to bufferSize.
      Parameters:
      path - the path of the file
      bufferSize - the maximum allowed size of the HttpData buffers
    • of

      @Deprecated static ByteStreamMessage of(Path path, ByteBufAllocator alloc, int bufferSize)
      Creates a new StreamMessage that streams the specified Path. The specified bufferSize is used to create a buffer used to read data from the Path. Therefore, the returned StreamMessage will emit HttpDatas chunked to size less than or equal to bufferSize.
      Parameters:
      path - the path of the file
      alloc - the ByteBufAllocator which will allocate the content buffer
      bufferSize - the maximum allowed size of the HttpData buffers
    • of

      @Deprecated static ByteStreamMessage of(Path path, @Nullable @Nullable ExecutorService executor, ByteBufAllocator alloc, int bufferSize)
      Creates a new StreamMessage that streams the specified Path. The specified bufferSize is used to create a buffer used to read data from the Path. Therefore, the returned StreamMessage will emit HttpDatas chunked to size less than or equal to bufferSize.
      Parameters:
      path - the path of the file
      executor - the ExecutorService which performs blocking IO read
      alloc - the ByteBufAllocator which will allocate the content buffer
      bufferSize - the maximum allowed size of the HttpData buffers
    • fromOutputStream

      static ByteStreamMessage fromOutputStream(Consumer<? super OutputStream> outputStreamConsumer)
      Creates a new ByteStreamMessage that publishes HttpDatas from the specified outputStreamConsumer.

      For example:

      
       ByteStreamMessage byteStreamMessage = StreamMessage.fromOutputStream(os -> {
           try {
               for (int i = 0; i < 5; i++) {
                   os.write(i);
               }
               os.close();
           } catch (IOException e) {
               throw new UncheckedIOException(e);
           }
       });
       byte[] result = byteStreamMessage.collectBytes().join();
      
       assert Arrays.equals(result, new byte[] { 0, 1, 2, 3, 4 });
       

      Please note that the try-with-resources statement is not used to call os.close() automatically. It's because when an exception is raised in the Consumer, the OutputStream is closed by the StreamMessage and the exception is propagated to the Subscriber automatically.

    • fromOutputStream

      static ByteStreamMessage fromOutputStream(Consumer<? super OutputStream> outputStreamConsumer, Executor blockingTaskExecutor)
      Creates a new ByteStreamMessage that publishes HttpDatas from the specified outputStreamConsumer.

      For example:

      
       ByteStreamMessage byteStreamMessage = StreamMessage.fromOutputStream(os -> {
           try {
               for (int i = 0; i < 5; i++) {
                   os.write(i);
               }
               os.close();
           } catch (IOException e) {
               throw new UncheckedIOException(e);
           }
       });
       byte[] result = byteStreamMessage.collectBytes().join();
      
       assert Arrays.equals(result, new byte[] { 0, 1, 2, 3, 4 });
       

      Please note that the try-with-resources statement is not used to call os.close() automatically. It's because when an exception is raised in the Consumer, the OutputStream is closed by the StreamMessage and the exception is propagated to the Subscriber automatically.

      Parameters:
      blockingTaskExecutor - the blocking task executor to execute OutputStream.write(int)
    • concat

      @SafeVarargs static <T> StreamMessage<T> concat(org.reactivestreams.Publisher<? extends T>... publishers)
      Returns a concatenated StreamMessage which relays items of the specified array of Publishers in order, non-overlappingly, one after the other finishes.
    • concat

      static <T> StreamMessage<T> concat(Iterable<? extends org.reactivestreams.Publisher<? extends T>> publishers)
      Returns a concatenated StreamMessage which relays items of the specified Publishers in order, non-overlappingly, one after the other finishes.
    • concat

      static <T> StreamMessage<T> concat(org.reactivestreams.Publisher<? extends org.reactivestreams.Publisher<? extends T>> publishers)
      Returns a concatenated StreamMessage which relays items of the specified Publisher of Publishers in order, non-overlappingly, one after the other finishes.
    • aborted

      static <T> StreamMessage<T> aborted(Throwable cause)
      Returns an aborted StreamMessage that terminates with the specified Throwable via Subscriber.onError(Throwable) immediately after being subscribed to.
    • isOpen

      boolean isOpen()
      Returns true if this stream is not closed yet. Note that a stream may not be complete even if it's closed; a stream is complete when it's fully consumed by a Subscriber.
    • isEmpty

      boolean isEmpty()
      Returns true if this stream has been closed and did not publish any elements. Note that this method will not return true when the stream is open even if it has not published anything so far, because it may publish something later.
    • demand

      long demand()
      Returns the current demand of this stream.
    • isComplete

      default boolean isComplete()
      Returns true if this stream is complete, either successfully or exceptionally, including cancellation and abortion.

      A StreamMessage is complete (or 'fully consumed') when:

      • the Subscriber consumes all elements and Subscriber.onComplete() is invoked,
      • an error occurred and Subscriber.onError(Throwable) is invoked,
      • the Subscription has been cancelled or
      • abort() has been requested.
    • whenComplete

      CompletableFuture<Void> whenComplete()
      Returns a CompletableFuture that completes when this stream is complete, either successfully or exceptionally, including cancellation and abortion.

      A StreamMessage is complete (or 'fully consumed') when:

      • the Subscriber consumes all elements and Subscriber.onComplete() is invoked,
      • an error occurred and Subscriber.onError(Throwable) is invoked,
      • the Subscription has been cancelled or
      • abort() has been requested.
    • subscribe

      default CompletableFuture<Void> subscribe()
      Drains and discards all objects in this StreamMessage.

      For example:

      
       StreamMessage<Integer> source = StreamMessage.of(1, 2, 3);
       List<Integer> collected = new ArrayList<>();
       CompletableFuture<Void> future = source.peek(collected::add).subscribe();
       future.join();
       assert collected.equals(List.of(1, 2, 3));
       assert future.isDone();
       
    • subscribe

      default void subscribe(org.reactivestreams.Subscriber<? super T> subscriber)
      Requests to start streaming data to the specified Subscriber. If there is a problem subscribing, Subscriber.onError(Throwable) will be invoked with one of the following exceptions:
      Specified by:
      subscribe in interface org.reactivestreams.Publisher<T>
    • subscribe

      default void subscribe(org.reactivestreams.Subscriber<? super T> subscriber, SubscriptionOption... options)
      Requests to start streaming data to the specified Subscriber. If there is a problem subscribing, Subscriber.onError(Throwable) will be invoked with one of the following exceptions:
      Parameters:
      options - SubscriptionOptions to subscribe with
    • subscribe

      default void subscribe(org.reactivestreams.Subscriber<? super T> subscriber, EventExecutor executor)
      Requests to start streaming data to the specified Subscriber. If there is a problem subscribing, Subscriber.onError(Throwable) will be invoked with one of the following exceptions:
      Parameters:
      executor - the executor to subscribe
    • subscribe

      void subscribe(org.reactivestreams.Subscriber<? super T> subscriber, EventExecutor executor, SubscriptionOption... options)
      Requests to start streaming data to the specified Subscriber. If there is a problem subscribing, Subscriber.onError(Throwable) will be invoked with one of the following exceptions:
      Parameters:
      executor - the executor to subscribe
      options - SubscriptionOptions to subscribe with
    • toDuplicator

      default StreamMessageDuplicator<T> toDuplicator()
      Returns a new StreamMessageDuplicator that duplicates this StreamMessage into one or more StreamMessages, which publish the same elements. Note that you cannot subscribe to this StreamMessage anymore after you call this method. To subscribe, call StreamMessageDuplicator.duplicate() from the returned StreamMessageDuplicator.
    • toDuplicator

      default StreamMessageDuplicator<T> toDuplicator(EventExecutor executor)
      Returns a new StreamMessageDuplicator that duplicates this StreamMessage into one or more StreamMessages, which publish the same elements. Note that you cannot subscribe to this StreamMessage anymore after you call this method. To subscribe, call StreamMessageDuplicator.duplicate() from the returned StreamMessageDuplicator.
      Parameters:
      executor - the executor to duplicate
    • defaultSubscriberExecutor

      default EventExecutor defaultSubscriberExecutor()
      Returns the default EventExecutor which will be used when a user subscribes using subscribe(Subscriber), subscribe(Subscriber, SubscriptionOption...).

      Please note that if this method is called multiple times, the returned EventExecutors can be different depending on this StreamMessage implementation.

    • abort

      void abort()
      Closes this stream with AbortedStreamException and prevents further subscription. A Subscriber that attempts to subscribe to an aborted stream will be notified with an AbortedStreamException via Subscriber.onError(Throwable). Calling this method on a closed or aborted stream has no effect.
    • abort

      void abort(Throwable cause)
      Closes this stream with the specified Throwable and prevents further subscription. A Subscriber that attempts to subscribe to an aborted stream will be notified with the specified Throwable via Subscriber.onError(Throwable). Calling this method on a closed or aborted stream has no effect.
    • decode

      @UnstableApi default <U> StreamMessage<U> decode(StreamDecoder<T,U> decoder)
      Creates a decoded StreamMessage which is decoded from a stream of T type objects using the specified StreamDecoder.
    • decode

      @UnstableApi default <U> StreamMessage<U> decode(StreamDecoder<T,U> decoder, ByteBufAllocator alloc)
      Creates a decoded StreamMessage which is decoded from a stream of T type objects using the specified StreamDecoder and ByteBufAllocator.
    • collect

      default CompletableFuture<List<T>> collect()
      Collects the elements published by this StreamMessage. The returned CompletableFuture will be notified when the elements are fully consumed.

      Note that if this StreamMessage was subscribed by other Subscriber already, the returned CompletableFuture will be completed with an IllegalStateException.

      
       StreamMessage<Integer> stream = StreamMessage.of(1, 2, 3);
       CompletableFuture<List<Integer>> collected = stream.collect();
       assert collected.join().equals(List.of(1, 2, 3));
       
    • collect

      default CompletableFuture<List<T>> collect(SubscriptionOption... options)
      Collects the elements published by this StreamMessage with the specified SubscriptionOptions. The returned CompletableFuture will be notified when the elements are fully consumed.

      Note that if this StreamMessage was subscribed by other Subscriber already, the returned CompletableFuture will be completed with an IllegalStateException.

    • collect

      default CompletableFuture<List<T>> collect(EventExecutor executor, SubscriptionOption... options)
      Collects the elements published by this StreamMessage with the specified EventExecutor and SubscriptionOptions. The returned CompletableFuture will be notified when the elements are fully consumed.

      Note that if this StreamMessage was subscribed by other Subscriber already, the returned CompletableFuture will be completed with an IllegalStateException.

    • filter

      default StreamMessage<T> filter(Predicate<? super T> predicate)
      Filters values emitted by this StreamMessage. If the Predicate test succeeds, the value is emitted. If the Predicate test fails, the value is ignored and a request of 1 is made to upstream.

      For example:

      
       StreamMessage<Integer> source = StreamMessage.of(1, 2, 3, 4, 5);
       StreamMessage<Integer> even = source.filter(x -> x % 2 == 0);
       
    • map

      default <U> StreamMessage<U> map(Function<? super T,? extends U> function)
      Transforms values emitted by this StreamMessage by applying the specified Function. As per Reactive Streams Specification 2.13, the specified Function should not return a null value.

      For example:

      
       StreamMessage<Integer> source = StreamMessage.of(1, 2, 3, 4, 5);
       StreamMessage<Boolean> isEven = source.map(x -> x % 2 == 0);
       
    • mapAsync

      default <U> StreamMessage<U> mapAsync(Function<? super T,? extends CompletableFuture<? extends U>> function)
      Transforms values emitted by this StreamMessage by applying the specified asynchronous Function and emitting the value the future completes with. The StreamMessage publishes items in order, non-overlappingly, one after the other finishes. As per Reactive Streams Specification 2.13, the specified Function should not return a null value nor a future which completes with a null value.

      Example:

      
       StreamMessage<Integer> streamMessage = StreamMessage.of(1, 2, 3, 4, 5);
       StreamMessage<Integer> transformed =
           streamMessage.mapAsync(x -> UnmodifiableFuture.completedFuture(x + 1));
       
    • mapParallel

      @UnstableApi default <U> StreamMessage<U> mapParallel(Function<? super T,? extends CompletableFuture<? extends U>> function)
      Transforms values emitted by this StreamMessage by applying the specified asynchronous Function and emitting the value the future completes with. The StreamMessage publishes items eagerly in the order that the futures complete. It does not necessarily preserve the order of the original stream. As per Reactive Streams Specification 2.13, the specified Function should not return a null value nor a future which completes with a null value.

      Example:

      
       StreamMessage<Integer> streamMessage = StreamMessage.of(1, 2, 3, 4, 5);
       StreamMessage<Integer> transformed =
           streamMessage.mapParallel(x -> UnmodifiableFuture.completedFuture(x + 1));
       
    • mapParallel

      @UnstableApi default <U> StreamMessage<U> mapParallel(Function<? super T,? extends CompletableFuture<? extends U>> function, int maxConcurrency)
      Transforms values emitted by this StreamMessage by applying the specified asynchronous Function and emitting the value the future completes with. The StreamMessage publishes items eagerly in the order that the futures complete. The number of pending futures will at most be maxConcurrency It does not necessarily preserve the order of the original stream. As per Reactive Streams Specification 2.13, the specified Function should not return a null value nor a future which completes with a null value.

      Example:

      
       StreamMessage<Integer> streamMessage = StreamMessage.of(1, 2, 3, 4, 5);
       StreamMessage<Integer> transformed =
           streamMessage.mapParallel(x -> UnmodifiableFuture.completedFuture(x + 1), 20);
       
    • mapError

      default StreamMessage<T> mapError(Function<? super Throwable,? extends Throwable> function)
      Transforms an error emitted by this StreamMessage by applying the specified Function. As per Reactive Streams Specification 2.13, the specified Function should not return a null value.

      For example:

      
       StreamMessage<Void> streamMessage = StreamMessage
           .aborted(new IllegalStateException("Something went wrong."));
       StreamMessage<Void> transformed = streamMessage.mapError(ex -> {
           if (ex instanceof IllegalStateException) {
               return new MyDomainException(ex);
           } else {
               return ex;
           }
       });
       
    • peek

      default StreamMessage<T> peek(Consumer<? super T> action)
      Peeks values emitted by this StreamMessage and applies the specified Consumer.

      For example:

      
       StreamMessage<Integer> source = StreamMessage.of(1, 2, 3, 4, 5);
       StreamMessage<Integer> ifEvenExistsThenThrow = source.peek(x -> {
            if (x % 2 == 0) {
                throw new IllegalArgumentException();
            }
       });
       
    • peek

      default <U extends T> StreamMessage<T> peek(Consumer<? super U> action, Class<? extends U> type)
      Peeks values emitted by this StreamMessage and applies the specified Consumer. Only values which are an instance of the specified type are peeked.

      For example:

      
       StreamMessage<Number> source = StreamMessage.of(0.1, 1, 0.2, 2, 0.3, 3);
       List<Integer> collected = new ArrayList<>();
       List<Number> peeked = source.peek(x -> collected.add(x), Integer.class).collect().join();
      
       assert collected.equals(List.of(1, 2, 3));
       assert peeked.equals(List.of(0.1, 1, 0.2, 2, 0.3, 3));
       
    • peekError

      default StreamMessage<T> peekError(Consumer<? super Throwable> action)
      Peeks an error emitted by this StreamMessage and applies the specified Consumer.

      For example:

      
       StreamMessage<Void> streamMessage = StreamMessage
           .aborted(new IllegalStateException("Something went wrong."));
       StreamMessage<Void> peeked = streamMessage.peekError(ex -> {
           assert ex instanceof IllegalStateException;
       });
       
    • recoverAndResume

      default StreamMessage<T> recoverAndResume(Function<? super Throwable,? extends StreamMessage<T>> function)
      Recovers a failed StreamMessage and resumes by subscribing to a returned fallback StreamMessage when any error occurs.

      Example:

      
       DefaultStreamMessage<Integer> stream = new DefaultStreamMessage<>();
       stream.write(1);
       stream.write(2);
       stream.close(new IllegalStateException("Oops..."));
       StreamMessage<Integer> resumed = stream.recoverAndResume(cause -> StreamMessage.of(3, 4));
      
       assert resumed.collect().join().equals(List.of(1, 2, 3, 4));
       
    • recoverAndResume

      @UnstableApi default <E extends Throwable> StreamMessage<T> recoverAndResume(Class<E> causeClass, Function<? super E,? extends StreamMessage<T>> function)
      Recovers a failed StreamMessage and resumes by subscribing to a returned fallback StreamMessage when the thrown Throwable is the same type or a subtype of the specified causeClass.

      Example:

      
       DefaultStreamMessage<Integer> stream = new DefaultStreamMessage<>();
       stream.write(1);
       stream.write(2);
       stream.close(new IllegalStateException("Oops..."));
       StreamMessage<Integer> resumed =
           stream.recoverAndResume(IllegalStateException.class, cause -> StreamMessage.of(3, 4));
      
       assert resumed.collect().join().equals(List.of(1, 2, 3, 4));
      
       DefaultStreamMessage<Integer> stream = new DefaultStreamMessage<>();
       stream.write(1);
       stream.write(2);
       stream.write(3);
       stream.close(new IllegalStateException("test exception"));
       // Use the shortcut recover method as a chain.
       StreamMessage<Integer> recoverChain =
           stream.recoverAndResume(RuntimeException.class, cause -> {
               final IllegalArgumentException ex = new IllegalArgumentException("oops..");
               // If a aborted StreamMessage returned from the first chain
               return StreamMessage.aborted(ex);
           })
           // If the shortcut exception type is correct, catch and recover in the second chain.
           .recoverAndResume(IllegalArgumentException.class, cause -> StreamMessage.of(4, 5));
      
       recoverChain.collect().join();
      
       DefaultStreamMessage<Integer> stream = new DefaultStreamMessage<>();
       stream.write(1);
       stream.write(2);
       stream.close(ClosedStreamException.get());
       // If the exception type does not match
       StreamMessage<Integer> mismatchRecovered =
           stream.recoverAndResume(IllegalStateException.class, cause -> StreamMessage.of(3, 4));
      
       // In this case, CompletionException is thrown. (can't recover exception)
       mismatchRecovered.collect().join();
       
    • writeTo

      default CompletableFuture<Void> writeTo(Function<? super T,? extends HttpData> mapper, Path destination, OpenOption... options)
      Writes this StreamMessage to the given Path with OpenOptions. If the OpenOption is not specified, defaults to StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING and StandardOpenOption.WRITE.

      Example:

      
       Path destination = Paths.get("foo.bin");
       ByteBuf[] bufs = new ByteBuf[10];
       for(int i = 0; i < 10; i++) {
           bufs[i] = Unpooled.wrappedBuffer(Integer.toString(i).getBytes());
       }
       StreamMessage<ByteBuf> streamMessage = StreamMessage.of(bufs);
       streamMessage.writeTo(HttpData::wrap, destination).join();
      
       assert Files.readString(destination).equals("0123456789");
       
      See Also:
    • toInputStream

      default InputStream toInputStream(Function<? super T,? extends HttpData> httpDataConverter)
      Adapts this StreamMessage to InputStream.

      For example:

      
       StreamMessage<String> streamMessage = StreamMessage.of("foo", "bar", "baz");
       InputStream inputStream = streamMessage.toInputStream(x -> HttpData.wrap(x.getBytes()));
       byte[] expected = "foobarbaz".getBytes();
      
       ByteBuf result = Unpooled.buffer();
       int read;
       while ((read = inputStream.read()) != -1) {
           result.writeByte(read);
       }
      
       int readableBytes = result.readableBytes();
       byte[] actual = new byte[readableBytes];
       for (int i = 0; i < readableBytes; i++) {
           actual[i] = result.readByte();
       }
       assert Arrays.equals(actual, expected);
       assert inputStream.available() == 0;
       
    • toInputStream

      default InputStream toInputStream(Function<? super T,? extends HttpData> httpDataConverter, EventExecutor executor)
      Adapts this StreamMessage to InputStream.
      Parameters:
      executor - the executor to subscribe