package net.minecraft.launcher.updater.download; import net.minecraft.launcher.Launcher; import java.util.*; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicInteger; public class DownloadJob { private static final int MAX_ATTEMPTS_PER_FILE = 5; private static final int ASSUMED_AVERAGE_FILE_SIZE = 5242880; private final Queue<Downloadable> remainingFiles = new ConcurrentLinkedQueue(); private final List<Downloadable> allFiles = Collections.synchronizedList(new ArrayList()); private final List<Downloadable> failures = Collections.synchronizedList(new ArrayList()); private final List<Downloadable> successful = Collections.synchronizedList(new ArrayList()); private final List<ProgressContainer> progressContainers = Collections.synchronizedList(new ArrayList()); private final DownloadListener listener; private final String name; private final boolean ignoreFailures; private final AtomicInteger remainingThreads = new AtomicInteger(); private boolean started; public DownloadJob(String name, boolean ignoreFailures, DownloadListener listener, Collection<Downloadable> files) { this.name = name; this.ignoreFailures = ignoreFailures; this.listener = listener; if (files != null) addDownloadables(files); } public DownloadJob(String name, boolean ignoreFailures, DownloadListener listener) { this(name, ignoreFailures, listener, null); } public void addDownloadables(Collection<Downloadable> downloadables) { if (this.started) throw new IllegalStateException("Cannot add to download job that has already started"); this.allFiles.addAll(downloadables); this.remainingFiles.addAll(downloadables); for (Downloadable downloadable : downloadables) { this.progressContainers.add(downloadable.getMonitor()); if (downloadable.getExpectedSize() == 0L) downloadable.getMonitor().setTotal(5242880L); else { downloadable.getMonitor().setTotal(downloadable.getExpectedSize()); } downloadable.getMonitor().setJob(this); } } public void addDownloadables(Downloadable[] downloadables) { if (this.started) throw new IllegalStateException("Cannot add to download job that has already started"); for (Downloadable downloadable : downloadables) { this.allFiles.add(downloadable); this.remainingFiles.add(downloadable); this.progressContainers.add(downloadable.getMonitor()); if (downloadable.getExpectedSize() == 0L) downloadable.getMonitor().setTotal(5242880L); else { downloadable.getMonitor().setTotal(downloadable.getExpectedSize()); } downloadable.getMonitor().setJob(this); } } public void startDownloading(ThreadPoolExecutor executorService) { if (this.started) throw new IllegalStateException("Cannot start download job that has already started"); this.started = true; if (this.allFiles.isEmpty()) { Launcher.getInstance().println("Download job '" + this.name + "' skipped as there are no files to download"); this.listener.onDownloadJobFinished(this); } else { int threads = executorService.getMaximumPoolSize(); this.remainingThreads.set(threads); Launcher.getInstance().println("Download job '" + this.name + "' started (" + threads + " threads, " + this.allFiles.size() + " files)"); for (int i = 0; i < threads; i++) executorService.submit(new Runnable() { public void run() { DownloadJob.this.popAndDownload(); } }); } } private void popAndDownload() { Downloadable downloadable; while ((downloadable = (Downloadable) this.remainingFiles.poll()) != null) { if (downloadable.getNumAttempts() > 5) { if (!this.ignoreFailures) this.failures.add(downloadable); Launcher.getInstance().println("Gave up trying to download " + downloadable.getUrl() + " for job '" + this.name + "'"); } else { try { String result = downloadable.download(); this.successful.add(downloadable); Launcher.getInstance().println("Finished downloading " + downloadable.getTarget() + " for job '" + this.name + "'" + ": " + result); } catch (Throwable t) { Launcher.getInstance().println("Couldn't download " + downloadable.getUrl() + " for job '" + this.name + "'", t); this.remainingFiles.add(downloadable); } } } if (this.remainingThreads.decrementAndGet() <= 0) this.listener.onDownloadJobFinished(this); } public boolean shouldIgnoreFailures() { return this.ignoreFailures; } public boolean isStarted() { return this.started; } public boolean isComplete() { return (this.started) && (this.remainingFiles.isEmpty()) && (this.remainingThreads.get() == 0); } public int getFailures() { return this.failures.size(); } public int getSuccessful() { return this.successful.size(); } public String getName() { return this.name; } public void updateProgress() { this.listener.onDownloadJobProgressChanged(this); } public float getProgress() { float current = 0.0F; float total = 0.0F; synchronized (this.progressContainers) { for (ProgressContainer progress : this.progressContainers) { total += (float) progress.getTotal(); current += (float) progress.getCurrent(); } } float result = -1.0F; if (total > 0.0F) result = current / total; return result; } }