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

io.baratine.service.ResultImpl 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.service;

import io.baratine.function.TriConsumer;
import io.baratine.service.Result.Fork;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Implementation classes for Result.fork and Result.from.
 *
 */
class ResultImpl
{
  static final Logger log = Logger.getLogger(ResultImpl.class.getName());
  
  abstract static class ResultJoinBase
  {
    private final AtomicInteger _counter;
    
    ResultJoinBase(int count)
    {
      _counter = new AtomicInteger(count);
    }
    
    ResultJoinBase()
    {
      _counter = new AtomicInteger(1);
    }
    
    void addFork()
    {
      _counter.incrementAndGet();
    }
    
    void completeFork()
    {
      if (_counter.decrementAndGet() == 0) {
        complete();
      }
    }
    
    void failFork(Throwable exn)
    {
      logFail(exn);
      
      if (_counter.decrementAndGet() == 0) {
        complete();
      }
    }
    
    abstract protected void complete();
    
    private void logFail(Throwable exn)
    {
      Logger log = Logger.getLogger(Result.class.getName());

      if (log.isLoggable(Level.FINER)) {
        log.finer(exn.toString());
      }
      
      log.log(Level.FINEST, exn.toString(), exn);
    }
    
    abstract boolean isFuture();
  }
  
  /**
   * A fork branch for a Result.fork call.
   */
  final static class ResultFork implements Result
  {
    private final ResultJoinBase _join;
    
    private U _value;
    private Throwable _failure;
    
    private Result _chain;
    private Object _chainValue;
    
    private boolean _isDone;
    
    ResultFork(ResultJoinBase join)
    {
      _join = join;
    }

    /**
     * The completion value.
     */
    public final U getValue()
    {
      if (_chain != null) {
        Result chain = _chain;
        Object chainValue = _chainValue;
        
        _chain = null;
        _chainValue = null;
        
        chain.completeFuture(chainValue);
      }
      
      return _value;
    }
    
    public final Throwable getFailure()
    {
      return _failure;
    }
    
    public void handle(U value, Throwable exn)
    {
      if (exn != null) {
        fail(exn);
      }
      else {
        ok(value);
      }
    }

    /**
     * Completes the branch result. 
     */
    @Override
    public void ok(U value)
    {
      _value = value;
      
      boolean isDonePrev = _isDone;
      
      if (! isDonePrev) {
        _isDone = true;

        _join.completeFork();
      }
    }
    
    @Override
    public boolean isFuture()
    {
      return _join.isFuture();
    }
    
    @Override
    public  void completeFuture(Result result, V value)
    {
      _chain = (Result) result;
      _chainValue = value;
      
      boolean isDonePrev = _isDone;
      if (! isDonePrev) {
        _isDone = true;

        _join.completeFork();
      }
    }
    
    @Override
    public void completeFuture(U value)
    {
      ok(value);
    }

    @Override
    public void fail(Throwable exn)
    {
      boolean isDonePrev = _isDone;
      
      _isDone = true;
      
      if (! isDonePrev) {
        _isDone = true;
        
        _failure = exn;
        
        _join.failFork(exn);
      }
    }
  }
  
  final static class ResultJoinFunction extends ResultJoinBase
    implements Result
  {
    private final Result _result;
    private final ResultFork []_resultsFork;
    
    private final Function,T> _finisher;
    
    private Throwable _fail;
    
    ResultJoinFunction(Result result,
                       ResultFork []resultsFork,
                       Function,T> finisher)
    {
      super(resultsFork.length);
      
      _result = result;
      _resultsFork = resultsFork;
      
      _finisher = finisher;
    }
    
    @Override
    protected void failFork(Throwable exn)
    {
      _fail = exn;
      
      super.failFork(exn);
    }
    
    @Override
    public void handle(Void value, Throwable exn)
    {
      if (exn != null) {
        fail(exn);
      }
      else {
        ok(null);
      }
    }
    
    @Override
    protected void complete()
    {
      try {
        if (_fail != null) {
          _result.fail(_fail);
          return;
        }
        
        if (_result.isFuture()) {
          _result.completeFuture(this, null);
        }
        else {
          completeFuture(null);
        }
      } catch (Throwable e) {
        _result.fail(e);
      }
    }
    
    @Override
    public boolean isFuture()
    {
      return _result.isFuture();
    }
    
    @Override
    public void ok(Void value)
    {
      throw new UnsupportedOperationException(getClass().getName());
    }
    
    @Override
    public void completeFuture(Void value)
    {
      ResultFork[] resultsFork = _resultsFork;
      int count = resultsFork.length;
  
      List values = new ArrayList<>();
  
      for (int i = 0; i < count; i++) {
        values.add(resultsFork[i].getValue());
      }

      _result.ok(_finisher.apply(values));
    }
  }
  
