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

io.baratine.stream.ResultStream Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * Copyright (c) 1998-2015 Caucho Technology -- all rights reserved
 *
 * This file is part of Baratine(TM)(TM)
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Baratine is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Baratine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Baratine; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package io.baratine.stream;

import java.io.Serializable;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.logging.Level;

import io.baratine.service.Cancel;
import io.baratine.service.Result;
import io.baratine.stream.ResultStreamImpl.ResultAsync;


/**
 * Interface {@code ResultStream} is used as a sink for returning an unbounded
 * sequence of values from a service method.
 *
 * The interface is used as a parameter in implementation of a method
 * corresponding to a service interface method returning
 * {@code ResultStreamBuilder}.
 *
 * e.g.
 * 
 * 
 *   public interface MyService {
 *     ResultStreamBuilder<String> results();
 *   }
 *
 *   @io.baratine.core.Service("public://pod/service")
 *   public class MyServiceImpl implements MyService {
 *     public void results(ResultStream<String> stream) {
 *       stream.accept("Hello");
 *       stream.accept("World");
 *
 *       stream.complete();
 *     }
 *
 *     @Override
 *     public void ResultStreamBuilder<String> results() {
 *       throw new UnsupportedOperationException();
 *     }
 *   }
 *
 * 
 * 
*

* Sending a new value can be accomplished with passing it to method {@code accept}. * * After the last value is submitted the service needs to call method {@code complete} * to signal the {@code ResultStream} that last value has been submitted} *

* The client obtaining the results from the corresponding {@code ResultStreamBuilder} * may choose to cancel further results. When {@code ResultStream} is cancelled * its cancelled state is set to true and method {@code onCancell(CancelHandle)} * is called. Method {@code isCancelled()} can be used to test if the stream * was cancelled to avoid sending values to a cancelled stream. * *

* * @see io.baratine.stream.ResultStreamBuilder */ @FunctionalInterface public interface ResultStream extends Consumer, Serializable { public static final int CREDIT_MAX = 1 << 24; /** * Method {@code start()} performs initial operations on preparing * an instance of {@code ResultStream} to accepting values. This method is * called internally by Baratine. */ default void start() {} /** * Supplies next value into the client's {@code ResultStreamBuilder} * @param value */ @Override default void accept(T value) { handle(value, null, false); } /** * Completes sending the values to the client and signals to the client * that no more values is expected. */ default void ok() { handle(null, null, true); } /** * Signals a failure to the client passing exception. * @param exn */ default void fail(Throwable exn) { handle(null, exn, false); } void handle(T value, Throwable exn, boolean isEnd); /** * Uses a {@code Supplier} to generate set of values and calls * method {@code complete()} on itself. * * A first null value returned by the {@code get()} method of the {@code Supplier} * completes accept() loop and calls complete() on the instance of {@code ResultStream}. * * @param supplier supplies value to be accepted by the instance of {@code ResultStream} */ default void generate(Supplier supplier) { T value; while (! isCancelled() && (value = supplier.get()) != null) { accept(value); } ok(); } // // cancel methods // /** * Tests if the instance of {@code ResultStream} was cancelled. * * @return true if cancelled, false otherwise. */ default boolean isCancelled() { return false; } /** * A callback method that is called when the {@code ResultStream} is cancelled. * * @param cancel */ default void onCancel(Cancel cancel) { throw new UnsupportedOperationException(getClass().getName()); } // // flow methods // /* default int getCredit() { return CREDIT_MAX; } default void onCreditAvailable(Runnable task) { task.run(); } */ default Result of(BiConsumer> consumer) { return Result.of(x->consumer.accept(x,this), exn->fail(exn)); } /** * @return */ default boolean isFuture() { return false; } default void acceptFuture(ResultStream result, Iterable values, boolean isComplete) { for (U value : values) { result.accept(value); } if (isComplete) { result.ok(); } } default ResultStream flush() { return this; } default ResultStream createJoin() { return this; } default ResultStream createFork(ResultStream resultJoin) { return (ResultStream) resultJoin; } @SuppressWarnings("serial") abstract public static class Wrapper implements ResultStream { private final ResultStream _next; public Wrapper(ResultStream next) { Objects.requireNonNull(next); _next = next; } protected ResultStream next() { return _next; } @Override public boolean isFuture() { return next().isFuture(); } @Override public void start() { next().start(); } @Override public void fail(Throwable e) { next().fail(e); } @Override abstract public void accept(U value); @Override public void ok() { next().ok(); } @Override public void handle(U value, Throwable exn, boolean isEnd) { if (isEnd) { ok(); } else if (exn != null) { fail(exn); } else { accept(value); } } // // cancel // @Override public boolean isCancelled() { return next().isCancelled(); } @Override public void onCancel(Cancel cancel) { next().onCancel(cancel); } /* @Override public int getCredit() { return getNext().getCredit(); } @Override public void onCreditAvailable(Runnable task) { if (getCredit() > 0) { task.run(); } else { getNext().onCreditAvailable(task); } } */ @Override public void acceptFuture(ResultStream result, Iterable values, boolean isComplete) { next().acceptFuture(result, values, isComplete); } /* @Override public ResultStream createReduce() { return getNext().createReduce(); } @Override public ResultStream createMap(ResultStream resultReduce) { ResultStream nextMap = getNext().createMap(resultReduce); return new ResultStreamMap(this, nextMap); } */ } @SuppressWarnings("serial") abstract public static class ResultWrapper extends Base { private final Result _result; public ResultWrapper(Result result) { Objects.requireNonNull(result); _result = result; } protected Result getNext() { return _result; } @Override public boolean isFuture() { return getNext().isFuture(); } @Override public void fail(Throwable e) { getNext().fail(e); } @Override public void acceptFuture(ResultStream result, Iterable values, boolean isComplete) { // async result support for blocking futures getNext().completeFuture(new ResultAsync(result, values, isComplete), null); } } @SuppressWarnings("serial") abstract static class Base implements ResultStream, Cancel { private transient boolean _isCancelled; private transient Cancel _cancel; @Override public boolean isCancelled() { return _isCancelled; } @Override public void cancel() { if (! _isCancelled) { _isCancelled = true; Cancel cancel = _cancel; if (cancel != null) { _cancel= null; cancel.cancel(); } } } @Override public void onCancel(Cancel cancel) { Objects.requireNonNull(cancel); if (_isCancelled) { cancel.cancel(); } else if (_cancel != null) { throw new IllegalStateException("Cancel is already assigned"); } else { _cancel = cancel; } } @Override public void handle(V value, Throwable exn, boolean isEnd) { if (isEnd) { ok(); } else if (exn != null) { fail(exn); } else { accept(value); } } } @SuppressWarnings("serial") public static class ForEach extends Base { private final Consumer _accept; private final Runnable _complete; private final Consumer _fail; public ForEach(Consumer accept, Runnable complete, Consumer fail) { Objects.requireNonNull(accept); Objects.requireNonNull(complete); Objects.requireNonNull(fail); _accept = accept; _complete = complete; _fail = fail; } public ForEach(Consumer accept, Runnable complete) { Objects.requireNonNull(accept); Objects.requireNonNull(complete); _accept = accept; _complete = complete; _fail = null; } public ForEach(Consumer accept) { Objects.requireNonNull(accept); _accept = accept; _complete = null; _fail = null; } @Override public void accept(V value) { _accept.accept(value); } @Override public void ok() { if (_complete != null) { _complete.run(); } } @Override public void fail(Throwable e) { if (_fail != null) { _fail.accept(e); } else { super.fail(e); } } } }