@Override
  protected Void doInBackground() throws Exception {
    // protein identification id
    Collection<Comparable> protIdentIds = controller.getIdentificationIds();

    // protein identification id and accession buffer
    Map<Comparable, String> accBuffer = new LinkedHashMap<Comparable, String>();

    // protein map
    Map<String, Protein> proteins = new HashMap<String, Protein>();

    try {
      // iterate over each protein
      for (Comparable protIdentId : protIdentIds) {
        // get mapped protein accession
        String protAcc = controller.getProteinAccession(protIdentId);
        String protAccVersion = controller.getProteinAccessionVersion(protIdentId);
        String database = controller.getSearchDatabase(protIdentId);
        AccessionResolver resolver = new AccessionResolver(protAcc, protAccVersion, database, true);
        String mappedProtAcc = resolver.isValidAccession() ? resolver.getAccession() : null;

        if (mappedProtAcc != null) {
          // get existing protein details
          Protein protDetails =
              PrideInspectorCacheManager.getInstance().getProteinDetails(mappedProtAcc);
          if (protDetails != null) {
            proteins.put(mappedProtAcc, protDetails);
          }

          accBuffer.put(protIdentId, mappedProtAcc);
          if (accBuffer.size() == MAX_BATCH_DOWNLOAD_SIZE) {
            // fetch and publish protein details
            fetchAndPublish(accBuffer, proteins);

            // clear accession buffer
            accBuffer.clear();

            // clear protein map
            proteins = new HashMap<String, Protein>();
          }
        }
      }

      // this is important for cancelling
      if (Thread.interrupted()) {
        throw new InterruptedException();
      }

      if (!accBuffer.isEmpty() || !proteins.isEmpty()) {
        fetchAndPublish(accBuffer, proteins);
      }
    } catch (InterruptedException e) {
      logger.warn("Protein name download has been cancelled");
    }

    return null;
  }