  final static class ResultJoinConsumer extends ResultJoinBase
    implements Result
  {
    private final Result _result;
    private final ResultFork []_resultsFork;
    
    private final BiConsumer,Result> _finisher;
    
    private Throwable _fail;
    
    ResultJoinConsumer(Result result,
                       ResultFork []resultsFork,
                       BiConsumer ,Result> finisher)
    {
      super(resultsFork.length);
      
      _result = result;
      _resultsFork = resultsFork;
      
      _finisher = finisher;
    }

    @Override
    protected void failFork(Throwable exn)
    {
      if (_fail == null) {
        _fail = exn;
      }
      
      super.failFork(exn);
    }
    
    @Override
    protected void complete()
    {
      try {
        if (_fail != null) {
          _result.fail(_fail);
          return;
        }
        
        if (_result.isFuture()) {
          _result.completeFuture(this, null);
        }
        else {
          completeFuture(null);
        }
      } catch (Throwable e) {
        _result.fail(e);
      }
    }
    
    @Override
    public boolean isFuture()
    {
      return _result.isFuture();
    }

    @Override
    public void ok(Void value)
    {
      throw new UnsupportedOperationException(getClass().getName());
    }

    @Override
    public void handle(Void value, Throwable exn)
    {
      if (exn != null) {
        fail(exn);
      }
      else {
        ok(value);
      }
    }

    @Override
    public void completeFuture(Void value)
    {
      ResultFork[] resultsFork = _resultsFork;
      int count = resultsFork.length;
    
      List values = new ArrayList<>();
    
      for (int i = 0; i < count; i++) {
        values.add(resultsFork[i].getValue());
      }
      
      _finisher.accept(values, _result);
    }
  }
  
  final static class ResultJoinConsumerFails extends ResultJoinBase
    implements Result
  {
    private final Result _result;
    private final ResultFork []_resultsFork;
    
    private final BiConsumer,Result> _finisher;
    private final TriConsumer,List,Result> _fails;
    
    private boolean _isFail;
    
    ResultJoinConsumerFails(Result result,
                       ResultFork []resultsFork,
                       BiConsumer ,Result> finisher,
                       TriConsumer ,List,Result> fails)
    {
      super(resultsFork.length);
      
      _result = result;
      _resultsFork = resultsFork;
      
      _finisher = finisher;
      _fails = fails;
    }

    @Override
    protected void failFork(Throwable exn)
    {
      _isFail = true;

      super.failFork(exn);
    }
    
    @Override
    protected void complete()
    {
      if (_result.isFuture()) {
        _result.completeFuture(this, null);
      }
      else {
        completeFuture(null);
      }
    }
    
    @Override
    public boolean isFuture()
    {
      return _result.isFuture();
    }

    @Override
    public void ok(Void value)
    {
      throw new UnsupportedOperationException(getClass().getName());
    }

    @Override
    public void handle(Void value, Throwable exn)
    {
      if (exn != null) {
        fail(exn);
      }
      else {
        ok(value);
      }
    }
    
    @Override
    public void completeFuture(Void value)
    {
      try {
        ResultFork[] resultsFork = _resultsFork;
        int count = resultsFork.length;
        
        List values = new ArrayList<>();
      
        for (int i = 0; i < count; i++) {
          values.add(resultsFork[i].getValue());
        }

        if (! _isFail) {
          _finisher.accept(values, _result);
        }
        else {
          List fails = new ArrayList<>();
        
          for (int i = 0; i < count; i++) {
            fails.add(resultsFork[i].getFailure());
          }
          
          _fails.accept(values, fails, _result);
        }
      } catch (Throwable e) {
        _result.fail(e);
      }
    }
  }
  
