From 306e6adca5ae8af802ee9fb0cd4865fe2d58759a Mon Sep 17 00:00:00 2001 From: FrancesTwisk Date: Thu, 25 Sep 2025 12:17:15 +0200 Subject: [PATCH 1/4] Added a new DownloadProgres class to Get.java to display a progressbar. --- .../org/apache/tools/ant/taskdefs/Get.java | 194 ++++++++++++++---- 1 file changed, 155 insertions(+), 39 deletions(-) diff --git a/src/main/org/apache/tools/ant/taskdefs/Get.java b/src/main/org/apache/tools/ant/taskdefs/Get.java index 03915cc500..8bc1f7311f 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Get.java +++ b/src/main/org/apache/tools/ant/taskdefs/Get.java @@ -16,13 +16,7 @@ * */ -package org.apache.tools.ant.taskdefs; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; +import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; @@ -56,6 +50,7 @@ * * @since Ant 1.1 * + * * @ant.task category="network" */ public class Get extends Task { @@ -77,6 +72,7 @@ public class Get extends Task { private final Resources sources = new Resources(); private File destination; // required private boolean verbose = false; + private boolean progressbar = false; private boolean quiet = false; private boolean useTimestamp = false; //off by default private boolean ignoreErrors = false; @@ -90,9 +86,9 @@ public class Get extends Task { private boolean tryGzipEncoding = false; private Mapper mapperElement = null; private String userAgent = - System.getProperty(MagicNames.HTTP_AGENT_PROPERTY, - DEFAULT_AGENT_PREFIX + "/" - + Main.getShortAntVersion()); + System.getProperty(MagicNames.HTTP_AGENT_PROPERTY, + DEFAULT_AGENT_PREFIX + "/" + + Main.getShortAntVersion()); // Store headers as key/value pair without duplicate in keys private Map headers = new LinkedHashMap<>(); @@ -127,17 +123,17 @@ public void execute() throws BuildException { final String[] d = mapper.mapFileName(source.toString()); if (d == null) { log("skipping " + r + " - mapper can't handle it", - Project.MSG_WARN); + Project.MSG_WARN); continue; } if (d.length == 0) { log("skipping " + r + " - mapper returns no file name", - Project.MSG_WARN); + Project.MSG_WARN); continue; } if (d.length > 1) { log("skipping " + r + " - mapper returns multiple file" - + " names", Project.MSG_WARN); + + " names", Project.MSG_WARN); continue; } dest = new File(destination, d[0]); @@ -149,6 +145,9 @@ public void execute() throws BuildException { DownloadProgress progress = null; if (verbose) { progress = new VerboseProgress(System.out); + } else if (progressbar) { + PrintStream rawOut = new PrintStream(new FileOutputStream(FileDescriptor.out), true); + progress = new ProgressBarProgress(rawOut); } //execute the get @@ -205,11 +204,11 @@ public boolean doGet(final int logLevel, final DownloadProgress progress) */ public boolean doGet(final URL source, final File dest, final int logLevel, DownloadProgress progress) - throws IOException { + throws IOException { if (dest.exists() && skipExisting) { log("Destination already exists (skipping): " - + dest.getAbsolutePath(), logLevel); + + dest.getAbsolutePath(), logLevel); return true; } @@ -234,8 +233,8 @@ public boolean doGet(final URL source, final File dest, final int logLevel, } final GetThread getThread = new GetThread(source, dest, - hasTimestamp, timestamp, progress, - logLevel, userAgent); + hasTimestamp, timestamp, progress, + logLevel, userAgent); getThread.setDaemon(true); getProject().registerThreadTask(getThread, this); getThread.start(); @@ -243,12 +242,12 @@ public boolean doGet(final URL source, final File dest, final int logLevel, getThread.join(maxTime * 1000); } catch (final InterruptedException ie) { log("interrupted waiting for GET to finish", - Project.MSG_VERBOSE); + Project.MSG_VERBOSE); } if (getThread.isAlive()) { final String msg = "The GET operation took longer than " + maxTime - + " seconds, stopping it."; + + " seconds, stopping it."; if (ignoreErrors) { log(msg); } @@ -280,13 +279,13 @@ private void checkAttributes() { if (sources.size() == 0) { throw new BuildException("at least one source is required", - getLocation()); + getLocation()); } for (final Resource r : sources) { final URLProvider up = r.as(URLProvider.class); if (up == null) { throw new BuildException( - "Only URLProvider resources are supported", getLocation()); + "Only URLProvider resources are supported", getLocation()); } } @@ -295,15 +294,15 @@ private void checkAttributes() { } if (destination.exists() && sources.size() > 1 - && !destination.isDirectory()) { + && !destination.isDirectory()) { throw new BuildException( - "The specified destination is not a directory", getLocation()); + "The specified destination is not a directory", getLocation()); } if (destination.exists() && !destination.canWrite()) { throw new BuildException("Can't write to " - + destination.getAbsolutePath(), - getLocation()); + + destination.getAbsolutePath(), + getLocation()); } if (sources.size() > 1 && !destination.exists()) { @@ -347,6 +346,15 @@ public void setVerbose(final boolean v) { verbose = v; } + /** + * If true, show progress bar with download information as percentage. + * + * @param v if "true" then show progress bar + */ + public void setProgressBar(final boolean v) { + progressbar = v; + } + /** * If true, set default log level to Project.MSG_ERR. * @@ -440,8 +448,8 @@ public void setMaxTime(final long maxTime) { public void setRetries(final int r) { if (r <= 0) { log("Setting retries to " + r - + " will make the task not even try to reach the URI at all", - Project.MSG_WARN); + + " will make the task not even try to reach the URI at all", + Project.MSG_WARN); } this.numberRetries = r; } @@ -522,7 +530,7 @@ public void addConfiguredHeader(Header header) { public Mapper createMapper() throws BuildException { if (mapperElement != null) { throw new BuildException("Cannot define more than one mapper", - getLocation()); + getLocation()); } mapperElement = new Mapper(getProject()); return mapperElement; @@ -541,7 +549,7 @@ public void add(final FileNameMapper fileNameMapper) { * Provide this for Backward Compatibility. */ protected static class Base64Converter - extends org.apache.tools.ant.util.Base64Converter { + extends org.apache.tools.ant.util.Base64Converter { } /** @@ -551,9 +559,9 @@ protected static class Base64Converter */ public static boolean isMoved(final int responseCode) { return responseCode == HttpURLConnection.HTTP_MOVED_PERM - || responseCode == HttpURLConnection.HTTP_MOVED_TEMP - || responseCode == HttpURLConnection.HTTP_SEE_OTHER - || responseCode == HTTP_MOVED_TEMP; + || responseCode == HttpURLConnection.HTTP_MOVED_TEMP + || responseCode == HttpURLConnection.HTTP_SEE_OTHER + || responseCode == HTTP_MOVED_TEMP; } /** @@ -654,8 +662,109 @@ public void endDownload() { } } - private class GetThread extends Thread { + public class ProgressBarProgress implements DownloadProgress { + private long contentLength; + private long downloadedBytes; + private long lastDownloadedBytes; + private final PrintStream out; + private long lastTime; + private int previousLineLength = 0; + + public ProgressBarProgress(PrintStream out) { + this.out = out; + } + + public void setContentLength(long contentLength) { + this.contentLength = contentLength; + } + + @Override + public void beginDownload() { + downloadedBytes = 0; + lastDownloadedBytes = 0; + lastTime = System.nanoTime(); + } + + @Override + public void onTick() { + int downloadedBytesPercentage = (int) ((downloadedBytes * 100) / contentLength); + printProgressBar(downloadedBytesPercentage, calculateDownloadSpeed()); + } + + public double calculateDownloadSpeed() { + long bytesSinceLast = downloadedBytes - lastDownloadedBytes; + long elapsedTime = System.nanoTime() - lastTime; + return (elapsedTime > 0) + ? (bytesSinceLast / (elapsedTime / 1_000_000_000.0)) + : 0.0; + } + + public void printProgressBar(int downloadedBytesPercentage, double downloadSpeed) { + int size = 50; + String iconLeftBoundary = "["; + String iconDownloadedBytes = "="; + String iconRemainingBytes = " "; + String iconArrow = ">"; + String iconRightBoundary = "]"; + + int downloadedBytesLength = size * downloadedBytesPercentage / 100; + + StringBuilder progressBar = new StringBuilder(iconLeftBoundary); + for (int i = 0; i < size; i++) { + if (i == downloadedBytesLength) { + progressBar.append(iconArrow); + } else if (i < downloadedBytesLength) { + progressBar.append(iconDownloadedBytes); + } else { + progressBar.append(iconRemainingBytes); + } + } + progressBar.append(iconRightBoundary); + + String progressBarLine = "Downloading..." + progressBar + " " + + downloadedBytesPercentage + "% | " + + remainingTime(downloadSpeed, downloadedBytes, contentLength) + + " | " + readableDownloadSpeed(downloadSpeed); + + int spaces = 0; + + if (previousLineLength - progressBarLine.length() > 0) { + spaces = previousLineLength - progressBarLine.length(); + } + + out.print("\r" + progressBarLine + " ".repeat(spaces)); + out.flush(); + previousLineLength = progressBarLine.length(); + } + + private String readableDownloadSpeed(double downloadSpeed) { + int unit = 1024; + if (downloadSpeed < unit) { + return String.format("%.1f B/S", downloadSpeed); + } else { + int exponent = (int) (Math.log(downloadSpeed) / Math.log(unit)); + String pre = "KMG".charAt(exponent - 1) + ""; + return String.format("%.1f %sB/s", downloadSpeed / Math.pow(unit, exponent), pre); + } + } + private String remainingTime(double downloadSpeed, double downloadedBytes, double contentLength) { + double remainingTime = (contentLength - downloadedBytes) / downloadSpeed; + return String.format("%.0f seconds remaining", remainingTime); + } + + @Override + public void endDownload() { + out.println(); + out.flush(); + } + + public void addBytes(long bytes) { + downloadedBytes += bytes; + } + } + + private class GetThread extends Thread { private final URL source; private final File dest; private final boolean hasTimestamp; @@ -748,7 +857,6 @@ private boolean redirectionAllowed(final URL aSource, final URL aDest) { private URLConnection openConnection(final URL aSource, final String uname, final String pword) throws IOException { - // set up the URL connection final URLConnection connection = aSource.openConnection(); // modify the headers @@ -801,15 +909,15 @@ private URLConnection openConnection(final URL aSource, final String uname, final String newLocation = httpConnection.getHeaderField("Location"); final String message = aSource + (responseCode == HttpURLConnection.HTTP_MOVED_PERM ? " permanently" - : "") + " moved to " + newLocation; + : "") + " moved to " + newLocation; log(message, logLevel); final URL newURL = new URL(aSource, newLocation); if (!redirectionAllowed(aSource, newURL)) { return null; } return openConnection(newURL, - authenticateOnRedirect ? uname : null, - authenticateOnRedirect ? pword : null); + authenticateOnRedirect ? uname : null, + authenticateOnRedirect ? pword : null); } // next test for a 304 result (HTTP only) final long lastModified = httpConnection.getLastModified(); @@ -863,10 +971,15 @@ private boolean downloadFile() throws IOException { } if (tryGzipEncoding - && GZIP_CONTENT_ENCODING.equals(connection.getContentEncoding())) { + && GZIP_CONTENT_ENCODING.equals(connection.getContentEncoding())) { is = new GZIPInputStream(is); } + if (progress instanceof ProgressBarProgress) { + long contentLength = connection.getContentLengthLong(); + ((ProgressBarProgress) progress).setContentLength(contentLength); + } + os = Files.newOutputStream(dest.toPath()); progress.beginDownload(); boolean finished = false; @@ -875,6 +988,9 @@ private boolean downloadFile() throws IOException { int length; while (!isInterrupted() && (length = is.read(buffer)) >= 0) { os.write(buffer, 0, length); + if (progress instanceof ProgressBarProgress) { + ((ProgressBarProgress) progress).addBytes(length); + } progress.onTick(); } finished = !isInterrupted(); @@ -898,7 +1014,7 @@ private void updateTimeStamp() { if (verbose) { final Date t = new Date(remoteTimestamp); log("last modified = " + t.toString() - + ((remoteTimestamp == 0) ? " - using current time instead" : ""), logLevel); + + ((remoteTimestamp == 0) ? " - using current time instead" : ""), logLevel); } if (remoteTimestamp != 0) { FILE_UTILS.setFileLastModified(dest, remoteTimestamp); From 08d0e07f0317d20d2f6e0b9a9ab52e3d43093a1e Mon Sep 17 00:00:00 2001 From: FrancesTwisk Date: Wed, 15 Oct 2025 11:20:59 +0200 Subject: [PATCH 2/4] Remove unnecessary spaces/indenting etc. --- .../org/apache/tools/ant/taskdefs/Get.java | 129 +++++++++++++++++- 1 file changed, 124 insertions(+), 5 deletions(-) diff --git a/src/main/org/apache/tools/ant/taskdefs/Get.java b/src/main/org/apache/tools/ant/taskdefs/Get.java index 03915cc500..679ea47652 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Get.java +++ b/src/main/org/apache/tools/ant/taskdefs/Get.java @@ -18,11 +18,7 @@ package org.apache.tools.ant.taskdefs; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; +import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; @@ -77,6 +73,7 @@ public class Get extends Task { private final Resources sources = new Resources(); private File destination; // required private boolean verbose = false; + private boolean progressbar = false; private boolean quiet = false; private boolean useTimestamp = false; //off by default private boolean ignoreErrors = false; @@ -149,6 +146,9 @@ public void execute() throws BuildException { DownloadProgress progress = null; if (verbose) { progress = new VerboseProgress(System.out); + } else if (progressbar) { + PrintStream rawOut = new PrintStream(new FileOutputStream(FileDescriptor.out), true); + progress = new ProgressBarProgress(rawOut); } //execute the get @@ -347,6 +347,15 @@ public void setVerbose(final boolean v) { verbose = v; } + /** + * If true, show progress bar with download information as percentage. + * + * @param v if "true" then show progress bar + */ + public void setProgressBar(final boolean v) { + progressbar = v; + } + /** * If true, set default log level to Project.MSG_ERR. * @@ -654,6 +663,108 @@ public void endDownload() { } } + public class ProgressBarProgress implements DownloadProgress { + private long contentLength; + private long downloadedBytes; + private long lastDownloadedBytes; + private final PrintStream out; + private long lastTime; + private int previousLineLength = 0; + + public ProgressBarProgress(PrintStream out) { + this.out = out; + } + + public void setContentLength(long contentLength) { + this.contentLength = contentLength; + } + + @Override + public void beginDownload() { + downloadedBytes = 0; + lastDownloadedBytes = 0; + lastTime = System.nanoTime(); + } + + @Override + public void onTick() { + int downloadedBytesPercentage = (int) ((downloadedBytes * 100) / contentLength); + printProgressBar(downloadedBytesPercentage, calculateDownloadSpeed()); + } + + public double calculateDownloadSpeed() { + long bytesSinceLast = downloadedBytes - lastDownloadedBytes; + long elapsedTime = System.nanoTime() - lastTime; + return (elapsedTime > 0) + ? (bytesSinceLast / (elapsedTime / 1_000_000_000.0)) + : 0.0; + } + + public void printProgressBar(int downloadedBytesPercentage, double downloadSpeed) { + int size = 50; + String iconLeftBoundary = "["; + String iconDownloadedBytes = "="; + String iconRemainingBytes = " "; + String iconArrow = ">"; + String iconRightBoundary = "]"; + + int downloadedBytesLength = size * downloadedBytesPercentage / 100; + + StringBuilder progressBar = new StringBuilder(iconLeftBoundary); + for (int i = 0; i < size; i++) { + if (i == downloadedBytesLength) { + progressBar.append(iconArrow); + } else if (i < downloadedBytesLength) { + progressBar.append(iconDownloadedBytes); + } else { + progressBar.append(iconRemainingBytes); + } + } + progressBar.append(iconRightBoundary); + + String progressBarLine = "Downloading..." + progressBar + " " + + downloadedBytesPercentage + "% | " + + remainingTime(downloadSpeed, downloadedBytes, contentLength) + + " | " + readableDownloadSpeed(downloadSpeed); + + int spaces = 0; + + if (previousLineLength - progressBarLine.length() > 0) { + spaces = previousLineLength - progressBarLine.length(); + } + + out.print("\r" + progressBarLine + " ".repeat(spaces)); + out.flush(); + previousLineLength = progressBarLine.length(); + } + + private String readableDownloadSpeed(double downloadSpeed) { + int unit = 1024; + if (downloadSpeed < unit) { + return String.format("%.1f B/S", downloadSpeed); + } else { + int exponent = (int) (Math.log(downloadSpeed) / Math.log(unit)); + String pre = "KMG".charAt(exponent - 1) + ""; + return String.format("%.1f %sB/s", downloadSpeed / Math.pow(unit, exponent), pre); + } + } + + private String remainingTime(double downloadSpeed, double downloadedBytes, double contentLength) { + double remainingTime = (contentLength - downloadedBytes) / downloadSpeed; + return String.format("%.0f seconds remaining", remainingTime); + } + + @Override + public void endDownload() { + out.println(); + out.flush(); + } + + public void addBytes(long bytes) { + downloadedBytes += bytes; + } + } + private class GetThread extends Thread { private final URL source; @@ -867,6 +978,11 @@ private boolean downloadFile() throws IOException { is = new GZIPInputStream(is); } + if (progress instanceof ProgressBarProgress) { + long contentLength = connection.getContentLengthLong(); + ((ProgressBarProgress) progress).setContentLength(contentLength); + } + os = Files.newOutputStream(dest.toPath()); progress.beginDownload(); boolean finished = false; @@ -875,6 +991,9 @@ private boolean downloadFile() throws IOException { int length; while (!isInterrupted() && (length = is.read(buffer)) >= 0) { os.write(buffer, 0, length); + if (progress instanceof ProgressBarProgress) { + ((ProgressBarProgress) progress).addBytes(length); + } progress.onTick(); } finished = !isInterrupted(); From 030d3969ea7bab2e25af605bc43c3681718f8b1d Mon Sep 17 00:00:00 2001 From: FrancesTwisk Date: Wed, 15 Oct 2025 13:35:31 +0200 Subject: [PATCH 3/4] Make unchanging variables final and replace .repeat(). --- src/main/org/apache/tools/ant/taskdefs/Get.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/org/apache/tools/ant/taskdefs/Get.java b/src/main/org/apache/tools/ant/taskdefs/Get.java index 679ea47652..9635d013ce 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Get.java +++ b/src/main/org/apache/tools/ant/taskdefs/Get.java @@ -701,12 +701,12 @@ public double calculateDownloadSpeed() { } public void printProgressBar(int downloadedBytesPercentage, double downloadSpeed) { - int size = 50; - String iconLeftBoundary = "["; - String iconDownloadedBytes = "="; - String iconRemainingBytes = " "; - String iconArrow = ">"; - String iconRightBoundary = "]"; + final int size = 50; + final String iconLeftBoundary = "["; + final String iconDownloadedBytes = "="; + final String iconRemainingBytes = " "; + final String iconArrow = ">"; + final String iconRightBoundary = "]"; int downloadedBytesLength = size * downloadedBytesPercentage / 100; @@ -727,13 +727,13 @@ public void printProgressBar(int downloadedBytesPercentage, double downloadSpeed + remainingTime(downloadSpeed, downloadedBytes, contentLength) + " | " + readableDownloadSpeed(downloadSpeed); - int spaces = 0; + int spaces = 1; if (previousLineLength - progressBarLine.length() > 0) { spaces = previousLineLength - progressBarLine.length(); } - out.print("\r" + progressBarLine + " ".repeat(spaces)); + out.print("\r" + progressBarLine + String.format("%" + spaces + "s", "")); out.flush(); previousLineLength = progressBarLine.length(); } From c44e8df448dac855ab1881aa3d287a299878fc67 Mon Sep 17 00:00:00 2001 From: FrancesTwisk Date: Thu, 6 Nov 2025 15:32:22 +0100 Subject: [PATCH 4/4] Replace VerboseProgress as verbose default. --- .../org/apache/tools/ant/taskdefs/Get.java | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/main/org/apache/tools/ant/taskdefs/Get.java b/src/main/org/apache/tools/ant/taskdefs/Get.java index 9635d013ce..00ad624ef8 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Get.java +++ b/src/main/org/apache/tools/ant/taskdefs/Get.java @@ -72,8 +72,7 @@ public class Get extends Task { private final Resources sources = new Resources(); private File destination; // required - private boolean verbose = false; - private boolean progressbar = false; + private String verbose = ""; private boolean quiet = false; private boolean useTimestamp = false; //off by default private boolean ignoreErrors = false; @@ -144,11 +143,11 @@ public void execute() throws BuildException { //set up logging final int logLevel = Project.MSG_INFO; DownloadProgress progress = null; - if (verbose) { - progress = new VerboseProgress(System.out); - } else if (progressbar) { + if (verbose.equals("true")) { PrintStream rawOut = new PrintStream(new FileOutputStream(FileDescriptor.out), true); progress = new ProgressBarProgress(rawOut); + } else if (verbose.equals("dots")) { + progress = new VerboseProgress(System.out); } //execute the get @@ -226,7 +225,7 @@ public boolean doGet(final URL source, final File dest, final int logLevel, boolean hasTimestamp = false; if (useTimestamp && dest.exists()) { timestamp = dest.lastModified(); - if (verbose) { + if (!verbose.isEmpty()) { final Date t = new Date(timestamp); log("local file date : " + t.toString(), logLevel); } @@ -343,19 +342,10 @@ public void setDest(final File dest) { * * @param v if "true" then be verbose */ - public void setVerbose(final boolean v) { + public void setVerbose(final String v) { verbose = v; } - /** - * If true, show progress bar with download information as percentage. - * - * @param v if "true" then show progress bar - */ - public void setProgressBar(final boolean v) { - progressbar = v; - } - /** * If true, set default log level to Project.MSG_ERR. * @@ -1014,7 +1004,7 @@ private boolean downloadFile() throws IOException { private void updateTimeStamp() { final long remoteTimestamp = connection.getLastModified(); - if (verbose) { + if (!verbose.isEmpty()) { final Date t = new Date(remoteTimestamp); log("last modified = " + t.toString() + ((remoteTimestamp == 0) ? " - using current time instead" : ""), logLevel);