@Scheduled(cron = "0 0/1 * * * ?")
  public void execute() {

    try {
      if (lock.acquire()) {

        List<Order> orders = orderRepository.findByOrderStatus(ORDER_STATUS_AWAITING_RESTAURANT);
        if (orders.size() > 0) {
          LOGGER.info("Found " + orders.size() + " orders with status 'AWAITING_RESTAURANT'");
        }

        for (Order order : orders) {

          DateTime orderPlacedTime = order.getOrderPlacedTime();
          String orderId = order.getOrderId();
          LOGGER.info("Order id: " + orderId + " was placed at: " + orderPlacedTime);

          Restaurant restaurant = order.getRestaurant();
          DateTime now = new DateTime();
          LOGGER.info("Current time is: " + now);

          String notificationStatus = order.getOrderNotificationStatus();
          LOGGER.info("Order id: " + orderId + " notification status is: " + notificationStatus);

          // Get the time the restaurant opened
          DateTime restaurantOpenedTime = restaurant.getEarlyOpeningTime(now);

          // Don't do anything if the restaurant is not currently open
          LOGGER.info(
              "Restaurant "
                  + restaurant.getName()
                  + " opened time today is: "
                  + restaurantOpenedTime);
          if (restaurantOpenedTime == null || restaurantOpenedTime.isAfter(now)) {
            LOGGER.info(
                "Restaurant "
                    + restaurant.getName()
                    + " has not opened yet, not doing any processing");
            continue;
          }

          // Auto cancel orders which have been awaiting confirmation for too long and the
          // restaurant has been open long enough to respond
          DateTime autoCancelCutoff = new DateTime().minusMinutes(minutesBeforeAutoCancelOrder);
          LOGGER.info("Auo cancel cutoff time is: " + autoCancelCutoff);
          if (orderPlacedTime.isBefore(autoCancelCutoff)
              && restaurantOpenedTime.isBefore(autoCancelCutoff)) {
            try {
              LOGGER.info(
                  "Order id: "
                      + orderId
                      + " has been awaiting confirmation for more than "
                      + minutesBeforeAutoCancelOrder
                      + " minutes, auto-cancelling");
              orderWorkflowEngine.processAction(order, ACTION_AUTO_CANCEL);
            } catch (WorkflowException e) {
              LOGGER.error(
                  "Exception sending auto cancel email for orderId: " + order.getOrderId(), e);
              order.setOrderStatus(ORDER_STATUS_AUTO_CANCELLED);
              orderRepository.saveOrder(order);
            } catch (Exception ex) {
              exceptionHandler.handleException(ex);
            }
            continue;
          }

          // Send email to customer giving them the option to cancel the order if it has been
          // awaiting confirmation for too long
          DateTime cancellationOfferCutoff =
              new DateTime().minusMinutes(minutesBeforeSendCancellationEmail);
          LOGGER.info("Cancellation offer cutoff time is: " + cancellationOfferCutoff);
          if (!order.getCancellationOfferEmailSent()
              && orderPlacedTime.isBefore(cancellationOfferCutoff)
              && restaurantOpenedTime.isBefore(cancellationOfferCutoff)) {
            try {
              LOGGER.info(
                  "Order id: "
                      + order.getOrderId()
                      + " has been awaiting confirmation for more than "
                      + minutesBeforeSendCancellationEmail
                      + " minutes, sending email");
              orderWorkflowEngine.processAction(order, ACTION_SEND_CANCEL_OFFER_TO_CUSTOMER);
            } catch (Exception ex) {
              exceptionHandler.handleException(ex);
            }
          }

          // If there is a call in progress then do not call again
          if (order.isCallInProgress()) {
            LOGGER.info(
                "There is already a call in progress for order id: "
                    + orderId
                    + ", not placing another call");
            continue;
          }

          // If restaurant did not respond at all, do not call again
          if (NOTIFICATION_STATUS_RESTAURANT_FAILED_TO_RESPOND.equals(notificationStatus)) {
            LOGGER.info(
                "Not attempting another call for order id:"
                    + orderId
                    + " as notification status is; "
                    + notificationStatus);
            continue;
          }

          // If call was answered but order not accepted/rejected, retry after 5 minutes otherwise
          // after 1 minute
          DateTime lastCallCutoff =
              new DateTime()
                  .minusSeconds(
                      NOTIFICATION_STATUS_RESTAURANT_ANSWERED.equals(notificationStatus)
                          ? secondsBeforeRetryAnsweredCall
                          : secondsBeforeRetryCall);
          DateTime lastCallTime = order.getLastCallPlacedTime();
          if (lastCallTime == null || lastCallTime.isBefore(lastCallCutoff)) {
            try {
              orderWorkflowEngine.processAction(order, ACTION_CALL_RESTAURANT);
            } catch (Exception ex) {
              LOGGER.error(
                  "Error occurred placing order call to restaurant for order id: "
                      + order.getOrderId());
            }
          }
        }
      }
    } catch (Exception ex) {
      LOGGER.error("Error occurred processing open orders: " + ex.getMessage(), ex);
    } finally {
      lock.release();
    }
  }