001package com.mrivanplays.process;
002
003import java.util.Objects;
004import java.util.function.Supplier;
005
006/**
007 * Represents a {@link Process} that has a result.
008 *
009 * @param <T> result value type parameter
010 * @author <a href="mailto:[email protected]">Ivan Pekov</a>
011 * @since 0.0.1
012 */
013public abstract class ResultedProcess<T> {
014
015  /**
016   * Creates a new {@code ResultedProcess} from a {@link Supplier}
017   *
018   * @param identifier resulted process identifier
019   * @param supplier input supplier
020   * @return resulted process
021   * @param <T> result value type parameter
022   */
023  public static <T> ResultedProcess<T> fromSupplier(String identifier, Supplier<T> supplier) {
024    return new ResultedProcess<T>(identifier) {
025      @Override
026      public T run() {
027        return supplier.get();
028      }
029    };
030  }
031
032  private final String identifier;
033
034  /**
035   * Base constructor for all {@code ResultedProcess} implementations.
036   *
037   * @param identifier the identifier of this resulted process. It is good to set it every time
038   *     implementation class(es) are created in order to properly defer results.
039   */
040  public ResultedProcess(String identifier) {
041    this.identifier = Objects.requireNonNull(identifier, "identifier");
042  }
043
044  /**
045   * Main logic for running the process
046   *
047   * @return a non null result value
048   * @throws Throwable if anything goes wrong, the implementation can throw any exception.
049   */
050  protected abstract T run() throws Throwable;
051
052  // ====================================
053
054  final void runProcess(ResultedProcessesCompletion<T> completion) {
055    try {
056      T result = this.run();
057      if (result != null) {
058        completion.countDown(this.identifier, ProcessResult.success(result));
059      } else {
060        throw new ProcessException(
061            "Null result in ResultedProcess '" + this.identifier + "' without errors!");
062      }
063    } catch (Throwable e) {
064      ProcessException error =
065          new ProcessException("in ResultedProcess '" + this.identifier + "'", e);
066      if (e instanceof ProcessException) {
067        error = (ProcessException) e;
068      }
069      completion.countDown(this.identifier, ProcessResult.failure(error));
070    }
071  }
072}