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

com.outbrain.ob1k.concurrent.eager.EagerComposableFuture Maven / Gradle / Ivy

package com.outbrain.ob1k.concurrent.eager;

import com.google.common.base.Function;
import com.outbrain.ob1k.concurrent.*;
import com.outbrain.ob1k.concurrent.handlers.*;
import com.outbrain.ob1k.concurrent.Producer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

/**
 * User: aronen
 * Date: 6/6/13
 * Time: 2:08 PM
 */
public final class EagerComposableFuture implements ComposableFuture, ComposablePromise {
  private static final Logger logger = LoggerFactory.getLogger(EagerComposableFuture.class);

  private final Executor threadPool;
  private final HandlersList handlers;
  private final AtomicReference> value = new AtomicReference<>();

  public EagerComposableFuture() {
    threadPool = null;
    handlers = new HandlersList();
  }

  public EagerComposableFuture(final Executor threadPool) {
    this.threadPool = threadPool;
    handlers = new HandlersList();
  }

  @Override
  public void set(final T result) {
    if (value.compareAndSet(null, Try.fromValue(result))) {
      done();
    }
  }

  @Override
  public void setException(final Throwable t) {
    if (value.compareAndSet(null, Try.fromError(t))) {
      done();
    }
  }

  @Override
  public ComposableFuture future() {
    return this;
  }

  public static  ComposableFuture fromValue(final T value) {
    final EagerComposableFuture result = new EagerComposableFuture<>();
    result.set(value);
    return result;
  }

  public static  ComposableFuture fromError(final Throwable error) {
    final EagerComposableFuture result = new EagerComposableFuture<>();
    result.setException(error);
    return result;
  }

  public static  ComposableFuture build(final Producer producer) {
    final EagerComposableFuture future = new EagerComposableFuture<>();
    producer.produce(new Consumer() {
      @Override
      public void consume(final Try result) {
        if (result.isSuccess()) {
          future.set(result.getValue());
        } else {
          future.setException(result.getError());
        }
      }
    });

    return future;
  }

  public static  ComposableFuture submit(final Executor executor, final Callable task, final boolean delegateHandler) {
    if (task == null)
      return fromError(new NullPointerException("task must not be null"));

    final EagerComposableFuture future = delegateHandler ?
        new EagerComposableFuture(executor) :
        new EagerComposableFuture();

    executor.execute(new Runnable() {
      @Override
      public void run() {
        try {
          future.set(task.call());
        } catch (final Exception e) {
          future.setException(e);
        }
      }
    });

    return future;
  }

  public static  ComposableFuture schedule(final Scheduler scheduler, final Callable task, final long delay, final TimeUnit unit) {
    final EagerComposableFuture res = new EagerComposableFuture<>();
    scheduler.schedule(new Runnable() {
      @Override
      public void run() {
        try {
          res.set(task.call());
        } catch (final Exception e) {
          res.setException(e);
        }
      }
    }, delay, unit);

    return res;
  }


  public static  ComposableFuture doubleDispatch(final FutureAction action, final long duration,
                                                       final TimeUnit unit, final Scheduler scheduler) {
    final ComposableFuture first = action.execute();
    final AtomicBoolean done = new AtomicBoolean();

    first.consume(new Consumer() {
      @Override
      public void consume(final Try result) {
        done.compareAndSet(false, true);
      }
    });

    final EagerComposableFuture second = new EagerComposableFuture<>();
    scheduler.schedule(new Runnable() {
      @Override
      public void run() {
        if (done.compareAndSet(false, true)) {
          try {
            final ComposableFuture innerSecond = action.execute();
            innerSecond.consume(new Consumer() {
              @Override
              public void consume(final Try result) {
                if (result.isSuccess()) {
                  second.set(result.getValue());
                } else {
                  second.setException(result.getError());
                }
              }
            });
          } catch (final Exception e) {
            second.setException(e);
          }
        }
      }
    }, duration, unit);

    return collectFirst(Arrays.asList(first, second));
  }