  final static class ResultJoinBuilder extends ResultJoinBase
    implements Result, Fork
  {
    private final Result _result;
    private final List> _forkList = new ArrayList<>();
    
    private Function,T> _finisherFunction;
    private BiConsumer,Result> _finisherConsumer;
    
    private TriConsumer,List,Result> _fails;
    
    private boolean _isFail;
    
    ResultJoinBuilder(Result result)
    {
      _result = result;
    }
    
    @Override
    public  ResultFork branch()
    {
      validateBuilder();
      
      ResultFork resultFork = new ResultFork<>(this);
      
      _forkList.add(resultFork);
    
      super.addFork();

      return resultFork;
    }
  

    @Override
    public Fork fail(TriConsumer, List, Result> fails)
    {
      validateBuilder();
      
      _fails = (TriConsumer) fails;

      return this;
    }

    @Override
    public void join(Function, T> finisher)
    {
      validateBuilder();
      
      _finisherFunction = finisher;
      
      completeFork();
    }

    @Override
    public void join(BiConsumer, Result> finisher)
    {
      validateBuilder();
      
      _finisherConsumer = finisher;
      
      completeFork();
    }
    
    private void validateBuilder()
    {
      if (_finisherFunction != null || _finisherConsumer != null) {
        throw new IllegalStateException();
      }
    }

    @Override
    protected void failFork(Throwable exn)
    {
      _isFail = true;

      super.failFork(exn);
    }
    
    @Override
    protected void complete()
    {
      if (_result.isFuture()) {
        _result.completeFuture(this, null);
      }
      else {
        completeFuture(null);
      }
    }
    
    @Override
    public boolean isFuture()
    {
      return _result.isFuture();
    }

    @Override
    public void ok(Void value)
    {
      throw new UnsupportedOperationException(getClass().getName());
    }

    @Override
    public void handle(Void value, Throwable exn)
    {
      if (exn != null) {
        fail(exn);
      }
      else {
        ok(value);
      }
    }
    
    @Override
    public void completeFuture(Void value)
    {
      try {
        List values = new ArrayList<>();
      
        for (int i = 0; i < _forkList.size(); i++) {
          values.add(_forkList.get(i).getValue());
        }

        if (! _isFail) {
          if (_finisherFunction != null) {
            _result.ok(_finisherFunction.apply(values));
          }
          else if (_finisherConsumer != null) {
            _finisherConsumer.accept(values, _result);
          }
          else {
            throw new IllegalStateException();
          }
        }
        else {
          List fails = new ArrayList<>();
          
          Throwable failFirst = null;
        
          for (int i = 0; i < _forkList.size(); i++) {
            Throwable fail = _forkList.get(i).getFailure();
            
            if (failFirst == null) {
              failFirst = fail;
            }
            
            fails.add(fail);
          }

          if (_fails != null) {
            _fails.accept(values, fails, _result);
          }
          else {
            _result.fail(failFirst);
          }
        }
      } catch (Throwable e) {
        _result.fail(e);
      }
    }
  }
  
  final static class ChainResult implements Result
  {
    private Result _next;
    private BiConsumer> _consumer;
    
    public ChainResult(Result next,
                       BiConsumer> consumer)
    {
      Objects.requireNonNull(next);
      _next = next;
      
      Objects.requireNonNull(consumer);
      _consumer = consumer;
    }
  
    @Override
    public void ok(T value)
    {
      try {
        _consumer.accept(value, _next);
      } catch (Throwable e) {
        fail(e);
      }
    }
    
    @Override
    public void fail(Throwable exn)
    {
      _next.fail(exn);
    }
    
    @Override
    public void handle(T value, Throwable exn)
    {
      try {
        if (exn != null) {
          _next.fail(exn);
        }
        else {
          _consumer.accept(value, _next);
        }
      } catch (Throwable e) {
        _next.fail(e);
      }
    }
  }
  
  final static class ChainResultAsync extends ChainResultBase
  {
    private BiConsumer> _consumer;
    
    public ChainResultAsync(Result next,
                            BiConsumer> consumer)
    {
      super(next);
      
      Objects.requireNonNull(consumer);
      _consumer = consumer;
    }
    
    @Override
    public boolean isFuture()
    {
      return true;
    }
  
    @Override
    public void ok(T value)
    {
      completeFuture(value);
    }
    
    @Override
    public  void completeFuture(Result result, V value)
    {
      getNext().completeFuture(result, value);
    }
    
    @Override
    public void completeFuture(T value)
    {
      //Object oldContext = beginAsyncContext();
      
      try {
        _consumer.accept(value, getNext());
      } catch (Throwable e) {
        fail(e);
      } finally {
        // endAsyncContext(oldContext);
      }
    }
  }
  
  /**
   * Chained Result using a function to map the returned value to 
   * the Result.
   */
  static class ChainResultFun extends ChainResultBase
  {
    private Function _fun;
    
    public ChainResultFun(Result next,
                          Function fun)
    {
      super(next);
      
      Objects.requireNonNull(fun);
      _fun = fun;
    }
    
    @Override
    public boolean isFuture()
    {
      return false;
    }
  
    @Override
    public void ok(T value)
    {
      try {
        U nextValue = _fun.apply(value);
        
        getNext().ok(nextValue);
      } catch (Throwable e) {
        fail(e);
      }
    }
  }
  
  /**
   * Chained Result using a function to map the returned value to 
   * the Result.
   */
  static class ChainResultFunExn extends ChainResultFun
  {
    private BiConsumer> _exnHandler;
    
