@NotNull
  List<Pair<File, DownloadableFileUrl>> download(@NotNull File targetDir) throws IOException {
    List<Pair<File, DownloadableFileUrl>> downloadedFiles = new CopyOnWriteArrayList<>();
    List<Pair<File, DownloadableFileUrl>> existingFiles = new CopyOnWriteArrayList<>();

    try {
      int expectedDownloads;
      if (myContext != null) {
        expectedDownloads = myContext.getTotalExpectedDownloads();
      } else {
        expectedDownloads = 0;
      }
      myNettyClient.sendDescriptionStatusMessage(JpsBuildBundle.message("progress.downloading.0.files.text", myFilesDescriptions.size()));
      long start = System.currentTimeMillis();
      List<Future<Void>> results = new ArrayList<>();
      final AtomicLong totalSize = new AtomicLong();
      for (final DownloadableFileUrl description : myFilesDescriptions) {
        results.add(EXECUTOR_SERVICE.submit(() -> {
          if (myContext != null) myContext.checkCanceled();

          final File existing = new File(targetDir, description.getDefaultFileName());
          byte attempt = 0;
          File downloaded = null;
          while (downloaded == null) {
            attempt++;
            try {
              downloaded = downloadFile(description, existing, expectedDownloads);
            } catch (IOException e) {
              //int httpStatusCode = -1;
              //if (e  instanceof HttpRequests.HttpStatusException) {
              //  httpStatusCode = ((HttpRequests.HttpStatusException)e).getStatusCode();
              //  if (httpStatusCode == 404) {
              //    LOG.info("File not found to download " + description.getDownloadUrl());
              //    indicator.finished();
              //    return null;
              //  }
              //} else {
              //  if (Registry.is("jps.cache.check.internet.connection")){
              //    JpsServerConnectionUtil.checkDomainIsReachable("google.com");
              //    JpsServerConnectionUtil.checkDomainIsReachable("d1lc5k9lerg6km.cloudfront.net");
              //    JpsServerConnectionUtil.checkDomainRouting("d1lc5k9lerg6km.cloudfront.net");
              //  }
              //}

              // If max attempt count exceeded, rethrow exception further
              if (attempt != MAX_RETRY_COUNT) {
                //if (httpStatusCode != -1) {
                //  LOG.info("Failed to download " + description.getDownloadUrl() + " HTTP code: " + httpStatusCode + ". Attempt " + attempt + " to download file again");
                //} else {
                  LOG.info("Failed to download " + description.getDownloadUrl() + " Root cause: " + e + ". Attempt " + attempt + " to download file again");
                //}
                Thread.sleep(250);
              } else {
                throw new IOException(JpsBuildBundle.message("error.file.download.failed", description.getDownloadUrl(), e.getMessage()), e);
              }
            }
          }

          assert downloaded != null : "Download result shouldn't be NULL";
          if (FileUtil.filesEqual(downloaded, existing)) {
            existingFiles.add(Pair.create(existing, description));
          }
          else {
            totalSize.addAndGet(downloaded.length());
            downloadedFiles.add(Pair.create(downloaded, description));
          }
          return null;
        }));
      }

      for (Future<Void> result : results) {
        try {
          result.get();
        }
        catch (InterruptedException e) {
          throw new ProcessCanceledException();
        }
        catch (ExecutionException e) {
          if (e.getCause() instanceof IOException) {
            throw ((IOException)e.getCause());
          }
          if (e.getCause() instanceof ProcessCanceledException) {
            throw ((ProcessCanceledException)e.getCause());
          }
          LOG.error(e);
        }
      }
      long duration = System.currentTimeMillis() - start;
      LOG.info("Downloaded " + StringUtil.formatFileSize(totalSize.get()) + " in " + StringUtil.formatDuration(duration) +
               "(" + duration + "ms). Percentage of CDN cache hits: " + (hitsCount * 100/myFilesDescriptions.size()) + "%");

      List<Pair<File, DownloadableFileUrl>> localFiles = new ArrayList<>();
      localFiles.addAll(moveToDir(downloadedFiles, targetDir));
      localFiles.addAll(existingFiles);
      return localFiles;
    }
    catch (ProcessCanceledException | IOException e) {
      for (Pair<File, DownloadableFileUrl> pair : downloadedFiles) {
        FileUtil.delete(pair.getFirst());
      }
      throw e;
    }
  }
@NotNull
  List<Pair<File, DownloadableFileUrl>> download(@NotNull File targetDir)