Hi, The attached patch consolidates the Get and Copy tasks. This lets the Get task user take advantage of the features that Copy provides - like filterset, etc.
The copy task recognizes a valid URL as a legal value to the file attribute. If this patch is committed, perhaps, the Get task can be deprecated? Magesh
Index: Copy.java =================================================================== RCS file: /home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Copy.java,v retrieving revision 1.22 diff -w -u -r1.22 Copy.java --- Copy.java 2001/10/28 21:26:29 1.22 +++ Copy.java 2001/10/29 00:49:44 @@ -70,6 +70,9 @@ import java.io.File; import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; +import java.net.MalformedURLException; import java.util.Vector; import java.util.Hashtable; import java.util.Enumeration; @@ -80,6 +83,8 @@ * than the destination file, or when the destination file does not * exist. It is possible to explicitly overwrite existing files.</p> * + * <p>The file attribute can take as its value a valid URL also.</p> + * * <p>This implementation is based on Arnout Kuiper's initial design * document, the following mailing list discussions, and the * copyfile/copydir tasks.</p> @@ -90,15 +95,20 @@ * @author <a href="mailto:[EMAIL PROTECTED]">Magesh Umasankar</a> */ public class Copy extends Task { + protected String fileStr = null; // the source file string + protected URL url = null; // the source url protected File file = null; // the source file protected File destFile = null; // the destination file protected File destDir = null; // the destination directory protected Vector filesets = new Vector(); + private String username = null; + private String password = null; protected boolean filtering = false; protected boolean preserveLastModified = false; protected boolean forceOverwrite = false; protected boolean flatten = false; + protected boolean isURLAllowed = false; protected int verbosity = Project.MSG_VERBOSE; protected boolean includeEmpty = true; @@ -111,6 +121,7 @@ public Copy() { fileUtils = FileUtils.newFileUtils(); + isURLAllowed = true; } protected FileUtils getFileUtils() {return fileUtils;} @@ -118,8 +129,8 @@ /** * Sets a single source file to copy. */ - public void setFile(File file) { - this.file = file; + public void setFile(String file) { + fileStr = file; } /** @@ -224,6 +235,24 @@ } /** + * Username for basic auth. + * + * @param username username for authentication + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * password for the basic auth. + * + * @param password password for authentication + */ + public void setPassword(String password) { + this.password = password; + } + + /** * Performs the copy operation. */ public void execute() throws BuildException { @@ -250,6 +279,37 @@ log(message); throw new BuildException(message); } + } else if (url != null) { + if (destFile == null) { + String fileName = url.getFile(); + if (fileName.endsWith("/")) { + throw new BuildException( + "Unable to form destination file name. " + + "Use tofile instead."); + } + destFile = new File(destDir, fileName); + if (!destFile.isFile()) { + throw new BuildException( + "Destination file name represents a directory"); + } + } + try { + URLConnection urlconn = url.openConnection(); + if (urlconn != null && + (forceOverwrite || + !destFile.exists() || + urlconn.getLastModified() == 0 || + urlconn.getLastModified() > destFile.lastModified())) { + fileCopyMap.put(url.toString(), destFile.getAbsolutePath()); + } else { + log(url + " omitted as " + destFile + " is up to date.", + Project.MSG_VERBOSE); + } + } catch (IOException ioe) { + String message = ioe.getMessage(); + log(message); + throw new BuildException(message); + } } // deal with the filesets @@ -284,9 +344,24 @@ * of attributes. */ protected void validateAttributes() throws BuildException { + if (fileStr != null) { + try { + this.url = new URL(fileStr); + } catch (MalformedURLException e) { + this.file = project.resolveFile(fileStr); + } + } + if (file == null && filesets.size() == 0) { - throw new BuildException("Specify at least one source - a file or a fileset."); + String message = "Specify at least one source - a file or a fileset."; + if (isURLAllowed) { + if (url == null) { + throw new BuildException(message); } + } else { + throw new BuildException(message); + } + } if (destFile != null && destDir != null) { throw new BuildException("Only one of destfile and destdir may be set."); @@ -327,7 +402,6 @@ if (destFile != null) { destDir = new File(destFile.getParent()); // be 1.1 friendly } - } /** @@ -407,7 +481,8 @@ executionFilters.addFilterSet((FilterSet)filterEnum.nextElement()); } fileUtils.copyFile(fromFile, toFile, executionFilters, - forceOverwrite, preserveLastModified); + forceOverwrite, preserveLastModified, + username, password); } catch (IOException ioe) { String msg = "Failed to copy " + fromFile + " to " + toFile + " due to " + ioe.getMessage(); @@ -438,5 +513,4 @@ } } } - } cvs diff -w -u Move.java Index: Move.java =================================================================== RCS file: /home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Move.java,v retrieving revision 1.9 diff -w -u -r1.9 Move.java --- Move.java 2001/10/28 21:26:29 1.9 +++ Move.java 2001/10/29 00:52:29 @@ -87,6 +87,7 @@ public Move() { super(); forceOverwrite = true; + isURLAllowed = false; } //************************************************************************ Index: FileUtils.java =================================================================== RCS file: /home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/util/FileUtils.java,v retrieving revision 1.7 diff -w -u -r1.7 FileUtils.java --- FileUtils.java 2001/10/28 21:27:20 1.7 +++ FileUtils.java 2001/10/29 00:48:43 @@ -60,9 +60,17 @@ import java.io.FileReader; import java.io.BufferedWriter; import java.io.FileWriter; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLConnection; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.util.StringTokenizer; import java.util.Stack; @@ -74,17 +82,18 @@ * This class also encapsulates methods which allow Files to be * refered to using abstract path names which are translated to native * system file paths at runtime as well as copying files or setting - * there last modification time. + * their last modification time. * * @author [EMAIL PROTECTED] * @author <a href="mailto:[EMAIL PROTECTED]">Conor MacNeill</a> * @author <a href="mailto:[EMAIL PROTECTED]">Stefan Bodewig</a> + * @author <a href="mailto:[EMAIL PROTECTED]">Magesh Umasankar</a> */ public class FileUtils { private static Object lockReflection = new Object(); private static java.lang.reflect.Method setLastModified = null; - + private static final int BUFFER_SIZE = 64512; //63 * 1024 /** * Factory method. */ @@ -144,9 +153,32 @@ public void copyFile(String sourceFile, String destFile, FilterSetCollection filters, boolean overwrite, boolean preserveLastModified) throws IOException { + copyFile(sourceFile, destFile, filters, overwrite, + preserveLastModified, null, null); + } + + /** + * Convienence method to copy a file from a source to a + * destination specifying if token filtering must be used, if + * source files may overwrite newer destination files and the + * last modified time of <code>destFile</code> file should be made equal + * to the last modified time of <code>sourceFile</code>. + * + * @throws IOException + */ + public void copyFile(String sourceFile, String destFile, FilterSetCollection filters, + boolean overwrite, boolean preserveLastModified, + String username, String password) + throws IOException { + try { + URL srcUrl = new URL(sourceFile); + copyURL(srcUrl, new File(destFile), filters, + overwrite, preserveLastModified, username, password); + } catch (MalformedURLException e) { copyFile(new File(sourceFile), new File(destFile), filters, overwrite, preserveLastModified); } + } /** * Convienence method to copy a file from a source to a destination. @@ -196,23 +228,101 @@ if (overwrite || !destFile.exists() || destFile.lastModified() < sourceFile.lastModified()) { - if (destFile.exists() && destFile.isFile()) { destFile.delete(); } + // ensure that parent dir of dest file exists! + // not using getParentFile method to stay 1.1 compat + File parent = new File(destFile.getParent()); + if (!parent.exists()) { + parent.mkdirs(); + } + FileInputStream sourceStream = new FileInputStream(sourceFile); + FileOutputStream destStream = new FileOutputStream(destFile); + copyStream(sourceStream, destStream, filters, sourceFile.length()); + if (preserveLastModified) { + setFileLastModified(destFile, sourceFile.lastModified()); + } + sourceStream.close(); + destStream.close(); + } + } + + public void copyURL(URL sourceURL, File destFile, + FilterSetCollection filters, boolean overwrite, + boolean preserveLastModified, String username, + String password) + throws IOException { + URLConnection conn; + conn = sourceURL.openConnection(); + conn.setDoInput(true); + conn.setDoOutput(false); + conn.setUseCaches(false); + conn.setAllowUserInteraction(false); + if (username != null || password != null) { + String up = username + ":" + password; + String encoding; + // check to see if sun's Base64 encoder is available. + try { + sun.misc.BASE64Encoder encoder = (sun.misc.BASE64Encoder) + Class.forName("sun.misc.BASE64Encoder").newInstance(); + encoding = encoder.encode(up.getBytes()); + } catch (Exception ex) { // sun's base64 encoder isn't available + Base64Converter encoder = new Base64Converter(); + encoding = encoder.encode(up.getBytes()); + } + conn.setRequestProperty ("Authorization", "Basic " + encoding); + } + conn.connect(); + if(conn instanceof HttpURLConnection) { + HttpURLConnection httpConnection=(HttpURLConnection) conn; + // test for 401 result (HTTP only) + if(httpConnection.getResponseCode() == + HttpURLConnection.HTTP_UNAUTHORIZED) { + throw new IOException("Not authorized."); + } + } + long length = conn.getContentLength(); + long lastModified = conn.getLastModified(); + if (overwrite || !destFile.exists() || + lastModified == 0 || + destFile.lastModified() < lastModified) { + if (destFile.exists() && destFile.isFile()) { + destFile.delete(); + } // ensure that parent dir of dest file exists! // not using getParentFile method to stay 1.1 compat File parent = new File(destFile.getParent()); if (!parent.exists()) { parent.mkdirs(); } + InputStream sourceStream = conn.getInputStream(); + FileOutputStream destStream = new FileOutputStream(destFile); + copyStream(sourceStream, destStream, filters, length); + if (preserveLastModified && lastModified != 0) { + setFileLastModified(destFile, lastModified); + } + sourceStream.close(); + destStream.close(); + } + } + public void copyStream(InputStream sourceStream, OutputStream destStream, + FilterSetCollection filters) + throws IOException { + copyStream(sourceStream, destStream, filters, -1); + } + + public void copyStream(InputStream sourceStream, OutputStream destStream, + FilterSetCollection filters, long length) + throws IOException { if (filters != null && filters.hasFilters()) { - BufferedReader in = new BufferedReader(new FileReader(sourceFile)); - BufferedWriter out = new BufferedWriter(new FileWriter(destFile)); + BufferedReader in = new BufferedReader( + new InputStreamReader(sourceStream)); + BufferedWriter out = new BufferedWriter( + new OutputStreamWriter(destStream)); - int length; String newline = null; String line = in.readLine(); while (line != null) { @@ -225,28 +335,62 @@ } line = in.readLine(); } - out.close(); in.close(); } else { - FileInputStream in = new FileInputStream(sourceFile); - FileOutputStream out = new FileOutputStream(destFile); - - byte[] buffer = new byte[8 * 1024]; - int count = 0; - do { - out.write(buffer, 0, count); - count = in.read(buffer, 0, buffer.length); - } while (count != -1); - - in.close(); - out.close(); + byte[] buffer = null; + if (length <= 0) { + int chunkSize = BUFFER_SIZE; + buffer = new byte[chunkSize]; + while (true) { + int bytesRead = readChunk(sourceStream, buffer, 0, + chunkSize); + if (bytesRead > 0) { + destStream.write(buffer, 0, bytesRead); + } else { + break; } + } + } else { + int chunkSize = (int) Math.min(BUFFER_SIZE, length); + long chunks = length/chunkSize; + int lastChunkSize = (int) (length%chunkSize); + buffer = new byte[chunkSize]; + for (long chunkNo = 0; chunkNo < chunks; chunkNo++) { + int bytesRead = readChunk(sourceStream, buffer, 0, + chunkSize); + if (bytesRead != chunkSize) { + throw new IOException( + "Unable to read expected number of bytes"); + } + destStream.write(buffer); + } + if (lastChunkSize > 0) { + int bytesRead = readChunk(sourceStream, buffer, 0, + lastChunkSize); + if (bytesRead != lastChunkSize) { + throw new IOException( + "Unable to read expected number of bytes"); + } + destStream.write(buffer, 0, lastChunkSize); + } + } + } + } - if (preserveLastModified) { - setFileLastModified(destFile, sourceFile.lastModified()); + protected int readChunk(InputStream sourceStream, byte[] buffer, + int offset, int length) throws IOException { + int totalBytesRead = 0; + while (totalBytesRead < length) { + int bytesRead = sourceStream.read(buffer, + offset + totalBytesRead, + length - totalBytesRead); + if (bytesRead < 0) { + break; } + totalBytesRead += bytesRead; } + return totalBytesRead; } /** @@ -466,6 +610,76 @@ path = path.replace('/', '\\'); } return new File(path); + } + + class Base64Converter { + + public final char [ ] alphabet = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0 to 7 + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8 to 15 + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16 to 23 + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24 to 31 + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32 to 39 + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40 to 47 + 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48 to 55 + '4', '5', '6', '7', '8', '9', '+', '/' }; // 56 to 63 + + public String encode ( String s ) { + return encode ( s.getBytes ( ) ); + } + + public String encode ( byte [ ] octetString ) { + int bits24; + int bits6; + + char [ ] out + = new char [ ( ( octetString.length - 1 ) / 3 + 1 ) * 4 ]; + + int outIndex = 0; + int i = 0; + + while ( ( i + 3 ) <= octetString.length ) { + // store the octets + bits24=( octetString [ i++ ] & 0xFF ) << 16; + bits24 |=( octetString [ i++ ] & 0xFF ) << 8; + + bits6=( bits24 & 0x00FC0000 )>> 18; + out [ outIndex++ ] = alphabet [ bits6 ]; + bits6 = ( bits24 & 0x0003F000 ) >> 12; + out [ outIndex++ ] = alphabet [ bits6 ]; + bits6 = ( bits24 & 0x00000FC0 ) >> 6; + out [ outIndex++ ] = alphabet [ bits6 ]; + bits6 = ( bits24 & 0x0000003F ); + out [ outIndex++ ] = alphabet [ bits6 ]; + } + + if ( octetString.length - i == 2 ) { + // store the octets + bits24 = ( octetString [ i ] & 0xFF ) << 16; + bits24 |=( octetString [ i + 1 ] & 0xFF ) << 8; + bits6=( bits24 & 0x00FC0000 )>> 18; + out [ outIndex++ ] = alphabet [ bits6 ]; + bits6 = ( bits24 & 0x0003F000 ) >> 12; + out [ outIndex++ ] = alphabet [ bits6 ]; + bits6 = ( bits24 & 0x00000FC0 ) >> 6; + out [ outIndex++ ] = alphabet [ bits6 ]; + + // padding + out [ outIndex++ ] = '='; + } else if ( octetString.length - i == 1 ) { + // store the octets + bits24 = ( octetString [ i ] & 0xFF ) << 16; + bits6=( bits24 & 0x00FC0000 )>> 18; + out [ outIndex++ ] = alphabet [ bits6 ]; + bits6 = ( bits24 & 0x0003F000 ) >> 12; + out [ outIndex++ ] = alphabet [ bits6 ]; + + // padding + out [ outIndex++ ] = '='; + out [ outIndex++ ] = '='; + } + return new String ( out ); + } } } Index: copy.html =================================================================== RCS file: /home/cvspublic/jakarta-ant/docs/manual/CoreTasks/copy.html,v retrieving revision 1.4 diff -w -u -r1.4 copy.html --- copy.html 2001/10/12 08:19:40 1.4 +++ copy.html 2001/10/29 00:47:25 @@ -24,7 +24,7 @@ </tr> <tr> <td valign="top">file</td> - <td valign="top">The file to copy.</td> + <td valign="top">The file to copy (URLs allowed).</td> <td valign="top" align="center">One of either <var>file</var> or at least one nested fileset element.</td> </tr> @@ -74,6 +74,16 @@ Defaults to "yes".</td> <td valign="top" align="center">No</td> </tr> + <tr> + <td valign="top">username</td> + <td valign="top">Username for basic authentication. Used if file attribute is a URL.</td> + <td valign="top" align="center">No</td> + </tr> + <tr> + <td valign="top">password</td> + <td valign="top">Password for basic authentication. Used if file attribute is a URL.</td> + <td valign="top" align="center">No</td> + </tr> </table> <h3>Parameters specified as nested elements</h3> @@ -97,6 +107,10 @@ <pre> <copy file="myfile.txt" tofile="mycopy.txt"/> </pre> +<p><b>Copy a single file using a url</b></p> +<pre> + <copy file="ftp://ftp.keystealth.org/pub/gnu/README" tofile="README.txt"/> +</pre> <p><b>Copy a file to a directory</b></p> <pre> <copy file="myfile.txt" todir="../some/dir/tree"/> @@ -135,6 +149,15 @@ <fileset dir="src_dir" /> <filterset> <filter token="TITLE" value="Foo Bar" /> + </filterset> + </copy> +</pre> + +<p><b>Copy a single file using a http url</b></p> +<pre> + <copy file="http://jakarta.apache.org/index.html" todir="/tmp"> + <filterset begintoken="#" endtoken="6"> + <filter token="525D7" value="#CCCCCC" /> </filterset> </copy> </pre>
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>