@Override
  public Void call() {
    ZimbraLog.clearContext();
    ZimbraLog.addMboxToContext(getMailboxId());
    ZimbraLog.datasource.debug("Running scheduled import for DataSource %s", getDataSourceId());
    Mailbox mbox = null;

    try {
      // Look up mailbox, account and data source
      mbox = MailboxManager.getInstance().getMailboxById(getMailboxId());
      Account account = mbox.getAccount();
      ZimbraLog.addAccountNameToContext(account.getName());
      Provisioning prov = Provisioning.getInstance();
      DataSource ds = prov.get(account, Key.DataSourceBy.id, getDataSourceId());
      if (ds != null) {
        ZimbraLog.addDataSourceNameToContext(ds.getName());
        if (!ds.isEnabled()) {
          ZimbraLog.datasource.info("DataSource is disabled.  Cancelling future tasks.");
          DataSourceManager.cancelTask(mbox, getDataSourceId());
          return null;
        }

        // Do the work
        DataSourceManager.importData(ds);
      } else {
        ZimbraLog.datasource.info(
            "DataSource %s was deleted.  Cancelling future tasks.", getDataSourceId());
        DataSourceManager.cancelTask(mbox, getDataSourceId());
      }
    } catch (ServiceException e) {
      ZimbraLog.datasource.warn("Scheduled DataSource import failed.", e);
      return null;
    }

    ZimbraLog.clearContext();
    return null;
  }
  /**
   * Collects data about external IMAP accounts from XML
   *
   * @param is
   * @return ExternalIMAPAccount list of account data containers
   * @throws DocumentException
   * @throws IOException
   * @throws ServiceException
   */
  private Map<accountState, List<ExternalIMAPAccount>> parseExternalIMAPAccounts(
      org.dom4j.Element root, ZimbraSoapContext zsc)
      throws DocumentException, IOException, ServiceException {
    List<ExternalIMAPAccount> idleAccts = new ArrayList<ExternalIMAPAccount>();
    List<ExternalIMAPAccount> runningAccts = new ArrayList<ExternalIMAPAccount>();
    List<ExternalIMAPAccount> finishedAccts = new ArrayList<ExternalIMAPAccount>();
    Map<accountState, List<ExternalIMAPAccount>> accts =
        new HashMap<accountState, List<ExternalIMAPAccount>>();
    Provisioning prov = Provisioning.getInstance();

    for (Iterator userIter = root.elementIterator(AdminExtConstants.E_User); userIter.hasNext(); ) {
      org.dom4j.Element elUser = (org.dom4j.Element) userIter.next();
      String userEmail = "";
      String userLogin = "";
      String userPassword = "";

      for (Iterator userPropsIter = elUser.elementIterator(); userPropsIter.hasNext(); ) {
        org.dom4j.Element el = (org.dom4j.Element) userPropsIter.next();
        /*
         * We support <ExchangeMail> element for compatibility with
         * desktop based Exchange Migration utility <RemoteEmailAddress>
         * takes prevalence over <ExchangeMail> element
         */
        if (AdminExtConstants.E_ExchangeMail.equalsIgnoreCase(el.getName())) {
          userEmail = el.getTextTrim();
        }
        if (AdminExtConstants.E_remoteEmail.equalsIgnoreCase(el.getName())) {
          userEmail = el.getTextTrim();
        }
        if (AdminExtConstants.E_remoteIMAPLogin.equalsIgnoreCase(el.getName())) {
          userLogin = el.getTextTrim();
        }
        if (AdminExtConstants.E_remoteIMAPPassword.equalsIgnoreCase(el.getName())) {
          userPassword = el.getTextTrim();
        }
      }

      Account localAccount = null;
      try {
        localAccount = prov.get(AccountBy.name, userEmail, zsc.getAuthToken());
      } catch (ServiceException se) {
        ZimbraLog.gal.warn("error looking up account", se);
      }

      if (localAccount == null) {
        throw AccountServiceException.NO_SUCH_ACCOUNT(userEmail);
      }
      checkAdminLoginAsRight(zsc, prov, localAccount);

      if (userLogin.length() == 0) {
        userLogin = userEmail;
      }
      // Check if an import is running on this account already
      boolean isRunning = false;
      boolean hasRun = false;
      List<DataSource> sources = Provisioning.getInstance().getAllDataSources(localAccount);
      for (DataSource ds : sources) {
        if (ZimbraBulkProvisionExt.IMAP_IMPORT_DS_NAME.equalsIgnoreCase(ds.getName())
            && ds.getType() == DataSourceType.imap
            && ds.isImportOnly()
            && "1".equalsIgnoreCase(ds.getAttr(Provisioning.A_zimbraDataSourceFolderId))) {
          ImportStatus importStatus = DataSourceManager.getImportStatus(localAccount, ds);
          if (!isRunning) {
            synchronized (importStatus) {
              isRunning = importStatus.isRunning();
              hasRun = importStatus.hasRun();
            }
          }

          if (!hasRun) {
            synchronized (importStatus) {
              hasRun = importStatus.hasRun();
            }
          }

          if (!isRunning) {
            runningAccts.add(
                new ExternalIMAPAccount(userEmail, userLogin, userPassword, localAccount));
            break;
          } else if (hasRun) {
            finishedAccts.add(
                new ExternalIMAPAccount(userEmail, userLogin, userPassword, localAccount));
            break;
          }
        }
      }
      if (!isRunning && !hasRun) {
        idleAccts.add(new ExternalIMAPAccount(userEmail, userLogin, userPassword, localAccount));
      }
    }
    accts.put(accountState.idle, idleAccts);
    accts.put(accountState.running, runningAccts);
    accts.put(accountState.finished, finishedAccts);
    return accts;
  }
  private Map<accountState, List<ExternalIMAPAccount>> getZimbraAccounts(
      Element request, ZimbraSoapContext zsc) throws ServiceException {
    List<Element> acctElems = request.listElements(AdminConstants.E_ACCOUNT);
    Provisioning prov = Provisioning.getInstance();
    List<ExternalIMAPAccount> idleAccts = new ArrayList<ExternalIMAPAccount>();
    List<ExternalIMAPAccount> runningAccts = new ArrayList<ExternalIMAPAccount>();
    List<ExternalIMAPAccount> finishedAccts = new ArrayList<ExternalIMAPAccount>();
    Map<accountState, List<ExternalIMAPAccount>> accts =
        new HashMap<accountState, List<ExternalIMAPAccount>>();
    if (acctElems != null && acctElems.size() > 0) {
      for (Element elem : acctElems) {
        String emailAddress = elem.getAttribute(AdminConstants.A_NAME);
        Account localAccount = null;
        try {
          localAccount = prov.get(AccountBy.name, emailAddress);
        } catch (ServiceException se) {
          ZimbraLog.gal.warn("error looking up account", se);
        }

        if (localAccount == null) {
          throw AccountServiceException.NO_SUCH_ACCOUNT(emailAddress);
        }
        checkAdminLoginAsRight(zsc, prov, localAccount);
        // Check if an import is running on this account already
        boolean isRunning = false;
        boolean hasRun = false;
        List<DataSource> sources = Provisioning.getInstance().getAllDataSources(localAccount);
        for (DataSource ds : sources) {
          if (ZimbraBulkProvisionExt.IMAP_IMPORT_DS_NAME.equalsIgnoreCase(ds.getName())
              && ds.getType() == DataSourceType.imap
              && ds.isImportOnly()
              && "1".equalsIgnoreCase(ds.getAttr(Provisioning.A_zimbraDataSourceFolderId))) {
            ImportStatus importStatus = DataSourceManager.getImportStatus(localAccount, ds);
            if (!isRunning) {
              synchronized (importStatus) {
                isRunning = importStatus.isRunning();
                hasRun = importStatus.hasRun();
              }
            }

            if (!hasRun) {
              synchronized (importStatus) {
                hasRun = importStatus.hasRun();
              }
            }

            if (!isRunning) {
              runningAccts.add(
                  new ExternalIMAPAccount(emailAddress, emailAddress, "", localAccount));
              break;
            } else if (hasRun) {
              finishedAccts.add(
                  new ExternalIMAPAccount(emailAddress, emailAddress, "", localAccount));
              break;
            }
          }
        }
        if (!isRunning && !hasRun) {
          idleAccts.add(new ExternalIMAPAccount(emailAddress, emailAddress, "", localAccount));
        }
      }
    }
    accts.put(accountState.idle, idleAccts);
    accts.put(accountState.running, runningAccts);
    accts.put(accountState.finished, finishedAccts);
    return accts;
  }