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

org.jacpfx.vertx.rest.response.blocking.ResponseBlockingExecution Maven / Gradle / Ivy

/*
 * Copyright [2017] [Andy Moncsek]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jacpfx.vertx.rest.response.blocking;

import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.shareddata.Counter;
import io.vertx.core.shareddata.Lock;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import org.jacpfx.common.ExecutionResult;
import org.jacpfx.common.VxmsShared;
import org.jacpfx.common.concurrent.LocalData;
import org.jacpfx.common.throwable.ThrowableFunction;
import org.jacpfx.common.throwable.ThrowableSupplier;
import org.jacpfx.vertx.rest.response.basic.ResponseExecution;

/**
 * Created by Andy Moncsek on 19.01.16.
 * Performs blocking Executions and prepares response
 */
public class ResponseBlockingExecution {

  private static final int DEFAULT_VALUE = 0;
  private static final long DEFAULT_LONG_VALUE = 0;
  private static final int DEFAULT_LOCK_TIMEOUT = 2000;
  private static final int STOP_CONDITION = -1;
  private static final long LOCK_VALUE = -1l;

  /**
   * Creates the response value based on the flow defined in the fluent API. The resulting response
   * will be passed to the resultHandler. This method should be executed in a  non blocking context.
   * When defining a timeout a second execution context will be created.
   *
   * @param _methodId the method name/id to be executed
   * @param _supplier the user defined supply method to be executed (mapToStringResponse,
   * mapToByteResponse, mapToObjectResponse)
   * @param _resultHandler the result handler, that takes the result
   * @param _errorHandler the intermediate error method, executed on each error
   * @param _onFailureRespond the method to be executed on failure
   * @param _errorMethodHandler the fallback method
   * @param vxmsShared the vxmsShared instance, containing the Vertx instance and other shared
   * objects per instance
   * @param _fail last thrown Exception
   * @param _retry the amount of retries
   * @param _timeout, the max timeout time for the method execution
   * @param _circuitBreakerTimeout the stateful circuit breaker release time
   * @param _delay the delay time between retry
   * @param  the type of response (String, byte, Object)
   */
  public static  void executeRetryAndCatchAsync(String _methodId,
      ThrowableSupplier _supplier,
      Future> _resultHandler,
      Consumer _errorHandler,
      ThrowableFunction _onFailureRespond,
      Consumer _errorMethodHandler,
      VxmsShared vxmsShared, Throwable _fail,
      int _retry,
      long _timeout,
      long _circuitBreakerTimeout,
      long _delay) {
    if (_circuitBreakerTimeout > DEFAULT_LONG_VALUE) {
      executeLocked((lock, counter) -> counter.get(counterHandler -> {
            long currentVal = counterHandler.result();
            if (currentVal == DEFAULT_LONG_VALUE) {
              executeInitialState(_methodId,
                  _supplier,
                  _resultHandler,
                  _errorHandler,
                  _onFailureRespond,
                  _errorMethodHandler,
                  vxmsShared, _fail,
                  _retry, _timeout,
                  _circuitBreakerTimeout,
                  _delay, lock, counter);
            } else if (currentVal > DEFAULT_LONG_VALUE) {
              executeDefault(_methodId,
                  _supplier,
                  _resultHandler,
                  _errorHandler,
                  _onFailureRespond,
                  _errorMethodHandler,
                  vxmsShared, _fail,
                  _retry, _timeout,
                  _circuitBreakerTimeout,
                  _delay, lock);
            } else {
              executeErrorState(_resultHandler, _errorHandler, _onFailureRespond, _errorMethodHandler,
                  _fail, lock);
            }
          }), _methodId, vxmsShared, _resultHandler, _errorHandler, _onFailureRespond,
          _errorMethodHandler, null);
    } else {
      executeStateless(_supplier, _resultHandler, _errorHandler, _onFailureRespond,
          _errorMethodHandler, vxmsShared, _retry, _timeout, _delay);
    }
  }

  private static  void executeErrorState(Future> _blockingHandler,
      Consumer _errorHandler,
      ThrowableFunction _onFailureRespond,
      Consumer _errorMethodHandler,
      Throwable t, Lock lock) {
    Optional.ofNullable(lock).ifPresent(Lock::release);
    handleErrorExecution(_blockingHandler,
        _errorHandler,
        _onFailureRespond,
        _errorMethodHandler,
        Optional.ofNullable(t).orElse(Future.failedFuture("circuit open").cause()));
  }

  private static  void executeDefault(String _methodId,
      ThrowableSupplier _supplier,
      Future> _blockingHandler,
      Consumer _errorHandler,
      ThrowableFunction _onFailureRespond,
      Consumer _errorMethodHandler,
      VxmsShared vxmsShared, Throwable _failure,
      int _retry, long _timeout,
      long _circuitBreakerTimeout,
      long _delay, Lock lock) {
    Optional.ofNullable(lock).ifPresent(Lock::release);
    final Vertx vertx = vxmsShared.getVertx();
    vertx.executeBlocking(bhandler -> {
      try {
        executeDefaultState(_supplier, _blockingHandler, vxmsShared, _timeout);
        bhandler.complete();
      } catch (Throwable e) {
        executeLocked((lck, counter) ->
                counter.decrementAndGet(valHandler -> {
                  if (valHandler.succeeded()) {
                    handleStatefulError(_methodId,
                        _supplier,
                        _blockingHandler,
                        _errorHandler,
                        _onFailureRespond,
                        _errorMethodHandler,
                        vxmsShared, _failure,
                        _retry,
                        _timeout,
                        _circuitBreakerTimeout,
                        _delay, e,
                        lck, counter,
                        valHandler);
                    bhandler.complete();
                  } else {
                    releaseLockAndHandleError(_blockingHandler, _errorHandler, _onFailureRespond,
                        _errorMethodHandler, valHandler.cause(), lck);
                    bhandler.complete();
                  }
                }), _methodId, vxmsShared, _blockingHandler, _errorHandler, _onFailureRespond,
            _errorMethodHandler, bhandler);
      }
    }, false, res -> {

    });
  }


  private static  void executeInitialState(String _methodId,
      ThrowableSupplier _supplier,
      Future> _blockingHandler,
      Consumer _errorHandler,
      ThrowableFunction _onFailureRespond,
      Consumer _errorMethodHandler,
      VxmsShared vxmsShared, Throwable _t, int _retry,
      long _timeout, long _circuitBreakerTimeout,
      long _delay, Lock lock, Counter counter) {
    final long initialRetryCounterValue = (long) (_retry + 1);
    counter.addAndGet(initialRetryCounterValue,
        rHandler -> executeDefault(_methodId, _supplier, _blockingHandler, _errorHandler,
            _onFailureRespond,
            _errorMethodHandler, vxmsShared, _t, _retry, _timeout, _circuitBreakerTimeout, _delay,
            lock));
  }

  private static  void releaseLockAndHandleError(Future> _blockingHandler,
      Consumer _errorHandler,
      ThrowableFunction _onFailureRespond,
      Consumer _errorMethodHandler,
      Throwable cause, Lock lock) {
    Optional.ofNullable(lock).ifPresent(Lock::release);
    handleErrorExecution(_blockingHandler, _errorHandler, _onFailureRespond, _errorMethodHandler,
        cause);
  }

  private static  void handleErrorExecution(Future> _blockingHandler,
      Consumer _errorHandler,
      ThrowableFunction _onFailureRespond,
      Consumer _errorMethodHandler,
      Throwable cause) {
    final T result = handleError(_errorHandler, _onFailureRespond, _errorMethodHandler, cause);
    if (!_blockingHandler.isComplete()) {
      _blockingHandler.complete(new ExecutionResult<>(result, true, true, null));
    }
  }

  private static  void handleStatefulError(String _methodId,
      ThrowableSupplier _supplier,
      Future> _blockingHandler,
      Consumer _errorHandler,
      ThrowableFunction _onFailureRespond,
      Consumer _errorMethodHandler,
      VxmsShared vxmsShared, Throwable _t, int _retry,
      long _timeout, long _circuitBreakerTimeout,
      long _delay, Throwable e, Lock lck,
      Counter counter, AsyncResult valHandler) {
    //////////////////////////////////////////
    long count = valHandler.result();
    if (count <= DEFAULT_LONG_VALUE) {
      setCircuitBreakerReleaseTimer(vxmsShared, _retry, _circuitBreakerTimeout, counter);
      openCircuitBreakerAndHandleError(_blockingHandler, _errorHandler, _onFailureRespond,
          _errorMethodHandler, vxmsShared, e, lck, counter);
    } else {
      lck.release();
      ResponseExecution.handleError(_errorHandler, e);
      handleDelay(_delay);
      executeRetryAndCatchAsync(_methodId, _supplier, _blockingHandler, _errorHandler,
          _onFailureRespond, _errorMethodHandler, vxmsShared, _t, _retry, _timeout,
          _circuitBreakerTimeout, _delay);
    }
    ////////////////////////////////////////
  }

  private static  void openCircuitBreakerAndHandleError(
      Future> _blockingHandler,
      Consumer _errorHandler,
      ThrowableFunction _onFailureRespond,
      Consumer _errorMethodHandler,
      VxmsShared vxmsShared, Throwable e,
      Lock lck, Counter counter) {
    counter.addAndGet(LOCK_VALUE, val -> {
      lck.release();
      final Vertx vertx = vxmsShared.getVertx();
      vertx.executeBlocking(bhandler -> {
        T result = handleError(_errorHandler, _onFailureRespond, _errorMethodHandler, e);
        if (!_blockingHandler.isComplete()) {
          _blockingHandler.complete(new ExecutionResult<>(result, true, true, null));
        }
      }, false, res -> {

      });
    });
  }

