public static final AggregateUtils.DocumentFetchResult fetchFormList(
      ServerConnectionInfo serverInfo,
      boolean alwaysResetCredentials,
      TerminationFuture terminationFuture)
      throws XmlDocumentFetchException {

    String urlString = serverInfo.getUrl();
    if (urlString.endsWith("/")) {
      urlString = urlString + "formList";
    } else {
      urlString = urlString + "/formList";
    }

    DocumentDescription formListDescription =
        new DocumentDescription(
            "Unable to fetch formList: ",
            "Unable to fetch formList.",
            "form list",
            terminationFuture);
    AggregateUtils.DocumentFetchResult result =
        AggregateUtils.getXmlDocument(
            urlString, serverInfo, alwaysResetCredentials, formListDescription, null);
    return result;
  }
  private void downloadSubmission(
      File formInstancesDir,
      DatabaseUtils formDatabase,
      BriefcaseFormDefinition lfd,
      FormStatus fs,
      String uri)
      throws Exception {

    if (formDatabase.hasRecordedInstance(uri) != null) {
      logger.info("already present - skipping fetch: " + uri);
      return;
    }

    String formId = lfd.getSubmissionKey(uri);

    if (isCancelled()) {
      fs.setStatusString("aborting fetch of submission...", true);
      EventBus.publish(new FormStatusEvent(fs));
      throw new SubmissionDownloadException("Transfer cancelled by user.");
    }

    String baseUrl = serverInfo.getUrl() + "/view/downloadSubmission";

    Map<String, String> params = new HashMap<String, String>();
    params.put("formId", formId);
    String fullUrl = WebUtils.createLinkWithProperties(baseUrl, params);
    AggregateUtils.DocumentFetchResult result;
    try {
      DocumentDescription submissionDescription =
          new DocumentDescription(
              "Fetch of a submission failed.  Detailed error: ",
              "Fetch of a submission failed.",
              "submission",
              terminationFuture);
      result =
          AggregateUtils.getXmlDocument(fullUrl, serverInfo, false, submissionDescription, null);
    } catch (XmlDocumentFetchException e) {
      throw new SubmissionDownloadException(e.getMessage());
    }

    // and parse the document...
    SubmissionManifest submissionManifest;
    try {
      submissionManifest = XmlManipulationUtils.parseDownloadSubmissionResponse(result.doc);
    } catch (ParsingException e) {
      throw new SubmissionDownloadException(e.getMessage());
    }

    String msg = "Fetched instanceID=" + submissionManifest.instanceID;
    logger.info(msg);

    if (FileSystemUtils.hasFormSubmissionDirectory(
        formInstancesDir, submissionManifest.instanceID)) {
      // create instance directory...
      File instanceDir =
          FileSystemUtils.assertFormSubmissionDirectory(
              formInstancesDir, submissionManifest.instanceID);

      // fetch attachments
      for (MediaFile m : submissionManifest.attachmentList) {
        downloadMediaFileIfChanged(instanceDir, m, fs);
      }

      // write submission file -- we rely on instanceId being unique...
      File submissionFile = new File(instanceDir, "submission.xml");
      OutputStreamWriter fo = new OutputStreamWriter(new FileOutputStream(submissionFile), "UTF-8");
      fo.write(submissionManifest.submissionXml);
      fo.close();

      // if we get here and it was a legacy server (0.9.x), we don't
      // actually know whether the submission was complete.  Otherwise,
      // if we get here, we know that this is a completed submission
      // (because it was in /view/submissionList) and that we safely
      // copied it into the storage area (because we didn't get any
      // exceptions).
      if (serverInfo.isOpenRosaServer()) {
        formDatabase.assertRecordedInstanceDirectory(uri, instanceDir);
      }
    } else {
      // create instance directory...
      File instanceDir =
          FileSystemUtils.assertFormSubmissionDirectory(
              formInstancesDir, submissionManifest.instanceID);

      // fetch attachments
      for (MediaFile m : submissionManifest.attachmentList) {
        downloadMediaFileIfChanged(instanceDir, m, fs);
      }

      // write submission file
      File submissionFile = new File(instanceDir, "submission.xml");
      OutputStreamWriter fo = new OutputStreamWriter(new FileOutputStream(submissionFile), "UTF-8");
      fo.write(submissionManifest.submissionXml);
      fo.close();

      // if we get here and it was a legacy server (0.9.x), we don't
      // actually know whether the submission was complete.  Otherwise,
      // if we get here, we know that this is a completed submission
      // (because it was in /view/submissionList) and that we safely
      // copied it into the storage area (because we didn't get any
      // exceptions).
      if (serverInfo.isOpenRosaServer()) {
        formDatabase.assertRecordedInstanceDirectory(uri, instanceDir);
      }
    }
  }
  private boolean downloadAllSubmissionsForForm(
      File formInstancesDir,
      DatabaseUtils formDatabase,
      BriefcaseFormDefinition lfd,
      FormStatus fs) {
    boolean allSuccessful = true;

    RemoteFormDefinition fd = (RemoteFormDefinition) fs.getFormDefinition();

    int count = 1;
    String baseUrl = serverInfo.getUrl() + "/view/submissionList";

    String oldWebsafeCursorString = "not-empty";
    String websafeCursorString = "";
    for (; !oldWebsafeCursorString.equals(websafeCursorString); ) {
      if (isCancelled()) {
        fs.setStatusString("aborting fetching submissions...", true);
        EventBus.publish(new FormStatusEvent(fs));
        return false;
      }

      fs.setStatusString("retrieving next chunk of instances from server...", true);
      EventBus.publish(new FormStatusEvent(fs));

      Map<String, String> params = new HashMap<String, String>();
      params.put("numEntries", Integer.toString(MAX_ENTRIES));
      params.put("formId", fd.getFormId());
      params.put("cursor", websafeCursorString);
      String fullUrl = WebUtils.createLinkWithProperties(baseUrl, params);
      oldWebsafeCursorString = websafeCursorString; // remember what we had...
      AggregateUtils.DocumentFetchResult result;
      try {
        DocumentDescription submissionChunkDescription =
            new DocumentDescription(
                "Fetch of submission download chunk failed.  Detailed error: ",
                "Fetch of submission download chunk failed.",
                "submission download chunk",
                terminationFuture);
        result =
            AggregateUtils.getXmlDocument(
                fullUrl, serverInfo, false, submissionChunkDescription, null);
      } catch (XmlDocumentFetchException e) {
        fs.setStatusString(
            "NOT ALL SUBMISSIONS RETRIEVED: Error fetching list of submissions: " + e.getMessage(),
            false);
        EventBus.publish(new FormStatusEvent(fs));
        return false;
      }

      SubmissionDownloadChunk chunk;
      try {
        chunk = XmlManipulationUtils.parseSubmissionDownloadListResponse(result.doc);
      } catch (ParsingException e) {
        fs.setStatusString(
            "NOT ALL SUBMISSIONS RETRIEVED: Error parsing the list of submissions: "
                + e.getMessage(),
            false);
        EventBus.publish(new FormStatusEvent(fs));
        return false;
      }
      websafeCursorString = chunk.websafeCursorString;

      for (String uri : chunk.uriList) {
        if (isCancelled()) {
          fs.setStatusString("aborting fetching submissions...", true);
          EventBus.publish(new FormStatusEvent(fs));
          return false;
        }

        try {
          fs.setStatusString("fetching instance " + count++ + " ...", true);
          EventBus.publish(new FormStatusEvent(fs));

          downloadSubmission(formInstancesDir, formDatabase, lfd, fs, uri);
        } catch (Exception e) {
          e.printStackTrace();
          allSuccessful = false;
          fs.setStatusString(
              "SUBMISSION NOT RETRIEVED: Error fetching submission uri: "
                  + uri
                  + " details: "
                  + e.getMessage(),
              false);
          EventBus.publish(new FormStatusEvent(fs));
          // but try to get the next one...
        }
      }
    }
    return allSuccessful;
  }