/** Returns a list of {@code Execution}s that are still not complete for a given actionInstance */
 private List<Execution> getInCompleteExecutionsBefore(
     String actionInstanceId, Execution currentExecution) {
   List<Execution> executions = executionDao.getExecutions(actionInstanceId);
   List<Execution> incomplete = new ArrayList<>();
   for (Execution execution : executions) {
     if (execution.isBefore(currentExecution)
         && !currentExecution.getId().equals(execution.getId())
         && (execution.getStatus() == null || !execution.getStatus().isComplete())) {
       incomplete.add(execution);
     }
   }
   return incomplete;
 }
 /**
  * Cancels the currently running {@code Execution} for given {@code ActionInstance} id
  *
  * @throws ActionInstanceNotFoundException
  */
 @Override
 public void cancel(String executionId)
     throws ExecutionNotFoundException, ActionInstanceNotFoundException {
   Execution execution = executionDao.getExecution(executionId);
   if (execution != null) {
     ActionInstance actionInstance =
         actionInstanceDao.getActionInstance(execution.getActionInstanceId());
     if (actionInstance != null) {
       cancel(execution, actionInstance);
     } else {
       throw new ExecutionNotFoundException(
           String.format("No actionInstance found associated with executionId: %s", executionId));
     }
   } else {
     throw new ActionInstanceNotFoundException(
         String.format("No execution found for executionId: %s", executionId));
   }
 }
  protected void cancelLocal(final Execution execution, final ActionInstance actionInstance) {
    Execution updatedExecution = executionDao.getExecution(execution.getId());
    Status status = updatedExecution.getStatus();

    if (status != null && status.isComplete()) {
      return;
    }

    cancelService.submit(
        new Runnable() {
          @Override
          public void run() {
            try {
              Action action = actionInstance.getAction().newInstance();
              executor.cancel(action, actionInstance, execution);
              logger.info("Cancelled action {} for execution {}", action, execution);
            } catch (InstantiationException | IllegalAccessException e) {
              Status status = Status.FAILED;
              status.setMessage(
                  String.format(
                      "Exception occurred while creating an action instance of type %s: %s",
                      actionInstance.getAction(), e.getMessage()));
              execution.setEndTime(new Date());
              execution.setStatus(status);
            } catch (ExecutionException e) {
              Status status = e.getStatus() != null ? e.getStatus() : Status.FAILED;
              status.setMessage(e.getMessage());
              execution.setEndTime(new Date());
              execution.setStatus(status);
            } catch (Exception e) {
              Status status = Status.FAILED;
              status.setMessage(
                  String.format(
                      "Exception occurred while cancelling execution %s: %s",
                      execution, e.getMessage()));
              execution.setEndTime(new Date());
              execution.setStatus(status);
            } finally {
              executionDao.updateExecution(execution);
            }
          }
        });
  }
  /**
   * Executes the {@code ActionInstance}
   *
   * @throws com.netflix.scheduledactions.exceptions.ExecutionException
   */
  public Execution execute(final ActionInstance actionInstance, String initiator) {

    if (actionInstance.isDisabled()) {
      return null;
    }

    final String actionInstanceId = actionInstance.getId();
    final Execution execution = new Execution(delegateId, actionInstanceId);
    final String executionId = executionDao.createExecution(actionInstanceId, execution);

    logger.info("[{}] Created execution for actionInstance: {}", actionInstanceId, executionId);
    execution.getLogger().info(String.format("Created execution %s", executionId));

    List<Execution> previousExecutions = getInCompleteExecutionsBefore(actionInstanceId, execution);
    if (previousExecutions.size() > 0) {
      ConcurrentExecutionStrategy strategy = actionInstance.getConcurrentExecutionStrategy();
      switch (strategy) {
        case ALLOW:
          execution
              .getLogger()
              .info("Concurrent execution strategy is: ALLOW, allowing execution...");
          logger.info(
              "[{}] actionInstance concurrent execution strategy is: ALLOW, allowing execution...",
              actionInstanceId);
          break;
        case REJECT:
          Status status = Status.SKIPPED;
          status.setMessage(
              String.format(
                  "ConcurrentExecutionStrategy for ActionInstance %s is REJECT and it has incomplete executions",
                  actionInstance));
          execution.setStatus(status);
          execution.setStartTime(new Date());
          execution.setEndTime(new Date());
          logger.info(
              "[{}] actionInstance concurrent execution strategy is: REJECT, skipping execution",
              actionInstanceId);
          execution
              .getLogger()
              .info("Concurrent execution strategy is: REJECT, skipping execution");
          executionDao.updateExecution(execution);
          return execution;
        case REPLACE:
          logger.info(
              "[{}] actionInstance concurrent execution strategy is: REPLACE, cancelling previous execution(s)",
              actionInstanceId);
          cancelPreviousExecutions(actionInstance, execution);
          break;
        default:
          break;
      }
    }

    logger.info("[{}] Submitting runnable for execution: {}", actionInstanceId, executionId);

    executeService.submit(
        new Runnable() {
          @Override
          public void run() {
            try {
              Action action = newInstance(actionInstance);
              execution.getLogger().info("Calling executor.execute()...");
              logger.info(
                  "[{}] Calling executor.execute() for execution {} ...",
                  actionInstanceId,
                  executionId);
              executor.execute(action, actionInstance, execution);
            } catch (ExecutionException e) {
              Status status = e.getStatus() != null ? e.getStatus() : Status.FAILED;
              status.setMessage(e.getMessage());
              execution.setEndTime(new Date());
              execution.setStatus(status);
              execution.getLogger().error("Exception occurred while executing action", e);
            } catch (Exception e) {
              Status status = Status.FAILED;
              status.setMessage(
                  String.format(
                      "Exception occurred while executing action %s: %s",
                      actionInstance.getAction(), e.getMessage()));
              execution.getLogger().error("Exception occurred while executing action", e);
              execution.setEndTime(new Date());
              execution.setStatus(status);
            } finally {
              executionDao.updateExecution(execution);
            }
          }
        });

    return execution;
  }