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

net.pincette.rs.Commit Maven / Gradle / Ivy

package net.pincette.rs;

import static java.lang.Boolean.TRUE;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static net.pincette.rs.Serializer.dispatch;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Flow.Processor;
import java.util.function.Function;

/**
 * When the down stream requests more messages this indicates all messages it has received were
 * processed correctly. This is a moment to perform a commit with a function that receives the list
 * of uncommitted messages. This supports at least once semantics. Note that if you put a buffer
 * between this processor and the next persistent one, the last set of messages may not be committed
 * when the up stream completes. This happens when the number of remaining messages is less than the
 * request size of the buffer.
 *
 * @param  the value type.
 * @author Werner Donn\u00e9
 * @since 3.0
 */
public class Commit extends ProcessorBase {
  private final Function, CompletionStage> fn;
  private final Deque uncommitted = new ArrayDeque<>(1000);
  private boolean completed;
  private long requested;

  /**
   * Create a Commit processor.
   *
   * @param commit the commit function. New messages are only requested when the completion stage
   *     returns true.
   */
  public Commit(final Function, CompletionStage> commit) {
    this.fn = commit;
  }

  public static  Processor commit(
      final Function, CompletionStage> commit) {
    return new Commit<>(commit);
  }

  @Override
  protected void emit(final long number) {
    dispatch(
        () ->
            last()
                .map(fn)
                .orElseGet(() -> completedFuture(true))
                .thenAccept(
                    result -> {
                      if (TRUE.equals(result)) {
                        dispatch(
                            () -> {
                              if (!completed) {
                                request(number);
                              } else {
                                super.onComplete();
                              }
                            });
                      }
                    }));
  }

  private Optional> last() {
    final List result = new ArrayList<>();

    while (!uncommitted.isEmpty()) {
      result.add(uncommitted.removeLast());
    }

    return Optional.of(result).filter(r -> !r.isEmpty());
  }

  @Override
  public void onComplete() {
    dispatch(
        () -> {
          completed = true;

          if (requested > 0) {
            super.onComplete();
          }
        });
  }

  @Override
  public void onNext(final T value) {
    dispatch(
        () -> {
          uncommitted.addFirst(value);
          --requested;
          subscriber.onNext(value);
        });
  }

  private void request(final long number) {
    requested += number;
    subscription.request(number);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy