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

org.jacpfx.vertx.event.eventbus.basic.EventbusBridgeExecution Maven / Gradle / Ivy

There is a newer version: 1.1
Show newest version
/*
 * 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.event.eventbus.basic;

import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.eventbus.Message;
import io.vertx.core.shareddata.Counter;
import io.vertx.core.shareddata.Lock;
import java.util.Optional;
import java.util.function.Consumer;
import org.jacpfx.common.VxmsShared;
import org.jacpfx.common.concurrent.LocalData;
import org.jacpfx.common.encoder.Encoder;
import org.jacpfx.common.throwable.ThrowableErrorConsumer;
import org.jacpfx.common.throwable.ThrowableFutureBiConsumer;
import org.jacpfx.common.throwable.ThrowableFutureConsumer;
import org.jacpfx.vertx.event.interfaces.basic.RecursiveExecutor;
import org.jacpfx.vertx.event.interfaces.basic.RetryExecutor;
import org.jacpfx.vertx.event.response.basic.ResponseExecution;

/**
 * Created by Andy Moncsek on 05.04.16.
 * Handles event-bus call and non-blocking execution of the message to create an event-bus response
 */
public class EventbusBridgeExecution {

  private static final long LOCK_VALUE = -1l;
  private static final int DEFAULT_LOCK_TIMEOUT = 2000;
  private static final long NO_TIMEOUT = 0l;


  /**
   * Send event-bus message and process the result in the passed function for the execution chain
   *
   * @param methodId the method identifier
   * @param targetId the event-bus target id
   * @param message the message to send
   * @param function the function to process the result message
   * @param requestDeliveryOptions the event-bus delivery options
   * @param vxmsShared the vxmsShared instance, containing the Vertx instance and other shared
   * objects per instance
   * @param errorMethodHandler the error-method handler
   * @param requestMessage the request message to respond after chain execution
   * @param encoder the encoder to serialize the response object
   * @param errorHandler the error handler
   * @param onFailureRespond the function that takes a Future with the alternate response value in
   * case of failure
   * @param responseDeliveryOptions the delivery options for the event response
   * @param retryCount the amount of retries before failure execution is triggered
   * @param timeout the amount of time before the execution will be aborted
   * @param circuitBreakerTimeout the amount of time before the circuit breaker closed again
   * @param executor the typed executor to process the chain
   * @param retryExecutor the typed retry executor of the chain
   * @param  the type of response
   */
  public static  void sendMessageAndSupplyHandler(String methodId,
      String targetId,
      Object message,
      ThrowableFutureBiConsumer>, T> function,
      DeliveryOptions requestDeliveryOptions,
      VxmsShared vxmsShared,
      Consumer errorMethodHandler,
      Message requestMessage,
      Encoder encoder,
      Consumer errorHandler,
      ThrowableErrorConsumer onFailureRespond,
      DeliveryOptions responseDeliveryOptions,
      int retryCount,
      long timeout,
      long circuitBreakerTimeout,
      RecursiveExecutor executor,
      RetryExecutor retryExecutor) {

    if (circuitBreakerTimeout == 0l) {
      executeDefaultState(targetId,
          message,
          function,
          requestDeliveryOptions,
          methodId, vxmsShared,
          errorMethodHandler,
          requestMessage,
          encoder,
          errorHandler,
          onFailureRespond,
          responseDeliveryOptions,
          retryCount, timeout, circuitBreakerTimeout, executor, retryExecutor, null);

    } else {
      executeStateful(targetId,
          message,
          function,
          requestDeliveryOptions,
          methodId, vxmsShared,
          errorMethodHandler,
          requestMessage,
          encoder, errorHandler,
          onFailureRespond,
          responseDeliveryOptions,
          retryCount,
          timeout,
          circuitBreakerTimeout, executor, retryExecutor);
    }

  }