    public ChainResultFunExn(Result next,
                             Function fun,
                             BiConsumer> exnHandler)
    {
      super(next, fun);
      
      Objects.requireNonNull(exnHandler);
      _exnHandler = exnHandler;
    }
  
    @Override
    public void fail(Throwable exn)
    {
      try {
        _exnHandler.accept(exn, getNext());
      } catch (Throwable e) {
        getNext().fail(e);
      }
    }
  }
  
  abstract static class ChainResultBase extends Result.Wrapper
  {
    public ChainResultBase(Result next)
    {
      super(next);
    }
    
    @Override
    public abstract void ok(T value);
  }
  
  /**
   * Chained Result using a function to map the returned value to 
   * the Result.
   */
  static class ChainResultFunFuture extends ChainResultBase
  {
    private Function _fun;
    
    public ChainResultFunFuture(Result next,
                                Function fun)
    {
      super(next);
      
      Objects.requireNonNull(fun);
      _fun = fun;
    }
    
    @Override
    public boolean isFuture()
    {
      return true;
    }
  
    @Override
    public void ok(T value)
    {
      completeFuture(value);
    }
    
    @Override
    public void completeFuture(T value)
    {
      // Object oldContext = beginAsyncContext();
      
      try {
        U nextValue = _fun.apply(value);
        
        getNext().completeFuture(nextValue);
      } catch (Throwable e) {
        fail(e);
      } finally {
        // endAsyncContext(oldContext);
      }
    }
  }
  
  /**
   * Chained Result using a function to map the returned value to 
   * the Result.
   */
  static class ChainResultFunFutureExn extends ChainResultFunFuture
  {
    private BiConsumer> _exnHandler;
    
    public ChainResultFunFutureExn(Result next,
                             Function fun,
                             BiConsumer> exnHandler)
    {
      super(next, fun);
      
      Objects.requireNonNull(exnHandler);
      _exnHandler = exnHandler;
    }
  
    @Override
    public void fail(Throwable exn)
    {
      try {
        _exnHandler.accept(exn, getNext());
      } catch (Throwable e) {
        getNext().fail(e);
      }
    }
  }

  static class Ignore implements Result
  {
    private static final Logger log = Logger.getLogger(ResultImpl.class.getName());
    private static final Ignore NULL_ADAPTER = new Ignore();
    
    public static  Result create()
    {
      return (Result) NULL_ADAPTER;
    }
    
    @Override
    public void handle(T result, Throwable exn)
    {
      if (exn != null) {
        if (log.isLoggable(Level.FINER)) {
          log.finer(this + " failed " + exn);
          exn.printStackTrace();
        }
        else if (log.isLoggable(Level.FINEST)) {
          log.log(Level.FINEST, exn.toString(), exn);
        }
        
      }
      else {
        if (log.isLoggable(Level.FINEST)) {
          log.finest(this + " completed " + result);
        }
      }
    }
    
    @Override
    public String toString()
    {
      return getClass().getSimpleName() + "[]";
    }
  }

  static class AdapterMake implements Result
  {
    private Consumer _result;
    private Consumer _exn;
    
    public AdapterMake(Consumer result, Consumer exn)
    {
      Objects.requireNonNull(result);
      Objects.requireNonNull(exn);
      
      _result = result;
      _exn = exn;
    }
    
    @Override
    public void handle(T result, Throwable exn)
    {
      try {
        if (exn != null) {
          _exn.accept(exn);
        }
        else {
          _result.accept(result);
        }
      } catch (Throwable exn2) {
        _exn.accept(exn);
      }
    }
  }

  static class ResultBuilder implements Result, Result.Builder
  {
    private Consumer _onComplete;
    private Consumer _onFail;
    
    public ResultBuilder(Consumer result, Consumer fail)
    {
      _onComplete = result;
      _onFail = fail;
    }
    
    public Result onOk(Consumer onComplete)
    {
      Objects.requireNonNull(onComplete);
      
      _onComplete = onComplete;
      
      return this;
    }
    
    @Override
    public void handle(T result, Throwable exn)
    {
      try {
        if (exn != null) {
          Consumer onFail = _onFail;
        
          if (onFail != null) {
            onFail.accept(exn);
          }
          else {
            if (log.isLoggable(Level.FINER)) {
              log.finer(exn.toString());
            }
          }
        }
        else {
          _onComplete.accept(result);
        }
      } catch (Throwable exn2) {
        Consumer onFail = _onFail;
        
        if (onFail != null) {
          onFail.accept(exn);
        }
        else {
          log.log(Level.FINER, exn2.toString(), exn2);
        }
      }
    }
    
    @Override
    public String toString()
    {
      return getClass().getSimpleName() + "[" + _onComplete + "," + _onFail + "]";
    }
  }
}