download libraries in parallel (IDEA-88561)

This commit is contained in:
nik
2013-11-11 09:46:21 +04:00
parent 674de91aaf
commit c8deccfaba
2 changed files with 135 additions and 34 deletions
@@ -16,6 +16,8 @@
package com.intellij.util.download.impl;
import com.google.common.util.concurrent.AtomicDouble;
import com.intellij.concurrency.JobLauncher;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.Result;
@@ -27,6 +29,7 @@ import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.util.AbstractProgressIndicatorBase;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
@@ -34,6 +37,8 @@ import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.util.Processor;
import com.intellij.util.containers.hash.LinkedHashMap;
import com.intellij.util.download.DownloadableFileDescription;
import com.intellij.util.download.FileDownloader;
import com.intellij.util.io.UrlConnectionUtil;
@@ -49,6 +54,7 @@ import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author nik
@@ -147,49 +153,60 @@ public class FileDownloaderImpl implements FileDownloader {
@NotNull
@Override
public List<Pair<File, DownloadableFileDescription>> download(@NotNull File targetDir) throws IOException {
public List<Pair<File, DownloadableFileDescription>> download(@NotNull final File targetDir) throws IOException {
final List<Pair<File, DownloadableFileDescription>> downloadedFiles = new ArrayList<Pair<File, DownloadableFileDescription>>();
final List<Pair<File, DownloadableFileDescription>> existingFiles = new ArrayList<Pair<File, DownloadableFileDescription>>();
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
if (indicator == null) {
indicator = new EmptyProgressIndicator();
}
final ProgressIndicator parentIndicator = getProgressIndicator();
try {
for (int i = 0; i < myFileDescriptions.size(); i++) {
DownloadableFileDescription description = myFileDescriptions.get(i);
indicator.checkCanceled();
indicator.setText(IdeBundle.message("progress.downloading.0.of.1.file.text", i + 1, myFileDescriptions.size()));
final AtomicReference<IOException> ioException = new AtomicReference<IOException>();
final ConcurrentTasksProgressManager progressManager = new ConcurrentTasksProgressManager(parentIndicator, myFileDescriptions.size());
parentIndicator.setText(IdeBundle.message("progress.downloading.0.files.text", myFileDescriptions.size()));
boolean finished = JobLauncher.getInstance().invokeConcurrentlyUnderProgress(myFileDescriptions, parentIndicator, false, false, new Processor<DownloadableFileDescription>() {
@Override
public boolean process(DownloadableFileDescription description) {
SubTaskProgressIndicator indicator = progressManager.createSubTaskIndicator();
indicator.checkCanceled();
final File existing = new File(targetDir, description.getDefaultFileName());
final String url = description.getDownloadUrl();
if (url.startsWith(LIB_SCHEMA)) {
indicator.setText2(IdeBundle.message("progress.locate.file.text", description.getPresentableFileName()));
final String path = FileUtil.toSystemDependentName(StringUtil.trimStart(url, LIB_SCHEMA));
final File file = PathManager.findFileInLibDirectory(path);
existingFiles.add(Pair.create(file, description));
}
else if (url.startsWith(LocalFileSystem.PROTOCOL_PREFIX)) {
String path = FileUtil.toSystemDependentName(StringUtil.trimStart(url, LocalFileSystem.PROTOCOL_PREFIX));
File file = new File(path);
if (file.exists()) {
final File existing = new File(targetDir, description.getDefaultFileName());
final String url = description.getDownloadUrl();
if (url.startsWith(LIB_SCHEMA)) {
final String path = FileUtil.toSystemDependentName(StringUtil.trimStart(url, LIB_SCHEMA));
final File file = PathManager.findFileInLibDirectory(path);
existingFiles.add(Pair.create(file, description));
}
}
else {
File downloaded;
try {
downloaded = downloadFile(description, existing, indicator);
}
catch (IOException e) {
throw new IOException(IdeBundle.message("error.file.download.failed", description.getDownloadUrl(), e.getMessage()), e);
}
if (FileUtil.filesEqual(downloaded, existing)) {
existingFiles.add(Pair.create(existing, description));
else if (url.startsWith(LocalFileSystem.PROTOCOL_PREFIX)) {
String path = FileUtil.toSystemDependentName(StringUtil.trimStart(url, LocalFileSystem.PROTOCOL_PREFIX));
File file = new File(path);
if (file.exists()) {
existingFiles.add(Pair.create(file, description));
}
}
else {
downloadedFiles.add(Pair.create(downloaded, description));
File downloaded;
try {
downloaded = downloadFile(description, existing, indicator);
}
catch (IOException e) {
ioException.compareAndSet(null, new IOException(IdeBundle.message("error.file.download.failed", description.getDownloadUrl(),
e.getMessage()), e));
return false;
}
if (FileUtil.filesEqual(downloaded, existing)) {
existingFiles.add(Pair.create(existing, description));
}
else {
downloadedFiles.add(Pair.create(downloaded, description));
}
}
indicator.finished();
return true;
}
});
if (!finished) {
if (ioException.get() != null) {
throw ioException.get();
}
throw new ProcessCanceledException();
}
List<Pair<File, DownloadableFileDescription>> localFiles = new ArrayList<Pair<File, DownloadableFileDescription>>();
localFiles.addAll(moveToDir(downloadedFiles, targetDir));
@@ -206,6 +223,12 @@ public class FileDownloaderImpl implements FileDownloader {
}
}
@NotNull
private static ProgressIndicator getProgressIndicator() {
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
return indicator == null ? new EmptyProgressIndicator() : indicator;
}
@Nullable
private static VirtualFile chooseDirectoryForFiles(Project project, JComponent parentComponent) {
final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
@@ -330,4 +353,82 @@ public class FileDownloaderImpl implements FileDownloader {
public List<Pair<VirtualFile, DownloadableFileDescription>> downloadAndReturnWithDescriptions() {
return downloadWithProgress(myDirectoryForDownloadedFilesPath, myProject, myParentComponent);
}
private static class ConcurrentTasksProgressManager {
private final ProgressIndicator myParent;
private final int myTasksCount;
private final AtomicDouble myTotalFraction;
private LinkedHashMap<SubTaskProgressIndicator, String> myText2Stack = new LinkedHashMap<SubTaskProgressIndicator, String>();
private ConcurrentTasksProgressManager(ProgressIndicator parent, int tasksCount) {
myParent = parent;
myTasksCount = tasksCount;
myTotalFraction = new AtomicDouble();
}
public void updateFraction(double delta) {
myTotalFraction.addAndGet(delta / myTasksCount);
myParent.setFraction(myTotalFraction.get());
}
public SubTaskProgressIndicator createSubTaskIndicator() {
return new SubTaskProgressIndicator(this);
}
public void setText2(@NotNull SubTaskProgressIndicator subTask, @Nullable String text) {
if (text != null) {
myText2Stack.put(subTask, text);
myParent.setText2(text);
}
else {
myText2Stack.remove(subTask);
String prev = myText2Stack.getLastAddedValue();
if (prev != null) {
myParent.setText2(prev);
}
}
}
}
private static class SubTaskProgressIndicator extends AbstractProgressIndicatorBase {
private final AtomicDouble myFraction;
private final ConcurrentTasksProgressManager myProgressManager;
private SubTaskProgressIndicator(ConcurrentTasksProgressManager progressManager) {
myProgressManager = progressManager;
myFraction = new AtomicDouble();
}
@Override
public void setFraction(double newValue) {
double oldValue = myFraction.getAndSet(newValue);
myProgressManager.updateFraction(newValue - oldValue);
}
@Override
public void setIndeterminate(boolean indeterminate) {
if (myProgressManager.myTasksCount > 1) return;
super.setIndeterminate(indeterminate);
}
@Override
public void setText2(String text) {
myProgressManager.setText2(this, text);
}
@Override
public double getFraction() {
return myFraction.get();
}
public void finished() {
setFraction(1);
myProgressManager.setText2(this, null);
}
@Override
public boolean isCanceled() {
return super.isCanceled() || myProgressManager.myParent.isCanceled();
}
}
}
@@ -987,7 +987,7 @@ progress.download.0.title=Downloading {0}
progress.download.file.text=Downloading ''{0}'' from ''{1}''...
progress.connecting.to.download.file.text=Connecting to ''{0}''...
progress.locate.file.text=Locating ''{0}''...
progress.downloading.0.of.1.file.text=Downloading {0} of {1} {1, choice, 1#file|2#files}...
progress.downloading.0.files.text=Downloading {0} {0, choice, 1#file|2#files}...
dialog.directory.for.downloaded.files.title=Downloaded files will be copied to selected directory
error.file.download.failed=Failed to download ''{0}'': {1}