diff --git a/src/main/org/apache/tools/ant/BuildException.java b/src/main/org/apache/tools/ant/BuildException.java index b0e6c185a..5e8819824 100644 --- a/src/main/org/apache/tools/ant/BuildException.java +++ b/src/main/org/apache/tools/ant/BuildException.java @@ -132,6 +132,18 @@ public class BuildException extends RuntimeException { this.location = location; } + /** + * Constructs an exception with the given exception as + * a root cause and a location in a file. + * @param cause Exception that might have cause this one. + * @param location Location in the project file where the error occured. + */ + + public BuildException(Throwable cause, Location location) { + this(cause); + this.location = location; + } + /** * Returns the nested exception. */ diff --git a/src/main/org/apache/tools/ant/taskdefs/Expand.java b/src/main/org/apache/tools/ant/taskdefs/Expand.java index 4edee4fd5..707f1dec1 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Expand.java +++ b/src/main/org/apache/tools/ant/taskdefs/Expand.java @@ -61,7 +61,7 @@ import java.util.zip.*; * Unzip a file. * * @author costin@dnt.ro - * @author Stefan Bodewig stefan.bodewig@megabit.net + * @author Stefan Bodewig */ public class Expand extends Task { private String dest; // req @@ -81,6 +81,8 @@ public class Expand extends Task { Touch touch = (Touch) project.createTask("touch"); touch.setOwningTarget(target); + touch.setTaskName(getTaskName()); + touch.setLocation(getLocation()); File srcF=project.resolveFile(source); File dir=project.resolveFile(dest); @@ -116,7 +118,7 @@ public class Expand extends Task { } if (project.getJavaVersion() != Project.JAVA_1_1) { - touch.setFile(f.getAbsolutePath()); + touch.setFile(f); touch.setMillis(ze.getTime()); touch.touch(); } diff --git a/src/main/org/apache/tools/ant/taskdefs/Get.java b/src/main/org/apache/tools/ant/taskdefs/Get.java index 1e32ffcc7..5c3f3b5dc 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Get.java +++ b/src/main/org/apache/tools/ant/taskdefs/Get.java @@ -57,40 +57,79 @@ package org.apache.tools.ant.taskdefs; import org.apache.tools.ant.*; import java.io.*; import java.net.*; +import java.util.*; + /** - * Get a particular source. + * Get a particular file from a URL source. + * Options include verbose reporting, timestamp based fetches and controlling + * actions on failures. NB: access through a firewall only works if the whole + * Java runtime is correctly configured. * * @author costin@dnt.ro */ public class Get extends Task { - private String source; // required - private String dest; // required - private String verbose = ""; - String ignoreErrors=null; + private URL source; // required + private File dest; // required + private boolean verbose = false; + private boolean useTimestamp = false; //off by default + private boolean ignoreErrors = false; /** * Does the work. * - * @exception BuildException Thrown in unrecovrable error. + * @exception BuildException Thrown in unrecoverable error. */ public void execute() throws BuildException { try { - URL url = null; - try { - url = new URL(source); - } catch (MalformedURLException e) { - throw new BuildException(e.toString()); - } log("Getting: " + source); - File destF=new File(dest); - FileOutputStream fos = new FileOutputStream(destF); + //set the timestamp to the file date. + long timestamp=0; + + boolean hasTimestamp=false; + if(useTimestamp && dest.exists()) { + timestamp=dest.lastModified(); + if (verbose) { + Date t=new Date(timestamp); + log("local file date : "+t.toString()); + } + + 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(useTimestamp && hasTimestamp) { + connection.setIfModifiedSince(timestamp); + } + + //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; + if(httpConnection.getResponseCode()==HttpURLConnection.HTTP_NOT_MODIFIED) { + //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 didnt + log("Not modified - so not downloaded"); + return; + } + } + + //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) dont include dates, of course. + + FileOutputStream fos = new FileOutputStream(dest); InputStream is=null; for( int i=0; i< 3 ; i++ ) { try { - is = url.openStream(); + is = connection.getInputStream(); break; } catch( IOException ex ) { log( "Error opening connection " + ex ); @@ -98,8 +137,10 @@ public class Get extends Task { } if( is==null ) { log( "Can't get " + source + " to " + dest); - if( ignoreErrors != null ) return; - throw new BuildException( "Can't get " + source + " to " + dest); + if(ignoreErrors) + return; + throw new BuildException( "Can't get " + source + " to " + dest, + location); } byte[] buffer = new byte[100 * 1024]; @@ -107,25 +148,70 @@ public class Get extends Task { while ((length = is.read(buffer)) >= 0) { fos.write(buffer, 0, length); - if ("true".equals(verbose)) System.out.print("."); + if (verbose) System.out.print("."); } - if( "true".equals(verbose)) System.out.println(); + if(verbose) System.out.println(); fos.close(); is.close(); + + //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":"")); + } + if(remoteTimestamp!=0) + touchFile(dest,remoteTimestamp); + } + + + } catch (IOException ioe) { log("Error getting " + source + " to " + dest ); - if( ignoreErrors != null ) return; - throw new BuildException(ioe.toString()); + if(ignoreErrors) + return; + throw new BuildException(ioe, location); } } + + /** + * set the timestamp of a named file to a specified time. + * + * @param filename + * @param time in milliseconds since the start of the era + * @return true if it succeeded. False means that this is a + * java1.1 system and that file times can not be set + *@exception BuildException Thrown in unrecoverable error. Likely + *this comes from file access failures. + */ + protected boolean touchFile(File file, long timemillis) + throws BuildException { + + if (project.getJavaVersion() != Project.JAVA_1_1) { + Touch touch = (Touch) project.createTask("touch"); + touch.setOwningTarget(target); + touch.setTaskName(getTaskName()); + touch.setLocation(getLocation()); + touch.setFile(file); + touch.setMillis(timemillis); + touch.touch(); + return true; + + } else { + return false; + } + } /** * Set the URL. * - * @param d URL for the file. + * @param u URL for the file. */ - public void setSrc(String d) { - this.source=d; + public void setSrc(URL u) { + this.source = u; } /** @@ -133,7 +219,7 @@ public class Get extends Task { * * @param dest Path to file. */ - public void setDest(String dest) { + public void setDest(File dest) { this.dest = dest; } @@ -142,16 +228,38 @@ public class Get extends Task { * * @param v if "true" then be verbose */ - public void setVerbose(String v) { + public void setVerbose(boolean v) { verbose = v; } /** * Don't stop if get fails if set to "true". * - * @param v if "true" then be verbose + * @param v if "true" then don't report download errors up to ant */ - public void setIgnoreErrors(String v) { + public void setIgnoreErrors(boolean v) { ignoreErrors = v; } + + /** + * Use timestamps, if set to "true". + * + *

In this situation, the if-modified-since header is set so that the file is + * only fetched if it is newer than the local file (or there is no local file) + * This flag is only valid on HTTP connections, it is ignored in other cases. + * When the flag is set, the local copy of the downloaded file will also + * have its timestamp set to the remote file time. + *
+ * Note that remote files of date 1/1/1970 (GMT) are treated as 'no timestamp', and + * web servers often serve files with a timestamp in the future by replacing their timestamp + * with that of the current time. Also, inter-computer clock differences can cause no end of + * grief. + * @param v "true" to enable file time fetching + */ + public void setUseTimestamp(boolean v) { + if (project.getJavaVersion() != Project.JAVA_1_1) { + useTimestamp = v; + } + } + } diff --git a/src/main/org/apache/tools/ant/taskdefs/Touch.java b/src/main/org/apache/tools/ant/taskdefs/Touch.java index acd3be1e7..1d158be84 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Touch.java +++ b/src/main/org/apache/tools/ant/taskdefs/Touch.java @@ -73,7 +73,7 @@ import java.util.Locale; * created. Setting the modification time of files is not supported in * JDK 1.1. * - * @author Stefan Bodewig stefan.bodewig@megabit.net + * @author Stefan Bodewig */ public class Touch extends Task { @@ -87,8 +87,8 @@ public class Touch extends Task { /** * The name of the file to touch. */ - public void setFile(String name) { - file = project.resolveFile(name); + public void setFile(File file) { + this.file = file; } /** diff --git a/src/main/org/apache/tools/ant/taskdefs/Untar.java b/src/main/org/apache/tools/ant/taskdefs/Untar.java index 8d3d719c0..162231483 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Untar.java +++ b/src/main/org/apache/tools/ant/taskdefs/Untar.java @@ -63,7 +63,7 @@ import java.io.*; * * Heavily based on the Expand task. * - * @author Stefan Bodewig stefan.bodewig@megabit.net + * @author Stefan Bodewig */ public class Untar extends Task { private String dest; // req @@ -78,6 +78,8 @@ public class Untar extends Task { Touch touch = (Touch) project.createTask("touch"); touch.setOwningTarget(target); + touch.setTaskName(getTaskName()); + touch.setLocation(getLocation()); File srcF=project.resolveFile(source); @@ -123,7 +125,7 @@ public class Untar extends Task { } if (project.getJavaVersion() != Project.JAVA_1_1) { - touch.setFile(f.getAbsolutePath()); + touch.setFile(f); touch.setMillis(te.getModTime().getTime()); touch.touch(); }