InputStreamSubscriber instantiation and Javadoc

See gh-31677
This commit is contained in:
rstoyanchev
2024-10-28 08:40:38 +00:00
parent dfaf7a0c6a
commit f1cfe7a3d4
4 changed files with 78 additions and 99 deletions

View File

@@ -33,6 +33,7 @@ import java.nio.file.StandardOpenOption;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Flow;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
@@ -457,23 +458,19 @@ public abstract class DataBufferUtils {
}
/**
* Subscribes to given {@link Publisher} and returns subscription
* as {@link InputStream} that allows reading all propagated {@link DataBuffer} messages via its imperative API.
* Given the {@link InputStream} implementation buffers messages as per configuration.
* The returned {@link InputStream} is considered terminated when the given {@link Publisher} signaled one of the
* terminal signal ({@link Subscriber#onComplete() or {@link Subscriber#onError(Throwable)}})
* and all the stored {@link DataBuffer} polled from the internal buffer.
* The returned {@link InputStream} will call {@link Subscription#cancel()} and release all stored {@link DataBuffer}
* when {@link InputStream#close()} is called.
* <p>
* Note: The implementation of the returned {@link InputStream} disallow concurrent call on
* any of the {@link InputStream#read} methods
* <p>
* Note: {@link Subscription#request(long)} happens eagerly for the first time upon subscription
* and then repeats every time {@code bufferSize - (bufferSize >> 2)} consumed.
* @param publisher the source of {@link DataBuffer} which should be represented as an {@link InputStream}
* @param demand the maximum number of buffers to request from the Publisher and buffer on an ongoing basis
* @return an {@link InputStream} instance representing given {@link Publisher} messages
* Subscribe to given {@link Publisher} of {@code DataBuffer}s, and return an
* {@link InputStream} to consume the byte content with.
* <p>Byte buffers are stored in a queue. The {@code demand} constructor value
* determines the number of buffers requested initially. When storage falls
* below a {@code (demand - (demand >> 2))} limit, a request is made to refill
* the queue.
* <p>The {@code InputStream} terminates after an onError or onComplete signal,
* and stored buffers are read. If the {@code InputStream} is closed,
* the {@link Flow.Subscription} is cancelled, and stored buffers released.
* @param publisher the source of {@code DataBuffer}s
* @param demand the number of buffers to request initially, and buffer
* internally on an ongoing basis.
* @return an {@link InputStream} backed by the {@link Publisher}
*/
public static <T extends DataBuffer> InputStream subscriberInputStream(Publisher<T> publisher, int demand) {
Assert.notNull(publisher, "Publisher must not be null");

View File

@@ -22,12 +22,12 @@ import java.util.ConcurrentModificationException;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Flow;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.Exceptions;
@@ -36,7 +36,17 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Bridges between {@link Publisher Publisher&lt;DataBuffer&gt;} and {@link InputStream}.
* An {@link InputStream} backed by {@link Flow.Subscriber Flow.Subscriber}
* receiving byte buffers from a {@link Flow.Publisher} source.
*
* <p>Byte buffers are stored in a queue. The {@code demand} constructor value
* determines the number of buffers requested initially. When storage falls
* below a {@code (demand - (demand >> 2))} limit, a request is made to refill
* the queue.
*
* <p>The {@code InputStream} terminates after an onError or onComplete signal,
* and stored buffers are read. If the {@code InputStream} is closed,
* the {@link Flow.Subscription} is cancelled, and stored buffers released.
*
* <p>Note that this class has a near duplicate in
* {@link org.springframework.http.client.SubscriberInputStream}.
@@ -82,6 +92,11 @@ final class SubscriberInputStream extends InputStream implements Subscriber<Data
private Throwable error;
/**
* Create an instance.
* @param demand the number of buffers to request initially, and buffer
* internally on an ongoing basis.
*/
SubscriberInputStream(int demand) {
this.prefetch = demand;
this.limit = (demand == Integer.MAX_VALUE ? Integer.MAX_VALUE : demand - (demand >> 2));