  public static  ComposableFuture collectFirst(final List> futures) {
    final int size = futures.size();
    if (size == 0) {
      return fromError(new IllegalArgumentException("empty future list"));
    }

    final EagerComposableFuture res = new EagerComposableFuture<>();
    final AtomicBoolean done = new AtomicBoolean();

    for (final ComposableFuture future : futures) {
      future.consume(new Consumer() {
        @Override
        public void consume(final Try result) {
          if (done.compareAndSet(false, true)) {
            if (result.isSuccess()) {
              res.set(result.getValue());
            } else {
              res.setException(result.getError());
            }
          }
        }
      });
    }

    return res;
  }

  private void done() {
    handlers.execute(threadPool);
  }

  @Override
  public  ComposableFuture continueWith(final FutureResultHandler handler) {
    final EagerComposableFuture future = new EagerComposableFuture<>(threadPool);
    this.consume(new Consumer() {
      @Override
      public void consume(final Try res) {
        try {
          final ComposableFuture nextResult = handler.handle(res);
          if (nextResult == null) {
            future.set(null);
          } else {
            nextResult.consume(new Consumer() {
              @Override
              public void consume(final Try result) {
                if (result.isSuccess()) {
                  future.set(result.getValue());
                } else {
                  future.setException(result.getError());
                }
              }
            });
          }

        } catch (final Exception e) {
          future.setException(e);
        }
      }
    });

    return future;
  }

  @Override
  public  ComposableFuture continueWith(final ResultHandler handler) {
    final EagerComposableFuture result = new EagerComposableFuture<>(threadPool);
    this.consume(new Consumer() {
      @Override
      public void consume(final Try res) {
        try {
          result.set(handler.handle(res));
        } catch (final Exception e) {
          result.setException(e);
        }
      }
    });

    return result;
  }

  @Override
  public  ComposableFuture continueOnSuccess(final FutureSuccessHandler handler) {
    final EagerComposableFuture future = new EagerComposableFuture<>(threadPool);
    this.consume(new Consumer() {
      @Override
      public void consume(final Try result) {
        if (result.isSuccess()) {
          try {
            final ComposableFuture res = handler.handle(result.getValue());
            if (res == null) {
              future.set(null);
            } else {
              res.consume(new Consumer() {
                @Override
                public void consume(final Try result) {
                  if (result.isSuccess()) {
                    future.set(result.getValue());
                  } else {
                    future.setException(result.getError());
                  }
                }
              });
            }

          } catch (final Exception e) {
            future.setException(e);
          }
        } else {
          future.setException(result.getError());
        }
      }
    });

    return future;
  }

  @Override
  public  ComposableFuture continueOnSuccess(final SuccessHandler handler) {
    final EagerComposableFuture future = new EagerComposableFuture<>(threadPool);
    this.consume(new Consumer() {
      @Override
      public void consume(final Try result) {
        if (result.isSuccess()) {
          try {
            future.set(handler.handle(result.getValue()));
          } catch (final ExecutionException e) {
            future.setException(e.getCause() != null ? e.getCause() : e);
          } catch (final Exception e) {
            future.setException(e);
          }
        } else {
          future.setException(result.getError());
        }
      }
    });

    return future;
  }

  @Override
  public ComposableFuture continueOnError(final FutureErrorHandler handler) {
    final EagerComposableFuture future = new EagerComposableFuture<>(threadPool);
    this.consume(new Consumer() {
      @Override
      public void consume(final Try result) {
        if (result.isSuccess()) {
          future.set(result.getValue());
        } else {
          try {
            final ComposableFuture res = handler.handle(result.getError());
            if (res == null) {
              future.set(null);
            } else {
              res.consume(new Consumer() {
                @Override
                public void consume(final Try result) {
                  if (result.isSuccess()) {
                    future.set(result.getValue());
                  } else {
                    future.setException(result.getError());
                  }
                }
              });
            }

          } catch (final Exception e) {
            future.setException(e);
          }
        }
      }
    });

    return future;
  }