  private static  void executeStateful(String targetId,
      Object message,
      ThrowableFutureBiConsumer>, T> objectFunction,
      DeliveryOptions requestDeliveryOptions,
      String methodId,
      VxmsShared vxmsShared,
      Consumer errorMethodHandler,
      Message requestMessage,
      Encoder encoder,
      Consumer errorHandler,
      ThrowableErrorConsumer onFailureRespond,
      DeliveryOptions responseDeliveryOptions,
      int retryCount, long timeout, long circuitBreakerTimeout,
      RecursiveExecutor executor, RetryExecutor retry) {

    executeLocked(((lock, counter) ->
            counter.get(counterHandler -> {
              long currentVal = counterHandler.result();
              if (currentVal == 0) {
                executeInitialState(targetId,
                    message,
                    objectFunction,
                    requestDeliveryOptions,
                    methodId, vxmsShared,
                    errorMethodHandler,
                    requestMessage,
                    encoder, errorHandler,
                    onFailureRespond,
                    responseDeliveryOptions,
                    retryCount, timeout,
                    circuitBreakerTimeout, executor, retry,
                    lock, counter);
              } else if (currentVal > 0) {
                executeDefaultState(targetId,
                    message,
                    objectFunction,
                    requestDeliveryOptions,
                    methodId, vxmsShared,
                    errorMethodHandler,
                    requestMessage,
                    encoder, errorHandler,
                    onFailureRespond,
                    responseDeliveryOptions,
                    retryCount, timeout,
                    circuitBreakerTimeout, executor, retry, lock);
              } else {
                executeErrorState(methodId,
                    vxmsShared,
                    errorMethodHandler,
                    requestMessage,
                    encoder, errorHandler,
                    onFailureRespond,
                    responseDeliveryOptions,
                    retryCount, timeout,
                    circuitBreakerTimeout, executor, lock);
              }
            })), methodId, vxmsShared, errorMethodHandler, requestMessage, encoder, errorHandler,
        onFailureRespond, responseDeliveryOptions, retryCount, timeout, circuitBreakerTimeout,
        executor);
  }


  private static  void executeInitialState(String targetId,
      Object message,
      ThrowableFutureBiConsumer>, T> objectFunction,
      DeliveryOptions requestDeliveryOptions,
      String methodId,
      VxmsShared vxmsShared,
      Consumer errorMethodHandler,
      Message requestMessage,
      Encoder encoder,
      Consumer errorHandler,
      ThrowableErrorConsumer onFailureRespond,
      DeliveryOptions responseDeliveryOptions,
      int retryCount, long timeout, long circuitBreakerTimeout, RecursiveExecutor executor,
      RetryExecutor retry, Lock lock, Counter counter) {
    int incrementCounter = retryCount + 1;
    counter.addAndGet(Integer.valueOf(incrementCounter).longValue(),
        rHandler -> executeDefaultState(targetId,
            message,
            objectFunction,
            requestDeliveryOptions,
            methodId, vxmsShared,
            errorMethodHandler,
            requestMessage,
            encoder,
            errorHandler,
            onFailureRespond,
            responseDeliveryOptions,
            retryCount, timeout, circuitBreakerTimeout, executor, retry, lock));
  }

  private static  void executeDefaultState(String targetId,
      Object message,
      ThrowableFutureBiConsumer>, T> objectFunction,
      DeliveryOptions requestDeliveryOptions,
      String methodId,
      VxmsShared vxmsShared,
      Consumer errorMethodHandler,
      Message requestMessage,
      Encoder encoder,
      Consumer errorHandler,
      ThrowableErrorConsumer onFailureRespond,
      DeliveryOptions responseDeliveryOptions,
      int retryCount, long timeout, long circuitBreakerTimeout, RecursiveExecutor executor,
      RetryExecutor retry, Lock lock) {
    Optional.ofNullable(lock).ifPresent(Lock::release);
    final Vertx vertx = vxmsShared.getVertx();
    vertx.
        eventBus().
        send(targetId, message, requestDeliveryOptions,
            event ->
                createSupplierAndExecute(targetId,
                    message,
                    objectFunction,
                    requestDeliveryOptions,
                    methodId, vxmsShared,
                    errorMethodHandler,
                    requestMessage,
                    encoder, errorHandler,
                    onFailureRespond,
                    responseDeliveryOptions,
                    retryCount, timeout,
                    circuitBreakerTimeout, executor, retry, event));
  }

  private static  void executeErrorState(String methodId,
      VxmsShared vxmsShared,
      Consumer errorMethodHandler,
      Message requestMessage,
      Encoder encoder,
      Consumer errorHandler,
      ThrowableErrorConsumer onFailureRespond,
      DeliveryOptions responseDeliveryOptions,
      int retryCount, long timeout, long circuitBreakerTimeout, RecursiveExecutor executor,
      Lock lock) {
    final Throwable cause = Future.failedFuture("circuit open").cause();
    handleError(methodId,
        vxmsShared, errorMethodHandler,
        requestMessage, encoder,
        errorHandler, onFailureRespond,
        responseDeliveryOptions,
        retryCount, timeout,
        circuitBreakerTimeout, executor, lock, cause);
  }

  private static  void createSupplierAndExecute(String targetId,
      Object message,
      ThrowableFutureBiConsumer>, T> objectFunction,
      DeliveryOptions requestDeliveryOptions,
      String methodId,
      VxmsShared vxmsShared,
      Consumer errorMethodHandler,
      Message requestMessage,
      Encoder encoder,
      Consumer errorHandler,
      ThrowableErrorConsumer onFailureRespond,
      DeliveryOptions responseDeliveryOptions,
      int retryCount, long timeout, long circuitBreakerTimeout,
      RecursiveExecutor executor, RetryExecutor retry, AsyncResult> event) {
    final ThrowableFutureConsumer objectConsumer = createSupplier(objectFunction, event);
    if (circuitBreakerTimeout == NO_TIMEOUT) {
      statelessExecution(targetId,
          message,
          objectFunction,
          requestDeliveryOptions,
          methodId, vxmsShared,
          errorMethodHandler,
          requestMessage,
          encoder,
          errorHandler,
          onFailureRespond,
          responseDeliveryOptions,
          retryCount, timeout, circuitBreakerTimeout, executor, retry, event, objectConsumer);
    } else {
      statefulExecution(targetId,
          message,
          objectFunction,
          requestDeliveryOptions,
          methodId, vxmsShared,
          errorMethodHandler,
          requestMessage,
          encoder,
          errorHandler,
          onFailureRespond,
          responseDeliveryOptions,
          retryCount, timeout, circuitBreakerTimeout, executor, retry, event, objectConsumer);

    }


  }

  private static  void statelessExecution(String targetId,
      Object message,
      ThrowableFutureBiConsumer>, T> objectFunction,
      DeliveryOptions requestDeliveryOptions,
      String methodId,
      VxmsShared vxmsShared,
      Consumer errorMethodHandler,
      Message requestMessage,
      Encoder encoder,
      Consumer errorHandler,
      ThrowableErrorConsumer onFailureRespond,
      DeliveryOptions responseDeliveryOptions,
      int retryCount, long timeout, long circuitBreakerTimeout, RecursiveExecutor executor,
      RetryExecutor retry,
      AsyncResult> event,
      ThrowableFutureConsumer objectConsumer) {
    if (!event.failed() || (event.failed() && retryCount <= 0)) {
      executor.execute(methodId,
          vxmsShared, event.cause(),
          errorMethodHandler,
          requestMessage,
          objectConsumer,
          encoder, errorHandler,
          onFailureRespond,
          responseDeliveryOptions,
          retryCount, timeout, circuitBreakerTimeout);
    } else if (event.failed() && retryCount > 0) {
      retryFunction(targetId,
          message,
          objectFunction,
          requestDeliveryOptions,
          methodId, vxmsShared,
          event.cause(), errorMethodHandler,
          requestMessage, encoder,
          errorHandler, onFailureRespond,
          responseDeliveryOptions, retryCount,
          timeout, circuitBreakerTimeout, retry);
    }
  }

