/**
   * 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;
  }