You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@ant.apache.org by bo...@apache.org on 2008/09/26 15:55:26 UTC
svn commit: r699324 - in /ant/core/trunk: WHATSNEW
docs/manual/CoreTasks/get.html
src/main/org/apache/tools/ant/taskdefs/Get.java
Author: bodewig
Date: Fri Sep 26 06:55:26 2008
New Revision: 699324
URL: http://svn.apache.org/viewvc?rev=699324&view=rev
Log:
Add a maxtime option to <get> to allow it to escape hanging downloads. PR 45181.
Modified:
ant/core/trunk/WHATSNEW
ant/core/trunk/docs/manual/CoreTasks/get.html
ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Get.java
Modified: ant/core/trunk/WHATSNEW
URL: http://svn.apache.org/viewvc/ant/core/trunk/WHATSNEW?rev=699324&r1=699323&r2=699324&view=diff
==============================================================================
--- ant/core/trunk/WHATSNEW (original)
+++ ant/core/trunk/WHATSNEW Fri Sep 26 06:55:26 2008
@@ -390,6 +390,10 @@
mapper that matches.
Bugzilla Report 44873
+ * <get> has a new maxtime attribute that terminates downloads that
+ are taking too long.
+ Bugzilla Report 45181.
+
Changes from Ant 1.7.0 TO Ant 1.7.1
=============================================
Modified: ant/core/trunk/docs/manual/CoreTasks/get.html
URL: http://svn.apache.org/viewvc/ant/core/trunk/docs/manual/CoreTasks/get.html?rev=699324&r1=699323&r2=699324&view=diff
==============================================================================
--- ant/core/trunk/docs/manual/CoreTasks/get.html (original)
+++ ant/core/trunk/docs/manual/CoreTasks/get.html Fri Sep 26 06:55:26 2008
@@ -95,7 +95,14 @@
<td valign="top">password: required </td>
<td align="center" valign="top">if username is set</td>
</tr>
-
+ <tr>
+ <td valign="top">maxtime</td>
+ <td valign="top">Maximum time in seconds the download may take,
+ otherwise it will be interrupted and treated like a download
+ error. <em>Since Ant 1.8.0</em></td>
+ <td align="center" valign="top">No: default 0 which means no
+ maximum time</td>
+ </tr>
</table>
<h3>Examples</h3>
<pre> <get src="http://ant.apache.org/" dest="help/index.html"/></pre>
Modified: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Get.java
URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Get.java?rev=699324&r1=699323&r2=699324&view=diff
==============================================================================
--- ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Get.java (original)
+++ ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Get.java Fri Sep 26 06:55:26 2008
@@ -27,6 +27,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.URL;
@@ -56,8 +57,7 @@
private boolean ignoreErrors = false;
private String uname = null;
private String pword = null;
-
-
+ private long maxTime = 0;
/**
* Does the work.
@@ -121,128 +121,32 @@
hasTimestamp = true;
}
- //set up the URL connection
- URLConnection connection = source.openConnection();
- //modify the headers
- //NB: things like user authentication could go in here too.
- if (hasTimestamp) {
- connection.setIfModifiedSince(timestamp);
- }
- // prepare Java 1.1 style credentials
- if (uname != null || pword != null) {
- String up = uname + ":" + pword;
- String encoding;
- //we do not use the sun impl for portability,
- //and always use our own implementation for consistent
- //testing
- Base64Converter encoder = new Base64Converter();
- encoding = encoder.encode(up.getBytes());
- connection.setRequestProperty ("Authorization",
- "Basic " + encoding);
- }
-
- //connect to the remote site (may take some time)
- connection.connect();
- //next test for a 304 result (HTTP only)
- if (connection instanceof HttpURLConnection) {
- HttpURLConnection httpConnection
- = (HttpURLConnection) connection;
- long lastModified = httpConnection.getLastModified();
- if (httpConnection.getResponseCode()
- == HttpURLConnection.HTTP_NOT_MODIFIED
- || (lastModified != 0 && hasTimestamp
- && timestamp >= lastModified)) {
- //not modified so no file download. just return
- //instead and trace out something so the user
- //doesn't think that the download happened when it
- //didn't
- log("Not modified - so not downloaded", logLevel);
- return false;
- }
- // test for 401 result (HTTP only)
- if (httpConnection.getResponseCode()
- == HttpURLConnection.HTTP_UNAUTHORIZED) {
- String message = "HTTP Authorization failure";
- if (ignoreErrors) {
- log(message, logLevel);
- return false;
- } else {
- throw new BuildException(message);
- }
- }
-
- }
-
- //REVISIT: at this point even non HTTP connections may
- //support the if-modified-since behaviour -we just check
- //the date of the content and skip the write if it is not
- //newer. Some protocols (FTP) don't include dates, of
- //course.
-
- InputStream is = null;
- for (int i = 0; i < NUMBER_RETRIES; i++) {
- //this three attempt trick is to get round quirks in different
- //Java implementations. Some of them take a few goes to bind
- //property; we ignore the first couple of such failures.
- try {
- is = connection.getInputStream();
- break;
- } catch (IOException ex) {
- log("Error opening connection " + ex, logLevel);
- }
- }
- if (is == null) {
- log("Can't get " + source + " to " + dest, logLevel);
- if (ignoreErrors) {
- return false;
- }
- throw new BuildException("Can't get " + source + " to " + dest,
- getLocation());
- }
-
- FileOutputStream fos = new FileOutputStream(dest);
- progress.beginDownload();
- boolean finished = false;
+ GetThread getThread = new GetThread(hasTimestamp, timestamp, progress,
+ logLevel);
+ getThread.setDaemon(true);
+ getProject().registerThreadTask(getThread, this);
+ getThread.start();
try {
- byte[] buffer = new byte[BIG_BUFFER_SIZE];
- int length;
- while ((length = is.read(buffer)) >= 0) {
- fos.write(buffer, 0, length);
- progress.onTick();
- }
- finished = true;
- } finally {
- FileUtils.close(fos);
- FileUtils.close(is);
-
- // we have started to (over)write dest, but failed.
- // Try to delete the garbage we'd otherwise leave
- // behind.
- if (!finished) {
- dest.delete();
- }
+ getThread.join(maxTime * 1000);
+ } catch (InterruptedException ie) {
+ log("interrupted waiting for GET to finish",
+ Project.MSG_VERBOSE);
}
- progress.endDownload();
- //if (and only if) the use file time option is set, then
- //the saved file now has its timestamp set to that of the
- //downloaded file
- if (useTimestamp) {
- long remoteTimestamp = connection.getLastModified();
- if (verbose) {
- Date t = new Date(remoteTimestamp);
- log("last modified = " + t.toString()
- + ((remoteTimestamp == 0)
- ? " - using current time instead"
- : ""), logLevel);
+ if (getThread.isAlive()) {
+ String msg = "The GET operation took longer than " + maxTime
+ + " seconds, stopping it.";
+ if (ignoreErrors) {
+ log(msg);
}
- if (remoteTimestamp != 0) {
- FILE_UTILS.setFileLastModified(dest, remoteTimestamp);
+ getThread.closeStreams();
+ if (!ignoreErrors) {
+ throw new BuildException(msg);
}
+ return false;
}
- //successful download
- return true;
+ return getThread.wasSuccessful();
}
/**
@@ -353,6 +257,16 @@
}
/**
+ * The time in seconds the download is allowed to take before
+ * being terminated.
+ *
+ * @since ant 1.8.0
+ */
+ public void setMaxTime(long maxTime) {
+ this.maxTime = maxTime;
+ }
+
+ /**
* Interface implemented for reporting
* progess of downloading.
*/
@@ -446,4 +360,183 @@
}
}
+ private class GetThread extends Thread {
+ private final boolean hasTimestamp;
+ private final long timestamp;
+ private final DownloadProgress progress;
+ private final int logLevel;
+
+ private boolean success = false;
+ private IOException ioexception = null;
+ private BuildException exception = null;
+ private InputStream is = null;
+ private OutputStream os = null;
+
+ GetThread(boolean h, long t, DownloadProgress p, int l) {
+ hasTimestamp = h;
+ timestamp = t;
+ progress = p;
+ logLevel = l;
+ }
+
+ public void run() {
+ try {
+ success = get();
+ } catch (IOException ioex) {
+ ioexception = ioex;
+ } catch (BuildException bex) {
+ exception = bex;
+ }
+ }
+
+ private boolean get() throws IOException, BuildException {
+ //set up the URL connection
+ URLConnection connection = source.openConnection();
+ //modify the headers
+ //NB: things like user authentication could go in here too.
+ if (hasTimestamp) {
+ connection.setIfModifiedSince(timestamp);
+ }
+ // prepare Java 1.1 style credentials
+ if (uname != null || pword != null) {
+ String up = uname + ":" + pword;
+ String encoding;
+ //we do not use the sun impl for portability,
+ //and always use our own implementation for consistent
+ //testing
+ Base64Converter encoder = new Base64Converter();
+ encoding = encoder.encode(up.getBytes());
+ connection.setRequestProperty ("Authorization",
+ "Basic " + encoding);
+ }
+
+ //connect to the remote site (may take some time)
+ connection.connect();
+ //next test for a 304 result (HTTP only)
+ if (connection instanceof HttpURLConnection) {
+ HttpURLConnection httpConnection
+ = (HttpURLConnection) connection;
+ long lastModified = httpConnection.getLastModified();
+ if (httpConnection.getResponseCode()
+ == HttpURLConnection.HTTP_NOT_MODIFIED
+ || (lastModified != 0 && hasTimestamp
+ && timestamp >= lastModified)) {
+ //not modified so no file download. just return
+ //instead and trace out something so the user
+ //doesn't think that the download happened when it
+ //didn't
+ log("Not modified - so not downloaded", logLevel);
+ return false;
+ }
+ // test for 401 result (HTTP only)
+ if (httpConnection.getResponseCode()
+ == HttpURLConnection.HTTP_UNAUTHORIZED) {
+ String message = "HTTP Authorization failure";
+ if (ignoreErrors) {
+ log(message, logLevel);
+ return false;
+ } else {
+ throw new BuildException(message);
+ }
+ }
+
+ }
+
+ //REVISIT: at this point even non HTTP connections may
+ //support the if-modified-since behaviour -we just check
+ //the date of the content and skip the write if it is not
+ //newer. Some protocols (FTP) don't include dates, of
+ //course.
+
+ for (int i = 0; i < NUMBER_RETRIES; i++) {
+ //this three attempt trick is to get round quirks in different
+ //Java implementations. Some of them take a few goes to bind
+ //property; we ignore the first couple of such failures.
+ try {
+ is = connection.getInputStream();
+ break;
+ } catch (IOException ex) {
+ log("Error opening connection " + ex, logLevel);
+ }
+ }
+ if (is == null) {
+ log("Can't get " + source + " to " + dest, logLevel);
+ if (ignoreErrors) {
+ return false;
+ }
+ throw new BuildException("Can't get " + source + " to "
+ + dest, getLocation());
+ }
+
+ os = new FileOutputStream(dest);
+ progress.beginDownload();
+ boolean finished = false;
+ try {
+ byte[] buffer = new byte[BIG_BUFFER_SIZE];
+ int length;
+ while (!isInterrupted() && (length = is.read(buffer)) >= 0) {
+ os.write(buffer, 0, length);
+ progress.onTick();
+ }
+ finished = !isInterrupted();
+ } finally {
+ FileUtils.close(os);
+ FileUtils.close(is);
+
+ // we have started to (over)write dest, but failed.
+ // Try to delete the garbage we'd otherwise leave
+ // behind.
+ if (!finished) {
+ dest.delete();
+ }
+ }
+ progress.endDownload();
+
+ //if (and only if) the use file time option is set, then
+ //the saved file now has its timestamp set to that of the
+ //downloaded file
+ if (useTimestamp) {
+ long remoteTimestamp = connection.getLastModified();
+ if (verbose) {
+ Date t = new Date(remoteTimestamp);
+ log("last modified = " + t.toString()
+ + ((remoteTimestamp == 0)
+ ? " - using current time instead"
+ : ""), logLevel);
+ }
+ if (remoteTimestamp != 0) {
+ FILE_UTILS.setFileLastModified(dest, remoteTimestamp);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Has the download completed successfully?
+ *
+ * <p>Re-throws any exception caught during executaion.</p>
+ */
+ boolean wasSuccessful() throws IOException, BuildException {
+ if (ioexception != null) {
+ throw ioexception;
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ return success;
+ }
+
+ /**
+ * Closes streams, interrupts the download, may delete the
+ * output file.
+ */
+ void closeStreams() {
+ interrupt();
+ FileUtils.close(os);
+ FileUtils.close(is);
+ if (!success && dest.exists()) {
+ dest.delete();
+ }
+ }
+ }
}