  private static  void statefulExecution(String targetId,
      Object message,
      ThrowableFutureBiConsumer>, T> objectFunction,
      DeliveryOptions requestDeliveryOptions,
      String methodId,
      VxmsShared vxmsShared,
      Consumer errorMethodHandler,
      Message requestMessage,
      Encoder encoder,
      Consumer errorHandler,
      ThrowableErrorConsumer onFailureRespond,
      DeliveryOptions responseDeliveryOptions,
      int retryCount, long timeout, long circuitBreakerTimeout, RecursiveExecutor executor,
      RetryExecutor retry, AsyncResult> event,
      ThrowableFutureConsumer objectConsumer) {
    if (event.succeeded()) {
      executor.execute(methodId, vxmsShared, event.cause(), errorMethodHandler, requestMessage,
          objectConsumer,
          encoder, errorHandler, onFailureRespond, responseDeliveryOptions, retryCount, timeout,
          circuitBreakerTimeout);
    } else {
      statefulErrorHandling(targetId,
          message, objectFunction,
          requestDeliveryOptions,
          methodId, vxmsShared,
          event.cause(),
          errorMethodHandler,
          requestMessage,
          encoder, errorHandler,
          onFailureRespond,
          responseDeliveryOptions,
          retryCount, timeout,
          circuitBreakerTimeout,
          executor, retry, event);
    }
  }

  private static  void statefulErrorHandling(String targetId,
      Object message,
      ThrowableFutureBiConsumer>, T> objectFunction,
      DeliveryOptions requestDeliveryOptions,
      String methodId,
      VxmsShared vxmsShared,
      Throwable t,
      Consumer errorMethodHandler,
      Message requestMessage,
      Encoder encoder,
      Consumer errorHandler,
      ThrowableErrorConsumer onFailureRespond,
      DeliveryOptions responseDeliveryOptions,
      int retryCount, long timeout, long circuitBreakerTimeout, RecursiveExecutor executor,
      RetryExecutor retry, AsyncResult> event) {

    executeLocked((lock, counter) ->
            decrementAndExecute(counter,
                valHandler -> {
                  if (valHandler.succeeded()) {
                    long count = valHandler.result();
                    if (count <= 0) {
                      openCircuitAndHandleError(methodId,
                          vxmsShared,
                          errorMethodHandler,
                          requestMessage,
                          encoder,
                          errorHandler,
                          onFailureRespond,
                          responseDeliveryOptions,
                          retryCount, timeout,
                          circuitBreakerTimeout,
                          executor, event,
                          lock, counter);
                    } else {
                      lock.release();
                      retryFunction(targetId,
                          message,
                          objectFunction,
                          requestDeliveryOptions,
                          methodId,
                          vxmsShared, t,
                          errorMethodHandler,
                          requestMessage,
                          encoder,
                          errorHandler,
                          onFailureRespond,
                          responseDeliveryOptions,
                          retryCount, timeout,
                          circuitBreakerTimeout, retry);
                    }
                  } else {
                    final Throwable cause = valHandler.cause();
                    handleError(methodId, vxmsShared, errorMethodHandler, requestMessage,
                        encoder, errorHandler, onFailureRespond, responseDeliveryOptions, retryCount,
                        timeout, circuitBreakerTimeout, executor, lock, cause);
                  }
                }), methodId, vxmsShared, errorMethodHandler, requestMessage,
        encoder, errorHandler, onFailureRespond, responseDeliveryOptions, retryCount, timeout,
        circuitBreakerTimeout, executor);


  }

  private static void decrementAndExecute(Counter counter,
      Handler> asyncResultHandler) {
    counter.decrementAndGet(asyncResultHandler);
  }


  private static  void executeLocked(LockedConsumer consumer,
      String methodId,
      VxmsShared vxmsShared,
      Consumer errorMethodHandler,
      Message requestMessage,
      Encoder encoder,
      Consumer errorHandler,
      ThrowableErrorConsumer onFailureRespond,
      DeliveryOptions responseDeliveryOptions,
      int retryCount, long timeout, long circuitBreakerTimeout, RecursiveExecutor executor) {
    final LocalData sharedData = vxmsShared.getLocalData();
    sharedData.getLockWithTimeout(methodId, DEFAULT_LOCK_TIMEOUT, lockHandler -> {
      if (lockHandler.succeeded()) {
        final Lock lock = lockHandler.result();
        sharedData.getCounter(methodId, resultHandler -> {
          if (resultHandler.succeeded()) {
            consumer.execute(lock, resultHandler.result());
          } else {
            final Throwable cause = resultHandler.cause();
            handleError(methodId,
                vxmsShared,
                errorMethodHandler,
                requestMessage,
                encoder, errorHandler,
                onFailureRespond,
                responseDeliveryOptions,
                retryCount, timeout,
                circuitBreakerTimeout,
                executor, lock, cause);
          }
        });
      } else {
        final Throwable cause = lockHandler.cause();
        handleError(methodId,
            vxmsShared,
            errorMethodHandler,
            requestMessage,
            encoder, errorHandler,
            onFailureRespond,
            responseDeliveryOptions,
            retryCount, timeout,
            circuitBreakerTimeout,
            executor, null, cause);
      }

    });
  }