  private static void setCircuitBreakerReleaseTimer(VxmsShared vxmsShared, int _retry,
      long _circuitBreakerTimeout, Counter counter) {
    final Vertx vertx = vxmsShared.getVertx();
    vertx.setTimer(_circuitBreakerTimeout, timer -> {
      final long initialRetryCounterValue = (long) (_retry + 1);
      counter.addAndGet(initialRetryCounterValue, val -> {
      });
    });
  }

  private static  void executeDefaultState(ThrowableSupplier _supplier,
      Future> _blockingHandler, VxmsShared vxmsShared, long _timeout)
      throws Throwable {
    T result;
    if (_timeout > DEFAULT_LONG_VALUE) {
      result = executeWithTimeout(_supplier, vxmsShared, _timeout);
    } else {
      result = _supplier.get();
    }
    if (!_blockingHandler.isComplete()) {
      _blockingHandler.complete(new ExecutionResult<>(result, true, false, null));
    }
  }

  private static  T executeWithTimeout(ThrowableSupplier _supplier, VxmsShared vxmsShared,
      long _timeout) throws Throwable {
    T result;
    final CompletableFuture timeoutFuture = new CompletableFuture<>();
    final Vertx vertx = vxmsShared.getVertx();
    vertx.executeBlocking((innerHandler) -> {
      try {
        timeoutFuture.complete(_supplier.get());
      } catch (Throwable throwable) {
        timeoutFuture.obtrudeException(throwable);
      }
    }, false, (val) -> {
    });

    try {
      result = timeoutFuture.get(_timeout, TimeUnit.MILLISECONDS);
    } catch (TimeoutException timeout) {
      throw new TimeoutException("operation _timeout");
    }

    return result;
  }

  private static  void executeStateless(ThrowableSupplier _supplier,
      Future> _blockingHandler,
      Consumer errorHandler,
      ThrowableFunction onFailureRespond,
      Consumer errorMethodHandler,
      VxmsShared vxmsShared, int _retry,
      long timeout, long delay) {
    T result = null;
    boolean errorHandling = false;
    while (_retry >= DEFAULT_LONG_VALUE) {
      errorHandling = false;
      try {
        if (timeout > DEFAULT_LONG_VALUE) {
          result = executeWithTimeout(_supplier, vxmsShared, timeout);
          _retry = STOP_CONDITION;
        } else {
          result = _supplier.get();
          _retry = STOP_CONDITION;
        }

      } catch (Throwable e) {
        _retry--;
        if (_retry < DEFAULT_VALUE) {
          try {
            result = handleError(errorHandler, onFailureRespond, errorMethodHandler, e);
            errorHandling = true;
          } catch (Exception ee) {
            _blockingHandler.fail(ee);
          }

        } else {
          ResponseExecution.handleError(errorHandler, e);
          handleDelay(delay);
        }
      }
    }
    if (!errorHandling || errorHandling && result != null) {
      if (!_blockingHandler.isComplete()) {
        _blockingHandler.complete(new ExecutionResult<>(result, true, errorHandling, null));
      }
    }
  }


  private static void handleDelay(long delay) {
    try {
      if (delay > DEFAULT_LONG_VALUE) {
        Thread.sleep(delay);
      }
    } catch (InterruptedException e1) {
      e1.printStackTrace();
    }
  }


  private static  T handleError(Consumer errorHandler,
      ThrowableFunction onFailureRespond,
      Consumer errorMethodHandler, Throwable e) {
    T result = null;
    try {
      if (errorHandler != null) {
        errorHandler.accept(e);
      }
      if (onFailureRespond != null) {
        result = onFailureRespond.apply(e);
      }
      if (errorHandler == null && onFailureRespond == null) {
        errorMethodHandler.accept(e);
        return null;

      }
    } catch (Throwable throwable) {
      errorMethodHandler.accept(throwable);
    }
    return result;
  }

  private static  void executeLocked(LockedConsumer consumer,
      String _methodId,
      VxmsShared vxmsShared,
      Future> _blockingHandler,
      Consumer _errorHandler,
      ThrowableFunction _onFailureRespond,
      Consumer _errorMethodHandler,
      Future blockingCodeHandler) {
    // TODO make cluster-aware
    final LocalData sharedData = vxmsShared.getLocalData();
    sharedData.getLockWithTimeout(_methodId, DEFAULT_LOCK_TIMEOUT, lockHandler -> {
      final Lock lock = lockHandler.result();
      if (lockHandler.succeeded()) {
        sharedData.getCounter(_methodId, resultHandler -> {
          if (resultHandler.succeeded()) {
            consumer.execute(lock, resultHandler.result());
          } else {
            releaseLockAndHandleError(_blockingHandler, _errorHandler, _onFailureRespond,
                _errorMethodHandler, resultHandler.cause(), lock);
            Optional.ofNullable(blockingCodeHandler).ifPresent(Future::complete);
          }
        });
      } else {
        handleErrorExecution(_blockingHandler, _errorHandler, _onFailureRespond,
            _errorMethodHandler, lockHandler.cause());
        Optional.ofNullable(blockingCodeHandler).ifPresent(Future::complete);
      }

    });
  }


  private interface LockedConsumer {

    void execute(Lock lock, Counter counter);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy