All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.versions.11.org.glassfish.jersey.internal.jsr166.SubmissionPublisher Maven / Gradle / Ivy

There is a newer version: 4.0.0-M1
Show newest version
/*
 * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.jersey.internal.jsr166;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.function.Consumer;

/**
 * A {@link Flow.Publisher} that asynchronously issues submitted
 * (non-null) items to current subscribers until it is closed.  Each
 * current subscriber receives newly submitted items in the same order
 * unless drops or exceptions are encountered.  Using a
 * SubmissionPublisher allows item generators to act as compliant  reactive-streams
 * Publishers relying on drop handling and/or blocking for flow
 * control.
 * 

*

A SubmissionPublisher uses the {@link Executor} supplied in its * constructor for delivery to subscribers. The best choice of * Executor depends on expected usage. If the generator(s) of * submitted items run in separate threads, and the number of * subscribers can be estimated, consider using a {@link * Executors#newFixedThreadPool}. Otherwise consider using the * default, normally the {@link ForkJoinPool#commonPool}. *

*

Buffering allows producers and consumers to transiently operate * at different rates. Each subscriber uses an independent buffer. * Buffers are created upon first use and expanded as needed up to the * given maximum. (The enforced capacity may be rounded up to the * nearest power of two and/or bounded by the largest value supported * by this implementation.) Invocations of {@link * Flow.Subscription#request(long) request} do not directly result in * buffer expansion, but risk saturation if unfilled requests exceed * the maximum capacity. The default value of {@link * Flow#defaultBufferSize()} may provide a useful starting point for * choosing a capacity based on expected rates, resources, and usages. *

*

Publication methods support different policies about what to do * when buffers are saturated. Method {@link #submit(Object) submit} * blocks until resources are available. This is simplest, but least * responsive. The {@code offer} methods may drop items (either * immediately or with bounded timeout), but provide an opportunity to * interpose a handler and then retry. *

*

If any Subscriber method throws an exception, its subscription * is cancelled. If a handler is supplied as a constructor argument, * it is invoked before cancellation upon an exception in method * {@link Flow.Subscriber#onNext onNext}, but exceptions in methods * {@link Flow.Subscriber#onSubscribe onSubscribe}, * {@link Flow.Subscriber#onError(Throwable) onError} and * {@link Flow.Subscriber#onComplete() onComplete} are not recorded or * handled before cancellation. If the supplied Executor throws * {@link RejectedExecutionException} (or any other RuntimeException * or Error) when attempting to execute a task, or a drop handler * throws an exception when processing a dropped item, then the * exception is rethrown. In these cases, not all subscribers will * have been issued the published item. It is usually good practice to * {@link #closeExceptionally closeExceptionally} in these cases. *

*

Method {@link #consume(Consumer)} simplifies support for a * common case in which the only action of a subscriber is to request * and process all items using a supplied function. *

*

This class may also serve as a convenient base for subclasses * that generate items, and use the methods in this class to publish * them. For example here is a class that periodically publishes the * items generated from a supplier. (In practice you might add methods * to independently start and stop generation, to share Executors * among publishers, and so on, or use a SubmissionPublisher as a * component rather than a superclass.) *

*

 {@code
 * class PeriodicPublisher extends SubmissionPublisher {
 *   final ScheduledFuture periodicTask;
 *   final ScheduledExecutorService scheduler;
 *   PeriodicPublisher(Executor executor, int maxBufferCapacity,
 *                     Supplier supplier,
 *                     long period, TimeUnit unit) {
 *     super(executor, maxBufferCapacity);
 *     scheduler = new ScheduledThreadPoolExecutor(1);
 *     periodicTask = scheduler.scheduleAtFixedRate(
 *       () -> submit(supplier.get()), 0, period, unit);
 *   }
 *   public void close() {
 *     periodicTask.cancel(false);
 *     scheduler.shutdown();
 *     super.close();
 *   }
 * }}
*

*

Here is an example of a {@link Flow.Processor} implementation. * It uses single-step requests to its publisher for simplicity of * illustration. A more adaptive version could monitor flow using the * lag estimate returned from {@code submit}, along with other utility * methods. *

*

 {@code
 * class TransformProcessor extends SubmissionPublisher
 *   implements Flow.Processor {
 *   final Function function;
 *   Flow.Subscription subscription;
 *   TransformProcessor(Executor executor, int maxBufferCapacity,
 *                      Function function) {
 *     super(executor, maxBufferCapacity);
 *     this.function = function;
 *   }
 *   public void onSubscribe(Flow.Subscription subscription) {
 *     (this.subscription = subscription).request(1);
 *   }
 *   public void onNext(S item) {
 *     subscription.request(1);
 *     submit(function.apply(item));
 *   }
 *   public void onError(Throwable ex) { closeExceptionally(ex); }
 *   public void onComplete() { close(); }
 * }}
* * @param the published item type * @author Doug Lea * @since 9 */ public class SubmissionPublisher implements SubmittableFlowPublisher { private final java.util.concurrent.SubmissionPublisher publisher; /** * Creates a new SubmissionPublisher using the given Executor for * async delivery to subscribers, with the given maximum buffer size * for each subscriber, and, if non-null, the given handler invoked * when any Subscriber throws an exception in method {@link * Flow.Subscriber#onNext(Object) onNext}. * * @param executor the executor to use for async delivery, * supporting creation of at least one independent thread * @param maxBufferCapacity the maximum capacity for each * subscriber's buffer (the enforced capacity may be rounded up to * the nearest power of two and/or bounded by the largest value * supported by this implementation; method {@link #getMaxBufferCapacity} * returns the actual value) * @param handler if non-null, procedure to invoke upon exception * thrown in method {@code onNext} * @throws NullPointerException if executor is null * @throws IllegalArgumentException if maxBufferCapacity not * positive */ public SubmissionPublisher(Executor executor, int maxBufferCapacity, BiConsumer, ? super Throwable> handler) { publisher = new java.util.concurrent.SubmissionPublisher(executor, maxBufferCapacity, convertConsumer(handler)); } /** * Creates a new SubmissionPublisher using the given Executor for * async delivery to subscribers, with the given maximum buffer size * for each subscriber, and no handler for Subscriber exceptions in * method {@link Flow.Subscriber#onNext(Object) onNext}. * * @param executor the executor to use for async delivery, * supporting creation of at least one independent thread * @param maxBufferCapacity the maximum capacity for each * subscriber's buffer (the enforced capacity may be rounded up to * the nearest power of two and/or bounded by the largest value * supported by this implementation; method {@link #getMaxBufferCapacity} * returns the actual value) * @throws NullPointerException if executor is null * @throws IllegalArgumentException if maxBufferCapacity not * positive */ public SubmissionPublisher(Executor executor, int maxBufferCapacity) { publisher = new java.util.concurrent.SubmissionPublisher(executor, maxBufferCapacity); } /** * Creates a new SubmissionPublisher using the {@link * ForkJoinPool#commonPool()} for async delivery to subscribers * (unless it does not support a parallelism level of at least two, * in which case, a new Thread is created to run each task), with * maximum buffer capacity of {@link Flow#defaultBufferSize}, and no * handler for Subscriber exceptions in method {@link * Flow.Subscriber#onNext(Object) onNext}. */ public SubmissionPublisher() { publisher = new java.util.concurrent.SubmissionPublisher(); } /** * {@inheritDoc} */ @Override public CompletableFuture consume(Consumer consumer) { return publisher.consume(consumer); } /** * {@inheritDoc} */ @Override public void close() { publisher.close(); } /** * {@inheritDoc} */ @Override public void closeExceptionally(Throwable error) { publisher.closeExceptionally(error); } /** * {@inheritDoc} */ @Override public long estimateMinimumDemand() { return publisher.estimateMinimumDemand(); } /** * {@inheritDoc} */ @Override public int estimateMaximumLag() { return publisher.estimateMaximumLag(); } /** * {@inheritDoc} */ @Override public Throwable getClosedException() { return publisher.getClosedException(); } /** * {@inheritDoc} */ @Override public int getMaxBufferCapacity() { return publisher.getMaxBufferCapacity(); } /** * {@inheritDoc} */ @Override public int offer(T item, long timeout, TimeUnit unit, BiPredicate, ? super T> onDrop) { return publisher.offer(item, timeout, unit, convertPredicate(onDrop)); } /** * {@inheritDoc} */ public int offer(T item, BiPredicate, ? super T> onDrop) { return publisher.offer(item, convertPredicate(onDrop)); } /** * {@inheritDoc} */ @Override public int submit(T item) { return publisher.submit(item); } /** * {@inheritDoc} */ @Override public void subscribe(Flow.Subscriber subscriber) { publisher.subscribe(convertSubscriber(subscriber)); } private static BiConsumer, ? super Throwable> convertConsumer( BiConsumer, ? super Throwable> consumer) { return new BiConsumer, Throwable>() { @Override public void accept(java.util.concurrent.Flow.Subscriber subscriber, Throwable throwable) { consumer.accept(convertSubscriber(subscriber), throwable); } }; } private static BiPredicate, ? super T> convertPredicate( BiPredicate, ? super T> predicate) { return new BiPredicate, T>() { @Override public boolean test(java.util.concurrent.Flow.Subscriber subscriber, T t) { return predicate.test(convertSubscriber(subscriber), t); } }; } private static Flow.Subscriber convertSubscriber(java.util.concurrent.Flow.Subscriber subscriber) { return new Flow.Subscriber() { @Override public void onSubscribe(Flow.Subscription subscription) { subscriber.onSubscribe(convertSubscription(subscription)); } @Override public void onNext(T item) { subscriber.onNext(item); } @Override public void onError(Throwable throwable) { subscriber.onError(throwable); } @Override public void onComplete() { subscriber.onComplete(); } }; } private static java.util.concurrent.Flow.Subscriber convertSubscriber(Flow.Subscriber subscriber) { return new java.util.concurrent.Flow.Subscriber() { @Override public void onSubscribe(java.util.concurrent.Flow.Subscription subscription) { subscriber.onSubscribe(convertSubscription(subscription)); } @Override public void onNext(T item) { subscriber.onNext(item); } @Override public void onError(Throwable throwable) { subscriber.onError(throwable); } @Override public void onComplete() { subscriber.onComplete(); } }; } private static java.util.concurrent.Flow.Subscription convertSubscription(Flow.Subscription subscription) { return new java.util.concurrent.Flow.Subscription() { @Override public void request(long n) { subscription.request(n); } @Override public void cancel() { subscription.cancel(); } }; } private static Flow.Subscription convertSubscription(java.util.concurrent.Flow.Subscription subscription) { return new Flow.Subscription() { @Override public void request(long n) { subscription.request(n); } @Override public void cancel() { subscription.cancel(); } }; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy