Browse Source

Add a maxtime option to <get> to allow it to escape hanging downloads. PR 45181.

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@699324 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 16 years ago
parent
commit
0a679803f0
3 changed files with 222 additions and 118 deletions
  1. +4
    -0
      WHATSNEW
  2. +8
    -1
      docs/manual/CoreTasks/get.html
  3. +210
    -117
      src/main/org/apache/tools/ant/taskdefs/Get.java

+ 4
- 0
WHATSNEW View File

@@ -390,6 +390,10 @@ Other changes:
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
=============================================



+ 8
- 1
docs/manual/CoreTasks/get.html View File

@@ -95,7 +95,14 @@ plain text' authentication is used. This is only secure over an HTTPS link.
<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> &lt;get src=&quot;http://ant.apache.org/&quot; dest=&quot;help/index.html&quot;/&gt;</pre>


+ 210
- 117
src/main/org/apache/tools/ant/taskdefs/Get.java View File

@@ -27,6 +27,7 @@ import java.io.File;
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 @@ public class Get extends Task {
private boolean ignoreErrors = false;
private String uname = null;
private String pword = null;


private long maxTime = 0;

/**
* Does the work.
@@ -121,128 +121,32 @@ public class Get extends Task {
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);
}
}

GetThread getThread = new GetThread(hasTimestamp, timestamp, progress,
logLevel);
getThread.setDaemon(true);
getProject().registerThreadTask(getThread, this);
getThread.start();
try {
getThread.join(maxTime * 1000);
} catch (InterruptedException ie) {
log("interrupted waiting for GET to finish",
Project.MSG_VERBOSE);
}

//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 (getThread.isAlive()) {
String msg = "The GET operation took longer than " + maxTime
+ " seconds, stopping it.";
if (ignoreErrors) {
return false;
log(msg);
}
throw new BuildException("Can't get " + source + " to " + dest,
getLocation());
}

FileOutputStream fos = new FileOutputStream(dest);
progress.beginDownload();
boolean finished = false;
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();
}
}
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);
getThread.closeStreams();
if (!ignoreErrors) {
throw new BuildException(msg);
}
return false;
}

//successful download
return true;
return getThread.wasSuccessful();
}

/**
@@ -352,6 +256,16 @@ public class Get extends Task {
extends org.apache.tools.ant.util.Base64Converter {
}

/**
* 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 @@ public class Get extends Task {
}
}

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();
}
}
}
}

Loading…
Cancel
Save