KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > Get


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18
19 package org.apache.tools.ant.taskdefs;
20
21 import org.apache.tools.ant.BuildException;
22 import org.apache.tools.ant.Project;
23 import org.apache.tools.ant.Task;
24 import org.apache.tools.ant.util.FileUtils;
25
26 import java.io.File JavaDoc;
27 import java.io.FileOutputStream JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.InputStream JavaDoc;
30 import java.io.PrintStream JavaDoc;
31 import java.net.HttpURLConnection JavaDoc;
32 import java.net.URL JavaDoc;
33 import java.net.URLConnection JavaDoc;
34 import java.util.Date JavaDoc;
35
36 /**
37  * Gets a particular file from a URL source.
38  * Options include verbose reporting, timestamp based fetches and controlling
39  * actions on failures. NB: access through a firewall only works if the whole
40  * Java runtime is correctly configured.
41  *
42  * @since Ant 1.1
43  *
44  * @ant.task category="network"
45  */

46 public class Get extends Task {
47
48     private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
49
50     private URL JavaDoc source; // required
51
private File JavaDoc dest; // required
52
private boolean verbose = false;
53     private boolean useTimestamp = false; //off by default
54
private boolean ignoreErrors = false;
55     private String JavaDoc uname = null;
56     private String JavaDoc pword = null;
57
58
59
60     /**
61      * Does the work.
62      *
63      * @exception BuildException Thrown in unrecoverable error.
64      */

65     public void execute() throws BuildException {
66
67         //set up logging
68
int logLevel = Project.MSG_INFO;
69         DownloadProgress progress = null;
70         if (verbose) {
71             progress = new VerboseProgress(System.out);
72         }
73
74         //execute the get
75
try {
76             doGet(logLevel, progress);
77         } catch (IOException JavaDoc ioe) {
78             log("Error getting " + source + " to " + dest);
79             if (!ignoreErrors) {
80                 throw new BuildException(ioe, getLocation());
81             }
82         }
83     }
84
85     /**
86      * make a get request, with the supplied progress and logging info.
87      * All the other config parameters are set at the task level,
88      * source, dest, ignoreErrors, etc.
89      * @param logLevel level to log at, see {@link Project#log(String, int)}
90      * @param progress progress callback; null for no-callbacks
91      * @return true for a successful download, false otherwise.
92      * The return value is only relevant when {@link #ignoreErrors} is true, as
93      * when false all failures raise BuildExceptions.
94      * @throws IOException for network trouble
95      * @throws BuildException for argument errors, or other trouble when ignoreErrors
96      * is false.
97      */

98     public boolean doGet(int logLevel, DownloadProgress progress)
99             throws IOException JavaDoc {
100         if (source == null) {
101             throw new BuildException("src attribute is required", getLocation());
102         }
103
104         if (dest == null) {
105             throw new BuildException("dest attribute is required", getLocation());
106         }
107
108         if (dest.exists() && dest.isDirectory()) {
109             throw new BuildException("The specified destination is a directory",
110                     getLocation());
111         }
112
113         if (dest.exists() && !dest.canWrite()) {
114             throw new BuildException("Can't write to " + dest.getAbsolutePath(),
115                     getLocation());
116         }
117         //dont do any progress, unless asked
118
if (progress == null) {
119             progress = new NullProgress();
120         }
121         log("Getting: " + source, logLevel);
122         log("To: " + dest.getAbsolutePath(), logLevel);
123
124         //set the timestamp to the file date.
125
long timestamp = 0;
126
127         boolean hasTimestamp = false;
128         if (useTimestamp && dest.exists()) {
129             timestamp = dest.lastModified();
130             if (verbose) {
131                 Date JavaDoc t = new Date JavaDoc(timestamp);
132                 log("local file date : " + t.toString(), logLevel);
133             }
134             hasTimestamp = true;
135         }
136
137         //set up the URL connection
138
URLConnection JavaDoc connection = source.openConnection();
139         //modify the headers
140
//NB: things like user authentication could go in here too.
141
if (hasTimestamp) {
142             connection.setIfModifiedSince(timestamp);
143         }
144         // prepare Java 1.1 style credentials
145
if (uname != null || pword != null) {
146             String JavaDoc up = uname + ":" + pword;
147             String JavaDoc encoding;
148             //we do not use the sun impl for portability,
149
//and always use our own implementation for consistent
150
//testing
151
Base64Converter encoder = new Base64Converter();
152             encoding = encoder.encode(up.getBytes());
153             connection.setRequestProperty ("Authorization",
154                     "Basic " + encoding);
155         }
156
157         //connect to the remote site (may take some time)
158
connection.connect();
159         //next test for a 304 result (HTTP only)
160
if (connection instanceof HttpURLConnection JavaDoc) {
161             HttpURLConnection JavaDoc httpConnection
162                     = (HttpURLConnection JavaDoc) connection;
163             long lastModified = httpConnection.getLastModified();
164             if (httpConnection.getResponseCode()
165                     == HttpURLConnection.HTTP_NOT_MODIFIED
166                 || (lastModified != 0 && hasTimestamp
167                 && timestamp >= lastModified)) {
168                 //not modified so no file download. just return
169
//instead and trace out something so the user
170
//doesn't think that the download happened when it
171
//didn't
172
log("Not modified - so not downloaded", logLevel);
173                 return false;
174             }
175             // test for 401 result (HTTP only)
176
if (httpConnection.getResponseCode()
177                     == HttpURLConnection.HTTP_UNAUTHORIZED) {
178                 String JavaDoc message = "HTTP Authorization failure";
179                 if (ignoreErrors) {
180                     log(message, logLevel);
181                     return false;
182                 } else {
183                     throw new BuildException(message);
184                 }
185             }
186
187         }
188
189         //REVISIT: at this point even non HTTP connections may
190
//support the if-modified-since behaviour -we just check
191
//the date of the content and skip the write if it is not
192
//newer. Some protocols (FTP) don't include dates, of
193
//course.
194

195         InputStream JavaDoc is = null;
196         for (int i = 0; i < 3; i++) {
197             //this three attempt trick is to get round quirks in different
198
//Java implementations. Some of them take a few goes to bind
199
//property; we ignore the first couple of such failures.
200
try {
201                 is = connection.getInputStream();
202                 break;
203             } catch (IOException JavaDoc ex) {
204                 log("Error opening connection " + ex, logLevel);
205             }
206         }
207         if (is == null) {
208             log("Can't get " + source + " to " + dest, logLevel);
209             if (ignoreErrors) {
210                 return false;
211             }
212             throw new BuildException("Can't get " + source + " to " + dest,
213                     getLocation());
214         }
215
216         FileOutputStream JavaDoc fos = new FileOutputStream JavaDoc(dest);
217         progress.beginDownload();
218         boolean finished = false;
219         try {
220             byte[] buffer = new byte[100 * 1024];
221             int length;
222             while ((length = is.read(buffer)) >= 0) {
223                 fos.write(buffer, 0, length);
224                 progress.onTick();
225             }
226             finished = true;
227         } finally {
228             FileUtils.close(fos);
229             FileUtils.close(is);
230
231             // we have started to (over)write dest, but failed.
232
// Try to delete the garbage we'd otherwise leave
233
// behind.
234
if (!finished) {
235                 dest.delete();
236             }
237         }
238         progress.endDownload();
239
240         //if (and only if) the use file time option is set, then
241
//the saved file now has its timestamp set to that of the
242
//downloaded file
243
if (useTimestamp) {
244             long remoteTimestamp = connection.getLastModified();
245             if (verbose) {
246                 Date JavaDoc t = new Date JavaDoc(remoteTimestamp);
247                 log("last modified = " + t.toString()
248                         + ((remoteTimestamp == 0)
249                         ? " - using current time instead"
250                         : ""), logLevel);
251             }
252             if (remoteTimestamp != 0) {
253                 FILE_UTILS.setFileLastModified(dest, remoteTimestamp);
254             }
255         }
256
257         //successful download
258
return true;
259     }
260
261
262     /**
263      * Set the URL to get.
264      *
265      * @param u URL for the file.
266      */

267     public void setSrc(URL JavaDoc u) {
268         this.source = u;
269     }
270
271     /**
272      * Where to copy the source file.
273      *
274      * @param dest Path to file.
275      */

276     public void setDest(File JavaDoc dest) {
277         this.dest = dest;
278     }
279
280     /**
281      * If true, show verbose progress information.
282      *
283      * @param v if "true" then be verbose
284      */

285     public void setVerbose(boolean v) {
286         verbose = v;
287     }
288
289     /**
290      * If true, log errors but do not treat as fatal.
291      *
292      * @param v if "true" then don't report download errors up to ant
293      */

294     public void setIgnoreErrors(boolean v) {
295         ignoreErrors = v;
296     }
297
298     /**
299      * If true, conditionally download a file based on the timestamp
300      * of the local copy.
301      *
302      * <p>In this situation, the if-modified-since header is set so
303      * that the file is only fetched if it is newer than the local
304      * file (or there is no local file) This flag is only valid on
305      * HTTP connections, it is ignored in other cases. When the flag
306      * is set, the local copy of the downloaded file will also have
307      * its timestamp set to the remote file time.</p>
308      *
309      * <p>Note that remote files of date 1/1/1970 (GMT) are treated as
310      * 'no timestamp', and web servers often serve files with a
311      * timestamp in the future by replacing their timestamp with that
312      * of the current time. Also, inter-computer clock differences can
313      * cause no end of grief.</p>
314      * @param v "true" to enable file time fetching
315      */

316     public void setUseTimestamp(boolean v) {
317         useTimestamp = v;
318     }
319
320
321     /**
322      * Username for basic auth.
323      *
324      * @param u username for authentication
325      */

326     public void setUsername(String JavaDoc u) {
327         this.uname = u;
328     }
329
330     /**
331      * password for the basic authentication.
332      *
333      * @param p password for authentication
334      */

335     public void setPassword(String JavaDoc p) {
336         this.pword = p;
337     }
338
339     /**
340      * Provide this for Backward Compatibility.
341      */

342     protected static class Base64Converter
343         extends org.apache.tools.ant.util.Base64Converter {
344     }
345
346     /**
347      * Interface implemented for reporting
348      * progess of downloading.
349      */

350     public interface DownloadProgress {
351         /**
352          * begin a download
353          */

354         void beginDownload();
355
356         /**
357          * tick handler
358          *
359          */

360         void onTick();
361
362         /**
363          * end a download
364          */

365         void endDownload();
366     }
367
368     /**
369      * do nothing with progress info
370      */

371     public static class NullProgress implements DownloadProgress {
372
373         /**
374          * begin a download
375          */

376         public void beginDownload() {
377
378         }
379
380         /**
381          * tick handler
382          *
383          */

384         public void onTick() {
385         }
386
387         /**
388          * end a download
389          */

390         public void endDownload() {
391
392         }
393     }
394
395     /**
396      * verbose progress system prints to some output stream
397      */

398     public static class VerboseProgress implements DownloadProgress {
399         private int dots = 0;
400         // CheckStyle:VisibilityModifier OFF - bc
401
PrintStream JavaDoc out;
402         // CheckStyle:VisibilityModifier ON
403

404         /**
405          * Construct a verbose progress reporter.
406          * @param out the output stream.
407          */

408         public VerboseProgress(PrintStream JavaDoc out) {
409             this.out = out;
410         }
411
412         /**
413          * begin a download
414          */

415         public void beginDownload() {
416             dots = 0;
417         }
418
419         /**
420          * tick handler
421          *
422          */

423         public void onTick() {
424             out.print(".");
425             if (dots++ > 50) {
426                 out.flush();
427                 dots = 0;
428             }
429         }
430
431         /**
432          * end a download
433          */

434         public void endDownload() {
435             out.println();
436             out.flush();
437         }
438     }
439
440 }
441
Popular Tags