  @Override
  public ComposableFuture continueOnError(final ErrorHandler handler) {
    final EagerComposableFuture future = new EagerComposableFuture<>(threadPool);
    this.consume(new Consumer() {
      @Override
      public void consume(final Try result) {
        if (result.isSuccess()) {
          future.set(result.getValue());
        } else {
          try {
            future.set(handler.handle(result.getError()));
          } catch (final ExecutionException e) {
            future.setException(e.getCause() != null ? e.getCause() : e);
          } catch (final Exception e) {
            future.setException(e);
          }
        }
      }
    });

    return future;
  }

  @Override
  public void consume(final Consumer consumer) {
    handlers.addHandler(new ConsumerAction<>(consumer, this), threadPool);
  }

  @Override
  public  ComposableFuture transform(final Function function) {
    return continueOnSuccess(new SuccessHandler() {
      @Override
      public R handle(final T result) {
        return function.apply(result);
      }
    });
  }

  @Override
  public ComposableFuture withTimeout(final long duration, final TimeUnit unit, final String taskDescription) {
    return withTimeout(ComposableFutures.getScheduler(), duration, unit, taskDescription);
  }

  @Override
  public ComposableFuture withTimeout(final Scheduler scheduler, final long timeout, final TimeUnit unit, final String taskDescription) {
    final ComposablePromise deadline = new EagerComposableFuture<>();
    final CancellationToken cancellationToken =  scheduler.schedule(new Runnable() {
      @Override
      public void run() {
        deadline.setException(new TimeoutException("Timeout occurred on task ('" + taskDescription + "' " + timeout + " " + unit + ")"));
      }
    }, timeout, unit);

    this.consume(new Consumer() {
      @Override
      public void consume(final Try result) {
        cancellationToken.cancel(false);
      }
    });
    return collectFirst(Arrays.asList(this, deadline.future()));
  }

  @Override
  public ComposableFuture withTimeout(final long duration, final TimeUnit unit) {
    return withTimeout(ComposableFutures.getScheduler(), duration, unit);
  }

  @Override
  public ComposableFuture withTimeout(final Scheduler scheduler, final long timeout, final TimeUnit unit) {
    return withTimeout(scheduler, timeout, unit, "unspecified task");
  }

  @Override
  public ComposableFuture materialize() {
    return this;
  }

  @Override
  public T get() throws InterruptedException, ExecutionException {
    final CountDownLatch latch = new CountDownLatch(1);
    this.consume(new Consumer() {
      @Override
      public void consume(final Try result) {
        latch.countDown();
      }
    });

    latch.await();
    final Try currentValue = this.value.get();
    if (currentValue.isSuccess()) {
      return currentValue.getValue();
    } else {
      throw new ExecutionException(currentValue.getError());
    }
  }

  @Override
  public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
    final CountDownLatch latch = new CountDownLatch(1);
    this.consume(new Consumer() {
      @Override
      public void consume(final Try result) {
        latch.countDown();
      }
    });

    if (latch.await(timeout, unit)) {
      final Try currentValue = this.value.get();
      if (currentValue.isSuccess()) {
        return currentValue.getValue();
      } else {
        throw new ExecutionException(currentValue.getError());
      }
    } else {
      throw new TimeoutException("Timeout occurred while waiting for value (" + timeout + unit + ")");
    }
  }

  private static class ConsumerAction implements Runnable {
    final Consumer inner;
    final EagerComposableFuture current;

    private ConsumerAction(final Consumer inner, final EagerComposableFuture current) {
      this.inner = inner;
      this.current = current;
    }

    @Override
    public void run() {
      try {
        final Try currentValue = current.value.get();
        inner.consume(currentValue);
      } catch (final Throwable error) {
        logger.warn("error while handling future callbacks", error);
      }
    }
  }



}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy