/** {@inheritDoc} */
    public void run() {
      logger.log(Level.FINE, "Starting a consumer for transactions");
      notifyThreadJoining();

      try {
        while (true) {
          // wait for the next task, at which point we may get
          // interrupted and should therefore return
          ScheduledTaskImpl task = (ScheduledTaskImpl) (backingQueue.getNextTask(true));

          // run the task, checking if it completed
          if (executeTask(task, false, true)) {
            // if it's a recurring task, schedule the next run
            if (task.isRecurring()) {
              long nextStart = task.getStartTime() + task.getPeriod();
              task = new ScheduledTaskImpl(task, nextStart);
              backingQueue.addTask(task);
            }
            // if it has dependent tasks, schedule the next one
            TaskQueueImpl queue = (TaskQueueImpl) (task.getTaskQueue());
            if (queue != null) {
              queue.scheduleNextTask();
            }
          }
        }
      } catch (InterruptedException ie) {
        if (logger.isLoggable(Level.FINE)) {
          logger.logThrow(Level.FINE, ie, "Consumer is finishing");
        }
      } catch (Exception e) {
        // this should never happen, since running the task should
        // never throw an exception that isn't handled
        logger.logThrow(Level.SEVERE, e, "Fatal error for consumer");
      } finally {
        notifyThreadLeaving();
      }
    }