  private static  void openCircuitAndHandleError(String methodId,
      VxmsShared vxmsShared,
      Consumer errorMethodHandler,
      Message requestMessage,
      Encoder encoder,
      Consumer errorHandler,
      ThrowableErrorConsumer onFailureRespond,
      DeliveryOptions responseDeliveryOptions,
      int retryCount, long timeout, long circuitBreakerTimeout,
      RecursiveExecutor executor, AsyncResult> event,
      Lock lock, Counter counter) {
    resetLockTimer(vxmsShared, retryCount, circuitBreakerTimeout, counter);
    lockAndHandle(counter, val -> {
      final Throwable cause = event.cause();
      handleError(methodId,
          vxmsShared,
          errorMethodHandler,
          requestMessage,
          encoder,
          errorHandler,
          onFailureRespond,
          responseDeliveryOptions,
          retryCount,
          timeout,
          circuitBreakerTimeout,
          executor, lock, cause);
    });
  }

  private static void lockAndHandle(Counter counter,
      Handler> asyncResultHandler) {
    counter.addAndGet(LOCK_VALUE, asyncResultHandler);
  }

  private static void resetLockTimer(VxmsShared vxmsShared, int retryCount,
      long circuitBreakerTimeout, Counter counter) {
    final Vertx vertx = vxmsShared.getVertx();
    vertx.setTimer(circuitBreakerTimeout,
        timer -> counter.addAndGet(Integer.valueOf(retryCount + 1).longValue(), val -> {
        }));
  }

  private static  void handleError(String methodId,
      VxmsShared vxmsShared,
      Consumer errorMethodHandler,
      Message requestMessage,
      Encoder encoder,
      Consumer errorHandler,
      ThrowableErrorConsumer onFailureRespond,
      DeliveryOptions responseDeliveryOptions,
      int retryCount, long timeout, long circuitBreakerTimeout, RecursiveExecutor executor,
      Lock lock, Throwable cause) {
    Optional.ofNullable(lock).ifPresent(Lock::release);
    final ThrowableFutureConsumer failConsumer = (future) -> future.fail(cause);
    executor.execute(methodId,
        vxmsShared,
        cause,
        errorMethodHandler,
        requestMessage,
        failConsumer,
        encoder,
        errorHandler,
        onFailureRespond,
        responseDeliveryOptions,
        retryCount,
        timeout, circuitBreakerTimeout);
  }

  private static  void retryFunction(String targetId,
      Object message,
      ThrowableFutureBiConsumer>, T> objectFunction,
      DeliveryOptions requestDeliveryOptions,
      String methodId,
      VxmsShared vxmsShared,
      Throwable t,
      Consumer errorMethodHandler,
      Message requestMessage,
      Encoder encoder,
      Consumer errorHandler,
      ThrowableErrorConsumer onFailureRespond,
      DeliveryOptions responseDeliveryOptions,
      int retryCount, long timeout, long circuitBreakerTimeout, RetryExecutor retry) {
    ResponseExecution.handleError(errorHandler, t);
    retry.execute(targetId,
        message,
        objectFunction,
        requestDeliveryOptions,
        methodId, vxmsShared, t,
        errorMethodHandler,
        requestMessage,
        null,
        encoder,
        errorHandler,
        onFailureRespond,
        responseDeliveryOptions,
        retryCount,
        timeout,
        circuitBreakerTimeout);
  }

  private static  ThrowableFutureConsumer createSupplier(
      ThrowableFutureBiConsumer>, T> objectFunction,
      AsyncResult> event) {
    return (future) -> {

      if (event.failed()) {
        future.fail(event.cause());
      } else {
        objectFunction.accept(event, future);
      }

    };
  }

  private interface LockedConsumer {

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